@open-mercato/search 0.4.2-canary-c02407ff85

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 (237) hide show
  1. package/AGENTS.md +678 -0
  2. package/build.mjs +92 -0
  3. package/dist/di.js +157 -0
  4. package/dist/di.js.map +7 -0
  5. package/dist/fulltext/drivers/index.js +21 -0
  6. package/dist/fulltext/drivers/index.js.map +7 -0
  7. package/dist/fulltext/drivers/meilisearch/index.js +320 -0
  8. package/dist/fulltext/drivers/meilisearch/index.js.map +7 -0
  9. package/dist/fulltext/index.js +7 -0
  10. package/dist/fulltext/index.js.map +7 -0
  11. package/dist/fulltext/types.js +1 -0
  12. package/dist/fulltext/types.js.map +7 -0
  13. package/dist/index.js +12 -0
  14. package/dist/index.js.map +7 -0
  15. package/dist/indexer/index.js +8 -0
  16. package/dist/indexer/index.js.map +7 -0
  17. package/dist/indexer/search-indexer.js +848 -0
  18. package/dist/indexer/search-indexer.js.map +7 -0
  19. package/dist/indexer/subscribers/delete.js +41 -0
  20. package/dist/indexer/subscribers/delete.js.map +7 -0
  21. package/dist/lib/debug.js +34 -0
  22. package/dist/lib/debug.js.map +7 -0
  23. package/dist/lib/fallback-presenter.js +107 -0
  24. package/dist/lib/fallback-presenter.js.map +7 -0
  25. package/dist/lib/field-policy.js +75 -0
  26. package/dist/lib/field-policy.js.map +7 -0
  27. package/dist/lib/index.js +19 -0
  28. package/dist/lib/index.js.map +7 -0
  29. package/dist/lib/merger.js +93 -0
  30. package/dist/lib/merger.js.map +7 -0
  31. package/dist/lib/presenter-enricher.js +192 -0
  32. package/dist/lib/presenter-enricher.js.map +7 -0
  33. package/dist/modules/search/acl.js +14 -0
  34. package/dist/modules/search/acl.js.map +7 -0
  35. package/dist/modules/search/ai-tools.js +284 -0
  36. package/dist/modules/search/ai-tools.js.map +7 -0
  37. package/dist/modules/search/api/embeddings/reindex/cancel/route.js +65 -0
  38. package/dist/modules/search/api/embeddings/reindex/cancel/route.js.map +7 -0
  39. package/dist/modules/search/api/embeddings/reindex/route.js +165 -0
  40. package/dist/modules/search/api/embeddings/reindex/route.js.map +7 -0
  41. package/dist/modules/search/api/embeddings/route.js +246 -0
  42. package/dist/modules/search/api/embeddings/route.js.map +7 -0
  43. package/dist/modules/search/api/index/route.js +245 -0
  44. package/dist/modules/search/api/index/route.js.map +7 -0
  45. package/dist/modules/search/api/reindex/cancel/route.js +65 -0
  46. package/dist/modules/search/api/reindex/cancel/route.js.map +7 -0
  47. package/dist/modules/search/api/reindex/route.js +332 -0
  48. package/dist/modules/search/api/reindex/route.js.map +7 -0
  49. package/dist/modules/search/api/search/global/route.js +100 -0
  50. package/dist/modules/search/api/search/global/route.js.map +7 -0
  51. package/dist/modules/search/api/search/route.js +101 -0
  52. package/dist/modules/search/api/search/route.js.map +7 -0
  53. package/dist/modules/search/api/settings/fulltext/route.js +55 -0
  54. package/dist/modules/search/api/settings/fulltext/route.js.map +7 -0
  55. package/dist/modules/search/api/settings/global-search/route.js +80 -0
  56. package/dist/modules/search/api/settings/global-search/route.js.map +7 -0
  57. package/dist/modules/search/api/settings/route.js +118 -0
  58. package/dist/modules/search/api/settings/route.js.map +7 -0
  59. package/dist/modules/search/api/settings/vector-store/route.js +77 -0
  60. package/dist/modules/search/api/settings/vector-store/route.js.map +7 -0
  61. package/dist/modules/search/backend/config/search/page.js +10 -0
  62. package/dist/modules/search/backend/config/search/page.js.map +7 -0
  63. package/dist/modules/search/backend/config/search/page.meta.js +24 -0
  64. package/dist/modules/search/backend/config/search/page.meta.js.map +7 -0
  65. package/dist/modules/search/cli.js +698 -0
  66. package/dist/modules/search/cli.js.map +7 -0
  67. package/dist/modules/search/di.js +32 -0
  68. package/dist/modules/search/di.js.map +7 -0
  69. package/dist/modules/search/frontend/components/GlobalSearchDialog.js +357 -0
  70. package/dist/modules/search/frontend/components/GlobalSearchDialog.js.map +7 -0
  71. package/dist/modules/search/frontend/components/HybridSearchTable.js +343 -0
  72. package/dist/modules/search/frontend/components/HybridSearchTable.js.map +7 -0
  73. package/dist/modules/search/frontend/components/SearchSettingsPageClient.js +303 -0
  74. package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +7 -0
  75. package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js +360 -0
  76. package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js.map +7 -0
  77. package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js +101 -0
  78. package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js.map +7 -0
  79. package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +608 -0
  80. package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +7 -0
  81. package/dist/modules/search/frontend/index.js +9 -0
  82. package/dist/modules/search/frontend/index.js.map +7 -0
  83. package/dist/modules/search/frontend/utils.js +41 -0
  84. package/dist/modules/search/frontend/utils.js.map +7 -0
  85. package/dist/modules/search/i18n/de.json +61 -0
  86. package/dist/modules/search/i18n/en.json +72 -0
  87. package/dist/modules/search/i18n/es.json +61 -0
  88. package/dist/modules/search/i18n/pl.json +61 -0
  89. package/dist/modules/search/index.js +11 -0
  90. package/dist/modules/search/index.js.map +7 -0
  91. package/dist/modules/search/lib/auto-indexing.js +29 -0
  92. package/dist/modules/search/lib/auto-indexing.js.map +7 -0
  93. package/dist/modules/search/lib/embedding-config.js +131 -0
  94. package/dist/modules/search/lib/embedding-config.js.map +7 -0
  95. package/dist/modules/search/lib/global-search-config.js +45 -0
  96. package/dist/modules/search/lib/global-search-config.js.map +7 -0
  97. package/dist/modules/search/lib/reindex-lock.js +99 -0
  98. package/dist/modules/search/lib/reindex-lock.js.map +7 -0
  99. package/dist/modules/search/subscribers/fulltext_upsert.js +64 -0
  100. package/dist/modules/search/subscribers/fulltext_upsert.js.map +7 -0
  101. package/dist/modules/search/subscribers/vector_delete.js +58 -0
  102. package/dist/modules/search/subscribers/vector_delete.js.map +7 -0
  103. package/dist/modules/search/subscribers/vector_purge.js +142 -0
  104. package/dist/modules/search/subscribers/vector_purge.js.map +7 -0
  105. package/dist/modules/search/subscribers/vector_upsert.js +58 -0
  106. package/dist/modules/search/subscribers/vector_upsert.js.map +7 -0
  107. package/dist/modules/search/workers/fulltext-index.worker.js +240 -0
  108. package/dist/modules/search/workers/fulltext-index.worker.js.map +7 -0
  109. package/dist/modules/search/workers/vector-index.worker.js +234 -0
  110. package/dist/modules/search/workers/vector-index.worker.js.map +7 -0
  111. package/dist/queue/fulltext-indexing.js +15 -0
  112. package/dist/queue/fulltext-indexing.js.map +7 -0
  113. package/dist/queue/index.js +3 -0
  114. package/dist/queue/index.js.map +7 -0
  115. package/dist/queue/vector-indexing.js +15 -0
  116. package/dist/queue/vector-indexing.js.map +7 -0
  117. package/dist/service.js +286 -0
  118. package/dist/service.js.map +7 -0
  119. package/dist/strategies/fulltext.strategy.js +116 -0
  120. package/dist/strategies/fulltext.strategy.js.map +7 -0
  121. package/dist/strategies/index.js +12 -0
  122. package/dist/strategies/index.js.map +7 -0
  123. package/dist/strategies/token.strategy.js +80 -0
  124. package/dist/strategies/token.strategy.js.map +7 -0
  125. package/dist/strategies/vector.strategy.js +137 -0
  126. package/dist/strategies/vector.strategy.js.map +7 -0
  127. package/dist/types.js +1 -0
  128. package/dist/types.js.map +7 -0
  129. package/dist/vector/drivers/chromadb/index.js +44 -0
  130. package/dist/vector/drivers/chromadb/index.js.map +7 -0
  131. package/dist/vector/drivers/index.js +9 -0
  132. package/dist/vector/drivers/index.js.map +7 -0
  133. package/dist/vector/drivers/pgvector/index.js +509 -0
  134. package/dist/vector/drivers/pgvector/index.js.map +7 -0
  135. package/dist/vector/drivers/qdrant/index.js +44 -0
  136. package/dist/vector/drivers/qdrant/index.js.map +7 -0
  137. package/dist/vector/index.js +4 -0
  138. package/dist/vector/index.js.map +7 -0
  139. package/dist/vector/lib/vector-logs.js +33 -0
  140. package/dist/vector/lib/vector-logs.js.map +7 -0
  141. package/dist/vector/services/checksum.js +20 -0
  142. package/dist/vector/services/checksum.js.map +7 -0
  143. package/dist/vector/services/embedding.js +222 -0
  144. package/dist/vector/services/embedding.js.map +7 -0
  145. package/dist/vector/services/index.js +4 -0
  146. package/dist/vector/services/index.js.map +7 -0
  147. package/dist/vector/services/vector-index.service.js +960 -0
  148. package/dist/vector/services/vector-index.service.js.map +7 -0
  149. package/dist/vector/types/pg.d.js +1 -0
  150. package/dist/vector/types/pg.d.js.map +7 -0
  151. package/dist/vector/types.js +75 -0
  152. package/dist/vector/types.js.map +7 -0
  153. package/jest.config.cjs +19 -0
  154. package/package.json +142 -0
  155. package/src/__tests__/queue.test.ts +148 -0
  156. package/src/__tests__/service.test.ts +345 -0
  157. package/src/__tests__/workers.test.ts +319 -0
  158. package/src/di.ts +291 -0
  159. package/src/fulltext/drivers/index.ts +41 -0
  160. package/src/fulltext/drivers/meilisearch/index.ts +410 -0
  161. package/src/fulltext/index.ts +13 -0
  162. package/src/fulltext/types.ts +115 -0
  163. package/src/index.ts +36 -0
  164. package/src/indexer/index.ts +13 -0
  165. package/src/indexer/search-indexer.ts +1141 -0
  166. package/src/indexer/subscribers/delete.ts +49 -0
  167. package/src/lib/debug.ts +46 -0
  168. package/src/lib/fallback-presenter.ts +106 -0
  169. package/src/lib/field-policy.ts +169 -0
  170. package/src/lib/index.ts +13 -0
  171. package/src/lib/merger.ts +159 -0
  172. package/src/lib/presenter-enricher.ts +323 -0
  173. package/src/modules/search/README.md +694 -0
  174. package/src/modules/search/acl.ts +10 -0
  175. package/src/modules/search/ai-tools.ts +467 -0
  176. package/src/modules/search/api/embeddings/reindex/cancel/route.ts +77 -0
  177. package/src/modules/search/api/embeddings/reindex/route.ts +197 -0
  178. package/src/modules/search/api/embeddings/route.ts +304 -0
  179. package/src/modules/search/api/index/route.ts +297 -0
  180. package/src/modules/search/api/reindex/cancel/route.ts +77 -0
  181. package/src/modules/search/api/reindex/route.ts +419 -0
  182. package/src/modules/search/api/search/global/route.ts +120 -0
  183. package/src/modules/search/api/search/route.ts +121 -0
  184. package/src/modules/search/api/settings/fulltext/route.ts +82 -0
  185. package/src/modules/search/api/settings/global-search/route.ts +91 -0
  186. package/src/modules/search/api/settings/route.ts +187 -0
  187. package/src/modules/search/api/settings/vector-store/route.ts +105 -0
  188. package/src/modules/search/backend/config/search/page.meta.ts +22 -0
  189. package/src/modules/search/backend/config/search/page.tsx +12 -0
  190. package/src/modules/search/cli.ts +818 -0
  191. package/src/modules/search/di.ts +50 -0
  192. package/src/modules/search/frontend/components/GlobalSearchDialog.tsx +436 -0
  193. package/src/modules/search/frontend/components/HybridSearchTable.tsx +418 -0
  194. package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +476 -0
  195. package/src/modules/search/frontend/components/sections/FulltextSearchSection.tsx +624 -0
  196. package/src/modules/search/frontend/components/sections/GlobalSearchSection.tsx +124 -0
  197. package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +943 -0
  198. package/src/modules/search/frontend/index.ts +3 -0
  199. package/src/modules/search/frontend/utils.ts +82 -0
  200. package/src/modules/search/i18n/de.json +61 -0
  201. package/src/modules/search/i18n/en.json +72 -0
  202. package/src/modules/search/i18n/es.json +61 -0
  203. package/src/modules/search/i18n/pl.json +61 -0
  204. package/src/modules/search/index.ts +9 -0
  205. package/src/modules/search/lib/auto-indexing.ts +35 -0
  206. package/src/modules/search/lib/embedding-config.ts +161 -0
  207. package/src/modules/search/lib/global-search-config.ts +69 -0
  208. package/src/modules/search/lib/reindex-lock.ts +201 -0
  209. package/src/modules/search/subscribers/fulltext_upsert.ts +83 -0
  210. package/src/modules/search/subscribers/vector_delete.ts +75 -0
  211. package/src/modules/search/subscribers/vector_purge.ts +161 -0
  212. package/src/modules/search/subscribers/vector_upsert.ts +75 -0
  213. package/src/modules/search/workers/fulltext-index.worker.ts +318 -0
  214. package/src/modules/search/workers/vector-index.worker.ts +292 -0
  215. package/src/queue/fulltext-indexing.ts +87 -0
  216. package/src/queue/index.ts +2 -0
  217. package/src/queue/vector-indexing.ts +66 -0
  218. package/src/service.ts +397 -0
  219. package/src/strategies/fulltext.strategy.ts +155 -0
  220. package/src/strategies/index.ts +17 -0
  221. package/src/strategies/token.strategy.ts +153 -0
  222. package/src/strategies/vector.strategy.ts +234 -0
  223. package/src/types.ts +38 -0
  224. package/src/vector/drivers/chromadb/index.ts +49 -0
  225. package/src/vector/drivers/index.ts +4 -0
  226. package/src/vector/drivers/pgvector/index.ts +627 -0
  227. package/src/vector/drivers/qdrant/index.ts +49 -0
  228. package/src/vector/index.ts +3 -0
  229. package/src/vector/lib/vector-logs.ts +46 -0
  230. package/src/vector/services/checksum.ts +18 -0
  231. package/src/vector/services/embedding.ts +275 -0
  232. package/src/vector/services/index.ts +3 -0
  233. package/src/vector/services/vector-index.service.ts +1234 -0
  234. package/src/vector/types/pg.d.ts +1 -0
  235. package/src/vector/types.ts +220 -0
  236. package/tsconfig.json +9 -0
  237. package/watch.mjs +6 -0
