@rubixstudios/payload-typesense 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +182 -0
  3. package/dist/components/HeadlessSearchInput.d.ts +86 -0
  4. package/dist/components/HeadlessSearchInput.d.ts.map +1 -0
  5. package/dist/components/HeadlessSearchInput.js +602 -0
  6. package/dist/components/ThemeProvider.d.ts +10 -0
  7. package/dist/components/ThemeProvider.d.ts.map +1 -0
  8. package/dist/components/ThemeProvider.js +17 -0
  9. package/dist/components/index.d.ts +6 -0
  10. package/dist/components/index.d.ts.map +1 -0
  11. package/dist/components/index.js +3 -0
  12. package/dist/components/themes/hooks.d.ts +52 -0
  13. package/dist/components/themes/hooks.d.ts.map +1 -0
  14. package/dist/components/themes/hooks.js +177 -0
  15. package/dist/components/themes/index.d.ts +5 -0
  16. package/dist/components/themes/index.d.ts.map +1 -0
  17. package/dist/components/themes/index.js +4 -0
  18. package/dist/components/themes/themes.d.ts +6 -0
  19. package/dist/components/themes/themes.d.ts.map +1 -0
  20. package/dist/components/themes/themes.js +156 -0
  21. package/dist/components/themes/types.d.ts +147 -0
  22. package/dist/components/themes/types.d.ts.map +1 -0
  23. package/dist/components/themes/types.js +1 -0
  24. package/dist/components/themes/utils.d.ts +30 -0
  25. package/dist/components/themes/utils.d.ts.map +1 -0
  26. package/dist/components/themes/utils.js +397 -0
  27. package/dist/endpoints/customEndpointHandler.d.ts +3 -0
  28. package/dist/endpoints/customEndpointHandler.d.ts.map +1 -0
  29. package/dist/endpoints/customEndpointHandler.js +5 -0
  30. package/dist/endpoints/health.d.ts +12 -0
  31. package/dist/endpoints/health.d.ts.map +1 -0
  32. package/dist/endpoints/health.js +174 -0
  33. package/dist/endpoints/search.d.ts +13 -0
  34. package/dist/endpoints/search.d.ts.map +1 -0
  35. package/dist/endpoints/search.js +375 -0
  36. package/dist/index.d.ts +39 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +148 -0
  39. package/dist/lib/cache.d.ts +41 -0
  40. package/dist/lib/cache.d.ts.map +1 -0
  41. package/dist/lib/cache.js +96 -0
  42. package/dist/lib/config-validation.d.ts +75 -0
  43. package/dist/lib/config-validation.d.ts.map +1 -0
  44. package/dist/lib/config-validation.js +174 -0
  45. package/dist/lib/hooks.d.ts +4 -0
  46. package/dist/lib/hooks.d.ts.map +1 -0
  47. package/dist/lib/hooks.js +54 -0
  48. package/dist/lib/initialization.d.ts +5 -0
  49. package/dist/lib/initialization.d.ts.map +1 -0
  50. package/dist/lib/initialization.js +102 -0
  51. package/dist/lib/schema-mapper.d.ts +14 -0
  52. package/dist/lib/schema-mapper.d.ts.map +1 -0
  53. package/dist/lib/schema-mapper.js +137 -0
  54. package/dist/lib/types.d.ts +183 -0
  55. package/dist/lib/types.d.ts.map +1 -0
  56. package/dist/lib/types.js +2 -0
  57. package/dist/lib/typesense-client.d.ts +5 -0
  58. package/dist/lib/typesense-client.d.ts.map +1 -0
  59. package/dist/lib/typesense-client.js +20 -0
  60. package/package.json +92 -0
@@ -0,0 +1,397 @@
1
+ import { defaultTheme, themes } from './themes.js';
2
+ /**
3
+ * Get a theme by name or return the default theme
4
+ */ export function getTheme(themeName) {
5
+ return themes[themeName] || defaultTheme;
6
+ }
7
+ /**
8
+ * Merge theme configurations with custom overrides
9
+ */ export function mergeThemeConfig(config) {
10
+ const baseTheme = typeof config.theme === 'string' ? getTheme(config.theme) : config.theme;
11
+ return {
12
+ ...baseTheme,
13
+ animations: {
14
+ ...baseTheme.animations,
15
+ ...config.animations
16
+ },
17
+ colors: {
18
+ ...baseTheme.colors,
19
+ ...config.colors
20
+ },
21
+ shadows: {
22
+ ...baseTheme.shadows,
23
+ ...config.shadows
24
+ },
25
+ spacing: {
26
+ ...baseTheme.spacing,
27
+ ...config.spacing
28
+ },
29
+ typography: {
30
+ ...baseTheme.typography,
31
+ ...config.typography
32
+ }
33
+ };
34
+ }
35
+ /**
36
+ * Generate CSS classes from theme configuration
37
+ */ export function generateThemeClasses(theme, config = {}) {
38
+ const enableAnimations = config.enableAnimations !== false;
39
+ const enableShadows = config.enableShadows !== false;
40
+ const enableRoundedCorners = config.enableRoundedCorners !== false;
41
+ // Helper function to create CSS string
42
+ const css = (styles)=>{
43
+ return Object.entries(styles).map(([key, value])=>{
44
+ if (typeof value === 'object' && value !== null) {
45
+ // Handle nested objects like ':hover', '::placeholder'
46
+ const nested = Object.entries(value).map(([nestedKey, nestedValue])=>`${nestedKey.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${nestedValue}`).join('; ');
47
+ return `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: { ${nested} }`;
48
+ }
49
+ return `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value}`;
50
+ }).join('; ');
51
+ };
52
+ // Container styles
53
+ const containerStyles = css({
54
+ margin: '0 auto',
55
+ maxWidth: '600px',
56
+ position: 'relative',
57
+ width: '100%'
58
+ });
59
+ const inputContainerStyles = css({
60
+ position: 'relative',
61
+ width: '100%'
62
+ });
63
+ const resultsContainerStyles = css({
64
+ backgroundColor: theme.colors.resultsBackground,
65
+ border: `1px solid ${theme.colors.resultsBorder}`,
66
+ borderRadius: enableRoundedCorners ? theme.spacing.resultsBorderRadius : '0',
67
+ boxShadow: enableShadows ? theme.shadows.shadowLg : 'none',
68
+ left: '0',
69
+ marginTop: '4px',
70
+ maxHeight: theme.spacing.resultsMaxHeight,
71
+ overflowY: 'auto',
72
+ position: 'absolute',
73
+ right: '0',
74
+ top: '100%',
75
+ zIndex: '1000',
76
+ ...enableAnimations && {
77
+ animation: 'slideDown 0.2s ease-out'
78
+ }
79
+ });
80
+ // Input styles
81
+ const inputStyles = css({
82
+ '::placeholder': {
83
+ color: theme.colors.inputPlaceholder
84
+ },
85
+ ':disabled': {
86
+ backgroundColor: theme.colors.inputBackground,
87
+ cursor: 'not-allowed',
88
+ opacity: '0.6'
89
+ },
90
+ ':focus': {
91
+ borderColor: theme.colors.inputBorderFocus,
92
+ boxShadow: enableShadows ? `${theme.shadows.focusShadow} ${theme.shadows.focusShadowColor}` : 'none'
93
+ },
94
+ backgroundColor: theme.colors.inputBackground,
95
+ border: `2px solid ${theme.colors.inputBorder}`,
96
+ borderRadius: enableRoundedCorners ? theme.spacing.inputBorderRadius : '0',
97
+ boxShadow: 'none',
98
+ color: theme.colors.inputText,
99
+ fontFamily: theme.typography.fontFamily,
100
+ fontSize: theme.spacing.inputFontSize,
101
+ fontWeight: theme.typography.fontWeightNormal,
102
+ lineHeight: theme.typography.lineHeightNormal,
103
+ outline: 'none',
104
+ padding: theme.spacing.inputPadding,
105
+ transition: enableAnimations ? `all ${theme.animations.transitionNormal} ${theme.animations.easeInOut}` : 'none',
106
+ width: '100%'
107
+ });
108
+ // Results styles
109
+ const resultsStyles = css({
110
+ backgroundColor: theme.colors.resultsBackground,
111
+ border: `1px solid ${theme.colors.resultsBorder}`,
112
+ borderRadius: enableRoundedCorners ? theme.spacing.resultsBorderRadius : '0',
113
+ boxShadow: enableShadows ? theme.shadows.shadowLg : 'none',
114
+ left: '0',
115
+ maxHeight: theme.spacing.resultsMaxHeight,
116
+ overflowY: 'auto',
117
+ position: 'absolute',
118
+ right: '0',
119
+ top: '100%',
120
+ zIndex: '1000'
121
+ });
122
+ const resultsHeaderStyles = css({
123
+ alignItems: 'center',
124
+ backgroundColor: theme.colors.headerBackground,
125
+ borderBottom: `1px solid ${theme.colors.headerBorder}`,
126
+ color: theme.colors.headerText,
127
+ display: 'flex',
128
+ fontFamily: theme.typography.fontFamily,
129
+ fontSize: theme.spacing.headerFontSize,
130
+ fontWeight: theme.typography.fontWeightMedium,
131
+ justifyContent: 'space-between',
132
+ padding: theme.spacing.headerPadding
133
+ });
134
+ const resultsListStyles = css({
135
+ padding: '8px 0'
136
+ });
137
+ // Result item styles
138
+ const resultItemStyles = css({
139
+ ':focus': {
140
+ backgroundColor: theme.colors.resultBackgroundFocus,
141
+ boxShadow: enableShadows ? `inset 0 0 0 2px ${theme.colors.inputBorderFocus}` : 'none'
142
+ },
143
+ ':hover': {
144
+ backgroundColor: theme.colors.resultBackgroundHover,
145
+ transform: enableAnimations ? 'translateX(2px)' : 'none'
146
+ },
147
+ ':last-child': {
148
+ borderBottom: 'none'
149
+ },
150
+ backgroundColor: theme.colors.resultBackground,
151
+ borderBottom: `1px solid ${theme.colors.resultBorder}`,
152
+ cursor: 'pointer',
153
+ outline: 'none',
154
+ padding: theme.spacing.itemPadding,
155
+ transition: enableAnimations ? `all ${theme.animations.transitionFast} ${theme.animations.easeInOut}` : 'none'
156
+ });
157
+ // Content styles
158
+ const resultTitleStyles = css({
159
+ color: theme.colors.titleText,
160
+ fontFamily: theme.typography.fontFamily,
161
+ fontSize: theme.typography.fontSizeLg,
162
+ fontWeight: theme.typography.fontWeightSemibold,
163
+ lineHeight: theme.typography.lineHeightTight,
164
+ marginBottom: '8px'
165
+ });
166
+ const resultDescriptionStyles = css({
167
+ color: theme.colors.descriptionText,
168
+ fontFamily: theme.typography.fontFamily,
169
+ fontSize: theme.typography.fontSizeSm,
170
+ lineHeight: theme.typography.lineHeightNormal,
171
+ marginBottom: '8px'
172
+ });
173
+ const resultHighlightStyles = css({
174
+ backgroundColor: theme.colors.highlightBackground,
175
+ borderRadius: enableRoundedCorners ? '6px' : '0',
176
+ color: theme.colors.descriptionText,
177
+ fontFamily: theme.typography.fontFamily,
178
+ fontSize: theme.typography.fontSizeSm,
179
+ lineHeight: theme.typography.lineHeightNormal,
180
+ marginBottom: '8px',
181
+ mark: {
182
+ backgroundColor: theme.colors.highlightBackground,
183
+ borderRadius: enableRoundedCorners ? '3px' : '0',
184
+ color: theme.colors.highlightText,
185
+ fontWeight: theme.typography.fontWeightMedium,
186
+ padding: '2px 4px'
187
+ },
188
+ padding: '8px 12px'
189
+ });
190
+ const resultMetaStyles = css({
191
+ alignItems: 'center',
192
+ color: theme.colors.metaText,
193
+ display: 'flex',
194
+ fontFamily: theme.typography.fontFamily,
195
+ fontSize: theme.typography.fontSizeXs,
196
+ justifyContent: 'space-between',
197
+ marginTop: '8px'
198
+ });
199
+ // Badge styles
200
+ const collectionBadgeStyles = css({
201
+ backgroundColor: theme.colors.collectionBadge,
202
+ borderRadius: enableRoundedCorners ? '12px' : '0',
203
+ color: theme.colors.collectionBadgeText,
204
+ display: 'inline-block',
205
+ fontFamily: theme.typography.fontFamily,
206
+ fontSize: theme.typography.fontSizeXs,
207
+ fontWeight: theme.typography.fontWeightSemibold,
208
+ letterSpacing: '0.5px',
209
+ padding: '4px 8px',
210
+ textTransform: 'uppercase'
211
+ });
212
+ const scoreBadgeStyles = css({
213
+ backgroundColor: theme.colors.scoreBadge,
214
+ borderRadius: enableRoundedCorners ? '4px' : '0',
215
+ color: theme.colors.scoreBadgeText,
216
+ display: 'inline-block',
217
+ fontFamily: theme.typography.fontFamily,
218
+ fontSize: theme.typography.fontSizeXs,
219
+ fontWeight: theme.typography.fontWeightMedium,
220
+ padding: '2px 6px'
221
+ });
222
+ // State styles
223
+ const loadingStyles = css({
224
+ alignItems: 'center',
225
+ color: theme.colors.loadingText,
226
+ display: 'flex',
227
+ fontFamily: theme.typography.fontFamily,
228
+ fontSize: theme.typography.fontSizeSm,
229
+ gap: '12px',
230
+ justifyContent: 'center',
231
+ padding: '24px'
232
+ });
233
+ const loadingSpinnerStyles = css({
234
+ animation: enableAnimations ? `spin 1s linear infinite` : 'none',
235
+ border: `2px solid ${theme.colors.inputBorder}`,
236
+ borderRadius: '50%',
237
+ borderTop: `2px solid ${theme.colors.inputBorderFocus}`,
238
+ height: '20px',
239
+ width: '20px'
240
+ });
241
+ const errorStyles = css({
242
+ alignItems: 'center',
243
+ backgroundColor: theme.colors.errorBackground,
244
+ borderBottom: `1px solid ${theme.colors.resultBorder}`,
245
+ color: theme.colors.errorText,
246
+ display: 'flex',
247
+ fontFamily: theme.typography.fontFamily,
248
+ fontSize: theme.typography.fontSizeSm,
249
+ gap: '8px',
250
+ padding: '16px'
251
+ });
252
+ const noResultsStyles = css({
253
+ color: theme.colors.noResultsText,
254
+ fontFamily: theme.typography.fontFamily,
255
+ fontSize: theme.typography.fontSizeSm,
256
+ padding: '40px 20px',
257
+ textAlign: 'center'
258
+ });
259
+ // Facet styles
260
+ const facetContainerStyles = css({
261
+ backgroundColor: theme.colors.headerBackground,
262
+ borderBottom: `1px solid ${theme.colors.headerBorder}`,
263
+ padding: '16px'
264
+ });
265
+ const facetItemStyles = css({
266
+ ':hover': {
267
+ backgroundColor: theme.colors.facetActiveBackground,
268
+ borderColor: theme.colors.facetActiveBackground,
269
+ color: theme.colors.facetActiveText
270
+ },
271
+ backgroundColor: theme.colors.facetBackground,
272
+ border: `1px solid ${theme.colors.facetBorder}`,
273
+ borderRadius: enableRoundedCorners ? '16px' : '0',
274
+ color: theme.colors.facetText,
275
+ cursor: 'pointer',
276
+ display: 'inline-block',
277
+ fontFamily: theme.typography.fontFamily,
278
+ fontSize: theme.typography.fontSizeXs,
279
+ fontWeight: theme.typography.fontWeightMedium,
280
+ margin: '4px',
281
+ padding: '6px 12px',
282
+ transition: enableAnimations ? `all ${theme.animations.transitionFast} ${theme.animations.easeInOut}` : 'none'
283
+ });
284
+ const facetItemActiveStyles = css({
285
+ backgroundColor: theme.colors.facetActiveBackground,
286
+ borderColor: theme.colors.facetActiveBackground,
287
+ color: theme.colors.facetActiveText
288
+ });
289
+ // Utility styles
290
+ const hiddenStyles = css({
291
+ display: 'none'
292
+ });
293
+ const visibleStyles = css({
294
+ display: 'block'
295
+ });
296
+ const focusableStyles = css({
297
+ ':focus': {
298
+ boxShadow: enableShadows ? `0 0 0 2px ${theme.colors.inputBorderFocus}` : 'none'
299
+ },
300
+ outline: 'none'
301
+ });
302
+ return {
303
+ // Container classes
304
+ container: containerStyles,
305
+ inputContainer: inputContainerStyles,
306
+ resultsContainer: resultsContainerStyles,
307
+ // Input classes
308
+ input: inputStyles,
309
+ inputDisabled: '',
310
+ inputFocus: '',
311
+ // Results classes
312
+ results: resultsStyles,
313
+ resultsHeader: resultsHeaderStyles,
314
+ resultsList: resultsListStyles,
315
+ // Item classes
316
+ resultItem: resultItemStyles,
317
+ resultItemActive: '',
318
+ resultItemFocus: '',
319
+ resultItemHover: '',
320
+ // Content classes
321
+ resultDescription: resultDescriptionStyles,
322
+ resultHighlight: resultHighlightStyles,
323
+ resultMeta: resultMetaStyles,
324
+ resultTitle: resultTitleStyles,
325
+ // Badge classes
326
+ collectionBadge: collectionBadgeStyles,
327
+ scoreBadge: scoreBadgeStyles,
328
+ // State classes
329
+ error: errorStyles,
330
+ loading: loadingStyles,
331
+ loadingSpinner: loadingSpinnerStyles,
332
+ noResults: noResultsStyles,
333
+ // Facet classes
334
+ facetContainer: facetContainerStyles,
335
+ facetItem: facetItemStyles,
336
+ facetItemActive: facetItemActiveStyles,
337
+ // Utility classes
338
+ focusable: focusableStyles,
339
+ hidden: hiddenStyles,
340
+ visible: visibleStyles
341
+ };
342
+ }
343
+ /**
344
+ * Apply theme to a CSS class with optional variant
345
+ */ export function applyTheme(theme, element, variant) {
346
+ const classes = generateThemeClasses(theme);
347
+ if (variant) {
348
+ const variantKey = `${element}${variant.charAt(0).toUpperCase() + variant.slice(1)}`;
349
+ return `${classes[element] || ''} ${classes[variantKey] || ''}`;
350
+ }
351
+ return classes[element] || '';
352
+ }
353
+ /**
354
+ * Check if theme is dark mode
355
+ */ export function isDarkTheme(theme) {
356
+ return theme.name === 'dark' || theme.colors.inputBackground.includes('#1f') || theme.colors.inputBackground.includes('#0f');
357
+ }
358
+ /**
359
+ * Check if theme is light mode
360
+ */ export function isLightTheme(theme) {
361
+ return !isDarkTheme(theme);
362
+ }
363
+ /**
364
+ * Get theme-specific CSS variables
365
+ */ export function getThemeVariables(theme) {
366
+ return {
367
+ '--search-collection-badge': theme.colors.collectionBadge,
368
+ '--search-collection-badge-text': theme.colors.collectionBadgeText,
369
+ '--search-description-text': theme.colors.descriptionText,
370
+ '--search-error-bg': theme.colors.errorBackground,
371
+ '--search-error-text': theme.colors.errorText,
372
+ '--search-facet-active-bg': theme.colors.facetActiveBackground,
373
+ '--search-facet-active-text': theme.colors.facetActiveText,
374
+ '--search-facet-bg': theme.colors.facetBackground,
375
+ '--search-facet-border': theme.colors.facetBorder,
376
+ '--search-facet-text': theme.colors.facetText,
377
+ '--search-header-bg': theme.colors.headerBackground,
378
+ '--search-header-text': theme.colors.headerText,
379
+ '--search-highlight-bg': theme.colors.highlightBackground,
380
+ '--search-highlight-text': theme.colors.highlightText,
381
+ '--search-input-bg': theme.colors.inputBackground,
382
+ '--search-input-border': theme.colors.inputBorder,
383
+ '--search-input-border-focus': theme.colors.inputBorderFocus,
384
+ '--search-input-placeholder': theme.colors.inputPlaceholder,
385
+ '--search-input-text': theme.colors.inputText,
386
+ '--search-loading-text': theme.colors.loadingText,
387
+ '--search-meta-text': theme.colors.metaText,
388
+ '--search-no-results-text': theme.colors.noResultsText,
389
+ '--search-result-bg': theme.colors.resultBackground,
390
+ '--search-result-bg-hover': theme.colors.resultBackgroundHover,
391
+ '--search-results-bg': theme.colors.resultsBackground,
392
+ '--search-results-border': theme.colors.resultsBorder,
393
+ '--search-score-badge': theme.colors.scoreBadge,
394
+ '--search-score-badge-text': theme.colors.scoreBadgeText,
395
+ '--search-title-text': theme.colors.titleText
396
+ };
397
+ }
@@ -0,0 +1,3 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ export declare const customEndpointHandler: PayloadHandler;
3
+ //# sourceMappingURL=customEndpointHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customEndpointHandler.d.ts","sourceRoot":"","sources":["../../src/endpoints/customEndpointHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,eAAO,MAAM,qBAAqB,EAAE,cAEnC,CAAA"}
@@ -0,0 +1,5 @@
1
+ export const customEndpointHandler = ()=>{
2
+ return Response.json({
3
+ message: 'Custom endpoint response'
4
+ });
5
+ };
@@ -0,0 +1,12 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ import type Typesense from 'typesense';
3
+ import type { TypesenseSearchConfig } from '../index.js';
4
+ /**
5
+ * Create health check handler
6
+ */
7
+ export declare const createHealthCheckHandler: (typesenseClient: Typesense.Client, pluginOptions: TypesenseSearchConfig, lastSyncTime?: number) => PayloadHandler;
8
+ /**
9
+ * Create detailed health check handler with more information
10
+ */
11
+ export declare const createDetailedHealthCheckHandler: (typesenseClient: Typesense.Client, pluginOptions: TypesenseSearchConfig, lastSyncTime?: number) => PayloadHandler;
12
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/endpoints/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AAEtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA2CxD;;GAEG;AACH,eAAO,MAAM,wBAAwB,GACnC,iBAAiB,SAAS,CAAC,MAAM,EACjC,eAAe,qBAAqB,EACpC,eAAe,MAAM,KACpB,cA4DF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAC3C,iBAAiB,SAAS,CAAC,MAAM,EACjC,eAAe,qBAAqB,EACpC,eAAe,MAAM,KACpB,cAuFF,CAAA"}
@@ -0,0 +1,174 @@
1
+ import { searchCache } from '../lib/cache.js';
2
+ /**
3
+ * Test Typesense connection
4
+ */ const testTypesenseConnection = async (typesenseClient)=>{
5
+ try {
6
+ const health = await typesenseClient.health.retrieve();
7
+ return health.ok === true;
8
+ } catch (_error) {
9
+ // Handle health check error
10
+ return false;
11
+ }
12
+ };
13
+ /**
14
+ * Get collection information
15
+ */ const getCollectionInfo = async (typesenseClient)=>{
16
+ try {
17
+ const collections = await typesenseClient.collections().retrieve();
18
+ return collections.map((col)=>col.name);
19
+ } catch (_error) {
20
+ // Handle collections retrieval error
21
+ return [];
22
+ }
23
+ };
24
+ /**
25
+ * Get cache statistics
26
+ */ const getCacheStats = ()=>{
27
+ const stats = searchCache.getStats();
28
+ return {
29
+ hitRate: stats.hitRate || 0,
30
+ maxSize: stats.maxSize,
31
+ size: stats.size
32
+ };
33
+ };
34
+ /**
35
+ * Create health check handler
36
+ */ export const createHealthCheckHandler = (typesenseClient, pluginOptions, lastSyncTime)=>{
37
+ return async ()=>{
38
+ try {
39
+ const startTime = Date.now();
40
+ // Test Typesense connection
41
+ const isTypesenseHealthy = await testTypesenseConnection(typesenseClient);
42
+ const typesenseInfo = isTypesenseHealthy ? {
43
+ ok: true,
44
+ version: 'unknown'
45
+ } // Typesense doesn't expose version in health check
46
+ : {
47
+ ok: false
48
+ };
49
+ // Get collection information
50
+ const collections = isTypesenseHealthy ? await getCollectionInfo(typesenseClient) : [];
51
+ // Get cache statistics
52
+ const cacheStats = getCacheStats();
53
+ // Determine overall health status
54
+ const isHealthy = isTypesenseHealthy && collections.length > 0;
55
+ const response = {
56
+ cache: cacheStats,
57
+ collections,
58
+ ...lastSyncTime !== undefined && {
59
+ lastSync: lastSyncTime
60
+ },
61
+ status: isHealthy ? 'healthy' : 'unhealthy',
62
+ typesense: typesenseInfo
63
+ };
64
+ // Add error details if unhealthy
65
+ if (!isHealthy) {
66
+ const errors = [];
67
+ if (!isTypesenseHealthy) {
68
+ errors.push('Typesense connection failed');
69
+ }
70
+ if (collections.length === 0) {
71
+ errors.push('No collections available');
72
+ }
73
+ response.error = errors.join(', ');
74
+ }
75
+ const responseTime = Date.now() - startTime;
76
+ return Response.json({
77
+ ...response,
78
+ responseTime,
79
+ timestamp: new Date().toISOString(),
80
+ version: '1.0.11'
81
+ });
82
+ } catch (_error) {
83
+ // Handle health check error
84
+ const errorResponse = {
85
+ cache: getCacheStats(),
86
+ error: _error instanceof Error ? _error.message : 'Unknown error',
87
+ status: 'unhealthy'
88
+ };
89
+ return Response.json(errorResponse, {
90
+ status: 500
91
+ });
92
+ }
93
+ };
94
+ };
95
+ /**
96
+ * Create detailed health check handler with more information
97
+ */ export const createDetailedHealthCheckHandler = (typesenseClient, pluginOptions, lastSyncTime)=>{
98
+ return async ()=>{
99
+ try {
100
+ const startTime = Date.now();
101
+ // Test Typesense connection
102
+ const isTypesenseHealthy = await testTypesenseConnection(typesenseClient);
103
+ // Get detailed collection information
104
+ let collections = [];
105
+ if (isTypesenseHealthy) {
106
+ try {
107
+ const collectionsData = await typesenseClient.collections().retrieve();
108
+ collections = collectionsData.map((col)=>({
109
+ name: col.name,
110
+ createdAt: col.created_at,
111
+ fields: col.fields?.length || 0,
112
+ numDocuments: col.num_documents
113
+ }));
114
+ } catch (_error) {
115
+ // Handle detailed collection info error
116
+ }
117
+ }
118
+ // Get cache statistics
119
+ const cacheStats = getCacheStats();
120
+ // Get plugin configuration info
121
+ const configInfo = {
122
+ enabledCollections: Object.entries(pluginOptions.collections || {}).filter(([_, config])=>config?.enabled).map(([name, config])=>({
123
+ name,
124
+ displayName: config?.displayName,
125
+ facetFields: config?.facetFields || [],
126
+ searchFields: config?.searchFields || []
127
+ })),
128
+ settings: pluginOptions.settings,
129
+ totalCollections: Object.keys(pluginOptions.collections || {}).length
130
+ };
131
+ // Determine overall health status
132
+ const isHealthy = isTypesenseHealthy && collections.length > 0;
133
+ const response = {
134
+ cache: cacheStats,
135
+ collectionDetails: collections,
136
+ collections: collections.map((col)=>col.name),
137
+ config: configInfo,
138
+ lastSync: lastSyncTime,
139
+ responseTime: Date.now() - startTime,
140
+ status: isHealthy ? 'healthy' : 'unhealthy',
141
+ timestamp: new Date().toISOString(),
142
+ typesense: {
143
+ ok: isTypesenseHealthy,
144
+ version: 'unknown'
145
+ },
146
+ version: '1.0.11'
147
+ };
148
+ // Add error details if unhealthy
149
+ if (!isHealthy) {
150
+ const errors = [];
151
+ if (!isTypesenseHealthy) {
152
+ errors.push('Typesense connection failed');
153
+ }
154
+ if (collections.length === 0) {
155
+ errors.push('No collections available');
156
+ }
157
+ response.error = errors.join(', ');
158
+ }
159
+ return Response.json(response);
160
+ } catch (_error) {
161
+ // Handle detailed health check error
162
+ const errorResponse = {
163
+ cache: getCacheStats(),
164
+ error: _error instanceof Error ? _error.message : 'Unknown error',
165
+ status: 'unhealthy',
166
+ timestamp: new Date().toISOString(),
167
+ version: '1.0.11'
168
+ };
169
+ return Response.json(errorResponse, {
170
+ status: 500
171
+ });
172
+ }
173
+ };
174
+ };
@@ -0,0 +1,13 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ import type Typesense from 'typesense';
3
+ import type { TypesenseSearchConfig } from '../index.js';
4
+ export declare const createSearchEndpoints: (typesenseClient: Typesense.Client, pluginOptions: TypesenseSearchConfig, lastSyncTime?: number) => ({
5
+ handler: PayloadHandler;
6
+ method: "get";
7
+ path: string;
8
+ } | {
9
+ handler: PayloadHandler;
10
+ method: "post";
11
+ path: string;
12
+ })[];
13
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/endpoints/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AAEtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA4HxD,eAAO,MAAM,qBAAqB,GAChC,iBAAiB,SAAS,CAAC,MAAM,EACjC,eAAe,qBAAqB,EACpC,eAAe,MAAM;;;;;;;;IAuCtB,CAAA"}