zerochat 7.3.0__py3-none-any.whl
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.
- zerochat-7.3.0.dist-info/METADATA +70 -0
- zerochat-7.3.0.dist-info/RECORD +130 -0
- zerochat-7.3.0.dist-info/WHEEL +5 -0
- zerochat-7.3.0.dist-info/entry_points.txt +2 -0
- zerochat-7.3.0.dist-info/licenses/LICENSE +34 -0
- zerochat-7.3.0.dist-info/top_level.txt +2 -0
- zerochat.py +2004 -0
- zerochat_runtime/__init__.py +1 -0
- zerochat_runtime/assets/css/base.css +199 -0
- zerochat_runtime/assets/css/components/composer.css +1258 -0
- zerochat_runtime/assets/css/components/debug.css +777 -0
- zerochat_runtime/assets/css/components/header.css +97 -0
- zerochat_runtime/assets/css/components/icons.css +39 -0
- zerochat_runtime/assets/css/components/markdown.css +685 -0
- zerochat_runtime/assets/css/components/messages.css +473 -0
- zerochat_runtime/assets/css/components/modals.css +1319 -0
- zerochat_runtime/assets/css/components/sidebar.css +622 -0
- zerochat_runtime/assets/css/components/tools.css +1223 -0
- zerochat_runtime/assets/css/layout.css +171 -0
- zerochat_runtime/assets/css/print.css +120 -0
- zerochat_runtime/assets/css/styles.css +18 -0
- zerochat_runtime/assets/css/theme-overrides.css +555 -0
- zerochat_runtime/assets/css/tokens.css +165 -0
- zerochat_runtime/assets/help/architecture.html +247 -0
- zerochat_runtime/assets/help/composer.html +134 -0
- zerochat_runtime/assets/help/conversations.html +105 -0
- zerochat_runtime/assets/help/debug.html +108 -0
- zerochat_runtime/assets/help/en/architecture.html +247 -0
- zerochat_runtime/assets/help/en/composer.html +134 -0
- zerochat_runtime/assets/help/en/conversations.html +104 -0
- zerochat_runtime/assets/help/en/debug.html +105 -0
- zerochat_runtime/assets/help/en/gemini-free.html +39 -0
- zerochat_runtime/assets/help/en/index.html +191 -0
- zerochat_runtime/assets/help/en/learning.html +71 -0
- zerochat_runtime/assets/help/en/mcp.html +143 -0
- zerochat_runtime/assets/help/en/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/en/profiles.html +173 -0
- zerochat_runtime/assets/help/en/rag.html +130 -0
- zerochat_runtime/assets/help/en/reasoning-telemetry.html +108 -0
- zerochat_runtime/assets/help/en/tools-agent.html +133 -0
- zerochat_runtime/assets/help/en/webllm.html +355 -0
- zerochat_runtime/assets/help/gemini-free.html +39 -0
- zerochat_runtime/assets/help/help.css +604 -0
- zerochat_runtime/assets/help/help.js +51 -0
- zerochat_runtime/assets/help/index.html +194 -0
- zerochat_runtime/assets/help/learning.html +71 -0
- zerochat_runtime/assets/help/mcp.html +143 -0
- zerochat_runtime/assets/help/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/profiles.html +174 -0
- zerochat_runtime/assets/help/rag.html +130 -0
- zerochat_runtime/assets/help/reasoning-telemetry.html +111 -0
- zerochat_runtime/assets/help/tools-agent.html +133 -0
- zerochat_runtime/assets/help/webllm.html +359 -0
- zerochat_runtime/assets/js/agent-core.js +1553 -0
- zerochat_runtime/assets/js/api.js +844 -0
- zerochat_runtime/assets/js/app.js +2558 -0
- zerochat_runtime/assets/js/attachments.js +220 -0
- zerochat_runtime/assets/js/charts.js +338 -0
- zerochat_runtime/assets/js/chat-engine.js +566 -0
- zerochat_runtime/assets/js/config-store.js +207 -0
- zerochat_runtime/assets/js/context-manager.js +584 -0
- zerochat_runtime/assets/js/conversation-service.js +484 -0
- zerochat_runtime/assets/js/cookies.js +688 -0
- zerochat_runtime/assets/js/data-reset-service.js +136 -0
- zerochat_runtime/assets/js/debug.js +508 -0
- zerochat_runtime/assets/js/defaults.js +16 -0
- zerochat_runtime/assets/js/export.js +179 -0
- zerochat_runtime/assets/js/file-parser.js +2088 -0
- zerochat_runtime/assets/js/generation-controller.js +417 -0
- zerochat_runtime/assets/js/i18n.js +1483 -0
- zerochat_runtime/assets/js/icons.js +156 -0
- zerochat_runtime/assets/js/ingestionEngine.js +436 -0
- zerochat_runtime/assets/js/markdown.js +588 -0
- zerochat_runtime/assets/js/mcp.js +1271 -0
- zerochat_runtime/assets/js/message-turns.js +62 -0
- zerochat_runtime/assets/js/profile-backup.js +118 -0
- zerochat_runtime/assets/js/profile-export-bundle.js +481 -0
- zerochat_runtime/assets/js/profile-repository.js +213 -0
- zerochat_runtime/assets/js/providers-webllm.js +486 -0
- zerochat_runtime/assets/js/providers.js +1560 -0
- zerochat_runtime/assets/js/rag-index.js +295 -0
- zerochat_runtime/assets/js/rag-service.js +530 -0
- zerochat_runtime/assets/js/rag-ui.js +997 -0
- zerochat_runtime/assets/js/ragStorage.js +676 -0
- zerochat_runtime/assets/js/sandbox.js +449 -0
- zerochat_runtime/assets/js/state.js +704 -0
- zerochat_runtime/assets/js/storage-db.js +144 -0
- zerochat_runtime/assets/js/tool-cards.js +330 -0
- zerochat_runtime/assets/js/tool-security.js +653 -0
- zerochat_runtime/assets/js/tools/README.md +50 -0
- zerochat_runtime/assets/js/tools/builtin/agent-checkpoint.tool.js +212 -0
- zerochat_runtime/assets/js/tools/builtin/download-pdf.tool.js +111 -0
- zerochat_runtime/assets/js/tools/builtin/execute-javascript.tool.js +219 -0
- zerochat_runtime/assets/js/tools/builtin/fetch-web-page.tool.js +112 -0
- zerochat_runtime/assets/js/tools/builtin/list-documents.tool.js +117 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-chunk.tool.js +129 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-image.tool.js +95 -0
- zerochat_runtime/assets/js/tools/builtin/render-chart.tool.js +90 -0
- zerochat_runtime/assets/js/tools/builtin/search-knowledge-base.tool.js +124 -0
- zerochat_runtime/assets/js/tools/builtin/search-web.tool.js +125 -0
- zerochat_runtime/assets/js/tools/tool-manifest.js +53 -0
- zerochat_runtime/assets/js/tools/tool-runtime.js +77 -0
- zerochat_runtime/assets/js/ui-composer.js +319 -0
- zerochat_runtime/assets/js/ui-conversation.js +655 -0
- zerochat_runtime/assets/js/ui-dialogs.js +132 -0
- zerochat_runtime/assets/js/ui-generation-status.js +119 -0
- zerochat_runtime/assets/js/ui-inspector.js +804 -0
- zerochat_runtime/assets/js/ui-mcp.js +604 -0
- zerochat_runtime/assets/js/ui-profiles.js +824 -0
- zerochat_runtime/assets/js/ui-reasoning.js +146 -0
- zerochat_runtime/assets/js/ui-settings.js +825 -0
- zerochat_runtime/assets/js/ui-shell.js +215 -0
- zerochat_runtime/assets/js/ui-sidebar.js +429 -0
- zerochat_runtime/assets/js/ui-telemetry.js +404 -0
- zerochat_runtime/assets/js/ui-transfer.js +262 -0
- zerochat_runtime/assets/js/utils.js +216 -0
- zerochat_runtime/assets/js/vendor/orama.browser.js +11 -0
- zerochat_runtime/assets/js/web-browser.js +569 -0
- zerochat_runtime/assets/js/web-search.js +519 -0
- zerochat_runtime/assets/manifest.webmanifest +20 -0
- zerochat_runtime/assets/services/dummy_mcp/dummy_mcp_server.py +47 -0
- zerochat_runtime/assets/services/dummy_mcp/service.json +22 -0
- zerochat_runtime/assets/services/lsp/installer.json +9 -0
- zerochat_runtime/assets/services/lsp/service.json +22 -0
- zerochat_runtime/assets/services/memory/installer.json +9 -0
- zerochat_runtime/assets/services/memory/service.json +24 -0
- zerochat_runtime/assets/services/playwright/installer.json +10 -0
- zerochat_runtime/assets/services/playwright/service.json +39 -0
- zerochat_runtime/assets/sw.js +164 -0
- zerochat_runtime/assets/zerochat.html +671 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/** In-memory Orama indexes derived from chunks stored in IndexedDB. */
|
|
2
|
+
(function (root, factory) {
|
|
3
|
+
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
|
4
|
+
module.exports = factory(require('@orama/orama'), require('./ragStorage.js'));
|
|
5
|
+
} else {
|
|
6
|
+
root.ChatRagIndex = factory(root.ZeroChatOrama, root.ChatRagStorage);
|
|
7
|
+
}
|
|
8
|
+
})(typeof self !== 'undefined' ? self : this, function (Orama, RagStorage) {
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const indexes = new Map();
|
|
12
|
+
const builds = new Map();
|
|
13
|
+
const documentIndexes = new Map();
|
|
14
|
+
const documentBuilds = new Map();
|
|
15
|
+
const revisions = new Map();
|
|
16
|
+
|
|
17
|
+
function assertDependencies() {
|
|
18
|
+
if (!Orama?.create || !Orama?.insertMultiple || !Orama?.search) {
|
|
19
|
+
throw new Error('El motor de búsqueda Orama no está disponible.');
|
|
20
|
+
}
|
|
21
|
+
if (!RagStorage?.getChunksByBranch) {
|
|
22
|
+
throw new Error('El almacenamiento RAG no está disponible.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function documentCacheKey(branchId, documentId) {
|
|
27
|
+
return `${branchId}:${documentId}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function createSearchIndex(branch, chunks, documents) {
|
|
31
|
+
const documentsById = new Map(documents.map(document => [document.id, document]));
|
|
32
|
+
const db = await Orama.create({
|
|
33
|
+
schema: {
|
|
34
|
+
id: 'string',
|
|
35
|
+
branchId: 'string',
|
|
36
|
+
documentId: 'string',
|
|
37
|
+
chunkId: 'string',
|
|
38
|
+
rawTitle: 'string',
|
|
39
|
+
documentTitle: 'string',
|
|
40
|
+
sectionTitle: 'string',
|
|
41
|
+
content: 'string',
|
|
42
|
+
fileType: 'string',
|
|
43
|
+
order: 'number'
|
|
44
|
+
},
|
|
45
|
+
components: { tokenizer: { language: branch.language || 'spanish', stemming: false } }
|
|
46
|
+
});
|
|
47
|
+
if (chunks.length > 0) {
|
|
48
|
+
const records = chunks.map(chunk => {
|
|
49
|
+
const document = documentsById.get(chunk.documentId) || {};
|
|
50
|
+
const rawDocTitle = document.title || '';
|
|
51
|
+
const unsluggedDocTitle = rawDocTitle.replace(/[_\-]+/g, ' ');
|
|
52
|
+
const searchableDocTitle = unsluggedDocTitle !== rawDocTitle
|
|
53
|
+
? `${rawDocTitle} ${unsluggedDocTitle}`
|
|
54
|
+
: rawDocTitle;
|
|
55
|
+
return {
|
|
56
|
+
id: chunk.id,
|
|
57
|
+
branchId: chunk.branchId || branch.id,
|
|
58
|
+
documentId: chunk.documentId,
|
|
59
|
+
chunkId: chunk.id,
|
|
60
|
+
rawTitle: rawDocTitle,
|
|
61
|
+
documentTitle: searchableDocTitle,
|
|
62
|
+
sectionTitle: chunk.title || '',
|
|
63
|
+
content: chunk.content || '',
|
|
64
|
+
fileType: document.fileType || '',
|
|
65
|
+
order: chunk.order || 0
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
await Orama.insertMultiple(db, records, 250);
|
|
69
|
+
}
|
|
70
|
+
return db;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function buildBranchIndex(branchId) {
|
|
74
|
+
if (!branchId) throw new Error('Se requiere una rama para construir el índice.');
|
|
75
|
+
assertDependencies();
|
|
76
|
+
if (indexes.has(branchId)) return indexes.get(branchId);
|
|
77
|
+
if (builds.has(branchId)) return builds.get(branchId);
|
|
78
|
+
|
|
79
|
+
const revision = revisions.get(branchId) || 0;
|
|
80
|
+
const pending = (async () => {
|
|
81
|
+
const branch = await RagStorage.getBranchById(branchId);
|
|
82
|
+
if (!branch) throw new Error(`No existe la rama ${branchId}.`);
|
|
83
|
+
const [chunks, documents] = await Promise.all([
|
|
84
|
+
RagStorage.getChunksByBranch(branchId),
|
|
85
|
+
RagStorage.getDocumentsByBranch(branchId)
|
|
86
|
+
]);
|
|
87
|
+
const db = await createSearchIndex(branch, chunks, documents);
|
|
88
|
+
if ((revisions.get(branchId) || 0) === revision) indexes.set(branchId, db);
|
|
89
|
+
return db;
|
|
90
|
+
})();
|
|
91
|
+
|
|
92
|
+
builds.set(branchId, pending);
|
|
93
|
+
try {
|
|
94
|
+
return await pending;
|
|
95
|
+
} finally {
|
|
96
|
+
if (builds.get(branchId) === pending) builds.delete(branchId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function buildDocumentIndex(branchId, documentId) {
|
|
101
|
+
if (!branchId || !documentId) throw new Error('Se requiere una rama y un documento para construir el índice.');
|
|
102
|
+
assertDependencies();
|
|
103
|
+
const key = documentCacheKey(branchId, documentId);
|
|
104
|
+
if (documentIndexes.has(key)) return documentIndexes.get(key);
|
|
105
|
+
if (documentBuilds.has(key)) return documentBuilds.get(key);
|
|
106
|
+
|
|
107
|
+
const revision = revisions.get(branchId) || 0;
|
|
108
|
+
const pending = (async () => {
|
|
109
|
+
const [branch, document] = await Promise.all([
|
|
110
|
+
RagStorage.getBranchById(branchId),
|
|
111
|
+
RagStorage.getDocumentById(documentId)
|
|
112
|
+
]);
|
|
113
|
+
if (!branch) throw new Error(`No existe la rama ${branchId}.`);
|
|
114
|
+
if (!document || document.branchId !== branchId) {
|
|
115
|
+
throw new Error(`No existe el documento ${documentId} en la rama ${branchId}.`);
|
|
116
|
+
}
|
|
117
|
+
const chunks = await RagStorage.getChunksByDocument(documentId);
|
|
118
|
+
const db = await createSearchIndex(branch, chunks, [document]);
|
|
119
|
+
if ((revisions.get(branchId) || 0) === revision) documentIndexes.set(key, db);
|
|
120
|
+
return db;
|
|
121
|
+
})();
|
|
122
|
+
|
|
123
|
+
documentBuilds.set(key, pending);
|
|
124
|
+
try {
|
|
125
|
+
return await pending;
|
|
126
|
+
} finally {
|
|
127
|
+
if (documentBuilds.get(key) === pending) documentBuilds.delete(key);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const COMMON_STOPWORDS = new Set([
|
|
132
|
+
'of', 'the', 'in', 'and', 'to', 'for', 'with', 'on', 'at', 'from', 'by', 'an', 'a',
|
|
133
|
+
'de', 'la', 'el', 'los', 'las', 'en', 'y', 'del', 'al', 'por', 'con', 'para'
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
function prepareSearchQuery(rawQuery) {
|
|
137
|
+
if (!rawQuery) return { cleanedTerm: '', tolerance: 0 };
|
|
138
|
+
// 1. Eliminar extensiones de archivo que solo añaden ruido (.pdf, .txt, etc.)
|
|
139
|
+
let cleaned = String(rawQuery).replace(/\.(?:pdf|txt|md|csv|json)\b/gi, ' ');
|
|
140
|
+
// 2. Eliminar operadores booleanos aislados en mayúsculas
|
|
141
|
+
cleaned = cleaned.replace(/\b(?:OR|AND|NOT)\b/g, ' ');
|
|
142
|
+
// 3. Normalizar guiones y barras bajas a espacios para que "3M_2018_10K" se busque como términos individuales
|
|
143
|
+
cleaned = cleaned.replace(/[_\-]+/g, ' ');
|
|
144
|
+
// 4. Limpiar comillas, corchetes y caracteres conflictivos para el tokenizador
|
|
145
|
+
cleaned = cleaned.replace(/["'()[\]{}#*:;]/g, ' ');
|
|
146
|
+
cleaned = cleaned.replace(/\s+/g, ' ').trim();
|
|
147
|
+
|
|
148
|
+
// 5. Filtrar palabras vacías (stopwords) comunes cuando hay múltiples términos
|
|
149
|
+
const tokens = cleaned.split(/\s+/).filter(Boolean);
|
|
150
|
+
const nonStopTokens = tokens.filter(t => !COMMON_STOPWORDS.has(t.toLowerCase()));
|
|
151
|
+
const effectiveTokens = (tokens.length > 2 && nonStopTokens.length > 0) ? nonStopTokens : tokens;
|
|
152
|
+
const finalTerm = effectiveTokens.join(' ');
|
|
153
|
+
|
|
154
|
+
// 6. Tolerancia inteligente:
|
|
155
|
+
// Si contiene tokens cortos (<= 3 caracteres como "3M", "AMD", "Q1", "SEC"), usar tolerance 0
|
|
156
|
+
// para evitar falsos positivos por distancia Levenshtein (ej. "3M" <-> "TM" o "PM")
|
|
157
|
+
const hasShortToken = effectiveTokens.some(t => t.length > 0 && t.length <= 3);
|
|
158
|
+
const tolerance = hasShortToken ? 0 : 1;
|
|
159
|
+
|
|
160
|
+
return { cleanedTerm: finalTerm || String(rawQuery).trim(), tolerance };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function searchIndex(db, term, options = {}) {
|
|
164
|
+
const rawQuery = String(term || '').trim();
|
|
165
|
+
if (!rawQuery) return { count: 0, elapsed: null, hits: [] };
|
|
166
|
+
const { cleanedTerm, tolerance: autoTolerance } = prepareSearchQuery(rawQuery);
|
|
167
|
+
if (!cleanedTerm) return { count: 0, elapsed: null, hits: [] };
|
|
168
|
+
|
|
169
|
+
const tolerance = Number.isInteger(options.tolerance) ? options.tolerance : autoTolerance;
|
|
170
|
+
const limit = Number(options.limit) > 0 ? Number(options.limit) : 10;
|
|
171
|
+
|
|
172
|
+
const searchParams = {
|
|
173
|
+
term: cleanedTerm,
|
|
174
|
+
properties: ['documentTitle', 'sectionTitle', 'content'],
|
|
175
|
+
boost: {
|
|
176
|
+
documentTitle: 5,
|
|
177
|
+
sectionTitle: 2,
|
|
178
|
+
content: 1
|
|
179
|
+
},
|
|
180
|
+
limit,
|
|
181
|
+
tolerance
|
|
182
|
+
};
|
|
183
|
+
if (options.groupByDocument) {
|
|
184
|
+
searchParams.groupBy = {
|
|
185
|
+
properties: ['documentId'],
|
|
186
|
+
maxResult: Number(options.maxPerDocument) > 0 ? Number(options.maxPerDocument) : 2
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const result = await Orama.search(db, searchParams);
|
|
190
|
+
let rawHits = result.hits || [];
|
|
191
|
+
if (options.groupByDocument && Array.isArray(result.groups)) {
|
|
192
|
+
const groups = result.groups.map(group => Array.isArray(group.result) ? group.result : []);
|
|
193
|
+
const groupedHits = [];
|
|
194
|
+
const maxPerDocument = Number(options.maxPerDocument) > 0 ? Number(options.maxPerDocument) : 2;
|
|
195
|
+
for (let round = 0; round < maxPerDocument && groupedHits.length < limit; round += 1) {
|
|
196
|
+
for (const group of groups) {
|
|
197
|
+
if (group[round]) groupedHits.push(group[round]);
|
|
198
|
+
if (groupedHits.length >= limit) break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
rawHits = groupedHits;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
count: result.count || 0,
|
|
205
|
+
elapsed: result.elapsed || null,
|
|
206
|
+
hits: rawHits.map(hit => ({
|
|
207
|
+
id: hit.id,
|
|
208
|
+
score: hit.score,
|
|
209
|
+
documentId: hit.document.documentId,
|
|
210
|
+
chunkId: hit.document.chunkId,
|
|
211
|
+
documentTitle: hit.document.rawTitle || hit.document.documentTitle,
|
|
212
|
+
sectionTitle: hit.document.sectionTitle,
|
|
213
|
+
content: hit.document.content,
|
|
214
|
+
order: hit.document.order,
|
|
215
|
+
branchId: hit.document.branchId
|
|
216
|
+
}))
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function searchBranch(branchId, term, options = {}) {
|
|
221
|
+
const db = await buildBranchIndex(branchId);
|
|
222
|
+
return searchIndex(db, term, options);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function searchDocuments(branchId, documentIds, term, options = {}) {
|
|
226
|
+
const ids = Array.from(new Set((Array.isArray(documentIds) ? documentIds : [documentIds])
|
|
227
|
+
.map(id => String(id || '').trim()).filter(Boolean)));
|
|
228
|
+
if (!branchId || ids.length === 0) return { count: 0, elapsed: null, hits: [] };
|
|
229
|
+
const limit = Number(options.limit) > 0 ? Number(options.limit) : 10;
|
|
230
|
+
const results = await Promise.all(ids.map(async documentId => {
|
|
231
|
+
const db = await buildDocumentIndex(branchId, documentId);
|
|
232
|
+
return searchIndex(db, term, { ...options, limit });
|
|
233
|
+
}));
|
|
234
|
+
const hits = results.flatMap(result => result.hits || [])
|
|
235
|
+
.sort((a, b) => (b.score || 0) - (a.score || 0))
|
|
236
|
+
.slice(0, limit);
|
|
237
|
+
return {
|
|
238
|
+
count: results.reduce((total, result) => total + (result.count || 0), 0),
|
|
239
|
+
elapsed: null,
|
|
240
|
+
hits
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function searchBranches(branchIds, term, options = {}) {
|
|
245
|
+
const list = Array.isArray(branchIds) ? branchIds : (branchIds ? [branchIds] : []);
|
|
246
|
+
const cleanIds = Array.from(new Set(list.map(id => String(id || '').trim()).filter(Boolean)));
|
|
247
|
+
if (cleanIds.length === 0) return { count: 0, elapsed: null, hits: [] };
|
|
248
|
+
if (cleanIds.length === 1) return searchBranch(cleanIds[0], term, options);
|
|
249
|
+
|
|
250
|
+
const limit = Number(options.limit) > 0 ? Number(options.limit) : 10;
|
|
251
|
+
const branchResults = await Promise.all(
|
|
252
|
+
cleanIds.map(async (id) => {
|
|
253
|
+
try {
|
|
254
|
+
const res = await searchBranch(id, term, { ...options, limit });
|
|
255
|
+
return (res.hits || []).map(hit => ({ ...hit, branchId: id }));
|
|
256
|
+
} catch (_) {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const allHits = branchResults.flat();
|
|
263
|
+
allHits.sort((a, b) => (b.score || 0) - (a.score || 0));
|
|
264
|
+
const topHits = allHits.slice(0, limit);
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
count: allHits.length,
|
|
268
|
+
elapsed: null,
|
|
269
|
+
hits: topHits
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function invalidateBranch(branchId) {
|
|
274
|
+
indexes.delete(branchId);
|
|
275
|
+
builds.delete(branchId);
|
|
276
|
+
const prefix = `${branchId}:`;
|
|
277
|
+
for (const key of documentIndexes.keys()) if (key.startsWith(prefix)) documentIndexes.delete(key);
|
|
278
|
+
for (const key of documentBuilds.keys()) if (key.startsWith(prefix)) documentBuilds.delete(key);
|
|
279
|
+
revisions.set(branchId, (revisions.get(branchId) || 0) + 1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function clearCache() {
|
|
283
|
+
indexes.clear();
|
|
284
|
+
builds.clear();
|
|
285
|
+
documentIndexes.clear();
|
|
286
|
+
documentBuilds.clear();
|
|
287
|
+
revisions.clear();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
buildBranchIndex, buildDocumentIndex,
|
|
292
|
+
searchBranch, searchBranches, searchDocuments,
|
|
293
|
+
invalidateBranch, clearCache
|
|
294
|
+
};
|
|
295
|
+
});
|