@rubixstudios/payload-typesense 1.1.0 → 1.1.2
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/README.md +1 -16
- package/dist/components/HeadlessSearchInput.d.ts +2 -92
- package/dist/components/HeadlessSearchInput.d.ts.map +1 -1
- package/dist/components/HeadlessSearchInput.js +23 -140
- package/dist/components/ThemeProvider.d.ts +3 -2
- package/dist/components/ThemeProvider.d.ts.map +1 -1
- package/dist/components/ThemeProvider.js +1 -1
- package/dist/components/index.d.ts +5 -5
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -3
- package/dist/components/themes/hooks.d.ts +5 -5
- package/dist/components/themes/hooks.d.ts.map +1 -1
- package/dist/components/themes/hooks.js +16 -16
- package/dist/components/themes/index.d.ts +4 -4
- package/dist/components/themes/index.js +4 -4
- package/dist/components/themes/themes.d.ts +1 -1
- package/dist/components/themes/themes.d.ts.map +1 -1
- package/dist/components/themes/themes.js +109 -109
- package/dist/components/themes/types.d.ts.map +1 -1
- package/dist/components/themes/utils.d.ts +1 -1
- package/dist/components/themes/utils.d.ts.map +1 -1
- package/dist/components/themes/utils.js +140 -140
- package/dist/endpoints/handler/createAdvancedSearch.d.ts +5 -0
- package/dist/endpoints/handler/createAdvancedSearch.d.ts.map +1 -0
- package/dist/endpoints/handler/createAdvancedSearch.js +40 -0
- package/dist/endpoints/handler/createCollections.d.ts +4 -0
- package/dist/endpoints/handler/createCollections.d.ts.map +1 -0
- package/dist/endpoints/handler/createCollections.js +23 -0
- package/dist/endpoints/handler/createSearch.d.ts +5 -0
- package/dist/endpoints/handler/createSearch.d.ts.map +1 -0
- package/dist/endpoints/handler/createSearch.js +119 -0
- package/dist/endpoints/handler/createSuggest.d.ts +5 -0
- package/dist/endpoints/handler/createSuggest.d.ts.map +1 -0
- package/dist/endpoints/handler/createSuggest.js +50 -0
- package/dist/endpoints/health.d.ts +4 -10
- package/dist/endpoints/health.d.ts.map +1 -1
- package/dist/endpoints/health.js +45 -103
- package/dist/endpoints/search.d.ts +4 -5
- package/dist/endpoints/search.d.ts.map +1 -1
- package/dist/endpoints/search.js +26 -355
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -101
- package/dist/lib/cache.d.ts +6 -27
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +11 -29
- package/dist/lib/client.d.ts +4 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/{typesense-client.js → client.js} +10 -18
- package/dist/lib/headlessSearch.d.ts +89 -0
- package/dist/lib/headlessSearch.d.ts.map +1 -0
- package/dist/lib/headlessSearch.js +2 -0
- package/dist/lib/hooks.d.ts +12 -3
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +37 -37
- package/dist/lib/initialization.d.ts +3 -3
- package/dist/lib/initialization.d.ts.map +1 -1
- package/dist/lib/initialization.js +36 -49
- package/dist/lib/schema-mapper.d.ts +17 -7
- package/dist/lib/schema-mapper.d.ts.map +1 -1
- package/dist/lib/schema-mapper.js +53 -89
- package/dist/lib/{config-validation.d.ts → validation.d.ts} +18 -22
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/{config-validation.js → validation.js} +32 -49
- package/dist/{lib/types.d.ts → types.d.ts} +22 -5
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/buildError.d.ts +2 -0
- package/dist/utils/buildError.d.ts.map +1 -0
- package/dist/utils/buildError.js +10 -0
- package/dist/utils/ensureCollection.d.ts +3 -0
- package/dist/utils/ensureCollection.d.ts.map +1 -0
- package/dist/utils/ensureCollection.js +13 -0
- package/dist/utils/extractText.d.ts +2 -0
- package/dist/utils/extractText.d.ts.map +1 -0
- package/dist/utils/extractText.js +20 -0
- package/dist/utils/getAllCollections.d.ts +9 -0
- package/dist/utils/getAllCollections.d.ts.map +1 -0
- package/dist/utils/getAllCollections.js +84 -0
- package/dist/utils/getCacheStats.d.ts +6 -0
- package/dist/utils/getCacheStats.d.ts.map +1 -0
- package/dist/utils/getCacheStats.js +9 -0
- package/dist/utils/getCollectionInfo.d.ts +3 -0
- package/dist/utils/getCollectionInfo.d.ts.map +1 -0
- package/dist/utils/getCollectionInfo.js +8 -0
- package/dist/utils/keyboard.d.ts +8 -0
- package/dist/utils/keyboard.d.ts.map +1 -0
- package/dist/utils/keyboard.js +41 -0
- package/dist/utils/testConnection.d.ts +3 -0
- package/dist/utils/testConnection.d.ts.map +1 -0
- package/dist/utils/testConnection.js +8 -0
- package/dist/utils/useDebounce.d.ts +2 -0
- package/dist/utils/useDebounce.d.ts.map +1 -0
- package/dist/utils/useDebounce.js +15 -0
- package/dist/utils/useSearch.d.ts +17 -0
- package/dist/utils/useSearch.d.ts.map +1 -0
- package/dist/utils/useSearch.js +78 -0
- package/package.json +14 -11
- package/dist/endpoints/customEndpointHandler.d.ts +0 -3
- package/dist/endpoints/customEndpointHandler.d.ts.map +0 -1
- package/dist/endpoints/customEndpointHandler.js +0 -5
- package/dist/lib/config-validation.d.ts.map +0 -1
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/typesense-client.d.ts +0 -5
- package/dist/lib/typesense-client.d.ts.map +0 -1
- /package/dist/{lib/types.js → types.js} +0 -0
package/dist/endpoints/search.js
CHANGED
|
@@ -1,373 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
// Check cache first
|
|
8
|
-
const cachedResult = searchCache.get(query, "universal", options);
|
|
9
|
-
if (cachedResult) {
|
|
10
|
-
// Return cached result
|
|
11
|
-
return Response.json(cachedResult);
|
|
12
|
-
}
|
|
13
|
-
const enabledCollections = Object.entries(pluginOptions.collections || {}).filter(([_, config])=>config?.enabled);
|
|
14
|
-
// Process enabled collections
|
|
15
|
-
if (enabledCollections.length === 0) {
|
|
16
|
-
return Response.json({
|
|
17
|
-
error: "No collections enabled for search"
|
|
18
|
-
}, {
|
|
19
|
-
status: 400
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
// Search all collections in parallel
|
|
23
|
-
const searchPromises = enabledCollections.map(async ([collectionName, config])=>{
|
|
24
|
-
try {
|
|
25
|
-
const searchParameters = {
|
|
26
|
-
highlight_full_fields: config?.searchFields?.join(",") || "title,content",
|
|
27
|
-
num_typos: 0,
|
|
28
|
-
page: options.page,
|
|
29
|
-
per_page: Math.ceil(options.per_page / enabledCollections.length),
|
|
30
|
-
q: query,
|
|
31
|
-
query_by: config?.searchFields?.join(",") || "title,content",
|
|
32
|
-
snippet_threshold: 30,
|
|
33
|
-
typo_tokens_threshold: 1
|
|
34
|
-
};
|
|
35
|
-
// Search collection
|
|
36
|
-
const results = await typesenseClient.collections(collectionName).documents().search(searchParameters);
|
|
37
|
-
// Add collection metadata to each hit
|
|
38
|
-
return {
|
|
39
|
-
collection: collectionName,
|
|
40
|
-
displayName: config?.displayName || collectionName,
|
|
41
|
-
icon: config?.icon || "📄",
|
|
42
|
-
...results,
|
|
43
|
-
hits: results.hits?.map((hit)=>({
|
|
44
|
-
...hit,
|
|
45
|
-
collection: collectionName,
|
|
46
|
-
displayName: config?.displayName || collectionName,
|
|
47
|
-
icon: config?.icon || "📄"
|
|
48
|
-
})) || []
|
|
49
|
-
};
|
|
50
|
-
} catch (_error) {
|
|
51
|
-
// Handle search error
|
|
52
|
-
return {
|
|
53
|
-
collection: collectionName,
|
|
54
|
-
displayName: config?.displayName || collectionName,
|
|
55
|
-
error: _error instanceof Error ? _error.message : "Unknown error",
|
|
56
|
-
found: 0,
|
|
57
|
-
hits: [],
|
|
58
|
-
icon: config?.icon || "📄"
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
const results = await Promise.all(searchPromises);
|
|
63
|
-
// Combine results
|
|
64
|
-
const combinedHits = results.flatMap((result)=>result.hits || []);
|
|
65
|
-
const totalFound = results.reduce((sum, result)=>sum + (result.found || 0), 0);
|
|
66
|
-
// Sort combined results by relevance (text_match score)
|
|
67
|
-
combinedHits.sort((a, b)=>(b.text_match || 0) - (a.text_match || 0));
|
|
68
|
-
const searchResult = {
|
|
69
|
-
collections: results.map((r)=>({
|
|
70
|
-
collection: r.collection,
|
|
71
|
-
displayName: r.displayName,
|
|
72
|
-
error: r.error,
|
|
73
|
-
found: r.found || 0,
|
|
74
|
-
icon: r.icon
|
|
75
|
-
})),
|
|
76
|
-
found: totalFound,
|
|
77
|
-
hits: combinedHits.slice(0, options.per_page),
|
|
78
|
-
page: options.page,
|
|
79
|
-
request_params: {
|
|
80
|
-
per_page: options.per_page,
|
|
81
|
-
q: query
|
|
82
|
-
},
|
|
83
|
-
search_cutoff: false,
|
|
84
|
-
search_time_ms: 0
|
|
85
|
-
};
|
|
86
|
-
// Cache the result
|
|
87
|
-
searchCache.set(query, searchResult, "universal", options);
|
|
88
|
-
return Response.json(searchResult);
|
|
89
|
-
} catch (error) {
|
|
90
|
-
// Handle universal search error
|
|
91
|
-
return Response.json({
|
|
92
|
-
details: error instanceof Error ? error.message : "Unknown error",
|
|
93
|
-
error: "Universal search failed"
|
|
94
|
-
}, {
|
|
95
|
-
status: 500
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
};
|
|
1
|
+
import { createAdvancedSearch } from './handler/createAdvancedSearch.js';
|
|
2
|
+
import { createCollections } from './handler/createCollections.js';
|
|
3
|
+
import { createSearch } from './handler/createSearch.js';
|
|
4
|
+
import { createSuggest } from './handler/createSuggest.js';
|
|
5
|
+
import { createDetailedHealthCheck, createHealthCheck } from './health.js';
|
|
99
6
|
export const createSearchEndpoints = (typesenseClient, pluginOptions, lastSyncTime)=>{
|
|
100
7
|
return [
|
|
101
8
|
{
|
|
102
|
-
handler:
|
|
103
|
-
method:
|
|
104
|
-
path:
|
|
9
|
+
handler: createCollections(pluginOptions),
|
|
10
|
+
method: 'get',
|
|
11
|
+
path: '/search/collections'
|
|
105
12
|
},
|
|
106
13
|
{
|
|
107
|
-
handler:
|
|
108
|
-
method:
|
|
109
|
-
path:
|
|
14
|
+
handler: createSuggest(typesenseClient, pluginOptions),
|
|
15
|
+
method: 'get',
|
|
16
|
+
path: '/search/:collectionName/suggest'
|
|
110
17
|
},
|
|
111
18
|
{
|
|
112
|
-
handler:
|
|
113
|
-
method:
|
|
114
|
-
path:
|
|
19
|
+
handler: createSearch(typesenseClient, pluginOptions),
|
|
20
|
+
method: 'get',
|
|
21
|
+
path: '/search/:collectionName'
|
|
115
22
|
},
|
|
116
23
|
{
|
|
117
|
-
handler:
|
|
118
|
-
method:
|
|
119
|
-
path:
|
|
24
|
+
handler: createAdvancedSearch(typesenseClient, pluginOptions),
|
|
25
|
+
method: 'post',
|
|
26
|
+
path: '/search/:collectionName'
|
|
120
27
|
},
|
|
121
28
|
{
|
|
122
|
-
handler:
|
|
123
|
-
method:
|
|
124
|
-
path:
|
|
29
|
+
handler: createSearch(typesenseClient, pluginOptions),
|
|
30
|
+
method: 'get',
|
|
31
|
+
path: '/search'
|
|
125
32
|
},
|
|
126
33
|
{
|
|
127
|
-
handler:
|
|
128
|
-
method:
|
|
129
|
-
path:
|
|
34
|
+
handler: createHealthCheck(typesenseClient, pluginOptions, lastSyncTime),
|
|
35
|
+
method: 'get',
|
|
36
|
+
path: '/search/health'
|
|
130
37
|
},
|
|
131
38
|
{
|
|
132
|
-
handler:
|
|
133
|
-
method:
|
|
134
|
-
path:
|
|
39
|
+
handler: createDetailedHealthCheck(typesenseClient, pluginOptions, lastSyncTime),
|
|
40
|
+
method: 'get',
|
|
41
|
+
path: '/search/health/detailed'
|
|
135
42
|
}
|
|
136
43
|
];
|
|
137
44
|
};
|
|
138
|
-
const createSearchHandler = (typesenseClient, pluginOptions)=>{
|
|
139
|
-
return async (request)=>{
|
|
140
|
-
try {
|
|
141
|
-
// Extract query parameters from the request
|
|
142
|
-
const { params, query } = request;
|
|
143
|
-
// Extract collection name from URL path (fallback to params if available)
|
|
144
|
-
let collectionName;
|
|
145
|
-
let collectionNameStr;
|
|
146
|
-
if (request.url && typeof request.url === "string") {
|
|
147
|
-
const url = new URL(request.url);
|
|
148
|
-
const pathParts = url.pathname.split("/");
|
|
149
|
-
const searchIndex = pathParts.indexOf("search");
|
|
150
|
-
if (searchIndex !== -1 && pathParts[searchIndex + 1]) {
|
|
151
|
-
collectionName = pathParts[searchIndex + 1] || "";
|
|
152
|
-
collectionNameStr = String(collectionName);
|
|
153
|
-
} else {
|
|
154
|
-
collectionName = "";
|
|
155
|
-
collectionNameStr = "";
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
// Fallback to params extraction
|
|
159
|
-
const { collectionName: paramCollectionName } = params || {};
|
|
160
|
-
collectionName = String(paramCollectionName || "");
|
|
161
|
-
collectionNameStr = collectionName;
|
|
162
|
-
}
|
|
163
|
-
// Extract search parameters
|
|
164
|
-
const q = typeof query?.q === "string" ? query.q : "";
|
|
165
|
-
const pageParam = query?.page;
|
|
166
|
-
const perPageParam = query?.per_page;
|
|
167
|
-
const page = typeof pageParam === "string" || typeof pageParam === "number" ? parseInt(String(pageParam), 10) : 1;
|
|
168
|
-
const per_page = typeof perPageParam === "string" || typeof perPageParam === "number" ? parseInt(String(perPageParam), 10) : 10;
|
|
169
|
-
const sort_by = query?.sort_by;
|
|
170
|
-
// Validate parsed numbers
|
|
171
|
-
if (isNaN(page) || page < 1) {
|
|
172
|
-
return Response.json({
|
|
173
|
-
error: "Invalid page parameter"
|
|
174
|
-
}, {
|
|
175
|
-
status: 400
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
if (isNaN(per_page) || per_page < 1 || per_page > 250) {
|
|
179
|
-
return Response.json({
|
|
180
|
-
error: "Invalid per_page parameter"
|
|
181
|
-
}, {
|
|
182
|
-
status: 400
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
// Process search request
|
|
186
|
-
// Validate search parameters
|
|
187
|
-
const searchParams = {
|
|
188
|
-
page,
|
|
189
|
-
per_page,
|
|
190
|
-
q,
|
|
191
|
-
sort_by: sort_by
|
|
192
|
-
};
|
|
193
|
-
const validation = validateSearchParams(searchParams);
|
|
194
|
-
if (!validation.success) {
|
|
195
|
-
return Response.json({
|
|
196
|
-
details: getValidationErrors(validation.errors || []),
|
|
197
|
-
error: "Invalid search parameters"
|
|
198
|
-
}, {
|
|
199
|
-
status: 400
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
// If no collection specified, search across all enabled collections
|
|
203
|
-
if (!collectionName) {
|
|
204
|
-
if (!q || q.trim() === "") {
|
|
205
|
-
return Response.json({
|
|
206
|
-
details: "Please provide a search query using ?q=your_search_term",
|
|
207
|
-
error: 'Query parameter "q" is required',
|
|
208
|
-
example: "/api/search?q=example"
|
|
209
|
-
}, {
|
|
210
|
-
status: 400
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
const searchOptions = {
|
|
214
|
-
filters: {},
|
|
215
|
-
page,
|
|
216
|
-
per_page
|
|
217
|
-
};
|
|
218
|
-
if (sort_by && typeof sort_by === "string") {
|
|
219
|
-
searchOptions.sort_by = sort_by;
|
|
220
|
-
}
|
|
221
|
-
return await searchAllCollections(typesenseClient, pluginOptions, q, searchOptions);
|
|
222
|
-
}
|
|
223
|
-
// Validate collection is enabled
|
|
224
|
-
if (!pluginOptions.collections?.[collectionNameStr]?.enabled) {
|
|
225
|
-
return Response.json({
|
|
226
|
-
error: "Collection not enabled for search"
|
|
227
|
-
}, {
|
|
228
|
-
status: 400
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
if (!q) {
|
|
232
|
-
return Response.json({
|
|
233
|
-
error: 'Query parameter "q" is required'
|
|
234
|
-
}, {
|
|
235
|
-
status: 400
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
const searchParameters = {
|
|
239
|
-
highlight_full_fields: pluginOptions.collections?.[collectionNameStr]?.searchFields?.join(",") || "title,content",
|
|
240
|
-
num_typos: 0,
|
|
241
|
-
page: Number(page),
|
|
242
|
-
per_page: Number(per_page),
|
|
243
|
-
q: String(q),
|
|
244
|
-
query_by: pluginOptions.collections?.[collectionNameStr]?.searchFields?.join(",") || "title,content",
|
|
245
|
-
snippet_threshold: 30,
|
|
246
|
-
typo_tokens_threshold: 1
|
|
247
|
-
};
|
|
248
|
-
// Add sorting
|
|
249
|
-
if (sort_by && typeof sort_by === "string") {
|
|
250
|
-
searchParameters.sort_by = sort_by;
|
|
251
|
-
}
|
|
252
|
-
// Execute Typesense search
|
|
253
|
-
// Check cache first
|
|
254
|
-
const cacheOptions = {
|
|
255
|
-
collection: collectionName,
|
|
256
|
-
page,
|
|
257
|
-
per_page,
|
|
258
|
-
sort_by
|
|
259
|
-
};
|
|
260
|
-
const cachedResult = searchCache.get(q, collectionNameStr, cacheOptions);
|
|
261
|
-
if (cachedResult) {
|
|
262
|
-
// Return cached result
|
|
263
|
-
return Response.json(cachedResult);
|
|
264
|
-
}
|
|
265
|
-
const searchResults = await typesenseClient.collections(collectionNameStr).documents().search(searchParameters);
|
|
266
|
-
// Process search results
|
|
267
|
-
// Cache the result
|
|
268
|
-
searchCache.set(q, searchResults, collectionNameStr, cacheOptions);
|
|
269
|
-
return Response.json(searchResults);
|
|
270
|
-
} catch (_error) {
|
|
271
|
-
// Handle search error
|
|
272
|
-
return Response.json({
|
|
273
|
-
details: _error instanceof Error ? _error.message : "Unknown error",
|
|
274
|
-
error: "Search handler failed"
|
|
275
|
-
}, {
|
|
276
|
-
status: 500
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
const createAdvancedSearchHandler = (typesenseClient, pluginOptions)=>{
|
|
282
|
-
return async (request)=>{
|
|
283
|
-
const { params, req } = request;
|
|
284
|
-
const { collectionName } = params || {};
|
|
285
|
-
const collectionNameStr = String(collectionName || "");
|
|
286
|
-
const body = await req?.json?.() || {};
|
|
287
|
-
if (!pluginOptions.collections?.[collectionNameStr]?.enabled) {
|
|
288
|
-
return Response.json({
|
|
289
|
-
error: "Collection not enabled for search"
|
|
290
|
-
}, {
|
|
291
|
-
status: 400
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
try {
|
|
295
|
-
const searchResults = await typesenseClient.collections(collectionNameStr).documents().search(body);
|
|
296
|
-
return Response.json(searchResults);
|
|
297
|
-
} catch (_error) {
|
|
298
|
-
// Handle advanced search error
|
|
299
|
-
return Response.json({
|
|
300
|
-
error: "Advanced search failed"
|
|
301
|
-
}, {
|
|
302
|
-
status: 500
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
};
|
|
307
|
-
const createSuggestHandler = (typesenseClient, pluginOptions)=>{
|
|
308
|
-
return async (request)=>{
|
|
309
|
-
// Extract collection name from URL path
|
|
310
|
-
const url = new URL(request.url);
|
|
311
|
-
const pathParts = url.pathname.split("/");
|
|
312
|
-
const collectionName = pathParts[pathParts.indexOf("search") + 1];
|
|
313
|
-
const collectionNameStr = String(collectionName || "");
|
|
314
|
-
// Extract query parameters
|
|
315
|
-
const q = url.searchParams.get("q");
|
|
316
|
-
const limit = url.searchParams.get("limit") || "5";
|
|
317
|
-
if (!collectionName || !pluginOptions.collections?.[collectionNameStr]?.enabled) {
|
|
318
|
-
return Response.json({
|
|
319
|
-
error: "Collection not enabled for search"
|
|
320
|
-
}, {
|
|
321
|
-
status: 400
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
if (!q) {
|
|
325
|
-
return Response.json({
|
|
326
|
-
error: 'Query parameter "q" is required'
|
|
327
|
-
}, {
|
|
328
|
-
status: 400
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
try {
|
|
332
|
-
const suggestResults = await typesenseClient.collections(collectionNameStr).documents().search({
|
|
333
|
-
highlight_full_fields: pluginOptions.collections?.[collectionNameStr]?.searchFields?.join(",") || "title,content",
|
|
334
|
-
per_page: Number(limit),
|
|
335
|
-
q,
|
|
336
|
-
query_by: pluginOptions.collections?.[collectionNameStr]?.searchFields?.join(",") || "title,content",
|
|
337
|
-
snippet_threshold: 30
|
|
338
|
-
});
|
|
339
|
-
return Response.json(suggestResults);
|
|
340
|
-
} catch (_error) {
|
|
341
|
-
// Handle suggest error
|
|
342
|
-
return Response.json({
|
|
343
|
-
error: "Suggest failed"
|
|
344
|
-
}, {
|
|
345
|
-
status: 500
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
};
|
|
350
|
-
const createCollectionsHandler = (pluginOptions)=>{
|
|
351
|
-
return ()=>{
|
|
352
|
-
try {
|
|
353
|
-
const collections = Object.entries(pluginOptions.collections || {}).filter(([_, config])=>config?.enabled).map(([slug, config])=>({
|
|
354
|
-
slug,
|
|
355
|
-
displayName: config?.displayName || slug.charAt(0).toUpperCase() + slug.slice(1),
|
|
356
|
-
facetFields: config?.facetFields || [],
|
|
357
|
-
icon: config?.icon || "📄",
|
|
358
|
-
searchFields: config?.searchFields || []
|
|
359
|
-
}));
|
|
360
|
-
return Response.json({
|
|
361
|
-
categorized: pluginOptions.settings?.categorized || false,
|
|
362
|
-
collections
|
|
363
|
-
});
|
|
364
|
-
} catch (_error) {
|
|
365
|
-
// Handle collections error
|
|
366
|
-
return Response.json({
|
|
367
|
-
error: "Failed to get collections"
|
|
368
|
-
}, {
|
|
369
|
-
status: 500
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Config } from
|
|
2
|
-
export * from
|
|
1
|
+
import type { Config } from 'payload';
|
|
2
|
+
export * from './components/index.js';
|
|
3
3
|
export type TypesenseSearchConfig = {
|
|
4
4
|
/**
|
|
5
5
|
* Collections to index in Typesense
|
|
@@ -31,7 +31,7 @@ export type TypesenseSearchConfig = {
|
|
|
31
31
|
nodes: Array<{
|
|
32
32
|
host: string;
|
|
33
33
|
port: number | string;
|
|
34
|
-
protocol:
|
|
34
|
+
protocol: 'http' | 'https';
|
|
35
35
|
}>;
|
|
36
36
|
};
|
|
37
37
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAOrC,cAAc,uBAAuB,CAAA;AAErC,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CACnB,MAAM,CACJ,MAAM,EACN;QACE,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,OAAO,EAAE,OAAO,CAAA;QAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;QACtB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;QACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KACtB,CACF,CACF,CAAA;IAED,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;OAEG;IACH,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,WAAW,CAAC,EAAE,OAAO,CAAA;QACrB,cAAc,CAAC,EAAE,MAAM,CAAA;KACxB,CAAA;IAED;;OAEG;IACH,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAA;QACd,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,KAAK,EAAE,KAAK,CAAC;YACX,IAAI,EAAE,MAAM,CAAA;YACZ,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;YACrB,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;SAC3B,CAAC,CAAA;KACH,CAAA;CACF,CAAA;AAED,eAAO,MAAM,eAAe,GACzB,eAAe,qBAAqB,MACpC,QAAQ,MAAM,KAAG,MA6DjB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { createSearchEndpoints } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
export * from
|
|
1
|
+
import { createSearchEndpoints } from './endpoints/search.js';
|
|
2
|
+
import { createClient } from './lib/client.js';
|
|
3
|
+
import { deleteDocumentFromTypesense, syncDocumentToTypesense } from './lib/hooks.js';
|
|
4
|
+
import { initializeTypesenseCollections } from './lib/initialization.js';
|
|
5
|
+
export * from './components/index.js';
|
|
6
6
|
export const typesenseSearch = (pluginOptions)=>(config)=>{
|
|
7
7
|
if (pluginOptions.disabled) {
|
|
8
8
|
return config;
|
|
9
9
|
}
|
|
10
10
|
// Initialize Typesense client
|
|
11
|
-
const typesenseClient =
|
|
11
|
+
const typesenseClient = createClient(pluginOptions.typesense);
|
|
12
12
|
// Add search endpoints
|
|
13
13
|
config.endpoints = [
|
|
14
14
|
...config.endpoints || [],
|
|
@@ -51,98 +51,3 @@ export const typesenseSearch = (pluginOptions)=>(config)=>{
|
|
|
51
51
|
};
|
|
52
52
|
return config;
|
|
53
53
|
};
|
|
54
|
-
// Helper function to create collection if it doesn't exist
|
|
55
|
-
const createCollectionIfNotExists = async (typesenseClient, collectionSlug, config)=>{
|
|
56
|
-
const searchableFields = config?.searchFields || [
|
|
57
|
-
"title",
|
|
58
|
-
"content",
|
|
59
|
-
"description"
|
|
60
|
-
];
|
|
61
|
-
const facetFields = config?.facetFields || [];
|
|
62
|
-
// Base fields that every collection should have
|
|
63
|
-
const baseFields = [
|
|
64
|
-
{
|
|
65
|
-
name: "id",
|
|
66
|
-
type: "string"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "createdAt",
|
|
70
|
-
type: "int64"
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: "updatedAt",
|
|
74
|
-
type: "int64"
|
|
75
|
-
}
|
|
76
|
-
];
|
|
77
|
-
// Map searchable fields
|
|
78
|
-
const searchFields = searchableFields.map((field)=>({
|
|
79
|
-
name: field,
|
|
80
|
-
type: "string",
|
|
81
|
-
facet: facetFields.includes(field)
|
|
82
|
-
}));
|
|
83
|
-
// Map facet-only fields (not in searchable fields)
|
|
84
|
-
const facetOnlyFields = facetFields.filter((field)=>!searchableFields.includes(field)).map((field)=>({
|
|
85
|
-
name: field,
|
|
86
|
-
type: "string",
|
|
87
|
-
facet: true
|
|
88
|
-
}));
|
|
89
|
-
const schema = {
|
|
90
|
-
name: collectionSlug,
|
|
91
|
-
fields: [
|
|
92
|
-
...baseFields,
|
|
93
|
-
...searchFields,
|
|
94
|
-
...facetOnlyFields
|
|
95
|
-
]
|
|
96
|
-
};
|
|
97
|
-
await typesenseClient.collections().create(schema);
|
|
98
|
-
// Collection created successfully
|
|
99
|
-
};
|
|
100
|
-
// Sync functions for hooks
|
|
101
|
-
const syncDocumentToTypesense = async (typesenseClient, collectionSlug, doc, operation, config)=>{
|
|
102
|
-
try {
|
|
103
|
-
// First check if the collection exists, create it if it doesn't
|
|
104
|
-
try {
|
|
105
|
-
await typesenseClient.collections(collectionSlug).retrieve();
|
|
106
|
-
} catch (collectionError) {
|
|
107
|
-
if (collectionError.httpStatus === 404) {
|
|
108
|
-
// Collection not found, creating it
|
|
109
|
-
await createCollectionIfNotExists(typesenseClient, collectionSlug, config);
|
|
110
|
-
} else {
|
|
111
|
-
throw collectionError;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const typesenseDoc = mapPayloadDocumentToTypesense(doc, collectionSlug, config);
|
|
115
|
-
await typesenseClient.collections(collectionSlug).documents().upsert(typesenseDoc);
|
|
116
|
-
// Document synced successfully
|
|
117
|
-
} catch (error) {
|
|
118
|
-
// Handle document sync error
|
|
119
|
-
// Log the problematic document for debugging
|
|
120
|
-
if (error.message.includes("validation")) {
|
|
121
|
-
// Log problematic document details
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
const deleteDocumentFromTypesense = async (typesenseClient, collectionSlug, docId)=>{
|
|
126
|
-
try {
|
|
127
|
-
// First check if the collection exists
|
|
128
|
-
try {
|
|
129
|
-
await typesenseClient.collections(collectionSlug).retrieve();
|
|
130
|
-
} catch (collectionError) {
|
|
131
|
-
if (collectionError.httpStatus === 404) {
|
|
132
|
-
// Collection not found, skipping delete
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
throw collectionError;
|
|
136
|
-
}
|
|
137
|
-
// Try to delete the document
|
|
138
|
-
await typesenseClient.collections(collectionSlug).documents(docId).delete();
|
|
139
|
-
// Document deleted successfully
|
|
140
|
-
} catch (error) {
|
|
141
|
-
// Handle specific error cases
|
|
142
|
-
if (error.httpStatus === 404) {
|
|
143
|
-
// Document not found, already deleted
|
|
144
|
-
} else {
|
|
145
|
-
// Handle document deletion error
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
};
|
package/dist/lib/cache.d.ts
CHANGED
|
@@ -1,41 +1,20 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export declare class SearchCache<T =
|
|
1
|
+
import { type CacheOptions } from '../types.js';
|
|
2
|
+
export declare class SearchCache<T = unknown> {
|
|
3
3
|
private cache;
|
|
4
4
|
private readonly defaultTTL;
|
|
5
5
|
private readonly maxSize;
|
|
6
6
|
constructor(options?: CacheOptions);
|
|
7
|
-
/**
|
|
8
|
-
* Generate cache key from search parameters
|
|
9
|
-
*/
|
|
10
7
|
private generateKey;
|
|
11
|
-
/**
|
|
12
|
-
* Clear expired entries
|
|
13
|
-
*/
|
|
14
8
|
cleanup(): void;
|
|
15
|
-
/**
|
|
16
|
-
* Clear cache entries matching pattern
|
|
17
|
-
*/
|
|
18
9
|
clear(pattern?: string): void;
|
|
19
|
-
|
|
20
|
-
* Get cached search result
|
|
21
|
-
*/
|
|
22
|
-
get(query: string, collection?: string, params?: Record<string, any>): null | T;
|
|
23
|
-
/**
|
|
24
|
-
* Get cache statistics
|
|
25
|
-
*/
|
|
10
|
+
get(query: string, collection?: string, params?: Record<string, unknown>): null | T;
|
|
26
11
|
getStats(): {
|
|
27
12
|
hitRate?: number;
|
|
28
13
|
maxSize: number;
|
|
29
14
|
size: number;
|
|
30
15
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*/
|
|
34
|
-
has(query: string, collection?: string, params?: Record<string, any>): boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Set cached search result
|
|
37
|
-
*/
|
|
38
|
-
set(query: string, data: T, collection?: string, params?: Record<string, any>, ttl?: number): void;
|
|
16
|
+
has(query: string, collection?: string, params?: Record<string, unknown>): boolean;
|
|
17
|
+
set(query: string, data: T, collection?: string, params?: Record<string, unknown>, ttl?: number): void;
|
|
39
18
|
}
|
|
40
|
-
export declare const searchCache: SearchCache<
|
|
19
|
+
export declare const searchCache: SearchCache<unknown>;
|
|
41
20
|
//# sourceMappingURL=cache.d.ts.map
|
package/dist/lib/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAEhE,qBAAa,WAAW,CAAC,CAAC,GAAG,OAAO;IAClC,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;gBAEpB,OAAO,GAAE,YAAiB;IAKtC,OAAO,CAAC,WAAW;IAmBnB,OAAO,IAAI,IAAI;IASf,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAY7B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;IAgBnF,QAAQ,IAAI;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAO/D,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAIlF,GAAG,CACD,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,EACP,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,GAAG,CAAC,EAAE,MAAM,GACX,IAAI;CAgBR;AAED,eAAO,MAAM,WAAW,sBAGtB,CAAA"}
|