libsql-search 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/index.cjs +91 -39
- package/dist/index.d.ts +107 -8
- package/dist/index.esm.js +91 -39
- package/dist/turso.cjs +96 -0
- package/dist/turso.d.ts +156 -0
- package/dist/turso.esm.js +94 -0
- package/docs/API.md +31 -4
- package/docs/INDEXING.md +1 -1
- package/docs/MIGRATIONS.md +1 -0
- package/docs/README.md +1 -0
- package/docs/TROUBLESHOOTING.md +38 -0
- package/docs/TURSO.md +230 -0
- package/package.json +17 -4
package/README.md
CHANGED
|
@@ -26,11 +26,23 @@ npm install libsql-search @libsql/client
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
deno add jsr:@logan/libsql-search npm:@libsql/client
|
|
29
|
+
deno add jsr:@logan/libsql-search npm:@libsql/client@^0.17.0
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
For npm usage, the package requires Node `>=22.12.0`.
|
|
33
33
|
|
|
34
|
+
`@tursodatabase/database` is supported as an **optional** peer, behind the separate `libsql-search/turso` entry point. It is experimental and exact-search-only, because Turso Database has no ANN vector index. Nothing is installed or resolved for it unless you opt in — see the [Turso Database backend guide](./docs/TURSO.md).
|
|
35
|
+
|
|
36
|
+
**On npm/pnpm**, the peer range is `@libsql/client ^0.15.0 || ^0.17.0`. Both lines are supported: every behavior this package depends on — `vector_top_k()`'s result shape, the vector index error wording that `search()` matches on, and transactional `batch()` rollback — is identical across them, so an existing `0.15.x` install does not have to move. There is no `0.16.x` line upstream, which is why the range is a disjunction rather than a span. The packaged build is smoke-tested against both arms on every release, at the newest release each arm admits (currently `0.15.15` and `0.17.4`).
|
|
37
|
+
|
|
38
|
+
**On JSR/Deno the range does not apply to you.** `deno.json` declares no dependency on `@libsql/client` — this package imports only its *types* — so the client you `deno add` separately is constrained by nothing on our side, and a plain `deno add npm:@libsql/client` will silently take whatever is newest, including a future major we have never tested. Deno also cannot express our range: `npm:@libsql/client@^0.15.0 || ^0.17.0` is a parse error, as is any `>=`/`<` span. Pin an arm yourself instead:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
deno add jsr:@logan/libsql-search npm:@libsql/client@^0.17.0
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Note for `0.17.x`: the client no longer exports `./package.json`, so `require("@libsql/client/package.json")` throws `ERR_PACKAGE_PATH_NOT_EXPORTED`. Nothing in this package reads it, but tooling of yours that inspected the client manifest by specifier needs a direct `node_modules` path instead. See [`@libsql/client` version differences](./docs/TROUBLESHOOTING.md#libsqlclient-version-differences) for the other upgrade-visible change.
|
|
45
|
+
|
|
34
46
|
## Quick Start
|
|
35
47
|
|
|
36
48
|
This example uses the default local provider. Local embeddings run in-process after the initial model download and cache warmup; they are not automatically air-gapped.
|
|
@@ -99,7 +111,7 @@ await search({ client, query, exact: true });
|
|
|
99
111
|
|
|
100
112
|
`exact: true` is the only way to guarantee exactness. Use it for small corpora, for correctness checks against the index path, and for tables that have no vector index.
|
|
101
113
|
|
|
102
|
-
Requirements: `vector_top_k()` and `libsql_vector_idx()` need a libSQL build with native vector support. The peer dependency is `@libsql/client ^0.15.0`, verified against `0.15.15`; remote Turso/libSQL servers must support vector indexes as well. See the [API reference](./docs/API.md#searchoptions) for full semantics, and [Indexing and operations](./docs/INDEXING.md) for tables created before the index existed.
|
|
114
|
+
Requirements: `vector_top_k()` and `libsql_vector_idx()` need a libSQL build with native vector support. The peer dependency is `@libsql/client ^0.15.0 || ^0.17.0`, verified against `0.15.15` and `0.17.4`; remote Turso/libSQL servers must support vector indexes as well. See the [API reference](./docs/API.md#searchoptions) for full semantics, and [Indexing and operations](./docs/INDEXING.md) for tables created before the index existed.
|
|
103
115
|
|
|
104
116
|
## Providers
|
|
105
117
|
|
package/dist/index.cjs
CHANGED
|
@@ -833,6 +833,48 @@ function normalizeVectorDimensions(dimensions) {
|
|
|
833
833
|
return dimensions;
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
+
function isDatabaseAdapter(client) {
|
|
837
|
+
return typeof client === "object" && client !== null && client.libsqlSearchAdapter === true;
|
|
838
|
+
}
|
|
839
|
+
function resolveDatabase(client) {
|
|
840
|
+
if (isDatabaseAdapter(client)) {
|
|
841
|
+
assertCompleteAdapter(client);
|
|
842
|
+
return client;
|
|
843
|
+
}
|
|
844
|
+
return createLibsqlAdapter(client);
|
|
845
|
+
}
|
|
846
|
+
const ADAPTER_METHODS = ["executeDdl", "executeQuery", "executeAtomicWrite"];
|
|
847
|
+
function assertCompleteAdapter(adapter) {
|
|
848
|
+
const missing = ADAPTER_METHODS.filter(
|
|
849
|
+
(method) => typeof adapter[method] !== "function"
|
|
850
|
+
);
|
|
851
|
+
if (missing.length > 0) {
|
|
852
|
+
throw new TypeError(
|
|
853
|
+
`This client is marked as a libsql-search database adapter but is missing ${missing.join(", ")}. The usual cause is two different versions of libsql-search resolved in one dependency tree, so the adapter was built by a different copy of the package than the one calling it. Deduplicate libsql-search, or build the adapter from the same copy you call.`
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function createLibsqlAdapter(client) {
|
|
858
|
+
return {
|
|
859
|
+
libsqlSearchAdapter: true,
|
|
860
|
+
backend: "libsql",
|
|
861
|
+
supportsVectorIndex: true,
|
|
862
|
+
async executeDdl(sql) {
|
|
863
|
+
await client.execute(sql);
|
|
864
|
+
},
|
|
865
|
+
async executeQuery(sql, args) {
|
|
866
|
+
const result = args === void 0 ? await client.execute(sql) : await client.execute({ sql, args });
|
|
867
|
+
return result.rows;
|
|
868
|
+
},
|
|
869
|
+
async executeAtomicWrite(statements) {
|
|
870
|
+
const batch = statements.map(
|
|
871
|
+
(statement) => statement.args === void 0 ? statement.sql : statement
|
|
872
|
+
);
|
|
873
|
+
await client.batch(batch, "write");
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
836
878
|
class IndexingError extends Error {
|
|
837
879
|
phase;
|
|
838
880
|
failures;
|
|
@@ -855,6 +897,7 @@ async function indexContent(options) {
|
|
|
855
897
|
failurePolicy = "abort",
|
|
856
898
|
allowEmptyIndex = false
|
|
857
899
|
} = options;
|
|
900
|
+
const database = resolveDatabase(client);
|
|
858
901
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
859
902
|
let files;
|
|
860
903
|
try {
|
|
@@ -928,7 +971,7 @@ async function indexContent(options) {
|
|
|
928
971
|
);
|
|
929
972
|
}
|
|
930
973
|
const success = documents.length;
|
|
931
|
-
await replaceIndex(
|
|
974
|
+
await replaceIndex(database, quotedTableName, documents, failures);
|
|
932
975
|
return {
|
|
933
976
|
success,
|
|
934
977
|
failed: failures.length,
|
|
@@ -1041,8 +1084,8 @@ function fallbackTitle(file) {
|
|
|
1041
1084
|
function describeType(value) {
|
|
1042
1085
|
return Array.isArray(value) ? "array" : typeof value;
|
|
1043
1086
|
}
|
|
1044
|
-
async function replaceIndex(
|
|
1045
|
-
const statements = [`DELETE FROM ${quotedTableName}`];
|
|
1087
|
+
async function replaceIndex(database, quotedTableName, documents, failures) {
|
|
1088
|
+
const statements = [{ sql: `DELETE FROM ${quotedTableName}` }];
|
|
1046
1089
|
try {
|
|
1047
1090
|
for (let i = 0; i < documents.length; i++) {
|
|
1048
1091
|
const document = documents[i];
|
|
@@ -1055,7 +1098,7 @@ async function replaceIndex(client, quotedTableName, documents, failures) {
|
|
|
1055
1098
|
documents.length = 0;
|
|
1056
1099
|
}
|
|
1057
1100
|
try {
|
|
1058
|
-
await
|
|
1101
|
+
await database.executeAtomicWrite(statements);
|
|
1059
1102
|
} catch (error) {
|
|
1060
1103
|
throw new IndexingError(
|
|
1061
1104
|
`Failed to replace the contents of ${quotedTableName}. The transaction was rolled back and the existing index was left unchanged.`,
|
|
@@ -1084,12 +1127,13 @@ function toError(error) {
|
|
|
1084
1127
|
return error instanceof Error ? error : new Error(String(error));
|
|
1085
1128
|
}
|
|
1086
1129
|
async function createTable(client, tableName = "articles", dimensions = 384) {
|
|
1130
|
+
const database = resolveDatabase(client);
|
|
1087
1131
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1088
1132
|
const vectorDimensions = normalizeVectorDimensions(dimensions);
|
|
1089
1133
|
const quotedEmbeddingIndexName = quoteSqlIdentifier(`${tableName}_embedding_idx`, "embedding index name");
|
|
1090
1134
|
const quotedFolderIndexName = quoteSqlIdentifier(`${tableName}_folder_idx`, "folder index name");
|
|
1091
1135
|
const quotedSlugIndexName = quoteSqlIdentifier(`${tableName}_slug_idx`, "slug index name");
|
|
1092
|
-
await
|
|
1136
|
+
await database.executeDdl(`
|
|
1093
1137
|
CREATE TABLE IF NOT EXISTS ${quotedTableName} (
|
|
1094
1138
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1095
1139
|
slug TEXT UNIQUE NOT NULL,
|
|
@@ -1102,15 +1146,17 @@ async function createTable(client, tableName = "articles", dimensions = 384) {
|
|
|
1102
1146
|
updated_at TEXT NOT NULL
|
|
1103
1147
|
)
|
|
1104
1148
|
`);
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1149
|
+
if (database.supportsVectorIndex) {
|
|
1150
|
+
await database.executeDdl(`
|
|
1151
|
+
CREATE INDEX IF NOT EXISTS ${quotedEmbeddingIndexName}
|
|
1152
|
+
ON ${quotedTableName}(libsql_vector_idx(embedding))
|
|
1153
|
+
`);
|
|
1154
|
+
}
|
|
1155
|
+
await database.executeDdl(`
|
|
1110
1156
|
CREATE INDEX IF NOT EXISTS ${quotedFolderIndexName}
|
|
1111
1157
|
ON ${quotedTableName}(folder)
|
|
1112
1158
|
`);
|
|
1113
|
-
await
|
|
1159
|
+
await database.executeDdl(`
|
|
1114
1160
|
CREATE INDEX IF NOT EXISTS ${quotedSlugIndexName}
|
|
1115
1161
|
ON ${quotedTableName}(slug)
|
|
1116
1162
|
`);
|
|
@@ -1133,6 +1179,7 @@ async function search(options) {
|
|
|
1133
1179
|
candidates,
|
|
1134
1180
|
exact = false
|
|
1135
1181
|
} = options;
|
|
1182
|
+
const database = resolveDatabase(client);
|
|
1136
1183
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1137
1184
|
const resultLimit = normalizeSearchLimit(limit);
|
|
1138
1185
|
const embeddingIndexName = validateSqlIdentifier(
|
|
@@ -1145,20 +1192,21 @@ async function search(options) {
|
|
|
1145
1192
|
intent: embeddingOptions.intent ?? "query"
|
|
1146
1193
|
});
|
|
1147
1194
|
const queryVector = JSON.stringify(queryEmbedding);
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1195
|
+
const useExactSearch = exact || !database.supportsVectorIndex;
|
|
1196
|
+
const rows = useExactSearch ? await executeExactSearch(database, quotedTableName, queryVector, resultLimit) : await executeIndexSearch(
|
|
1197
|
+
database,
|
|
1150
1198
|
quotedTableName,
|
|
1151
1199
|
embeddingIndexName,
|
|
1152
1200
|
queryVector,
|
|
1153
1201
|
candidateCount,
|
|
1154
1202
|
resultLimit
|
|
1155
1203
|
);
|
|
1156
|
-
return
|
|
1204
|
+
return rows.map(toSearchResult);
|
|
1157
1205
|
}
|
|
1158
|
-
async function executeIndexSearch(
|
|
1206
|
+
async function executeIndexSearch(database, quotedTableName, embeddingIndexName, queryVector, candidateCount, resultLimit) {
|
|
1159
1207
|
try {
|
|
1160
|
-
return await
|
|
1161
|
-
|
|
1208
|
+
return await database.executeQuery(
|
|
1209
|
+
`
|
|
1162
1210
|
SELECT
|
|
1163
1211
|
${RESULT_COLUMNS},
|
|
1164
1212
|
vector_distance_cos(a.embedding, vector(:queryVector)) as distance
|
|
@@ -1167,20 +1215,20 @@ async function executeIndexSearch(client, quotedTableName, embeddingIndexName, q
|
|
|
1167
1215
|
ORDER BY distance, a.id
|
|
1168
1216
|
LIMIT :resultLimit
|
|
1169
1217
|
`,
|
|
1170
|
-
|
|
1218
|
+
{
|
|
1171
1219
|
queryVector,
|
|
1172
1220
|
indexName: embeddingIndexName,
|
|
1173
1221
|
candidates: candidateCount,
|
|
1174
1222
|
resultLimit
|
|
1175
1223
|
}
|
|
1176
|
-
|
|
1224
|
+
);
|
|
1177
1225
|
} catch (error) {
|
|
1178
1226
|
throw wrapIndexPathError(error, embeddingIndexName);
|
|
1179
1227
|
}
|
|
1180
1228
|
}
|
|
1181
|
-
async function executeExactSearch(
|
|
1182
|
-
return
|
|
1183
|
-
|
|
1229
|
+
async function executeExactSearch(database, quotedTableName, queryVector, resultLimit) {
|
|
1230
|
+
return database.executeQuery(
|
|
1231
|
+
`
|
|
1184
1232
|
SELECT
|
|
1185
1233
|
${RESULT_COLUMNS},
|
|
1186
1234
|
vector_distance_cos(a.embedding, vector(:queryVector)) as distance
|
|
@@ -1189,8 +1237,8 @@ async function executeExactSearch(client, quotedTableName, queryVector, resultLi
|
|
|
1189
1237
|
ORDER BY distance, a.id
|
|
1190
1238
|
LIMIT :resultLimit
|
|
1191
1239
|
`,
|
|
1192
|
-
|
|
1193
|
-
|
|
1240
|
+
{ queryVector, resultLimit }
|
|
1241
|
+
);
|
|
1194
1242
|
}
|
|
1195
1243
|
function wrapIndexPathError(error, embeddingIndexName) {
|
|
1196
1244
|
if (!(error instanceof Error)) {
|
|
@@ -1223,13 +1271,14 @@ function toSearchResult(row) {
|
|
|
1223
1271
|
};
|
|
1224
1272
|
}
|
|
1225
1273
|
async function getAllArticles(client, tableName = "articles") {
|
|
1274
|
+
const database = resolveDatabase(client);
|
|
1226
1275
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1227
|
-
const
|
|
1276
|
+
const rows = await database.executeQuery(`
|
|
1228
1277
|
SELECT id, slug, title, folder, tags, created_at, updated_at
|
|
1229
1278
|
FROM ${quotedTableName}
|
|
1230
1279
|
ORDER BY title
|
|
1231
1280
|
`);
|
|
1232
|
-
return
|
|
1281
|
+
return rows.map((row) => ({
|
|
1233
1282
|
id: row.id,
|
|
1234
1283
|
slug: row.slug,
|
|
1235
1284
|
title: row.title,
|
|
@@ -1240,20 +1289,21 @@ async function getAllArticles(client, tableName = "articles") {
|
|
|
1240
1289
|
}));
|
|
1241
1290
|
}
|
|
1242
1291
|
async function getArticleBySlug(client, slug, tableName = "articles") {
|
|
1292
|
+
const database = resolveDatabase(client);
|
|
1243
1293
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1294
|
+
const rows = await database.executeQuery(
|
|
1295
|
+
`
|
|
1246
1296
|
SELECT id, slug, title, content, folder, tags, created_at, updated_at
|
|
1247
1297
|
FROM ${quotedTableName}
|
|
1248
1298
|
WHERE slug = ?
|
|
1249
1299
|
LIMIT 1
|
|
1250
1300
|
`,
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
if (
|
|
1301
|
+
[slug]
|
|
1302
|
+
);
|
|
1303
|
+
if (rows.length === 0) {
|
|
1254
1304
|
return null;
|
|
1255
1305
|
}
|
|
1256
|
-
const row =
|
|
1306
|
+
const row = rows[0];
|
|
1257
1307
|
return {
|
|
1258
1308
|
id: row.id,
|
|
1259
1309
|
slug: row.slug,
|
|
@@ -1266,17 +1316,18 @@ async function getArticleBySlug(client, slug, tableName = "articles") {
|
|
|
1266
1316
|
};
|
|
1267
1317
|
}
|
|
1268
1318
|
async function getArticlesByFolder(client, folder, tableName = "articles") {
|
|
1319
|
+
const database = resolveDatabase(client);
|
|
1269
1320
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1321
|
+
const rows = await database.executeQuery(
|
|
1322
|
+
`
|
|
1272
1323
|
SELECT id, slug, title, folder, tags
|
|
1273
1324
|
FROM ${quotedTableName}
|
|
1274
1325
|
WHERE folder = ?
|
|
1275
1326
|
ORDER BY title
|
|
1276
1327
|
`,
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
return
|
|
1328
|
+
[folder]
|
|
1329
|
+
);
|
|
1330
|
+
return rows.map((row) => ({
|
|
1280
1331
|
id: row.id,
|
|
1281
1332
|
slug: row.slug,
|
|
1282
1333
|
title: row.title,
|
|
@@ -1285,13 +1336,14 @@ async function getArticlesByFolder(client, folder, tableName = "articles") {
|
|
|
1285
1336
|
}));
|
|
1286
1337
|
}
|
|
1287
1338
|
async function getFolders(client, tableName = "articles") {
|
|
1339
|
+
const database = resolveDatabase(client);
|
|
1288
1340
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1289
|
-
const
|
|
1341
|
+
const rows = await database.executeQuery(`
|
|
1290
1342
|
SELECT DISTINCT folder
|
|
1291
1343
|
FROM ${quotedTableName}
|
|
1292
1344
|
ORDER BY folder
|
|
1293
1345
|
`);
|
|
1294
|
-
return
|
|
1346
|
+
return rows.map((row) => row.folder);
|
|
1295
1347
|
}
|
|
1296
1348
|
|
|
1297
1349
|
exports.DEFAULT_SEARCH_CANDIDATE_MULTIPLIER = DEFAULT_SEARCH_CANDIDATE_MULTIPLIER;
|
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,84 @@ declare function prepareTextForEmbedding(fields: {
|
|
|
78
78
|
[key: string]: unknown;
|
|
79
79
|
}): string;
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Database backend boundary.
|
|
83
|
+
*
|
|
84
|
+
* Every SQL call this library makes goes through {@link DatabaseAdapter}. The
|
|
85
|
+
* `@libsql/client` adapter is built here, because that client is the declared
|
|
86
|
+
* peer dependency and the only backend the main entry point knows about. Other
|
|
87
|
+
* backends supply their own adapter from their own entry point, so the main
|
|
88
|
+
* bundle never imports, resolves, or references their package.
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The narrow database contract the indexer, search, and retrieval helpers use.
|
|
93
|
+
*
|
|
94
|
+
* This interface is deliberately self-contained: it names no other type from
|
|
95
|
+
* this package and no type from any backend package. That keeps the declaration
|
|
96
|
+
* emitted into every entry point's `.d.ts` structurally identical, so an adapter
|
|
97
|
+
* built by one entry point is assignable to the parameter types of another
|
|
98
|
+
* without the two entry points sharing a nominal symbol.
|
|
99
|
+
*
|
|
100
|
+
* Do not brand this with a `unique symbol`. Each bundled `.d.ts` declares its
|
|
101
|
+
* own copy of the interface, and a `unique symbol` would make those copies
|
|
102
|
+
* nominally distinct — an adapter from the `libsql-search/turso` entry point
|
|
103
|
+
* would stop being assignable to `SearchOptions['client']`.
|
|
104
|
+
*/
|
|
105
|
+
interface DatabaseAdapter {
|
|
106
|
+
/**
|
|
107
|
+
* Marker set by this package's adapter factories.
|
|
108
|
+
*
|
|
109
|
+
* Public functions accept either a raw `@libsql/client` `Client` or an
|
|
110
|
+
* adapter, and this property is how they tell the two apart. It is a plain
|
|
111
|
+
* property rather than structural method sniffing on purpose: guessing a
|
|
112
|
+
* backend from its method names would risk picking the wrong atomicity
|
|
113
|
+
* primitive, and the two backends disagree about which primitive is
|
|
114
|
+
* transactional.
|
|
115
|
+
*/
|
|
116
|
+
readonly libsqlSearchAdapter: true;
|
|
117
|
+
/** Which backend this adapter drives. Informational; behavior keys off the capability flags. */
|
|
118
|
+
readonly backend: string;
|
|
119
|
+
/**
|
|
120
|
+
* Whether the backend can build a `libsql_vector_idx()` index and query it
|
|
121
|
+
* through `vector_top_k()`.
|
|
122
|
+
*
|
|
123
|
+
* When false, `createTable()` skips the vector index and `search()` runs the
|
|
124
|
+
* exact full-scan path automatically. Both are required, not optimizations:
|
|
125
|
+
* on a backend without the index, `CREATE INDEX ... libsql_vector_idx(...)`
|
|
126
|
+
* is a parse error and `vector_top_k` is not a table.
|
|
127
|
+
*/
|
|
128
|
+
readonly supportsVectorIndex: boolean;
|
|
129
|
+
/** Run a schema statement that returns no rows. */
|
|
130
|
+
executeDdl(sql: string): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Run one statement and return its rows as plain column-keyed objects.
|
|
133
|
+
*
|
|
134
|
+
* The bind-value union is spelled out inline rather than named, so that this
|
|
135
|
+
* interface stays free of any other symbol. It is narrower than `unknown` on
|
|
136
|
+
* purpose: it is the only compile-time check that a caller is binding
|
|
137
|
+
* something a SQLite driver can actually store.
|
|
138
|
+
*/
|
|
139
|
+
executeQuery(sql: string, args?: Readonly<Record<string, string | number | bigint | Uint8Array | null>> | ReadonlyArray<string | number | bigint | Uint8Array | null>): Promise<Array<Record<string, unknown>>>;
|
|
140
|
+
/**
|
|
141
|
+
* Apply every statement atomically: either all of them commit, or none do.
|
|
142
|
+
*
|
|
143
|
+
* The whole "a failed rebuild leaves the previous index intact" guarantee
|
|
144
|
+
* rests on this method. A backend whose batch primitive is not transactional
|
|
145
|
+
* must implement this with an explicit transaction instead.
|
|
146
|
+
*/
|
|
147
|
+
executeAtomicWrite(statements: ReadonlyArray<{
|
|
148
|
+
sql: string;
|
|
149
|
+
args?: ReadonlyArray<string | number | bigint | Uint8Array | null>;
|
|
150
|
+
}>): Promise<void>;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* A client accepted by this library's public functions: either the
|
|
154
|
+
* `@libsql/client` client, or an adapter built by one of this package's
|
|
155
|
+
* adapter factories.
|
|
156
|
+
*/
|
|
157
|
+
type DatabaseClient = Client | DatabaseAdapter;
|
|
158
|
+
|
|
81
159
|
/**
|
|
82
160
|
* Content indexer for markdown and other formats
|
|
83
161
|
*/
|
|
@@ -105,7 +183,11 @@ interface IndexFailure {
|
|
|
105
183
|
error: Error;
|
|
106
184
|
}
|
|
107
185
|
interface IndexerOptions {
|
|
108
|
-
|
|
186
|
+
/**
|
|
187
|
+
* A `@libsql/client` client, or an adapter from one of this package's
|
|
188
|
+
* backend entry points, such as `tursoAdapter()` from `libsql-search/turso`.
|
|
189
|
+
*/
|
|
190
|
+
client: DatabaseClient;
|
|
109
191
|
contentPath: string;
|
|
110
192
|
embeddingOptions?: EmbeddingOptions;
|
|
111
193
|
fileExtensions?: string[];
|
|
@@ -157,9 +239,14 @@ declare class IndexingError extends Error {
|
|
|
157
239
|
*/
|
|
158
240
|
declare function indexContent(options: IndexerOptions): Promise<IndexResult>;
|
|
159
241
|
/**
|
|
160
|
-
* Create the articles table if it doesn't exist
|
|
242
|
+
* Create the articles table if it doesn't exist.
|
|
243
|
+
*
|
|
244
|
+
* Creates the table, the vector index, the folder index, and the slug index.
|
|
245
|
+
* The vector index is created only on backends that support
|
|
246
|
+
* `libsql_vector_idx()`; on Turso Database, which does not, everything else is
|
|
247
|
+
* still created and `search()` falls back to the exact full scan.
|
|
161
248
|
*/
|
|
162
|
-
declare function createTable(client:
|
|
249
|
+
declare function createTable(client: DatabaseClient, tableName?: string, dimensions?: number): Promise<void>;
|
|
163
250
|
|
|
164
251
|
/**
|
|
165
252
|
* Multiplier applied to `limit` when deriving the default candidate count.
|
|
@@ -187,7 +274,11 @@ declare const MAX_SEARCH_CANDIDATES: number;
|
|
|
187
274
|
*/
|
|
188
275
|
|
|
189
276
|
interface SearchOptions {
|
|
190
|
-
|
|
277
|
+
/**
|
|
278
|
+
* A `@libsql/client` client, or an adapter from one of this package's
|
|
279
|
+
* backend entry points, such as `tursoAdapter()` from `libsql-search/turso`.
|
|
280
|
+
*/
|
|
281
|
+
client: DatabaseClient;
|
|
191
282
|
query: string;
|
|
192
283
|
limit?: number;
|
|
193
284
|
tableName?: string;
|
|
@@ -206,6 +297,10 @@ interface SearchOptions {
|
|
|
206
297
|
/**
|
|
207
298
|
* Bypass the vector index and score every row instead. Exact but linear in
|
|
208
299
|
* table size. Defaults to `false`.
|
|
300
|
+
*
|
|
301
|
+
* Backends with no vector index take this path whether or not it is set: on
|
|
302
|
+
* Turso Database `vector_top_k()` does not exist, so there is nothing to fall
|
|
303
|
+
* back from.
|
|
209
304
|
*/
|
|
210
305
|
exact?: boolean;
|
|
211
306
|
}
|
|
@@ -231,12 +326,16 @@ interface SearchResult {
|
|
|
231
326
|
* deterministic even though the candidate set is not.
|
|
232
327
|
*
|
|
233
328
|
* Pass `exact: true` for a guaranteed-exact full scan.
|
|
329
|
+
*
|
|
330
|
+
* On a backend with no ANN vector index — Turso Database, reached through
|
|
331
|
+
* `tursoAdapter()` — the exact path is the only path, and is selected
|
|
332
|
+
* automatically without `exact: true`.
|
|
234
333
|
*/
|
|
235
334
|
declare function search(options: SearchOptions): Promise<SearchResult[]>;
|
|
236
335
|
/**
|
|
237
336
|
* Get all articles (for building static pages, navigation, etc.)
|
|
238
337
|
*/
|
|
239
|
-
declare function getAllArticles(client:
|
|
338
|
+
declare function getAllArticles(client: DatabaseClient, tableName?: string): Promise<Array<{
|
|
240
339
|
id: number;
|
|
241
340
|
slug: string;
|
|
242
341
|
title: string;
|
|
@@ -248,7 +347,7 @@ declare function getAllArticles(client: Client, tableName?: string): Promise<Arr
|
|
|
248
347
|
/**
|
|
249
348
|
* Get a single article by slug
|
|
250
349
|
*/
|
|
251
|
-
declare function getArticleBySlug(client:
|
|
350
|
+
declare function getArticleBySlug(client: DatabaseClient, slug: string, tableName?: string): Promise<{
|
|
252
351
|
id: number;
|
|
253
352
|
slug: string;
|
|
254
353
|
title: string;
|
|
@@ -261,7 +360,7 @@ declare function getArticleBySlug(client: Client, slug: string, tableName?: stri
|
|
|
261
360
|
/**
|
|
262
361
|
* Get articles by folder
|
|
263
362
|
*/
|
|
264
|
-
declare function getArticlesByFolder(client:
|
|
363
|
+
declare function getArticlesByFolder(client: DatabaseClient, folder: string, tableName?: string): Promise<Array<{
|
|
265
364
|
id: number;
|
|
266
365
|
slug: string;
|
|
267
366
|
title: string;
|
|
@@ -271,7 +370,7 @@ declare function getArticlesByFolder(client: Client, folder: string, tableName?:
|
|
|
271
370
|
/**
|
|
272
371
|
* Get all unique folders
|
|
273
372
|
*/
|
|
274
|
-
declare function getFolders(client:
|
|
373
|
+
declare function getFolders(client: DatabaseClient, tableName?: string): Promise<string[]>;
|
|
275
374
|
|
|
276
375
|
export { DEFAULT_SEARCH_CANDIDATE_MULTIPLIER, IndexingError, MAX_SEARCH_CANDIDATES, MIN_SEARCH_CANDIDATES, createEmbeddingProvider, createTable, generateEmbedding, generateEmbeddings, getAllArticles, getArticleBySlug, getArticlesByFolder, getEmbeddingProviderMetadata, getFolders, indexContent, padEmbedding, prepareTextForEmbedding, search, validateEmbeddingBatch };
|
|
277
376
|
export type { EmbeddingBatchBehavior, EmbeddingBatchItem, EmbeddingBatchItemResult, EmbeddingBatchMode, EmbeddingBatchResult, EmbeddingIntent, EmbeddingOptions, EmbeddingProvider, EmbeddingProviderClient, EmbeddingProviderMetadata, EmbeddingRequestOptions, IndexFailure, IndexFailurePolicy, IndexFailureStage, IndexResult, IndexedDocument, IndexerOptions, IndexingErrorPhase, SearchOptions, SearchResult };
|