libsql-search 0.9.1 → 0.10.1
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 +2 -0
- package/dist/index.cjs +95 -40
- package/dist/index.d.ts +107 -8
- package/dist/index.esm.js +95 -40
- package/dist/turso.cjs +96 -0
- package/dist/turso.d.ts +156 -0
- package/dist/turso.esm.js +94 -0
- package/docs/API.md +28 -3
- package/docs/README.md +1 -0
- package/docs/TURSO.md +230 -0
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@ deno add jsr:@logan/libsql-search npm:@libsql/client@^0.17.0
|
|
|
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
|
+
|
|
34
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`).
|
|
35
37
|
|
|
36
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:
|
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,
|
|
@@ -974,11 +1017,13 @@ async function buildDocument(file, embeddingOptions) {
|
|
|
974
1017
|
return { ok: false, stage: "parse", error: toError(error) };
|
|
975
1018
|
}
|
|
976
1019
|
let embedding;
|
|
1020
|
+
let serializedEmbedding;
|
|
977
1021
|
try {
|
|
978
1022
|
embedding = await generateEmbedding(parsed.embeddingText, {
|
|
979
1023
|
...embeddingOptions,
|
|
980
1024
|
intent: embeddingOptions.intent ?? "document"
|
|
981
1025
|
});
|
|
1026
|
+
serializedEmbedding = JSON.stringify(embedding);
|
|
982
1027
|
} catch (error) {
|
|
983
1028
|
return { ok: false, stage: "embed", error: toError(error) };
|
|
984
1029
|
}
|
|
@@ -992,6 +1037,7 @@ async function buildDocument(file, embeddingOptions) {
|
|
|
992
1037
|
tags: parsed.tags,
|
|
993
1038
|
serializedTags: parsed.serializedTags,
|
|
994
1039
|
embedding,
|
|
1040
|
+
serializedEmbedding,
|
|
995
1041
|
metadata: parsed.metadata
|
|
996
1042
|
}
|
|
997
1043
|
};
|
|
@@ -1041,8 +1087,8 @@ function fallbackTitle(file) {
|
|
|
1041
1087
|
function describeType(value) {
|
|
1042
1088
|
return Array.isArray(value) ? "array" : typeof value;
|
|
1043
1089
|
}
|
|
1044
|
-
async function replaceIndex(
|
|
1045
|
-
const statements = [`DELETE FROM ${quotedTableName}`];
|
|
1090
|
+
async function replaceIndex(database, quotedTableName, documents, failures) {
|
|
1091
|
+
const statements = [{ sql: `DELETE FROM ${quotedTableName}` }];
|
|
1046
1092
|
try {
|
|
1047
1093
|
for (let i = 0; i < documents.length; i++) {
|
|
1048
1094
|
const document = documents[i];
|
|
@@ -1055,7 +1101,7 @@ async function replaceIndex(client, quotedTableName, documents, failures) {
|
|
|
1055
1101
|
documents.length = 0;
|
|
1056
1102
|
}
|
|
1057
1103
|
try {
|
|
1058
|
-
await
|
|
1104
|
+
await database.executeAtomicWrite(statements);
|
|
1059
1105
|
} catch (error) {
|
|
1060
1106
|
throw new IndexingError(
|
|
1061
1107
|
`Failed to replace the contents of ${quotedTableName}. The transaction was rolled back and the existing index was left unchanged.`,
|
|
@@ -1076,7 +1122,7 @@ function createInsertStatement(document, quotedTableName) {
|
|
|
1076
1122
|
document.content,
|
|
1077
1123
|
document.folder,
|
|
1078
1124
|
document.serializedTags,
|
|
1079
|
-
|
|
1125
|
+
document.serializedEmbedding
|
|
1080
1126
|
]
|
|
1081
1127
|
};
|
|
1082
1128
|
}
|
|
@@ -1084,12 +1130,13 @@ function toError(error) {
|
|
|
1084
1130
|
return error instanceof Error ? error : new Error(String(error));
|
|
1085
1131
|
}
|
|
1086
1132
|
async function createTable(client, tableName = "articles", dimensions = 384) {
|
|
1133
|
+
const database = resolveDatabase(client);
|
|
1087
1134
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1088
1135
|
const vectorDimensions = normalizeVectorDimensions(dimensions);
|
|
1089
1136
|
const quotedEmbeddingIndexName = quoteSqlIdentifier(`${tableName}_embedding_idx`, "embedding index name");
|
|
1090
1137
|
const quotedFolderIndexName = quoteSqlIdentifier(`${tableName}_folder_idx`, "folder index name");
|
|
1091
1138
|
const quotedSlugIndexName = quoteSqlIdentifier(`${tableName}_slug_idx`, "slug index name");
|
|
1092
|
-
await
|
|
1139
|
+
await database.executeDdl(`
|
|
1093
1140
|
CREATE TABLE IF NOT EXISTS ${quotedTableName} (
|
|
1094
1141
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1095
1142
|
slug TEXT UNIQUE NOT NULL,
|
|
@@ -1102,15 +1149,17 @@ async function createTable(client, tableName = "articles", dimensions = 384) {
|
|
|
1102
1149
|
updated_at TEXT NOT NULL
|
|
1103
1150
|
)
|
|
1104
1151
|
`);
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1152
|
+
if (database.supportsVectorIndex) {
|
|
1153
|
+
await database.executeDdl(`
|
|
1154
|
+
CREATE INDEX IF NOT EXISTS ${quotedEmbeddingIndexName}
|
|
1155
|
+
ON ${quotedTableName}(libsql_vector_idx(embedding))
|
|
1156
|
+
`);
|
|
1157
|
+
}
|
|
1158
|
+
await database.executeDdl(`
|
|
1110
1159
|
CREATE INDEX IF NOT EXISTS ${quotedFolderIndexName}
|
|
1111
1160
|
ON ${quotedTableName}(folder)
|
|
1112
1161
|
`);
|
|
1113
|
-
await
|
|
1162
|
+
await database.executeDdl(`
|
|
1114
1163
|
CREATE INDEX IF NOT EXISTS ${quotedSlugIndexName}
|
|
1115
1164
|
ON ${quotedTableName}(slug)
|
|
1116
1165
|
`);
|
|
@@ -1133,6 +1182,7 @@ async function search(options) {
|
|
|
1133
1182
|
candidates,
|
|
1134
1183
|
exact = false
|
|
1135
1184
|
} = options;
|
|
1185
|
+
const database = resolveDatabase(client);
|
|
1136
1186
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1137
1187
|
const resultLimit = normalizeSearchLimit(limit);
|
|
1138
1188
|
const embeddingIndexName = validateSqlIdentifier(
|
|
@@ -1145,20 +1195,21 @@ async function search(options) {
|
|
|
1145
1195
|
intent: embeddingOptions.intent ?? "query"
|
|
1146
1196
|
});
|
|
1147
1197
|
const queryVector = JSON.stringify(queryEmbedding);
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1198
|
+
const useExactSearch = exact || !database.supportsVectorIndex;
|
|
1199
|
+
const rows = useExactSearch ? await executeExactSearch(database, quotedTableName, queryVector, resultLimit) : await executeIndexSearch(
|
|
1200
|
+
database,
|
|
1150
1201
|
quotedTableName,
|
|
1151
1202
|
embeddingIndexName,
|
|
1152
1203
|
queryVector,
|
|
1153
1204
|
candidateCount,
|
|
1154
1205
|
resultLimit
|
|
1155
1206
|
);
|
|
1156
|
-
return
|
|
1207
|
+
return rows.map(toSearchResult);
|
|
1157
1208
|
}
|
|
1158
|
-
async function executeIndexSearch(
|
|
1209
|
+
async function executeIndexSearch(database, quotedTableName, embeddingIndexName, queryVector, candidateCount, resultLimit) {
|
|
1159
1210
|
try {
|
|
1160
|
-
return await
|
|
1161
|
-
|
|
1211
|
+
return await database.executeQuery(
|
|
1212
|
+
`
|
|
1162
1213
|
SELECT
|
|
1163
1214
|
${RESULT_COLUMNS},
|
|
1164
1215
|
vector_distance_cos(a.embedding, vector(:queryVector)) as distance
|
|
@@ -1167,20 +1218,20 @@ async function executeIndexSearch(client, quotedTableName, embeddingIndexName, q
|
|
|
1167
1218
|
ORDER BY distance, a.id
|
|
1168
1219
|
LIMIT :resultLimit
|
|
1169
1220
|
`,
|
|
1170
|
-
|
|
1221
|
+
{
|
|
1171
1222
|
queryVector,
|
|
1172
1223
|
indexName: embeddingIndexName,
|
|
1173
1224
|
candidates: candidateCount,
|
|
1174
1225
|
resultLimit
|
|
1175
1226
|
}
|
|
1176
|
-
|
|
1227
|
+
);
|
|
1177
1228
|
} catch (error) {
|
|
1178
1229
|
throw wrapIndexPathError(error, embeddingIndexName);
|
|
1179
1230
|
}
|
|
1180
1231
|
}
|
|
1181
|
-
async function executeExactSearch(
|
|
1182
|
-
return
|
|
1183
|
-
|
|
1232
|
+
async function executeExactSearch(database, quotedTableName, queryVector, resultLimit) {
|
|
1233
|
+
return database.executeQuery(
|
|
1234
|
+
`
|
|
1184
1235
|
SELECT
|
|
1185
1236
|
${RESULT_COLUMNS},
|
|
1186
1237
|
vector_distance_cos(a.embedding, vector(:queryVector)) as distance
|
|
@@ -1189,8 +1240,8 @@ async function executeExactSearch(client, quotedTableName, queryVector, resultLi
|
|
|
1189
1240
|
ORDER BY distance, a.id
|
|
1190
1241
|
LIMIT :resultLimit
|
|
1191
1242
|
`,
|
|
1192
|
-
|
|
1193
|
-
|
|
1243
|
+
{ queryVector, resultLimit }
|
|
1244
|
+
);
|
|
1194
1245
|
}
|
|
1195
1246
|
function wrapIndexPathError(error, embeddingIndexName) {
|
|
1196
1247
|
if (!(error instanceof Error)) {
|
|
@@ -1223,13 +1274,14 @@ function toSearchResult(row) {
|
|
|
1223
1274
|
};
|
|
1224
1275
|
}
|
|
1225
1276
|
async function getAllArticles(client, tableName = "articles") {
|
|
1277
|
+
const database = resolveDatabase(client);
|
|
1226
1278
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1227
|
-
const
|
|
1279
|
+
const rows = await database.executeQuery(`
|
|
1228
1280
|
SELECT id, slug, title, folder, tags, created_at, updated_at
|
|
1229
1281
|
FROM ${quotedTableName}
|
|
1230
1282
|
ORDER BY title
|
|
1231
1283
|
`);
|
|
1232
|
-
return
|
|
1284
|
+
return rows.map((row) => ({
|
|
1233
1285
|
id: row.id,
|
|
1234
1286
|
slug: row.slug,
|
|
1235
1287
|
title: row.title,
|
|
@@ -1240,20 +1292,21 @@ async function getAllArticles(client, tableName = "articles") {
|
|
|
1240
1292
|
}));
|
|
1241
1293
|
}
|
|
1242
1294
|
async function getArticleBySlug(client, slug, tableName = "articles") {
|
|
1295
|
+
const database = resolveDatabase(client);
|
|
1243
1296
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1297
|
+
const rows = await database.executeQuery(
|
|
1298
|
+
`
|
|
1246
1299
|
SELECT id, slug, title, content, folder, tags, created_at, updated_at
|
|
1247
1300
|
FROM ${quotedTableName}
|
|
1248
1301
|
WHERE slug = ?
|
|
1249
1302
|
LIMIT 1
|
|
1250
1303
|
`,
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
if (
|
|
1304
|
+
[slug]
|
|
1305
|
+
);
|
|
1306
|
+
if (rows.length === 0) {
|
|
1254
1307
|
return null;
|
|
1255
1308
|
}
|
|
1256
|
-
const row =
|
|
1309
|
+
const row = rows[0];
|
|
1257
1310
|
return {
|
|
1258
1311
|
id: row.id,
|
|
1259
1312
|
slug: row.slug,
|
|
@@ -1266,17 +1319,18 @@ async function getArticleBySlug(client, slug, tableName = "articles") {
|
|
|
1266
1319
|
};
|
|
1267
1320
|
}
|
|
1268
1321
|
async function getArticlesByFolder(client, folder, tableName = "articles") {
|
|
1322
|
+
const database = resolveDatabase(client);
|
|
1269
1323
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1324
|
+
const rows = await database.executeQuery(
|
|
1325
|
+
`
|
|
1272
1326
|
SELECT id, slug, title, folder, tags
|
|
1273
1327
|
FROM ${quotedTableName}
|
|
1274
1328
|
WHERE folder = ?
|
|
1275
1329
|
ORDER BY title
|
|
1276
1330
|
`,
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
return
|
|
1331
|
+
[folder]
|
|
1332
|
+
);
|
|
1333
|
+
return rows.map((row) => ({
|
|
1280
1334
|
id: row.id,
|
|
1281
1335
|
slug: row.slug,
|
|
1282
1336
|
title: row.title,
|
|
@@ -1285,13 +1339,14 @@ async function getArticlesByFolder(client, folder, tableName = "articles") {
|
|
|
1285
1339
|
}));
|
|
1286
1340
|
}
|
|
1287
1341
|
async function getFolders(client, tableName = "articles") {
|
|
1342
|
+
const database = resolveDatabase(client);
|
|
1288
1343
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
1289
|
-
const
|
|
1344
|
+
const rows = await database.executeQuery(`
|
|
1290
1345
|
SELECT DISTINCT folder
|
|
1291
1346
|
FROM ${quotedTableName}
|
|
1292
1347
|
ORDER BY folder
|
|
1293
1348
|
`);
|
|
1294
|
-
return
|
|
1349
|
+
return rows.map((row) => row.folder);
|
|
1295
1350
|
}
|
|
1296
1351
|
|
|
1297
1352
|
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 };
|