@@ -0,0 +1,303 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
6
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
7
+ import { Button } from "@open-mercato/ui/primitives/button";
8
+ import { Spinner } from "@open-mercato/ui/primitives/spinner";
9
+ import { GlobalSearchSection } from "./sections/GlobalSearchSection.js";
10
+ import { FulltextSearchSection } from "./sections/FulltextSearchSection.js";
11
+ import { VectorSearchSection } from "./sections/VectorSearchSection.js";
12
+ const normalizeErrorMessage = (error, fallback) => {
13
+ if (typeof error === "string" && error.trim().length) return error.trim();
14
+ if (error instanceof Error && error.message.trim().length) return error.message.trim();
15
+ return fallback;
16
+ };
17
+ function SearchSettingsPageClient() {
18
+ const t = useT();
19
+ const [settings, setSettings] = React.useState(null);
20
+ const [loading, setLoading] = React.useState(true);
21
+ const [error, setError] = React.useState(null);
22
+ const [embeddingSettings, setEmbeddingSettings] = React.useState(null);
23
+ const [embeddingLoading, setEmbeddingLoading] = React.useState(true);
24
+ const [globalSearchStrategies, setGlobalSearchStrategies] = React.useState(() => /* @__PURE__ */ new Set(["fulltext", "vector", "tokens"]));
25
+ const [globalSearchInitial, setGlobalSearchInitial] = React.useState(() => /* @__PURE__ */ new Set(["fulltext", "vector", "tokens"]));
26
+ const [globalSearchLoading, setGlobalSearchLoading] = React.useState(true);
27
+ const [globalSearchSaving, setGlobalSearchSaving] = React.useState(false);
28
+ const [fulltextConfig, setFulltextConfig] = React.useState(null);
29
+ const [fulltextConfigLoading, setFulltextConfigLoading] = React.useState(true);
30
+ const [vectorStoreConfig, setVectorStoreConfig] = React.useState(null);
31
+ const [vectorStoreConfigLoading, setVectorStoreConfigLoading] = React.useState(true);
32
+ const fetchSettings = React.useCallback(async () => {
33
+ setLoading(true);
34
+ setError(null);
35
+ try {
36
+ const body = await readApiResultOrThrow(
37
+ "/api/search/settings",
38
+ void 0,
39
+ { errorMessage: t("search.settings.errorLabel", "Failed to load settings"), allowNullResult: true }
40
+ );
41
+ if (body?.settings) {
42
+ setSettings(body.settings);
43
+ } else {
44
+ setSettings({
45
+ strategies: [],
46
+ fulltextConfigured: false,
47
+ fulltextStats: null,
48
+ vectorConfigured: false,
49
+ tokensEnabled: true,
50
+ defaultStrategies: [],
51
+ reindexLock: null,
52
+ fulltextReindexLock: null,
53
+ vectorReindexLock: null
54
+ });
55
+ }
56
+ } catch (err) {
57
+ const message = normalizeErrorMessage(err, t("search.settings.errorLabel", "Failed to load settings"));
58
+ setError(message);
59
+ flash(message, "error");
60
+ } finally {
61
+ setLoading(false);
62
+ }
63
+ }, [t]);
64
+ React.useEffect(() => {
65
+ fetchSettings();
66
+ }, [fetchSettings]);
67
+ const refreshStatsOnly = React.useCallback(async () => {
68
+ try {
69
+ const body = await readApiResultOrThrow(
70
+ "/api/search/settings",
71
+ { cache: "no-store" },
72
+ { errorMessage: "", allowNullResult: true }
73
+ );
74
+ if (body?.settings) {
75
+ setSettings(body.settings);
76
+ }
77
+ } catch {
78
+ }
79
+ }, []);
80
+ const refreshEmbeddingStatsOnly = React.useCallback(async () => {
81
+ try {
82
+ const body = await readApiResultOrThrow(
83
+ "/api/search/embeddings",
84
+ { cache: "no-store" },
85
+ { errorMessage: "", allowNullResult: true }
86
+ );
87
+ if (body?.settings) {
88
+ setEmbeddingSettings(body.settings);
89
+ }
90
+ } catch {
91
+ }
92
+ }, []);
93
+ const wasPollingRef = React.useRef(false);
94
+ const pollCountAfterClearRef = React.useRef(0);
95
+ React.useEffect(() => {
96
+ const hasFulltextLock = settings?.fulltextReindexLock !== null;
97
+ const hasVectorLock = settings?.vectorReindexLock !== null;
98
+ const shouldPoll = hasFulltextLock || hasVectorLock || wasPollingRef.current && pollCountAfterClearRef.current < 3;
99
+ if (!shouldPoll) {
100
+ wasPollingRef.current = false;
101
+ pollCountAfterClearRef.current = 0;
102
+ return;
103
+ }
104
+ if (hasFulltextLock || hasVectorLock) {
105
+ wasPollingRef.current = true;
106
+ pollCountAfterClearRef.current = 0;
107
+ }
108
+ const pollInterval = setInterval(() => {
109
+ if (!hasFulltextLock && !hasVectorLock) {
110
+ pollCountAfterClearRef.current += 1;
111
+ }
112
+ refreshStatsOnly();
113
+ if (hasVectorLock) {
114
+ refreshEmbeddingStatsOnly();
115
+ }
116
+ }, 3e3);
117
+ return () => clearInterval(pollInterval);
118
+ }, [settings?.fulltextReindexLock, settings?.vectorReindexLock, refreshStatsOnly, refreshEmbeddingStatsOnly]);
119
+ const fetchEmbeddingSettings = React.useCallback(async () => {
120
+ setEmbeddingLoading(true);
121
+ try {
122
+ const body = await readApiResultOrThrow(
123
+ "/api/search/embeddings",
124
+ void 0,
125
+ { errorMessage: t("search.settings.errors.loadFailed", "Failed to load settings"), allowNullResult: true }
126
+ );
127
+ if (body?.settings) {
128
+ setEmbeddingSettings(body.settings);
129
+ } else {
130
+ setEmbeddingSettings({
131
+ openaiConfigured: false,
132
+ autoIndexingEnabled: true,
133
+ autoIndexingLocked: false,
134
+ lockReason: null,
135
+ embeddingConfig: null,
136
+ configuredProviders: [],
137
+ indexedDimension: null,
138
+ reindexRequired: false,
139
+ documentCount: null
140
+ });
141
+ }
142
+ } catch {
143
+ } finally {
144
+ setEmbeddingLoading(false);
145
+ }
146
+ }, [t]);
147
+ React.useEffect(() => {
148
+ fetchEmbeddingSettings();
149
+ }, [fetchEmbeddingSettings]);
150
+ const fetchGlobalSearchSettings = React.useCallback(async () => {
151
+ setGlobalSearchLoading(true);
152
+ try {
153
+ const response = await fetch("/api/search/settings/global-search");
154
+ if (response.ok) {
155
+ const body = await response.json();
156
+ if (body.enabledStrategies && Array.isArray(body.enabledStrategies) && body.enabledStrategies.length > 0) {
157
+ const strategies = new Set(body.enabledStrategies);
158
+ setGlobalSearchStrategies(strategies);
159
+ setGlobalSearchInitial(new Set(strategies));
160
+ }
161
+ }
162
+ } catch {
163
+ } finally {
164
+ setGlobalSearchLoading(false);
165
+ }
166
+ }, []);
167
+ React.useEffect(() => {
168
+ fetchGlobalSearchSettings();
169
+ }, [fetchGlobalSearchSettings]);
170
+ const fetchFulltextConfig = React.useCallback(async () => {
171
+ setFulltextConfigLoading(true);
172
+ try {
173
+ const response = await fetch("/api/search/settings/fulltext");
174
+ if (response.ok) {
175
+ const body = await response.json();
176
+ setFulltextConfig(body);
177
+ }
178
+ } catch {
179
+ } finally {
180
+ setFulltextConfigLoading(false);
181
+ }
182
+ }, []);
183
+ React.useEffect(() => {
184
+ fetchFulltextConfig();
185
+ }, [fetchFulltextConfig]);
186
+ const fetchVectorStoreConfig = React.useCallback(async () => {
187
+ setVectorStoreConfigLoading(true);
188
+ try {
189
+ const response = await fetch("/api/search/settings/vector-store");
190
+ if (response.ok) {
191
+ const body = await response.json();
192
+ setVectorStoreConfig(body);
193
+ }
194
+ } catch {
195
+ } finally {
196
+ setVectorStoreConfigLoading(false);
197
+ }
198
+ }, []);
199
+ React.useEffect(() => {
200
+ fetchVectorStoreConfig();
201
+ }, [fetchVectorStoreConfig]);
202
+ const toggleGlobalSearchStrategy = React.useCallback(async (strategyId) => {
203
+ const newStrategies = new Set(globalSearchStrategies);
204
+ if (newStrategies.has(strategyId)) {
205
+ if (newStrategies.size > 1) {
206
+ newStrategies.delete(strategyId);
207
+ } else {
208
+ return;
209
+ }
210
+ } else {
211
+ newStrategies.add(strategyId);
212
+ }
213
+ setGlobalSearchStrategies(newStrategies);
214
+ setGlobalSearchSaving(true);
215
+ try {
216
+ const response = await fetch("/api/search/settings/global-search", {
217
+ method: "POST",
218
+ headers: { "Content-Type": "application/json" },
219
+ body: JSON.stringify({ enabledStrategies: Array.from(newStrategies) })
220
+ });
221
+ if (!response.ok) {
222
+ const body = await response.json().catch(() => ({}));
223
+ throw new Error(body.error || t("search.settings.globalSearch.saveError", "Failed to save settings"));
224
+ }
225
+ setGlobalSearchInitial(new Set(newStrategies));
226
+ } catch (err) {
227
+ setGlobalSearchStrategies(globalSearchInitial);
228
+ flash(normalizeErrorMessage(err, t("search.settings.globalSearch.saveError", "Failed to save settings")), "error");
229
+ } finally {
230
+ setGlobalSearchSaving(false);
231
+ }
232
+ }, [globalSearchStrategies, globalSearchInitial, t]);
233
+ const handleFulltextStatsUpdate = React.useCallback((stats) => {
234
+ setSettings((prev) => prev ? { ...prev, fulltextStats: stats } : prev);
235
+ }, []);
236
+ const handleEmbeddingSettingsUpdate = React.useCallback((newSettings) => {
237
+ setEmbeddingSettings(newSettings);
238
+ }, []);
239
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
240
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
241
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: t("search.settings.pageTitle", "Search Settings") }),
242
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: t("search.settings.pageDescription", "Configure search strategies and view their availability.") })
243
+ ] }),
244
+ /* @__PURE__ */ jsx(
245
+ GlobalSearchSection,
246
+ {
247
+ loading: globalSearchLoading,
248
+ saving: globalSearchSaving,
249
+ strategies: globalSearchStrategies,
250
+ fulltextConfigured: settings?.fulltextConfigured ?? false,
251
+ vectorConfigured: settings?.vectorConfigured ?? false,
252
+ onToggleStrategy: toggleGlobalSearchStrategy
253
+ }
254
+ ),
255
+ /* @__PURE__ */ jsx(
256
+ FulltextSearchSection,
257
+ {
258
+ fulltextConfig,
259
+ fulltextConfigLoading,
260
+ fulltextStats: settings?.fulltextStats ?? null,
261
+ fulltextReindexLock: settings?.fulltextReindexLock ?? null,
262
+ loading,
263
+ onStatsUpdate: handleFulltextStatsUpdate,
264
+ onRefresh: fetchSettings
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsx(
268
+ VectorSearchSection,
269
+ {
270
+ embeddingSettings,
271
+ embeddingLoading,
272
+ vectorStoreConfig,
273
+ vectorStoreConfigLoading,
274
+ vectorReindexLock: settings?.vectorReindexLock ?? null,
275
+ onEmbeddingSettingsUpdate: handleEmbeddingSettingsUpdate,
276
+ onRefreshEmbeddings: fetchEmbeddingSettings
277
+ }
278
+ ),
279
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
280
+ /* @__PURE__ */ jsx(
281
+ Button,
282
+ {
283
+ type: "button",
284
+ variant: "outline",
285
+ size: "sm",
286
+ onClick: () => fetchSettings(),
287
+ disabled: loading,
288
+ children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
289
+ /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "mr-2" }),
290
+ t("search.settings.loadingLabel", "Loading settings...")
291
+ ] }) : t("search.settings.refreshLabel", "Refresh")
292
+ }
293
+ ),
294
+ error && /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: error })
295
+ ] })
296
+ ] });
297
+ }
298
+ var SearchSettingsPageClient_default = SearchSettingsPageClient;
299
+ export {
300
+ SearchSettingsPageClient,
301
+ SearchSettingsPageClient_default as default
302
+ };
303
+ //# sourceMappingURL=SearchSettingsPageClient.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/search/frontend/components/SearchSettingsPageClient.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { GlobalSearchSection } from './sections/GlobalSearchSection'\nimport { FulltextSearchSection } from './sections/FulltextSearchSection'\nimport { VectorSearchSection } from './sections/VectorSearchSection'\n\n// Types\ntype StrategyStatus = {\n id: string\n name: string\n priority: number\n available: boolean\n}\n\ntype FulltextStats = {\n numberOfDocuments: number\n isIndexing: boolean\n fieldDistribution: Record<string, number>\n}\n\ntype ReindexLock = {\n type: 'fulltext' | 'vector'\n action: string\n startedAt: string\n elapsedMinutes: number\n}\n\ntype SearchSettings = {\n strategies: StrategyStatus[]\n fulltextConfigured: boolean\n fulltextStats: FulltextStats | null\n vectorConfigured: boolean\n tokensEnabled: boolean\n defaultStrategies: string[]\n reindexLock: ReindexLock | null\n fulltextReindexLock: ReindexLock | null\n vectorReindexLock: ReindexLock | null\n}\n\ntype SettingsResponse = {\n settings?: SearchSettings\n error?: string\n}\n\n// Embedding types\ntype EmbeddingProviderId = 'openai' | 'google' | 'mistral' | 'cohere' | 'bedrock' | 'ollama'\n\ntype EmbeddingProviderConfig = {\n providerId: EmbeddingProviderId\n model: string\n dimension: number\n outputDimensionality?: number\n baseUrl?: string\n updatedAt: string\n}\n\ntype EmbeddingSettings = {\n openaiConfigured: boolean\n autoIndexingEnabled: boolean\n autoIndexingLocked: boolean\n lockReason: string | null\n embeddingConfig: EmbeddingProviderConfig | null\n configuredProviders: EmbeddingProviderId[]\n indexedDimension: number | null\n reindexRequired: boolean\n documentCount: number | null\n}\n\ntype EmbeddingSettingsResponse = {\n settings?: EmbeddingSettings\n error?: string\n}\n\n// Full-text search config types\ntype FulltextEnvVarStatus = {\n set: boolean\n hint: string\n}\n\ntype FulltextOptionalEnvVarStatus = {\n set: boolean\n value?: string | boolean\n default?: string | boolean\n hint: string\n}\n\ntype FulltextConfigResponse = {\n driver: 'meilisearch' | null\n configured: boolean\n envVars: {\n MEILISEARCH_HOST: FulltextEnvVarStatus\n MEILISEARCH_API_KEY: FulltextEnvVarStatus\n }\n optionalEnvVars: {\n MEILISEARCH_INDEX_PREFIX: FulltextOptionalEnvVarStatus\n SEARCH_EXCLUDE_ENCRYPTED_FIELDS: FulltextOptionalEnvVarStatus\n }\n}\n\n// Vector store driver types\ntype VectorDriverId = 'pgvector' | 'qdrant' | 'chromadb'\n\ntype VectorDriverEnvVar = {\n name: string\n set: boolean\n hint: string\n}\n\ntype VectorDriverStatus = {\n id: VectorDriverId\n name: string\n configured: boolean\n implemented: boolean\n envVars: VectorDriverEnvVar[]\n}\n\ntype VectorStoreConfigResponse = {\n currentDriver: VectorDriverId\n configured: boolean\n drivers: VectorDriverStatus[]\n}\n\nconst normalizeErrorMessage = (error: unknown, fallback: string): string => {\n if (typeof error === 'string' && error.trim().length) return error.trim()\n if (error instanceof Error && error.message.trim().length) return error.message.trim()\n return fallback\n}\n\nexport function SearchSettingsPageClient() {\n const t = useT()\n\n // Main settings state\n const [settings, setSettings] = React.useState<SearchSettings | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n // Embedding settings state\n const [embeddingSettings, setEmbeddingSettings] = React.useState<EmbeddingSettings | null>(null)\n const [embeddingLoading, setEmbeddingLoading] = React.useState(true)\n\n // Global search settings state\n const [globalSearchStrategies, setGlobalSearchStrategies] = React.useState<Set<string>>(() => new Set(['fulltext', 'vector', 'tokens']))\n const [globalSearchInitial, setGlobalSearchInitial] = React.useState<Set<string>>(() => new Set(['fulltext', 'vector', 'tokens']))\n const [globalSearchLoading, setGlobalSearchLoading] = React.useState(true)\n const [globalSearchSaving, setGlobalSearchSaving] = React.useState(false)\n\n // Full-text search config state\n const [fulltextConfig, setFulltextConfig] = React.useState<FulltextConfigResponse | null>(null)\n const [fulltextConfigLoading, setFulltextConfigLoading] = React.useState(true)\n\n // Vector store config state\n const [vectorStoreConfig, setVectorStoreConfig] = React.useState<VectorStoreConfigResponse | null>(null)\n const [vectorStoreConfigLoading, setVectorStoreConfigLoading] = React.useState(true)\n\n // Fetch main settings\n const fetchSettings = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const body = await readApiResultOrThrow<SettingsResponse>(\n '/api/search/settings',\n undefined,\n { errorMessage: t('search.settings.errorLabel', 'Failed to load settings'), allowNullResult: true },\n )\n if (body?.settings) {\n setSettings(body.settings)\n } else {\n setSettings({\n strategies: [],\n fulltextConfigured: false,\n fulltextStats: null,\n vectorConfigured: false,\n tokensEnabled: true,\n defaultStrategies: [],\n reindexLock: null,\n fulltextReindexLock: null,\n vectorReindexLock: null,\n })\n }\n } catch (err) {\n const message = normalizeErrorMessage(err, t('search.settings.errorLabel', 'Failed to load settings'))\n setError(message)\n flash(message, 'error')\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n fetchSettings()\n }, [fetchSettings])\n\n // Lightweight stats refresh for polling during reindex\n const refreshStatsOnly = React.useCallback(async () => {\n try {\n const body = await readApiResultOrThrow<SettingsResponse>(\n '/api/search/settings',\n { cache: 'no-store' },\n { errorMessage: '', allowNullResult: true },\n )\n if (body?.settings) {\n setSettings(body.settings)\n }\n } catch {\n // Silently ignore errors during polling\n }\n }, [])\n\n // Lightweight embedding stats refresh\n const refreshEmbeddingStatsOnly = React.useCallback(async () => {\n try {\n const body = await readApiResultOrThrow<EmbeddingSettingsResponse>(\n '/api/search/embeddings',\n { cache: 'no-store' },\n { errorMessage: '', allowNullResult: true },\n )\n if (body?.settings) {\n setEmbeddingSettings(body.settings)\n }\n } catch {\n // Silently ignore errors during polling\n }\n }, [])\n\n // Polling logic\n const wasPollingRef = React.useRef(false)\n const pollCountAfterClearRef = React.useRef(0)\n\n React.useEffect(() => {\n const hasFulltextLock = settings?.fulltextReindexLock !== null\n const hasVectorLock = settings?.vectorReindexLock !== null\n\n const shouldPoll = hasFulltextLock || hasVectorLock ||\n (wasPollingRef.current && pollCountAfterClearRef.current < 3)\n\n if (!shouldPoll) {\n wasPollingRef.current = false\n pollCountAfterClearRef.current = 0\n return\n }\n\n if (hasFulltextLock || hasVectorLock) {\n wasPollingRef.current = true\n pollCountAfterClearRef.current = 0\n }\n\n const pollInterval = setInterval(() => {\n if (!hasFulltextLock && !hasVectorLock) {\n pollCountAfterClearRef.current += 1\n }\n\n refreshStatsOnly()\n if (hasVectorLock) {\n refreshEmbeddingStatsOnly()\n }\n }, 3000)\n\n return () => clearInterval(pollInterval)\n }, [settings?.fulltextReindexLock, settings?.vectorReindexLock, refreshStatsOnly, refreshEmbeddingStatsOnly])\n\n // Fetch embedding settings\n const fetchEmbeddingSettings = React.useCallback(async () => {\n setEmbeddingLoading(true)\n try {\n const body = await readApiResultOrThrow<EmbeddingSettingsResponse>(\n '/api/search/embeddings',\n undefined,\n { errorMessage: t('search.settings.errors.loadFailed', 'Failed to load settings'), allowNullResult: true },\n )\n if (body?.settings) {\n setEmbeddingSettings(body.settings)\n } else {\n setEmbeddingSettings({\n openaiConfigured: false,\n autoIndexingEnabled: true,\n autoIndexingLocked: false,\n lockReason: null,\n embeddingConfig: null,\n configuredProviders: [],\n indexedDimension: null,\n reindexRequired: false,\n documentCount: null,\n })\n }\n } catch {\n // Error already handled\n } finally {\n setEmbeddingLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n fetchEmbeddingSettings()\n }, [fetchEmbeddingSettings])\n\n // Fetch global search settings\n const fetchGlobalSearchSettings = React.useCallback(async () => {\n setGlobalSearchLoading(true)\n try {\n const response = await fetch('/api/search/settings/global-search')\n if (response.ok) {\n const body = await response.json() as { enabledStrategies?: string[] }\n if (body.enabledStrategies && Array.isArray(body.enabledStrategies) && body.enabledStrategies.length > 0) {\n const strategies = new Set(body.enabledStrategies)\n setGlobalSearchStrategies(strategies)\n setGlobalSearchInitial(new Set(strategies))\n }\n }\n } catch {\n // Silently use defaults\n } finally {\n setGlobalSearchLoading(false)\n }\n }, [])\n\n React.useEffect(() => {\n fetchGlobalSearchSettings()\n }, [fetchGlobalSearchSettings])\n\n // Fetch fulltext config\n const fetchFulltextConfig = React.useCallback(async () => {\n setFulltextConfigLoading(true)\n try {\n const response = await fetch('/api/search/settings/fulltext')\n if (response.ok) {\n const body = await response.json() as FulltextConfigResponse\n setFulltextConfig(body)\n }\n } catch {\n // Silently use null\n } finally {\n setFulltextConfigLoading(false)\n }\n }, [])\n\n React.useEffect(() => {\n fetchFulltextConfig()\n }, [fetchFulltextConfig])\n\n // Fetch vector store config\n const fetchVectorStoreConfig = React.useCallback(async () => {\n setVectorStoreConfigLoading(true)\n try {\n const response = await fetch('/api/search/settings/vector-store')\n if (response.ok) {\n const body = await response.json() as VectorStoreConfigResponse\n setVectorStoreConfig(body)\n }\n } catch {\n // Silently use null\n } finally {\n setVectorStoreConfigLoading(false)\n }\n }, [])\n\n React.useEffect(() => {\n fetchVectorStoreConfig()\n }, [fetchVectorStoreConfig])\n\n // Global search settings handlers - auto-save on toggle\n const toggleGlobalSearchStrategy = React.useCallback(async (strategyId: string) => {\n const newStrategies = new Set(globalSearchStrategies)\n if (newStrategies.has(strategyId)) {\n if (newStrategies.size > 1) {\n newStrategies.delete(strategyId)\n } else {\n return\n }\n } else {\n newStrategies.add(strategyId)\n }\n\n setGlobalSearchStrategies(newStrategies)\n setGlobalSearchSaving(true)\n\n try {\n const response = await fetch('/api/search/settings/global-search', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ enabledStrategies: Array.from(newStrategies) }),\n })\n\n if (!response.ok) {\n const body = await response.json().catch(() => ({})) as { error?: string }\n throw new Error(body.error || t('search.settings.globalSearch.saveError', 'Failed to save settings'))\n }\n\n setGlobalSearchInitial(new Set(newStrategies))\n } catch (err) {\n setGlobalSearchStrategies(globalSearchInitial)\n flash(normalizeErrorMessage(err, t('search.settings.globalSearch.saveError', 'Failed to save settings')), 'error')\n } finally {\n setGlobalSearchSaving(false)\n }\n }, [globalSearchStrategies, globalSearchInitial, t])\n\n // Callbacks for section components\n const handleFulltextStatsUpdate = React.useCallback((stats: FulltextStats | null) => {\n setSettings(prev => prev ? { ...prev, fulltextStats: stats } : prev)\n }, [])\n\n const handleEmbeddingSettingsUpdate = React.useCallback((newSettings: EmbeddingSettings) => {\n setEmbeddingSettings(newSettings)\n }, [])\n\n return (\n <div className=\"flex flex-col gap-6\">\n {/* Header */}\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl font-bold\">{t('search.settings.pageTitle', 'Search Settings')}</h1>\n <p className=\"text-muted-foreground\">{t('search.settings.pageDescription', 'Configure search strategies and view their availability.')}</p>\n </div>\n\n {/* Section 1: Global Search Settings */}\n <GlobalSearchSection\n loading={globalSearchLoading}\n saving={globalSearchSaving}\n strategies={globalSearchStrategies}\n fulltextConfigured={settings?.fulltextConfigured ?? false}\n vectorConfigured={settings?.vectorConfigured ?? false}\n onToggleStrategy={toggleGlobalSearchStrategy}\n />\n\n {/* Section 2: Full-Text Search (with tabs) */}\n <FulltextSearchSection\n fulltextConfig={fulltextConfig}\n fulltextConfigLoading={fulltextConfigLoading}\n fulltextStats={settings?.fulltextStats ?? null}\n fulltextReindexLock={settings?.fulltextReindexLock ?? null}\n loading={loading}\n onStatsUpdate={handleFulltextStatsUpdate}\n onRefresh={fetchSettings}\n />\n\n {/* Section 3: Vector Search (with tabs) */}\n <VectorSearchSection\n embeddingSettings={embeddingSettings}\n embeddingLoading={embeddingLoading}\n vectorStoreConfig={vectorStoreConfig}\n vectorStoreConfigLoading={vectorStoreConfigLoading}\n vectorReindexLock={settings?.vectorReindexLock ?? null}\n onEmbeddingSettingsUpdate={handleEmbeddingSettingsUpdate}\n onRefreshEmbeddings={fetchEmbeddingSettings}\n />\n\n {/* Refresh Button */}\n <div className=\"flex items-center gap-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => fetchSettings()}\n disabled={loading}\n >\n {loading ? (\n <>\n <Spinner size=\"sm\" className=\"mr-2\" />\n {t('search.settings.loadingLabel', 'Loading settings...')}\n </>\n ) : (\n t('search.settings.refreshLabel', 'Refresh')\n )}\n </Button>\n {error && <span className=\"text-sm text-destructive\">{error}</span>}\n </div>\n </div>\n )\n}\n\nexport default SearchSettingsPageClient\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }