rspress-plugin-typesense 0.0.3 → 0.1.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.
package/README.md CHANGED
@@ -108,12 +108,6 @@ export interface TypesensePluginOptions {
108
108
  */
109
109
  indexCodeBlocks?: boolean;
110
110
 
111
- /**
112
- * Whether a failed indexing attempt should crash the build process.
113
- * Defaults to `true`.
114
- */
115
- failOnIndexError?: boolean;
116
-
117
111
  /**
118
112
  * Whether to automatically filter search results by the active documentation version.
119
113
  * Defaults to `true`.
@@ -195,13 +189,6 @@ In the frontend, you could now pass `typesenseSearchParams: { filter_by: 'catego
195
189
 
196
190
  By default, the plugin only indexes headers (`h1-h6`), paragraphs, lists and tables. Enabling this will also extract text from inside code blocks.
197
191
 
198
- ### failOnIndexError
199
-
200
- - **Type**: `boolean`
201
- - **Default**: `true`
202
-
203
- By default, if the Typesense indexing step fails (e.g., network timeout), the build process will crash. Set this to `false` if you want your CI/CD deployments to succeed even if search indexing fails.
204
-
205
192
  ### versionedSearch
206
193
 
207
194
  - **Type**: `boolean`
@@ -265,6 +252,40 @@ import { Search as PluginTypesenseSearch } from 'rspress-plugin-typesense/runtim
265
252
  />;
266
253
  ```
267
254
 
255
+ ## Local development
256
+
257
+ Install dependencies and watch the library for changes:
258
+
259
+ ```bash
260
+ bun install
261
+ bun run dev
262
+ ```
263
+
264
+ In a second terminal, start the example Rspress site:
265
+
266
+ ```bash
267
+ cd docs
268
+ bun install
269
+ bun run dev
270
+ ```
271
+
272
+ If you want to test indexing and search locally, start Typesense as well:
273
+
274
+ ```bash
275
+ docker compose up -d
276
+ ```
277
+
278
+ ### Integration tests
279
+
280
+ Integration tests use Rstest and require a running Typesense instance. Start Typesense and run the suite:
281
+
282
+ ```bash
283
+ docker compose up -d
284
+ bun run test:integration
285
+ ```
286
+
287
+ The suite builds the package, installs the generated bun tarball into a temporary copy of `docs`, builds the Rspress site, and verifies the results from the Typesense Docker instance.
288
+
268
289
  ## License
269
290
 
270
291
  Licensed under the Apache 2.0 License, Copyright © Typesense.
package/dist/index.d.ts CHANGED
@@ -30,11 +30,6 @@ export interface TypesensePluginOptions {
30
30
  * Defaults to `false` to avoid search noise and bloated index sizes.
31
31
  */
32
32
  indexCodeBlocks?: boolean;
33
- /**
34
- * Whether a failed indexing attempt should crash the build process.
35
- * Defaults to `true`.
36
- */
37
- failOnIndexError?: boolean;
38
33
  /**
39
34
  * Whether to automatically filter search results by the active documentation version.
40
35
  * Defaults to `true`.
package/dist/index.js CHANGED
@@ -72,9 +72,8 @@ function pluginTypesense(options) {
72
72
  } catch (error) {
73
73
  console.error(`\n\x1b[31m✖ [TypesensePlugin] Failed to initialize/create collection:\x1b[0m \x1b[37m${collectionNameTmp}\x1b[0m`);
74
74
  console.error(` \x1b[31m↳ ${error instanceof Error ? error.message : error}\x1b[0m\n`);
75
- if (false !== options.failOnIndexError) throw error;
76
- console.warn("\x1b[33m⚠ [TypesensePlugin] Skipping group indexing due to failOnIndexError=false.\x1b[0m");
77
- continue;
75
+ await helper.discardTmpCollection();
76
+ throw error;
78
77
  }
79
78
  let totalRecords = 0;
80
79
  for (const route of routes){
@@ -95,7 +94,8 @@ function pluginTypesense(options) {
95
94
  const fillerLength = Math.max(2, padLength - route.routePath.length);
96
95
  const filler = '\x1b[90m' + '.'.repeat(fillerLength) + '\x1b[0m';
97
96
  console.warn(` \x1b[33m⚠\x1b[0m \x1b[37m${route.routePath}\x1b[0m ${filler} \x1b[33mskipped\x1b[0m \x1b[90m(HTML not found)\x1b[0m`);
98
- continue;
97
+ await helper.discardTmpCollection();
98
+ throw new Error(`HTML not found for route ${route.routePath}`);
99
99
  }
100
100
  try {
101
101
  const htmlContent = fs.readFileSync(htmlPath, 'utf-8');
@@ -110,9 +110,9 @@ function pluginTypesense(options) {
110
110
  if (error instanceof ImportError) {
111
111
  console.error(" \x1b[31m↳ Import error\x1b[0m");
112
112
  console.error(error.importResults);
113
- if (false !== options.failOnIndexError) throw error;
114
- console.warn("\x1b[33m⚠ [TypesensePlugin] Skipping failure due to failOnIndexError=false.\x1b[0m");
115
113
  } else console.error(` \x1b[31m↳ ${error instanceof Error ? error.message : error}\x1b[0m`);
114
+ await helper.discardTmpCollection();
115
+ throw error;
116
116
  }
117
117
  }
118
118
  try {
@@ -121,8 +121,7 @@ function pluginTypesense(options) {
121
121
  } catch (error) {
122
122
  console.error(`\n\x1b[31m✖ [TypesensePlugin] Failed to commit collection:\x1b[0m \x1b[37m${aliasName}\x1b[0m`);
123
123
  console.error(` \x1b[31m↳ ${error instanceof Error ? error.message : error}\x1b[0m\n`);
124
- if (false !== options.failOnIndexError) throw error;
125
- console.warn("\x1b[33m⚠ [TypesensePlugin] Skipping failure due to failOnIndexError=false.\x1b[0m");
124
+ throw error;
126
125
  }
127
126
  }
128
127
  }
@@ -1,76 +1,9 @@
1
- [class*="DocSearch"] {
2
- --docsearch-searchbox-shadow: inset 0 0 0 2px var(--rp-c-brand);
3
- --docsearch-primary-color: var(--rp-c-brand);
4
- --docsearch-text-color: var(--rp-c-text-1);
5
- --docsearch-secondary-text-color: var(--rp-c-text-2);
6
- --docsearch-muted-color: var(--rp-c-text-2);
7
- --docsearch-subtle-color: var(--rp-c-divider-light);
8
- --docsearch-container-background: #3c3c3c66;
9
- --docsearch-modal-background: var(--rp-c-bg);
10
- --docsearch-search-button-background: color-mix(in srgb,
11
- var(--rp-c-bg) 30%,
12
- transparent);
13
- --docsearch-search-button-text-color: var(--rp-c-text-2);
14
- --docsearch-searchbox-background: color-mix(in srgb,
15
- var(--rp-c-bg) 30%,
16
- transparent);
17
- --docsearch-searchbox-focus-background: color-mix(in srgb,
18
- var(--rp-c-bg) 30%,
19
- transparent);
20
- --docsearch-hit-highlight-color: var(--rp-c-brand-tint);
21
- --docsearch-hit-color: var(--rp-c-text-1);
22
- --docsearch-hit-background: var(--rp-c-bg-soft);
23
- --docsearch-hit-shadow: var(--rp-shadow-2);
24
- --docsearch-footer-background: var(--rp-c-bg);
25
- --docsearch-key-background: var(--rp-c-bg-mute);
26
- --docsearch-key-color: var(--rp-c-text-2);
27
- --docsearch-highlight-color: var(--rp-c-brand);
28
- --docsearch-icon-color: var(--rp-c-text-1);
29
- --docsearch-background-color: var(--rp-c-bg-mute);
30
- --docsearch-focus-color: var(--rp-c-brand);
31
- --docsearch-dropdown-menu-background: var(--rp-c-bg-soft);
32
- --docsearch-dropdown-menu-item-hover-background: var(--rp-c-bg);
33
- }
34
-
35
- html.dark {
36
- --docsearch-text-color: #f5f6f7;
37
- --docsearch-container-background: #090a11cc;
38
- --docsearch-modal-background: #15172a;
39
- --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
40
- --docsearch-searchbox-background: #090a11;
41
- --docsearch-searchbox-focus-background: #000;
42
- --docsearch-hit-color: #bec3c9;
43
- --docsearch-hit-shadow: none;
44
- --docsearch-hit-background: #090a11;
45
- --docsearch-key-gradient: linear-gradient(-26.5deg,
46
- #565872 0%,
47
- #31355b 100%);
48
- --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
49
- 0 2px 2px 0 #0304094d;
50
- --docsearch-footer-background: #1e2136;
51
- --docsearch-footer-shadow: inset 0 1px 0 0 #494c6a80, 0 -4px 8px 0 #0003;
52
- --docsearch-logo-color: #fff;
53
- --docsearch-muted-color: #7f8497;
54
- }
55
-
56
- .DocSearch {
57
- border-radius: var(--rp-radius-small);
58
- }
59
-
60
- .DocSearch-Button-Container > .DocSearch-Button-Placeholder {
61
- font-size: .825rem;
62
- }
63
-
64
1
  .DocSearch-Button {
65
- border: 1px solid var(--docsearch-subtle-color);
66
- }
67
-
68
- .DocSearch-Button:hover {
69
- box-shadow: none;
70
- color: var(--docsearch-search-button-text-color);
71
- }
72
-
73
- .DocSearch-Screen-Icon svg {
74
- margin: auto;
2
+ color: var(--docsearch-muted-color);
3
+ border-color: var(--rp-c-divider-light, #d1d1d1);
4
+ background-color: var(--docsearch-search-button-background);
5
+ background-color: color-mix(in srgb,
6
+ var(--docsearch-search-button-background) 30%,
7
+ transparent);
75
8
  }
76
9
 
@@ -1,3 +1,3 @@
1
- export { type Locales, RU_LOCALES, ZH_LOCALES, VN_LOCALES } from './locales.js';
1
+ export { type Locales, RU_LOCALES, ZH_LOCALES, VI_LOCALES } from './locales.js';
2
2
  export { Search, type SearchProps } from './Search.js';
3
3
  export type { TypesenseDocSearchProps } from './RealSearch.js';
@@ -1,2 +1,2 @@
1
- export { RU_LOCALES, VN_LOCALES, ZH_LOCALES } from "./locales.js";
1
+ export { RU_LOCALES, VI_LOCALES, ZH_LOCALES } from "./locales.js";
2
2
  export { Search } from "./Search.js";
@@ -1,8 +1,22 @@
1
1
  import type { DocSearchProps } from 'typesense-docsearch-react';
2
- export type Locales = Record<string, {
3
- translations: DocSearchProps['translations'];
2
+ /**
3
+ * Recursively removes `?` and `undefined` from all nested properties
4
+ */
5
+ type DeepRequired<T> = T extends (...args: any[]) => any ? T : T extends readonly (infer U)[] ? readonly DeepRequired<NonNullable<U>>[] : T extends object ? {
6
+ [K in keyof T]-?: DeepRequired<NonNullable<T[K]>>;
7
+ } : NonNullable<T>;
8
+ export type RequiredTranslations = DeepRequired<NonNullable<DocSearchProps['translations']>>;
9
+ export type StrictLocaleConfig = {
4
10
  placeholder: string;
5
- }>;
6
- export declare const ZH_LOCALES: Locales;
7
- export declare const RU_LOCALES: Locales;
8
- export declare const VN_LOCALES: Locales;
11
+ translations: RequiredTranslations;
12
+ };
13
+ export type LocaleConfig = {
14
+ placeholder: string;
15
+ translations?: DocSearchProps['translations'];
16
+ };
17
+ export type StrictLocales = Record<string, StrictLocaleConfig>;
18
+ export type Locales = Record<string, LocaleConfig>;
19
+ export declare const ZH_LOCALES: StrictLocales;
20
+ export declare const RU_LOCALES: StrictLocales;
21
+ export declare const VI_LOCALES: StrictLocales;
22
+ export {};
@@ -8,10 +8,13 @@ const ZH_LOCALES = {
8
8
  },
