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 +34 -13
- package/dist/index.d.ts +0 -5
- package/dist/index.js +7 -8
- package/dist/runtime/Search.css +6 -73
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/locales.d.ts +20 -6
- package/dist/runtime/locales.js +93 -15
- package/dist/typesenseHelper.d.ts +1 -0
- package/dist/typesenseHelper.js +34 -8
- package/package.json +7 -4
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
console.warn("\x1b[33m⚠ [TypesensePlugin] Skipping failure due to failOnIndexError=false.\x1b[0m");
|
|
124
|
+
throw error;
|
|
126
125
|
}
|
|
127
126
|
}
|
|
128
127
|
}
|
package/dist/runtime/Search.css
CHANGED
|
@@ -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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { type Locales, RU_LOCALES, ZH_LOCALES,
|
|
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';
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { RU_LOCALES,
|
|
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
|
-
|
|
3
|
-
|
|
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
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
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 {};
|
package/dist/runtime/locales.js
CHANGED
|
@@ -8,10 +8,13 @@ const ZH_LOCALES = {
|
|
|
8
8
|
},
|
|
9
9
|
modal: {
|
|
10
10
|
searchBox: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
88
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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,
|
|
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;
|
package/dist/typesenseHelper.js
CHANGED
|
@@ -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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
await this.
|
|
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
|
|
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": "
|
|
37
|
-
"typesense-docsearch-react": "
|
|
39
|
+
"typesense-docsearch-css": "5.0.2-0",
|
|
40
|
+
"typesense-docsearch-react": "5.0.2-0"
|
|
38
41
|
},
|
|
39
42
|
"keywords": [
|
|
40
43
|
"typesense",
|