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 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(client, quotedTableName, documents, failures);
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(client, quotedTableName, documents, failures) {
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 client.batch(statements, "write");
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
- JSON.stringify(document.embedding)
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 client.execute(`
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
- await client.execute(`
1106
- CREATE INDEX IF NOT EXISTS ${quotedEmbeddingIndexName}
1107
- ON ${quotedTableName}(libsql_vector_idx(embedding))
1108
- `);
1109
- await client.execute(`
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 client.execute(`
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 results = exact ? await executeExactSearch(client, quotedTableName, queryVector, resultLimit) : await executeIndexSearch(
1149
- client,
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 results.rows.map(toSearchResult);
1207
+ return rows.map(toSearchResult);
1157
1208
  }
1158
- async function executeIndexSearch(client, quotedTableName, embeddingIndexName, queryVector, candidateCount, resultLimit) {
1209
+ async function executeIndexSearch(database, quotedTableName, embeddingIndexName, queryVector, candidateCount, resultLimit) {
1159
1210
  try {
1160
- return await client.execute({
1161
- sql: `
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
- args: {
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(client, quotedTableName, queryVector, resultLimit) {
1182
- return client.execute({
1183
- sql: `
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
- args: { queryVector, resultLimit }
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 results = await client.execute(`
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 results.rows.map((row) => ({
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 results = await client.execute({
1245
- sql: `
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
- args: [slug]
1252
- });
1253
- if (results.rows.length === 0) {
1304
+ [slug]
1305
+ );
1306
+ if (rows.length === 0) {
1254
1307
  return null;
1255
1308
  }
1256
- const row = results.rows[0];
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 results = await client.execute({
1271
- sql: `
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
- args: [folder]
1278
- });
1279
- return results.rows.map((row) => ({
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 results = await client.execute(`
1344
+ const rows = await database.executeQuery(`
1290
1345
  SELECT DISTINCT folder
1291
1346
  FROM ${quotedTableName}
1292
1347
  ORDER BY folder
1293
1348
  `);
1294
- return results.rows.map((row) => row.folder);
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
- client: Client;
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: Client, tableName?: string, dimensions?: number): Promise<void>;
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
- client: Client;
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: Client, tableName?: string): Promise<Array<{
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: Client, slug: string, tableName?: string): Promise<{
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: Client, folder: string, tableName?: string): Promise<Array<{
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: Client, tableName?: string): Promise<string[]>;
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 };