9
9
  modal: {
10
10
  searchBox: {
11
- resetButtonTitle: '清除查询条件',
12
- resetButtonAriaLabel: '清除查询条件',
13
- cancelButtonText: '取消',
14
- cancelButtonAriaLabel: '取消'
11
+ clearButtonTitle: '清除查询条件',
12
+ clearButtonAriaLabel: '清除查询条件',
13
+ closeButtonText: '取消',
14
+ closeButtonAriaLabel: '取消',
15
+ placeholderText: '搜索文档',
16
+ enterKeyHint: 'search',
17
+ searchInputLabel: '搜索'
15
18
  },
16
19
  startScreen: {
17
20
  recentSearchesTitle: '搜索历史',
@@ -27,8 +30,15 @@ const ZH_LOCALES = {
27
30
  },
28
31
  footer: {
29
32
  selectText: '选择',
33
+ submitQuestionText: '提交问题',
34
+ selectKeyAriaLabel: '回车键',
30
35
  navigateText: '切换',
36
+ navigateUpKeyAriaLabel: '向上箭头',
37
+ navigateDownKeyAriaLabel: '向下箭头',
31
38
  closeText: '关闭',
39
+ backToSearchText: '返回搜索',
40
+ closeKeyAriaLabel: 'Esc 键',
41
+ poweredByText: '由…提供支持',
32
42
  searchByText: '搜索提供者'
33
43
  },
34
44
  noResultsScreen: {
@@ -36,6 +46,22 @@ const ZH_LOCALES = {
36
46
  suggestedQueryText: '你可以尝试查询',
37
47
  reportMissingResultsText: '你认为该查询应该有结果?',
38
48
  reportMissingResultsLinkText: '点击反馈'
49
+ },
50
+ facets: {
51
+ defaultValueLabel: '全部',
52
+ facetMenuTriggerAriaLabel: '筛选菜单',
53
+ clearAllLabel: '清除全部',
54
+ facetsAriaLabel: '筛选条件',
55
+ selectedFacetsAriaLabel: '已选筛选条件',
56
+ clearFacetAriaLabel: '清除筛选'
57
+ },
58
+ resultsScreen: {
59
+ askAiPlaceholder: '询问 AI:',
60
+ noResultsAskAiPlaceholder: '文档里没找到?让 AI 帮忙:',
61
+ resultsSectionTitle: '搜索结果',
62
+ askAiResultsTitle: 'AI 回答',
63
+ resultBadgeLabelText: '分类',
64
+ recentConversationTimestampFallback: '刚刚'
39
65
  }
40
66
  }
41
67
  }
@@ -51,10 +77,13 @@ const RU_LOCALES = {
51
77
  },
52
78
  modal: {
53
79
  searchBox: {
54
- resetButtonTitle: 'Очистить поиск',
55
- resetButtonAriaLabel: 'Очистить поиск',
56
- cancelButtonText: 'Закрыть',
57
- cancelButtonAriaLabel: 'Закрыть'
80
+ clearButtonTitle: 'Очистить поиск',
81
+ clearButtonAriaLabel: 'Очистить поиск',
82
+ closeButtonText: 'Закрыть',
83
+ closeButtonAriaLabel: 'Закрыть',
84
+ placeholderText: 'Поиск в документации',
85
+ enterKeyHint: 'search',
86
+ searchInputLabel: 'Поиск'
58
87
  },
59
88
  startScreen: {
60
89
  recentSearchesTitle: 'История поиска',
@@ -70,8 +99,15 @@ const RU_LOCALES = {
70
99
  },
71
100
  footer: {
72
101
  selectText: 'выбрать',
102
+ submitQuestionText: 'Задать вопрос',
103
+ selectKeyAriaLabel: 'Клавиша Enter',
73
104
  navigateText: 'перейти',
105
+ navigateUpKeyAriaLabel: 'Стрелка вверх',
106
+ navigateDownKeyAriaLabel: 'Стрелка вниз',
74
107
  closeText: 'закрыть',
108
+ backToSearchText: 'Назад к поиску',
109
+ closeKeyAriaLabel: 'Клавиша Escape',
110
+ poweredByText: 'При поддержке',
75
111
  searchByText: 'поиск от'
76
112
  },
77
113
  noResultsScreen: {
@@ -79,13 +115,29 @@ const RU_LOCALES = {
79
115
  suggestedQueryText: 'Попробуйте изменить запрос',
80
116
  reportMissingResultsText: 'Считаете, что результаты должны быть?',
81
117
  reportMissingResultsLinkText: 'Сообщите об этом'
118
+ },
119
+ facets: {
120
+ defaultValueLabel: 'Все',
121
+ facetMenuTriggerAriaLabel: 'Меню фильтров',
122
+ clearAllLabel: 'Очистить все',
123
+ facetsAriaLabel: 'Фильтры',
124
+ selectedFacetsAriaLabel: 'Выбранные фильтры',
125
+ clearFacetAriaLabel: 'Удалить фильтр'
126
+ },
127
+ resultsScreen: {
128
+ askAiPlaceholder: 'Спросить AI: ',
129
+ noResultsAskAiPlaceholder: 'Не нашли в документации? Спросите AI: ',
130
+ resultsSectionTitle: 'Результаты',
131
+ askAiResultsTitle: 'Ответ AI',
132
+ resultBadgeLabelText: 'Категория',
133
+ recentConversationTimestampFallback: 'Недавно'
82
134
  }
83
135
  }
84
136
  }
85
137
  }
86
138
  };
87
- const VN_LOCALES = {
88
- vn: {
139
+ const VI_LOCALES = {
140
+ vi: {
89
141
  placeholder: 'Tìm kiếm tài liệu',
90
142
  translations: {
91
143
  button: {
@@ -94,10 +146,13 @@ const VN_LOCALES = {
94
146
  },
95
147
  modal: {
96
148
  searchBox: {
97
- resetButtonTitle: 'Xóa truy vấn',
98
- resetButtonAriaLabel: 'Xóa truy vấn',
99
- cancelButtonText: 'Hủy',
100
- cancelButtonAriaLabel: 'Hủy'
149
+ clearButtonTitle: 'Xóa truy vấn',
150
+ clearButtonAriaLabel: 'Xóa truy vấn',
151
+ closeButtonText: 'Hủy',
152
+ closeButtonAriaLabel: 'Hủy',
153
+ placeholderText: 'Tìm kiếm tài liệu',
154
+ enterKeyHint: 'search',
155
+ searchInputLabel: 'Tìm kiếm'
101
156
  },
102
157
  startScreen: {
103
158
  recentSearchesTitle: 'Gần đây',
@@ -113,8 +168,15 @@ const VN_LOCALES = {
113
168
  },
114
169
  footer: {
115
170
  selectText: 'để chọn',
171
+ submitQuestionText: 'Gửi câu hỏi',
172
+ selectKeyAriaLabel: 'Phím Enter',
116
173
  navigateText: 'để di chuyển',
174
+ navigateUpKeyAriaLabel: 'Mũi tên lên',
175
+ navigateDownKeyAriaLabel: 'Mũi tên xuống',
117
176
  closeText: 'để đóng',
177
+ backToSearchText: 'Quay lại tìm kiếm',
178
+ closeKeyAriaLabel: 'Phím Escape',
179
+ poweredByText: 'Vận hành bởi',
118
180
  searchByText: 'Vận hành bởi'
119
181
  },
120
182
  noResultsScreen: {
@@ -122,9 +184,25 @@ const VN_LOCALES = {
122
184
  suggestedQueryText: 'Hãy thử tìm với từ khóa',
123
185
  reportMissingResultsText: 'Bạn nghĩ truy vấn này nên có kết quả?',
124
186
  reportMissingResultsLinkText: 'Hãy cho chúng tôi biết.'
187
+ },
188
+ facets: {
189
+ defaultValueLabel: 'Tất cả',
190
+ facetMenuTriggerAriaLabel: 'Menu bộ lọc',
191
+ clearAllLabel: 'Xóa tất cả',
192
+ facetsAriaLabel: 'Bộ lọc',
193
+ selectedFacetsAriaLabel: 'Các bộ lọc đã chọn',
194
+ clearFacetAriaLabel: 'Xóa bộ lọc'
195
+ },
196
+ resultsScreen: {
197
+ askAiPlaceholder: 'Hỏi AI: ',
198
+ noResultsAskAiPlaceholder: 'Không tìm thấy trong tài liệu? Hỏi AI: ',
199
+ resultsSectionTitle: 'Kết quả tìm kiếm',
200
+ askAiResultsTitle: 'Câu trả lời từ AI',
201
+ resultBadgeLabelText: 'Danh mục',
202
+ recentConversationTimestampFallback: 'Vừa xong'
125
203
  }
126
204
  }
127
205
  }
128
206
  }
129
207
  };
130
- export { RU_LOCALES, VN_LOCALES, ZH_LOCALES };
208
+ export { RU_LOCALES, VI_LOCALES, ZH_LOCALES };
@@ -21,6 +21,7 @@ export declare class TypesenseHelper {
21
21
  init(): Promise<void>;
22
22
  createTmpCollection(): Promise<void>;
23
23
  addRecords(records: DocSearchRecord[], fileName: string, fromSitemap: boolean, padLength?: number): Promise<number>;
24
+ discardTmpCollection(): Promise<void>;
24
25
  commitTmpCollection(): Promise<void>;
25
26
  static transformRecord(record: DocSearchRecord, isVersioned: boolean): any;
26
27
  private getOldCollectionName;
@@ -80,16 +80,42 @@ class TypesenseHelper {
80
80
  console.log(` ${iconColor}✔\x1b[0m \x1b[37m${fileName}\x1b[0m ${filler} \x1b[33m${paddedCount}\x1b[0m \x1b[90mrecords\x1b[0m`);
81
81
  return recordCount;
82
82
  }
83
+ async discardTmpCollection() {
84
+ try {
85
+ await this.typesenseClient.collections(this.collectionNameTmp).delete();
86
+ } catch (error) {
87
+ if (error?.httpStatus !== 404) throw error;
88
+ }
89
+ }
83
90
  async commitTmpCollection() {
84
- const oldCollectionName = await this.getOldCollectionName();
85
- if (oldCollectionName) {
86
- await this.transferSynonyms(oldCollectionName);
87
- await this.transferOverrides(oldCollectionName);
91
+ let oldCollectionName = null;
92
+ let aliasUpdated = false;
93
+ try {
94
+ oldCollectionName = await this.getOldCollectionName();
95
+ if (oldCollectionName) {
96
+ await this.transferSynonyms(oldCollectionName);
97
+ await this.transferOverrides(oldCollectionName);
98
+ }
99
+ await this.typesenseClient.aliases().upsert(this.aliasName, {
100
+ collection_name: this.collectionNameTmp
101
+ });
102
+ aliasUpdated = true;
103
+ if (oldCollectionName) await this.typesenseClient.collections(oldCollectionName).delete();
104
+ } catch (error) {
105
+ try {
106
+ if (aliasUpdated) if (oldCollectionName) await this.typesenseClient.aliases().upsert(this.aliasName, {
107
+ collection_name: oldCollectionName
108
+ });
109
+ else await this.typesenseClient.aliases(this.aliasName).delete();
110
+ await this.discardTmpCollection();
111
+ } catch (rollbackError) {
112
+ const message = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
113
+ throw new Error(`Failed to roll back collection commit for ${this.aliasName}: ${message}`, {
114
+ cause: rollbackError
115
+ });
116
+ }
117
+ throw error;
88
118
  }
89
- await this.typesenseClient.aliases().upsert(this.aliasName, {
90
- collection_name: this.collectionNameTmp
91
- });
92
- if (oldCollectionName) await this.typesenseClient.collections(oldCollectionName).delete();
93
119
  }
94
120
  static transformRecord(record, isVersioned) {
95
121
  const transformedRecord = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rspress-plugin-typesense",
3
3
  "description": "A plugin that adds lightning-fast, typo-tolerant, Typesense-powered search to your Rspress site",
4
- "version": "0.0.3",
4
+ "version": "0.1.0",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
@@ -19,10 +19,13 @@
19
19
  ],
20
20
  "scripts": {
21
21
  "build": "rslib build",
22
- "dev": "rslib build --watch"
22
+ "dev": "rslib build --watch",
23
+ "typecheck": "tsc --noEmit",
24
+ "test:integration": "rstest"
23
25
  },
24
26
  "devDependencies": {
25
27
  "@rslib/core": "^0.20.1",
28
+ "@rstest/core": "0.11.11",
26
29
  "@types/node": "^24.12.0",
27
30
  "rsbuild-plugin-publint": "^0.3.4",
28
31
  "typescript": "^6.0.2"
@@ -33,8 +36,8 @@
33
36
  "dependencies": {
34
37
  "cheerio": "^1.2.0",
35
38
  "typesense": "^3.0.6",
36
- "typesense-docsearch-css": "^0.4.1",
37
- "typesense-docsearch-react": "^3.4.1"
39
+ "typesense-docsearch-css": "5.0.2-0",
40
+ "typesense-docsearch-react": "5.0.2-0"
38
41
  },
39
42
  "keywords": [
40
43
  "typesense",