mcmodding-mcp 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/LICENSE +21 -0
- package/README.md +324 -0
- package/dist/db-versioning.d.ts +31 -0
- package/dist/db-versioning.d.ts.map +1 -0
- package/dist/db-versioning.js +206 -0
- package/dist/db-versioning.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/chunker.d.ts +32 -0
- package/dist/indexer/chunker.d.ts.map +1 -0
- package/dist/indexer/chunker.js +157 -0
- package/dist/indexer/chunker.js.map +1 -0
- package/dist/indexer/crawler.d.ts +28 -0
- package/dist/indexer/crawler.d.ts.map +1 -0
- package/dist/indexer/crawler.js +550 -0
- package/dist/indexer/crawler.js.map +1 -0
- package/dist/indexer/embeddings.d.ts +56 -0
- package/dist/indexer/embeddings.d.ts.map +1 -0
- package/dist/indexer/embeddings.js +200 -0
- package/dist/indexer/embeddings.js.map +1 -0
- package/dist/indexer/sitemap.d.ts +35 -0
- package/dist/indexer/sitemap.d.ts.map +1 -0
- package/dist/indexer/sitemap.js +263 -0
- package/dist/indexer/sitemap.js.map +1 -0
- package/dist/indexer/store.d.ts +237 -0
- package/dist/indexer/store.d.ts.map +1 -0
- package/dist/indexer/store.js +857 -0
- package/dist/indexer/store.js.map +1 -0
- package/dist/indexer/types.d.ts +77 -0
- package/dist/indexer/types.d.ts.map +1 -0
- package/dist/indexer/types.js +2 -0
- package/dist/indexer/types.js.map +1 -0
- package/dist/indexer/updater.d.ts +52 -0
- package/dist/indexer/updater.d.ts.map +1 -0
- package/dist/indexer/updater.js +263 -0
- package/dist/indexer/updater.js.map +1 -0
- package/dist/services/concept-service.d.ts +49 -0
- package/dist/services/concept-service.d.ts.map +1 -0
- package/dist/services/concept-service.js +554 -0
- package/dist/services/concept-service.js.map +1 -0
- package/dist/services/example-service.d.ts +52 -0
- package/dist/services/example-service.d.ts.map +1 -0
- package/dist/services/example-service.js +302 -0
- package/dist/services/example-service.js.map +1 -0
- package/dist/services/search-service.d.ts +54 -0
- package/dist/services/search-service.d.ts.map +1 -0
- package/dist/services/search-service.js +519 -0
- package/dist/services/search-service.js.map +1 -0
- package/dist/services/search-utils.d.ts +34 -0
- package/dist/services/search-utils.d.ts.map +1 -0
- package/dist/services/search-utils.js +239 -0
- package/dist/services/search-utils.js.map +1 -0
- package/dist/tools/explainConcept.d.ts +9 -0
- package/dist/tools/explainConcept.d.ts.map +1 -0
- package/dist/tools/explainConcept.js +93 -0
- package/dist/tools/explainConcept.js.map +1 -0
- package/dist/tools/getExample.d.ts +20 -0
- package/dist/tools/getExample.d.ts.map +1 -0
- package/dist/tools/getExample.js +88 -0
- package/dist/tools/getExample.js.map +1 -0
- package/dist/tools/getMinecraftVersion.d.ts +6 -0
- package/dist/tools/getMinecraftVersion.d.ts.map +1 -0
- package/dist/tools/getMinecraftVersion.js +57 -0
- package/dist/tools/getMinecraftVersion.js.map +1 -0
- package/dist/tools/searchDocs.d.ts +15 -0
- package/dist/tools/searchDocs.d.ts.map +1 -0
- package/dist/tools/searchDocs.js +144 -0
- package/dist/tools/searchDocs.js.map +1 -0
- package/package.json +111 -0
- package/scripts/postinstall.js +859 -0
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
const SCHEMA_VERSION = 1;
|
|
3
|
+
export class DocumentStore {
|
|
4
|
+
db;
|
|
5
|
+
constructor(dbPath) {
|
|
6
|
+
this.db = new Database(dbPath);
|
|
7
|
+
this.initializeSchema();
|
|
8
|
+
}
|
|
9
|
+
initializeSchema() {
|
|
10
|
+
this.db.exec(`
|
|
11
|
+
-- Metadata table for versioning
|
|
12
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
13
|
+
key TEXT PRIMARY KEY,
|
|
14
|
+
value TEXT NOT NULL
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- Documents table
|
|
18
|
+
CREATE TABLE IF NOT EXISTS documents (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
url TEXT UNIQUE NOT NULL,
|
|
21
|
+
title TEXT NOT NULL,
|
|
22
|
+
content TEXT NOT NULL,
|
|
23
|
+
raw_html TEXT,
|
|
24
|
+
category TEXT NOT NULL,
|
|
25
|
+
loader TEXT NOT NULL,
|
|
26
|
+
minecraft_version TEXT,
|
|
27
|
+
hash TEXT NOT NULL,
|
|
28
|
+
indexed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
-- Sections table for structured content
|
|
33
|
+
CREATE TABLE IF NOT EXISTS sections (
|
|
34
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35
|
+
document_id INTEGER NOT NULL,
|
|
36
|
+
heading TEXT NOT NULL,
|
|
37
|
+
level INTEGER NOT NULL,
|
|
38
|
+
content TEXT NOT NULL,
|
|
39
|
+
order_num INTEGER NOT NULL,
|
|
40
|
+
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
-- Code blocks table
|
|
44
|
+
CREATE TABLE IF NOT EXISTS code_blocks (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
section_id INTEGER NOT NULL,
|
|
47
|
+
language TEXT NOT NULL,
|
|
48
|
+
code TEXT NOT NULL,
|
|
49
|
+
caption TEXT,
|
|
50
|
+
FOREIGN KEY (section_id) REFERENCES sections(id) ON DELETE CASCADE
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- Chunks table for optimized search
|
|
54
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
55
|
+
id TEXT PRIMARY KEY,
|
|
56
|
+
document_id INTEGER NOT NULL,
|
|
57
|
+
chunk_type TEXT NOT NULL,
|
|
58
|
+
content TEXT NOT NULL,
|
|
59
|
+
section_heading TEXT,
|
|
60
|
+
section_level INTEGER,
|
|
61
|
+
code_language TEXT,
|
|
62
|
+
order_num INTEGER NOT NULL,
|
|
63
|
+
word_count INTEGER NOT NULL,
|
|
64
|
+
has_code BOOLEAN NOT NULL,
|
|
65
|
+
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
-- Embeddings table for semantic search
|
|
69
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
70
|
+
chunk_id TEXT PRIMARY KEY,
|
|
71
|
+
embedding BLOB NOT NULL,
|
|
72
|
+
dimension INTEGER NOT NULL,
|
|
73
|
+
model TEXT NOT NULL,
|
|
74
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
75
|
+
FOREIGN KEY (chunk_id) REFERENCES chunks(id) ON DELETE CASCADE
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
-- Full-text search indexes
|
|
79
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
|
|
80
|
+
title,
|
|
81
|
+
content,
|
|
82
|
+
category,
|
|
83
|
+
content='documents',
|
|
84
|
+
content_rowid='id'
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
|
|
88
|
+
content,
|
|
89
|
+
section_heading,
|
|
90
|
+
content='chunks',
|
|
91
|
+
content_rowid='rowid'
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
-- Indexes for performance
|
|
95
|
+
CREATE INDEX IF NOT EXISTS idx_documents_loader ON documents(loader);
|
|
96
|
+
CREATE INDEX IF NOT EXISTS idx_documents_category ON documents(category);
|
|
97
|
+
CREATE INDEX IF NOT EXISTS idx_documents_hash ON documents(hash);
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_documents_version ON documents(minecraft_version);
|
|
99
|
+
CREATE INDEX IF NOT EXISTS idx_chunks_document ON chunks(document_id);
|
|
100
|
+
CREATE INDEX IF NOT EXISTS idx_chunks_type ON chunks(chunk_type);
|
|
101
|
+
CREATE INDEX IF NOT EXISTS idx_embeddings_model ON embeddings(model);
|
|
102
|
+
|
|
103
|
+
-- Triggers for FTS sync
|
|
104
|
+
CREATE TRIGGER IF NOT EXISTS documents_ai AFTER INSERT ON documents BEGIN
|
|
105
|
+
INSERT INTO documents_fts(rowid, title, content, category)
|
|
106
|
+
VALUES (new.id, new.title, new.content, new.category);
|
|
107
|
+
END;
|
|
108
|
+
|
|
109
|
+
CREATE TRIGGER IF NOT EXISTS documents_ad AFTER DELETE ON documents BEGIN
|
|
110
|
+
DELETE FROM documents_fts WHERE rowid = old.id;
|
|
111
|
+
END;
|
|
112
|
+
|
|
113
|
+
CREATE TRIGGER IF NOT EXISTS documents_au AFTER UPDATE ON documents BEGIN
|
|
114
|
+
UPDATE documents_fts SET
|
|
115
|
+
title = new.title,
|
|
116
|
+
content = new.content,
|
|
117
|
+
category = new.category
|
|
118
|
+
WHERE rowid = new.id;
|
|
119
|
+
END;
|
|
120
|
+
|
|
121
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
|
|
122
|
+
INSERT INTO chunks_fts(rowid, content, section_heading)
|
|
123
|
+
VALUES (new.rowid, new.content, new.section_heading);
|
|
124
|
+
END;
|
|
125
|
+
|
|
126
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON chunks BEGIN
|
|
127
|
+
DELETE FROM chunks_fts WHERE rowid = old.rowid;
|
|
128
|
+
END;
|
|
129
|
+
`);
|
|
130
|
+
this.initializeMetadata();
|
|
131
|
+
}
|
|
132
|
+
initializeMetadata() {
|
|
133
|
+
const stmt = this.db.prepare('INSERT OR IGNORE INTO metadata (key, value) VALUES (?, ?)');
|
|
134
|
+
stmt.run('schema_version', SCHEMA_VERSION.toString());
|
|
135
|
+
stmt.run('index_version', '0.1.0');
|
|
136
|
+
stmt.run('last_updated', new Date().toISOString());
|
|
137
|
+
}
|
|
138
|
+
needsUpdate(url, hash) {
|
|
139
|
+
const stmt = this.db.prepare('SELECT hash FROM documents WHERE url = ?');
|
|
140
|
+
const result = stmt.get(url);
|
|
141
|
+
return !result || result.hash !== hash;
|
|
142
|
+
}
|
|
143
|
+
storeDocument(doc) {
|
|
144
|
+
const insert = this.db.prepare(`
|
|
145
|
+
INSERT OR REPLACE INTO documents (url, title, content, raw_html, category, loader, hash, minecraft_version, updated_at)
|
|
146
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
147
|
+
`);
|
|
148
|
+
const result = insert.run(doc.url, doc.title, doc.content, doc.rawHtml, doc.category, doc.loader, doc.hash, doc.minecraftVersion || null);
|
|
149
|
+
const documentId = result.lastInsertRowid;
|
|
150
|
+
this.db.prepare('DELETE FROM sections WHERE document_id = ?').run(documentId);
|
|
151
|
+
for (const section of doc.sections) {
|
|
152
|
+
this.storeSection(documentId, section);
|
|
153
|
+
}
|
|
154
|
+
return documentId;
|
|
155
|
+
}
|
|
156
|
+
storeSection(documentId, section) {
|
|
157
|
+
const insert = this.db.prepare(`
|
|
158
|
+
INSERT INTO sections (document_id, heading, level, content, order_num)
|
|
159
|
+
VALUES (?, ?, ?, ?, ?)
|
|
160
|
+
`);
|
|
161
|
+
const result = insert.run(documentId, section.heading, section.level, section.content, section.order);
|
|
162
|
+
const sectionId = result.lastInsertRowid;
|
|
163
|
+
const codeInsert = this.db.prepare(`
|
|
164
|
+
INSERT INTO code_blocks (section_id, language, code, caption)
|
|
165
|
+
VALUES (?, ?, ?, ?)
|
|
166
|
+
`);
|
|
167
|
+
for (const block of section.codeBlocks) {
|
|
168
|
+
codeInsert.run(sectionId, block.language, block.code, block.caption || null);
|
|
169
|
+
}
|
|
170
|
+
return sectionId;
|
|
171
|
+
}
|
|
172
|
+
storeChunks(chunks, documentId) {
|
|
173
|
+
this.db.prepare('DELETE FROM chunks WHERE document_id = ?').run(documentId);
|
|
174
|
+
const insert = this.db.prepare(`
|
|
175
|
+
INSERT INTO chunks (
|
|
176
|
+
id, document_id, chunk_type, content, section_heading, section_level,
|
|
177
|
+
code_language, order_num, word_count, has_code
|
|
178
|
+
)
|
|
179
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
180
|
+
`);
|
|
181
|
+
for (const chunk of chunks) {
|
|
182
|
+
insert.run(chunk.id, documentId, chunk.chunkType, chunk.content, chunk.sectionHeading || null, chunk.sectionLevel || null, chunk.codeLanguage || null, chunk.order, chunk.metadata.wordCount, chunk.metadata.hasCode ? 1 : 0);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
searchChunks(query, category, loader, limit = 10) {
|
|
186
|
+
let sql = `
|
|
187
|
+
SELECT
|
|
188
|
+
c.id,
|
|
189
|
+
c.content,
|
|
190
|
+
c.chunk_type,
|
|
191
|
+
c.section_heading,
|
|
192
|
+
c.code_language,
|
|
193
|
+
d.url,
|
|
194
|
+
d.title,
|
|
195
|
+
d.category,
|
|
196
|
+
d.loader,
|
|
197
|
+
chunks_fts.rank
|
|
198
|
+
FROM chunks_fts
|
|
199
|
+
JOIN chunks c ON chunks_fts.rowid = c.rowid
|
|
200
|
+
JOIN documents d ON c.document_id = d.id
|
|
201
|
+
WHERE chunks_fts MATCH ?
|
|
202
|
+
`;
|
|
203
|
+
const params = [query];
|
|
204
|
+
if (category && category !== 'all') {
|
|
205
|
+
sql += ' AND d.category = ?';
|
|
206
|
+
params.push(category);
|
|
207
|
+
}
|
|
208
|
+
if (loader) {
|
|
209
|
+
sql += ' AND d.loader = ?';
|
|
210
|
+
params.push(loader);
|
|
211
|
+
}
|
|
212
|
+
sql += ` ORDER BY chunks_fts.rank LIMIT ?`;
|
|
213
|
+
params.push(limit);
|
|
214
|
+
const stmt = this.db.prepare(sql);
|
|
215
|
+
return stmt.all(...params);
|
|
216
|
+
}
|
|
217
|
+
searchDocuments(query, category, loader, limit = 10) {
|
|
218
|
+
let sql = `
|
|
219
|
+
SELECT
|
|
220
|
+
d.id,
|
|
221
|
+
d.url,
|
|
222
|
+
d.title,
|
|
223
|
+
d.content,
|
|
224
|
+
d.category,
|
|
225
|
+
d.loader,
|
|
226
|
+
documents_fts.rank
|
|
227
|
+
FROM documents_fts
|
|
228
|
+
JOIN documents d ON documents_fts.rowid = d.id
|
|
229
|
+
WHERE documents_fts MATCH ?
|
|
230
|
+
`;
|
|
231
|
+
const params = [query];
|
|
232
|
+
if (category && category !== 'all') {
|
|
233
|
+
sql += ' AND d.category = ?';
|
|
234
|
+
params.push(category);
|
|
235
|
+
}
|
|
236
|
+
if (loader) {
|
|
237
|
+
sql += ' AND d.loader = ?';
|
|
238
|
+
params.push(loader);
|
|
239
|
+
}
|
|
240
|
+
sql += ` ORDER BY documents_fts.rank LIMIT ?`;
|
|
241
|
+
params.push(limit);
|
|
242
|
+
const stmt = this.db.prepare(sql);
|
|
243
|
+
return stmt.all(...params);
|
|
244
|
+
}
|
|
245
|
+
getStats() {
|
|
246
|
+
const docCount = this.db.prepare('SELECT COUNT(*) as count FROM documents').get();
|
|
247
|
+
const sectionCount = this.db.prepare('SELECT COUNT(*) as count FROM sections').get();
|
|
248
|
+
const codeCount = this.db.prepare('SELECT COUNT(*) as count FROM code_blocks').get();
|
|
249
|
+
const loaderCounts = this.db
|
|
250
|
+
.prepare('SELECT loader, COUNT(*) as count FROM documents GROUP BY loader')
|
|
251
|
+
.all();
|
|
252
|
+
const lastUpdated = this.db
|
|
253
|
+
.prepare("SELECT value FROM metadata WHERE key = 'last_updated'")
|
|
254
|
+
.get();
|
|
255
|
+
const version = this.db
|
|
256
|
+
.prepare("SELECT value FROM metadata WHERE key = 'index_version'")
|
|
257
|
+
.get();
|
|
258
|
+
const loaders = {
|
|
259
|
+
fabric: 0,
|
|
260
|
+
neoforge: 0,
|
|
261
|
+
shared: 0,
|
|
262
|
+
};
|
|
263
|
+
for (const item of loaderCounts) {
|
|
264
|
+
if (item.loader in loaders) {
|
|
265
|
+
loaders[item.loader] = item.count;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
totalDocuments: docCount.count,
|
|
270
|
+
totalSections: sectionCount.count,
|
|
271
|
+
totalCodeBlocks: codeCount.count,
|
|
272
|
+
lastUpdated: new Date(lastUpdated.value),
|
|
273
|
+
version: version.value,
|
|
274
|
+
loaders,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
updateTimestamp() {
|
|
278
|
+
this.db
|
|
279
|
+
.prepare("UPDATE metadata SET value = ? WHERE key = 'last_updated'")
|
|
280
|
+
.run(new Date().toISOString());
|
|
281
|
+
}
|
|
282
|
+
getAllUrls() {
|
|
283
|
+
const results = this.db.prepare('SELECT url FROM documents').all();
|
|
284
|
+
return results.map((r) => r.url);
|
|
285
|
+
}
|
|
286
|
+
storeEmbeddings(embeddings, model) {
|
|
287
|
+
const insert = this.db.prepare(`
|
|
288
|
+
INSERT OR REPLACE INTO embeddings (chunk_id, embedding, dimension, model)
|
|
289
|
+
VALUES (?, ?, ?, ?)
|
|
290
|
+
`);
|
|
291
|
+
for (const item of embeddings) {
|
|
292
|
+
const buffer = Buffer.from(new Float32Array(item.embedding).buffer);
|
|
293
|
+
insert.run(item.chunkId, buffer, item.embedding.length, model);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
getEmbedding(chunkId) {
|
|
297
|
+
const stmt = this.db.prepare('SELECT embedding, dimension FROM embeddings WHERE chunk_id = ?');
|
|
298
|
+
const result = stmt.get(chunkId);
|
|
299
|
+
if (!result)
|
|
300
|
+
return undefined;
|
|
301
|
+
const float32Array = new Float32Array(result.embedding.buffer, result.embedding.byteOffset, result.embedding.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
302
|
+
return Array.from(float32Array);
|
|
303
|
+
}
|
|
304
|
+
getAllEmbeddings() {
|
|
305
|
+
const stmt = this.db.prepare('SELECT chunk_id, embedding, dimension FROM embeddings');
|
|
306
|
+
const results = stmt.all();
|
|
307
|
+
return results.map((row) => {
|
|
308
|
+
const float32Array = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
309
|
+
return {
|
|
310
|
+
chunkId: row.chunk_id,
|
|
311
|
+
embedding: Array.from(float32Array),
|
|
312
|
+
};
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
getEmbeddingsBatch(batchSize = 500, offset = 0) {
|
|
316
|
+
const stmt = this.db.prepare('SELECT chunk_id, embedding, dimension FROM embeddings LIMIT ? OFFSET ?');
|
|
317
|
+
const results = stmt.all(batchSize, offset);
|
|
318
|
+
return results.map((row) => {
|
|
319
|
+
const float32Array = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
320
|
+
return {
|
|
321
|
+
chunkId: row.chunk_id,
|
|
322
|
+
embedding: Array.from(float32Array),
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
getEmbeddingCount() {
|
|
327
|
+
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM embeddings');
|
|
328
|
+
const result = stmt.get();
|
|
329
|
+
return result.count;
|
|
330
|
+
}
|
|
331
|
+
deleteEmbeddings(documentId) {
|
|
332
|
+
this.db
|
|
333
|
+
.prepare(`
|
|
334
|
+
DELETE FROM embeddings
|
|
335
|
+
WHERE chunk_id IN (
|
|
336
|
+
SELECT id FROM chunks WHERE document_id = ?
|
|
337
|
+
)
|
|
338
|
+
`)
|
|
339
|
+
.run(documentId);
|
|
340
|
+
}
|
|
341
|
+
getDocumentsByVersion(version) {
|
|
342
|
+
const stmt = this.db.prepare(`
|
|
343
|
+
SELECT * FROM documents WHERE minecraft_version = ?
|
|
344
|
+
`);
|
|
345
|
+
return stmt.all(version);
|
|
346
|
+
}
|
|
347
|
+
getAllVersions() {
|
|
348
|
+
const stmt = this.db.prepare(`
|
|
349
|
+
SELECT DISTINCT minecraft_version FROM documents
|
|
350
|
+
WHERE minecraft_version IS NOT NULL
|
|
351
|
+
ORDER BY minecraft_version DESC
|
|
352
|
+
`);
|
|
353
|
+
const results = stmt.all();
|
|
354
|
+
return results.map((r) => r.minecraft_version);
|
|
355
|
+
}
|
|
356
|
+
searchByVersion(query, version, category, loader, limit = 10) {
|
|
357
|
+
let sql = `
|
|
358
|
+
SELECT
|
|
359
|
+
c.id,
|
|
360
|
+
c.content,
|
|
361
|
+
c.chunk_type,
|
|
362
|
+
c.section_heading,
|
|
363
|
+
c.code_language,
|
|
364
|
+
d.url,
|
|
365
|
+
d.title,
|
|
366
|
+
d.category,
|
|
367
|
+
d.loader,
|
|
368
|
+
d.minecraft_version,
|
|
369
|
+
chunks_fts.rank
|
|
370
|
+
FROM chunks_fts
|
|
371
|
+
JOIN chunks c ON chunks_fts.rowid = c.rowid
|
|
372
|
+
JOIN documents d ON c.document_id = d.id
|
|
373
|
+
WHERE chunks_fts MATCH ? AND d.minecraft_version = ?
|
|
374
|
+
`;
|
|
375
|
+
const params = [query, version];
|
|
376
|
+
if (category && category !== 'all') {
|
|
377
|
+
sql += ' AND d.category = ?';
|
|
378
|
+
params.push(category);
|
|
379
|
+
}
|
|
380
|
+
if (loader) {
|
|
381
|
+
sql += ' AND d.loader = ?';
|
|
382
|
+
params.push(loader);
|
|
383
|
+
}
|
|
384
|
+
sql += ` ORDER BY chunks_fts.rank LIMIT ?`;
|
|
385
|
+
params.push(limit);
|
|
386
|
+
const stmt = this.db.prepare(sql);
|
|
387
|
+
return stmt.all(...params);
|
|
388
|
+
}
|
|
389
|
+
getEmbeddingStats() {
|
|
390
|
+
const total = this.db.prepare('SELECT COUNT(*) as count FROM embeddings').get();
|
|
391
|
+
const models = this.db
|
|
392
|
+
.prepare('SELECT model, COUNT(*) as count FROM embeddings GROUP BY model')
|
|
393
|
+
.all();
|
|
394
|
+
return {
|
|
395
|
+
totalEmbeddings: total.count,
|
|
396
|
+
models,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
searchCodeExamples(topic, options = {}) {
|
|
400
|
+
const { language, minecraftVersion, loader, category, limit = 10 } = options;
|
|
401
|
+
const searchQuery = topic.trim();
|
|
402
|
+
let sql = `
|
|
403
|
+
SELECT
|
|
404
|
+
c.id,
|
|
405
|
+
c.content,
|
|
406
|
+
c.chunk_type,
|
|
407
|
+
c.section_heading,
|
|
408
|
+
c.section_level,
|
|
409
|
+
c.code_language,
|
|
410
|
+
c.has_code,
|
|
411
|
+
d.id as document_id,
|
|
412
|
+
d.url,
|
|
413
|
+
d.title,
|
|
414
|
+
d.category,
|
|
415
|
+
d.loader,
|
|
416
|
+
d.minecraft_version,
|
|
417
|
+
chunks_fts.rank
|
|
418
|
+
FROM chunks_fts
|
|
419
|
+
JOIN chunks c ON chunks_fts.rowid = c.rowid
|
|
420
|
+
JOIN documents d ON c.document_id = d.id
|
|
421
|
+
WHERE chunks_fts MATCH ?
|
|
422
|
+
AND c.has_code = 1
|
|
423
|
+
`;
|
|
424
|
+
const params = [searchQuery];
|
|
425
|
+
if (language) {
|
|
426
|
+
sql += ' AND c.code_language = ?';
|
|
427
|
+
params.push(language);
|
|
428
|
+
}
|
|
429
|
+
if (minecraftVersion) {
|
|
430
|
+
sql += ' AND d.minecraft_version = ?';
|
|
431
|
+
params.push(minecraftVersion);
|
|
432
|
+
}
|
|
433
|
+
if (loader) {
|
|
434
|
+
sql += ' AND d.loader = ?';
|
|
435
|
+
params.push(loader);
|
|
436
|
+
}
|
|
437
|
+
if (category && category !== 'all') {
|
|
438
|
+
sql += ' AND d.category = ?';
|
|
439
|
+
params.push(category);
|
|
440
|
+
}
|
|
441
|
+
sql += ` ORDER BY chunks_fts.rank LIMIT ?`;
|
|
442
|
+
params.push(limit);
|
|
443
|
+
const stmt = this.db.prepare(sql);
|
|
444
|
+
return stmt.all(...params);
|
|
445
|
+
}
|
|
446
|
+
getCodeBlocksWithContext(documentId) {
|
|
447
|
+
const sql = `
|
|
448
|
+
SELECT
|
|
449
|
+
cb.id,
|
|
450
|
+
cb.language,
|
|
451
|
+
cb.code,
|
|
452
|
+
cb.caption,
|
|
453
|
+
s.heading as section_heading,
|
|
454
|
+
s.level as section_level,
|
|
455
|
+
s.content as section_content,
|
|
456
|
+
d.id as document_id,
|
|
457
|
+
d.title as document_title,
|
|
458
|
+
d.url as document_url,
|
|
459
|
+
d.category,
|
|
460
|
+
d.loader,
|
|
461
|
+
d.minecraft_version
|
|
462
|
+
FROM code_blocks cb
|
|
463
|
+
JOIN sections s ON cb.section_id = s.id
|
|
464
|
+
JOIN documents d ON s.document_id = d.id
|
|
465
|
+
WHERE d.id = ?
|
|
466
|
+
ORDER BY s.order_num, cb.id
|
|
467
|
+
`;
|
|
468
|
+
const stmt = this.db.prepare(sql);
|
|
469
|
+
return stmt.all(documentId);
|
|
470
|
+
}
|
|
471
|
+
getCodeBlocksByLanguage(language, options = {}) {
|
|
472
|
+
const { loader, minecraftVersion, limit = 20 } = options;
|
|
473
|
+
let sql = `
|
|
474
|
+
SELECT
|
|
475
|
+
cb.id,
|
|
476
|
+
cb.language,
|
|
477
|
+
cb.code,
|
|
478
|
+
cb.caption,
|
|
479
|
+
s.heading as section_heading,
|
|
480
|
+
s.level as section_level,
|
|
481
|
+
s.content as section_content,
|
|
482
|
+
d.id as document_id,
|
|
483
|
+
d.title as document_title,
|
|
484
|
+
d.url as document_url,
|
|
485
|
+
d.category,
|
|
486
|
+
d.loader,
|
|
487
|
+
d.minecraft_version
|
|
488
|
+
FROM code_blocks cb
|
|
489
|
+
JOIN sections s ON cb.section_id = s.id
|
|
490
|
+
JOIN documents d ON s.document_id = d.id
|
|
491
|
+
WHERE cb.language = ?
|
|
492
|
+
`;
|
|
493
|
+
const params = [language];
|
|
494
|
+
if (loader) {
|
|
495
|
+
sql += ' AND d.loader = ?';
|
|
496
|
+
params.push(loader);
|
|
497
|
+
}
|
|
498
|
+
if (minecraftVersion) {
|
|
499
|
+
sql += ' AND d.minecraft_version = ?';
|
|
500
|
+
params.push(minecraftVersion);
|
|
501
|
+
}
|
|
502
|
+
sql += ' ORDER BY d.id, s.order_num LIMIT ?';
|
|
503
|
+
params.push(limit);
|
|
504
|
+
const stmt = this.db.prepare(sql);
|
|
505
|
+
return stmt.all(...params);
|
|
506
|
+
}
|
|
507
|
+
searchSections(query, options = {}) {
|
|
508
|
+
const { loader, minecraftVersion, category, limit = 10 } = options;
|
|
509
|
+
let sql = `
|
|
510
|
+
SELECT
|
|
511
|
+
s.id,
|
|
512
|
+
s.heading,
|
|
513
|
+
s.level,
|
|
514
|
+
s.content,
|
|
515
|
+
s.order_num,
|
|
516
|
+
d.id as document_id,
|
|
517
|
+
d.title as document_title,
|
|
518
|
+
d.url as document_url,
|
|
519
|
+
d.category,
|
|
520
|
+
d.loader,
|
|
521
|
+
d.minecraft_version
|
|
522
|
+
FROM sections s
|
|
523
|
+
JOIN documents d ON s.document_id = d.id
|
|
524
|
+
WHERE s.heading LIKE ? OR s.content LIKE ?
|
|
525
|
+
`;
|
|
526
|
+
const searchPattern = `%${query}%`;
|
|
527
|
+
const params = [searchPattern, searchPattern];
|
|
528
|
+
if (loader) {
|
|
529
|
+
sql += ' AND d.loader = ?';
|
|
530
|
+
params.push(loader);
|
|
531
|
+
}
|
|
532
|
+
if (minecraftVersion) {
|
|
533
|
+
sql += ' AND d.minecraft_version = ?';
|
|
534
|
+
params.push(minecraftVersion);
|
|
535
|
+
}
|
|
536
|
+
if (category && category !== 'all') {
|
|
537
|
+
sql += ' AND d.category = ?';
|
|
538
|
+
params.push(category);
|
|
539
|
+
}
|
|
540
|
+
sql += ' ORDER BY s.order_num LIMIT ?';
|
|
541
|
+
params.push(limit);
|
|
542
|
+
const stmt = this.db.prepare(sql);
|
|
543
|
+
return stmt.all(...params);
|
|
544
|
+
}
|
|
545
|
+
getAvailableLanguages() {
|
|
546
|
+
const sql = `
|
|
547
|
+
SELECT code_language as language, COUNT(*) as count
|
|
548
|
+
FROM chunks
|
|
549
|
+
WHERE has_code = 1 AND code_language IS NOT NULL
|
|
550
|
+
GROUP BY code_language
|
|
551
|
+
ORDER BY count DESC
|
|
552
|
+
`;
|
|
553
|
+
const stmt = this.db.prepare(sql);
|
|
554
|
+
return stmt.all();
|
|
555
|
+
}
|
|
556
|
+
findSimilarChunks(embedding, options = {}) {
|
|
557
|
+
const { limit = 10, loader, minecraftVersion, category } = options;
|
|
558
|
+
let sql = `
|
|
559
|
+
SELECT
|
|
560
|
+
e.chunk_id,
|
|
561
|
+
e.embedding,
|
|
562
|
+
c.content,
|
|
563
|
+
c.chunk_type,
|
|
564
|
+
c.section_heading,
|
|
565
|
+
c.section_level,
|
|
566
|
+
c.code_language,
|
|
567
|
+
c.has_code,
|
|
568
|
+
d.id as document_id,
|
|
569
|
+
d.url,
|
|
570
|
+
d.title,
|
|
571
|
+
d.category,
|
|
572
|
+
d.loader,
|
|
573
|
+
d.minecraft_version
|
|
574
|
+
FROM embeddings e
|
|
575
|
+
JOIN chunks c ON e.chunk_id = c.id
|
|
576
|
+
JOIN documents d ON c.document_id = d.id
|
|
577
|
+
WHERE 1=1
|
|
578
|
+
`;
|
|
579
|
+
const params = [];
|
|
580
|
+
if (loader) {
|
|
581
|
+
sql += ' AND d.loader = ?';
|
|
582
|
+
params.push(loader);
|
|
583
|
+
}
|
|
584
|
+
if (category && category !== 'all') {
|
|
585
|
+
sql += ' AND d.category = ?';
|
|
586
|
+
params.push(category);
|
|
587
|
+
}
|
|
588
|
+
if (minecraftVersion) {
|
|
589
|
+
if (minecraftVersion.includes('%')) {
|
|
590
|
+
sql += ' AND d.minecraft_version LIKE ?';
|
|
591
|
+
params.push(minecraftVersion);
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
sql += ' AND d.minecraft_version = ?';
|
|
595
|
+
params.push(minecraftVersion);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const stmt = this.db.prepare(sql);
|
|
599
|
+
const candidates = stmt.all(...params);
|
|
600
|
+
const results = candidates.map((candidate) => {
|
|
601
|
+
const vector = new Float32Array(candidate.embedding.buffer, candidate.embedding.byteOffset, candidate.embedding.byteLength / 4);
|
|
602
|
+
const similarity = this.cosineSimilarity(embedding, vector);
|
|
603
|
+
return { ...candidate, similarity };
|
|
604
|
+
});
|
|
605
|
+
return results.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
606
|
+
}
|
|
607
|
+
cosineSimilarity(a, b) {
|
|
608
|
+
let dotProduct = 0;
|
|
609
|
+
let normA = 0;
|
|
610
|
+
let normB = 0;
|
|
611
|
+
for (let i = 0; i < a.length; i++) {
|
|
612
|
+
dotProduct += a[i] * b[i];
|
|
613
|
+
normA += a[i] * a[i];
|
|
614
|
+
normB += b[i] * b[i];
|
|
615
|
+
}
|
|
616
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
617
|
+
}
|
|
618
|
+
searchChunksAdvanced(ftsQuery, likePatterns, options = {}) {
|
|
619
|
+
const { hasCode = true, language, loader, minecraftVersion, category, limit = 30 } = options;
|
|
620
|
+
let results = [];
|
|
621
|
+
if (ftsQuery && ftsQuery.length > 0) {
|
|
622
|
+
try {
|
|
623
|
+
let sql = `
|
|
624
|
+
SELECT
|
|
625
|
+
c.id,
|
|
626
|
+
c.content,
|
|
627
|
+
c.chunk_type,
|
|
628
|
+
c.section_heading,
|
|
629
|
+
c.section_level,
|
|
630
|
+
c.code_language,
|
|
631
|
+
c.has_code,
|
|
632
|
+
d.id as document_id,
|
|
633
|
+
d.url,
|
|
634
|
+
d.title,
|
|
635
|
+
d.category,
|
|
636
|
+
d.loader,
|
|
637
|
+
d.minecraft_version
|
|
638
|
+
FROM chunks_fts
|
|
639
|
+
JOIN chunks c ON chunks_fts.rowid = c.rowid
|
|
640
|
+
JOIN documents d ON c.document_id = d.id
|
|
641
|
+
WHERE chunks_fts MATCH ?
|
|
642
|
+
`;
|
|
643
|
+
const params = [ftsQuery];
|
|
644
|
+
if (hasCode) {
|
|
645
|
+
sql += ' AND c.has_code = 1';
|
|
646
|
+
}
|
|
647
|
+
if (language) {
|
|
648
|
+
sql += ' AND c.code_language = ?';
|
|
649
|
+
params.push(language);
|
|
650
|
+
}
|
|
651
|
+
if (loader) {
|
|
652
|
+
sql += ' AND d.loader = ?';
|
|
653
|
+
params.push(loader);
|
|
654
|
+
}
|
|
655
|
+
if (minecraftVersion) {
|
|
656
|
+
sql += ' AND d.minecraft_version = ?';
|
|
657
|
+
params.push(minecraftVersion);
|
|
658
|
+
}
|
|
659
|
+
if (category && category !== 'all') {
|
|
660
|
+
sql += ' AND d.category = ?';
|
|
661
|
+
params.push(category);
|
|
662
|
+
}
|
|
663
|
+
sql += ` ORDER BY rank LIMIT ?`;
|
|
664
|
+
params.push(limit);
|
|
665
|
+
const stmt = this.db.prepare(sql);
|
|
666
|
+
results = stmt.all(...params);
|
|
667
|
+
}
|
|
668
|
+
catch {
|
|
669
|
+
console.error('[store] FTS query failed, trying LIKE fallback');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (results.length === 0 && likePatterns.length > 0) {
|
|
673
|
+
const likeConditions = likePatterns
|
|
674
|
+
.slice(0, 3)
|
|
675
|
+
.map(() => '(c.content LIKE ? OR c.section_heading LIKE ?)')
|
|
676
|
+
.join(' OR ');
|
|
677
|
+
let sql = `
|
|
678
|
+
SELECT
|
|
679
|
+
c.id,
|
|
680
|
+
c.content,
|
|
681
|
+
c.chunk_type,
|
|
682
|
+
c.section_heading,
|
|
683
|
+
c.section_level,
|
|
684
|
+
c.code_language,
|
|
685
|
+
c.has_code,
|
|
686
|
+
d.id as document_id,
|
|
687
|
+
d.url,
|
|
688
|
+
d.title,
|
|
689
|
+
d.category,
|
|
690
|
+
d.loader,
|
|
691
|
+
d.minecraft_version
|
|
692
|
+
FROM chunks c
|
|
693
|
+
JOIN documents d ON c.document_id = d.id
|
|
694
|
+
WHERE (${likeConditions})
|
|
695
|
+
`;
|
|
696
|
+
const params = [];
|
|
697
|
+
for (const pattern of likePatterns.slice(0, 3)) {
|
|
698
|
+
params.push(pattern, pattern);
|
|
699
|
+
}
|
|
700
|
+
if (hasCode) {
|
|
701
|
+
sql += ' AND c.has_code = 1';
|
|
702
|
+
}
|
|
703
|
+
if (language) {
|
|
704
|
+
sql += ' AND c.code_language = ?';
|
|
705
|
+
params.push(language);
|
|
706
|
+
}
|
|
707
|
+
if (loader) {
|
|
708
|
+
sql += ' AND d.loader = ?';
|
|
709
|
+
params.push(loader);
|
|
710
|
+
}
|
|
711
|
+
if (minecraftVersion) {
|
|
712
|
+
sql += ' AND d.minecraft_version = ?';
|
|
713
|
+
params.push(minecraftVersion);
|
|
714
|
+
}
|
|
715
|
+
if (category && category !== 'all') {
|
|
716
|
+
sql += ' AND d.category = ?';
|
|
717
|
+
params.push(category);
|
|
718
|
+
}
|
|
719
|
+
sql += ` LIMIT ?`;
|
|
720
|
+
params.push(limit);
|
|
721
|
+
const stmt = this.db.prepare(sql);
|
|
722
|
+
results = stmt.all(...params);
|
|
723
|
+
}
|
|
724
|
+
return results;
|
|
725
|
+
}
|
|
726
|
+
searchDocumentsLike(patterns, options = {}) {
|
|
727
|
+
const { loader, minecraftVersion, category, limit = 20 } = options;
|
|
728
|
+
if (patterns.length === 0)
|
|
729
|
+
return [];
|
|
730
|
+
const likeConditions = patterns
|
|
731
|
+
.slice(0, 3)
|
|
732
|
+
.map(() => '(d.title LIKE ? OR d.content LIKE ?)')
|
|
733
|
+
.join(' OR ');
|
|
734
|
+
let sql = `
|
|
735
|
+
SELECT
|
|
736
|
+
d.id,
|
|
737
|
+
d.url,
|
|
738
|
+
d.title,
|
|
739
|
+
d.content,
|
|
740
|
+
d.category,
|
|
741
|
+
d.loader,
|
|
742
|
+
d.minecraft_version
|
|
743
|
+
FROM documents d
|
|
744
|
+
WHERE (${likeConditions})
|
|
745
|
+
`;
|
|
746
|
+
const params = [];
|
|
747
|
+
for (const pattern of patterns.slice(0, 3)) {
|
|
748
|
+
params.push(pattern, pattern);
|
|
749
|
+
}
|
|
750
|
+
if (loader) {
|
|
751
|
+
sql += ' AND d.loader = ?';
|
|
752
|
+
params.push(loader);
|
|
753
|
+
}
|
|
754
|
+
if (minecraftVersion) {
|
|
755
|
+
sql += ' AND d.minecraft_version = ?';
|
|
756
|
+
params.push(minecraftVersion);
|
|
757
|
+
}
|
|
758
|
+
if (category && category !== 'all') {
|
|
759
|
+
sql += ' AND d.category = ?';
|
|
760
|
+
params.push(category);
|
|
761
|
+
}
|
|
762
|
+
sql += ` LIMIT ?`;
|
|
763
|
+
params.push(limit);
|
|
764
|
+
const stmt = this.db.prepare(sql);
|
|
765
|
+
return stmt.all(...params);
|
|
766
|
+
}
|
|
767
|
+
searchCodeBlocksByPatterns(codePatterns, options = {}) {
|
|
768
|
+
const { language, loader, minecraftVersion, limit = 20 } = options;
|
|
769
|
+
if (codePatterns.length === 0)
|
|
770
|
+
return [];
|
|
771
|
+
const likeConditions = codePatterns
|
|
772
|
+
.slice(0, 5)
|
|
773
|
+
.map(() => 'cb.code LIKE ?')
|
|
774
|
+
.join(' OR ');
|
|
775
|
+
let sql = `
|
|
776
|
+
SELECT
|
|
777
|
+
cb.id,
|
|
778
|
+
cb.language,
|
|
779
|
+
cb.code,
|
|
780
|
+
cb.caption,
|
|
781
|
+
s.heading as section_heading,
|
|
782
|
+
s.level as section_level,
|
|
783
|
+
s.content as section_content,
|
|
784
|
+
d.id as document_id,
|
|
785
|
+
d.title as document_title,
|
|
786
|
+
d.url as document_url,
|
|
787
|
+
d.category,
|
|
788
|
+
d.loader,
|
|
789
|
+
d.minecraft_version
|
|
790
|
+
FROM code_blocks cb
|
|
791
|
+
JOIN sections s ON cb.section_id = s.id
|
|
792
|
+
JOIN documents d ON s.document_id = d.id
|
|
793
|
+
WHERE (${likeConditions})
|
|
794
|
+
`;
|
|
795
|
+
const params = codePatterns.slice(0, 5).map((p) => `%${p}%`);
|
|
796
|
+
if (language) {
|
|
797
|
+
sql += ' AND cb.language = ?';
|
|
798
|
+
params.push(language);
|
|
799
|
+
}
|
|
800
|
+
if (loader) {
|
|
801
|
+
sql += ' AND d.loader = ?';
|
|
802
|
+
params.push(loader);
|
|
803
|
+
}
|
|
804
|
+
if (minecraftVersion) {
|
|
805
|
+
sql += ' AND d.minecraft_version = ?';
|
|
806
|
+
params.push(minecraftVersion);
|
|
807
|
+
}
|
|
808
|
+
sql += ` ORDER BY d.id, s.order_num LIMIT ?`;
|
|
809
|
+
params.push(limit);
|
|
810
|
+
const stmt = this.db.prepare(sql);
|
|
811
|
+
return stmt.all(...params);
|
|
812
|
+
}
|
|
813
|
+
getAllCodeBlocksWithContext(options = {}) {
|
|
814
|
+
const { language, loader, minecraftVersion, limit = 100 } = options;
|
|
815
|
+
let sql = `
|
|
816
|
+
SELECT
|
|
817
|
+
cb.id,
|
|
818
|
+
cb.language,
|
|
819
|
+
cb.code,
|
|
820
|
+
cb.caption,
|
|
821
|
+
s.heading as section_heading,
|
|
822
|
+
s.level as section_level,
|
|
823
|
+
s.content as section_content,
|
|
824
|
+
d.id as document_id,
|
|
825
|
+
d.title as document_title,
|
|
826
|
+
d.url as document_url,
|
|
827
|
+
d.category,
|
|
828
|
+
d.loader,
|
|
829
|
+
d.minecraft_version
|
|
830
|
+
FROM code_blocks cb
|
|
831
|
+
JOIN sections s ON cb.section_id = s.id
|
|
832
|
+
JOIN documents d ON s.document_id = d.id
|
|
833
|
+
WHERE 1=1
|
|
834
|
+
`;
|
|
835
|
+
const params = [];
|
|
836
|
+
if (language) {
|
|
837
|
+
sql += ' AND cb.language = ?';
|
|
838
|
+
params.push(language);
|
|
839
|
+
}
|
|
840
|
+
if (loader) {
|
|
841
|
+
sql += ' AND d.loader = ?';
|
|
842
|
+
params.push(loader);
|
|
843
|
+
}
|
|
844
|
+
if (minecraftVersion) {
|
|
845
|
+
sql += ' AND d.minecraft_version = ?';
|
|
846
|
+
params.push(minecraftVersion);
|
|
847
|
+
}
|
|
848
|
+
sql += ` ORDER BY d.id, s.order_num LIMIT ?`;
|
|
849
|
+
params.push(limit);
|
|
850
|
+
const stmt = this.db.prepare(sql);
|
|
851
|
+
return stmt.all(...params);
|
|
852
|
+
}
|
|
853
|
+
close() {
|
|
854
|
+
this.db.close();
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
//# sourceMappingURL=store.js.map
|