libsql-search 0.7.0 → 0.8.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 +28 -48
- package/dist/index.cjs +184 -31
- package/dist/index.d.ts +56 -8
- package/dist/index.esm.js +184 -32
- package/docs/API.md +182 -112
- package/docs/INDEXING.md +88 -26
- package/docs/INTEGRATIONS.md +220 -40
- package/docs/MIGRATIONS.md +176 -0
- package/docs/PROVIDERS.md +86 -245
- package/docs/README.md +7 -9
- package/docs/RELEASING.md +11 -3
- package/docs/TESTING.md +138 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,27 +5,13 @@
|
|
|
5
5
|
[](https://github.com/llbbl/libsql-search/actions/workflows/ci.yml)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
`libsql-search` adds semantic search to Markdown-backed sites
|
|
9
|
-
It indexes frontmatter and content from files on disk, stores vectors in your
|
|
10
|
-
database, and lets you query by meaning instead of exact keywords.
|
|
8
|
+
`libsql-search` adds semantic search to Markdown-backed sites with a small TypeScript API. It indexes frontmatter and content from files on disk, stores vectors in libSQL/Turso, and lets you query by meaning instead of exact keywords.
|
|
11
9
|
|
|
12
10
|
Use it when you want:
|
|
13
11
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- direct control over table names, dimensions, content shape, and deployment
|
|
18
|
-
|
|
19
|
-
## What It Supports
|
|
20
|
-
|
|
21
|
-
- Markdown indexing from local directories with frontmatter via `gray-matter`
|
|
22
|
-
- libSQL/Turso storage and vector search
|
|
23
|
-
- Embedding providers: local Hugging Face `Xenova/all-MiniLM-L6-v2`,
|
|
24
|
-
Cloudflare Workers AI `@cf/baai/bge-m3`, Mistral `mistral-embed`, Google Gemini
|
|
25
|
-
`gemini-embedding-2`, and OpenAI `text-embedding-3-small` /
|
|
26
|
-
`text-embedding-3-large`; self-hosted OpenAI-compatible endpoints are also
|
|
27
|
-
available for TEI and similar trusted deployments
|
|
28
|
-
- npm distribution plus JSR publishing
|
|
12
|
+
- one indexing/search API across local and hosted embedding providers
|
|
13
|
+
- direct control over vector dimensions, table names, and deployment shape
|
|
14
|
+
- a lightweight library instead of a hosted search product
|
|
29
15
|
|
|
30
16
|
## Install
|
|
31
17
|
|
|
@@ -44,18 +30,10 @@ deno add jsr:@logan/libsql-search npm:@libsql/client
|
|
|
44
30
|
```
|
|
45
31
|
|
|
46
32
|
For npm usage, the package requires Node `>=22.12.0`.
|
|
47
|
-
Node examples in this README import from `libsql-search` and `@libsql/client`.
|
|
48
|
-
In Deno, after `deno add`, import from `@logan/libsql-search` and
|
|
49
|
-
`@libsql/client`.
|
|
50
33
|
|
|
51
34
|
## Quick Start
|
|
52
35
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
1. create a libSQL client
|
|
56
|
-
2. create the search table
|
|
57
|
-
3. index a Markdown directory
|
|
58
|
-
4. query it with the same embedding provider and dimensions
|
|
36
|
+
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.
|
|
59
37
|
|
|
60
38
|
```ts
|
|
61
39
|
import { createClient } from "@libsql/client";
|
|
@@ -66,11 +44,12 @@ const client = createClient({
|
|
|
66
44
|
authToken: "your-auth-token",
|
|
67
45
|
});
|
|
68
46
|
|
|
69
|
-
await createTable(client);
|
|
47
|
+
await createTable(client, "articles_local_384", 384);
|
|
70
48
|
|
|
71
49
|
await indexContent({
|
|
72
50
|
client,
|
|
73
51
|
contentPath: "./content",
|
|
52
|
+
tableName: "articles_local_384",
|
|
74
53
|
embeddingOptions: {
|
|
75
54
|
provider: "local",
|
|
76
55
|
},
|
|
@@ -79,6 +58,7 @@ await indexContent({
|
|
|
79
58
|
const results = await search({
|
|
80
59
|
client,
|
|
81
60
|
query: "how do I deploy my docs site",
|
|
61
|
+
tableName: "articles_local_384",
|
|
82
62
|
limit: 5,
|
|
83
63
|
embeddingOptions: {
|
|
84
64
|
provider: "local",
|
|
@@ -95,33 +75,33 @@ console.log(results.map((result) => ({
|
|
|
95
75
|
Important behavior:
|
|
96
76
|
|
|
97
77
|
- Call `createTable()` before indexing or searching.
|
|
98
|
-
- Keep
|
|
99
|
-
- `indexContent()`
|
|
100
|
-
- `
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
##
|
|
105
|
-
|
|
106
|
-
-
|
|
107
|
-
|
|
108
|
-
- `
|
|
109
|
-
- `
|
|
110
|
-
- `
|
|
111
|
-
- `
|
|
112
|
-
- `
|
|
113
|
-
- `
|
|
114
|
-
- `prepareTextForEmbedding(fields)`
|
|
78
|
+
- Keep table width, provider, and dimensions aligned across create/index/query.
|
|
79
|
+
- `indexContent()` embeds every document before it touches the database, then replaces the table in one transaction, so a failed rebuild leaves the previous index intact.
|
|
80
|
+
- `indexContent()` throws `IndexingError` when a file fails; pass `failurePolicy: "skip"` to rebuild from the remaining files.
|
|
81
|
+
- `indexContent()` throws `IndexingError` when no source files are found; pass `allowEmptyIndex: true` to intentionally empty the index.
|
|
82
|
+
- Hosted providers send indexed and queried text to external services and may incur provider charges.
|
|
83
|
+
|
|
84
|
+
## Providers
|
|
85
|
+
|
|
86
|
+
Built-in providers:
|
|
87
|
+
|
|
88
|
+
- `local` with `Xenova/all-MiniLM-L6-v2` at 384 dimensions
|
|
89
|
+
- `cloudflare` with `@cf/baai/bge-m3` at 1024 dimensions
|
|
90
|
+
- `mistral` with `mistral-embed` at 1024 dimensions
|
|
91
|
+
- `gemini` with `gemini-embedding-2` at 128-3072 dimensions, default 3072
|
|
92
|
+
- `openai` with `text-embedding-3-small` or `text-embedding-3-large`, default 768
|
|
93
|
+
- `openai-compatible` for trusted OpenAI-compatible endpoints such as TEI
|
|
115
94
|
|
|
116
95
|
## Docs
|
|
117
96
|
|
|
118
|
-
- [
|
|
119
|
-
- [Provider
|
|
97
|
+
- [Documentation index](./docs/README.md)
|
|
98
|
+
- [Provider selection and configuration](./docs/PROVIDERS.md)
|
|
120
99
|
- [API reference](./docs/API.md)
|
|
121
100
|
- [Integration examples](./docs/INTEGRATIONS.md)
|
|
101
|
+
- [Migration and reindexing guide](./docs/MIGRATIONS.md)
|
|
102
|
+
- [Testing guidance](./docs/TESTING.md)
|
|
122
103
|
- [Indexing and operations](./docs/INDEXING.md)
|
|
123
104
|
- [Troubleshooting](./docs/TROUBLESHOOTING.md)
|
|
124
|
-
- [Release workflow](./docs/RELEASING.md)
|
|
125
105
|
|
|
126
106
|
## License
|
|
127
107
|
|
package/dist/index.cjs
CHANGED
|
@@ -813,6 +813,16 @@ function normalizeVectorDimensions(dimensions) {
|
|
|
813
813
|
return dimensions;
|
|
814
814
|
}
|
|
815
815
|
|
|
816
|
+
class IndexingError extends Error {
|
|
817
|
+
phase;
|
|
818
|
+
failures;
|
|
819
|
+
constructor(message, phase, failures = [], options) {
|
|
820
|
+
super(message, options);
|
|
821
|
+
this.name = "IndexingError";
|
|
822
|
+
this.phase = phase;
|
|
823
|
+
this.failures = failures;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
816
826
|
async function indexContent(options) {
|
|
817
827
|
const {
|
|
818
828
|
client,
|
|
@@ -821,32 +831,92 @@ async function indexContent(options) {
|
|
|
821
831
|
fileExtensions = [".md", ".markdown"],
|
|
822
832
|
exclude = ["node_modules", ".git", "dist", "build"],
|
|
823
833
|
tableName = "articles",
|
|
824
|
-
onProgress
|
|
834
|
+
onProgress,
|
|
835
|
+
failurePolicy = "abort",
|
|
836
|
+
allowEmptyIndex = false
|
|
825
837
|
} = options;
|
|
826
838
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
839
|
+
let files;
|
|
840
|
+
try {
|
|
841
|
+
files = await findFiles(contentPath, contentPath, fileExtensions, exclude);
|
|
842
|
+
} catch (error) {
|
|
843
|
+
throw new IndexingError(
|
|
844
|
+
`Failed to scan ${contentPath} for source files. The existing index was left unchanged.`,
|
|
845
|
+
"build",
|
|
846
|
+
[],
|
|
847
|
+
{ cause: toError(error) }
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
files.sort((a, b) => a.relativePath < b.relativePath ? -1 : a.relativePath > b.relativePath ? 1 : 0);
|
|
851
|
+
if (files.length === 0 && !allowEmptyIndex) {
|
|
852
|
+
throw new IndexingError(
|
|
853
|
+
`No source files found in ${contentPath}. The existing index was left unchanged. Pass allowEmptyIndex: true to intentionally empty the index.`,
|
|
854
|
+
"build"
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
const documents = [];
|
|
858
|
+
const failures = [];
|
|
859
|
+
const slugOwners = /* @__PURE__ */ new Map();
|
|
835
860
|
for (let i = 0; i < files.length; i++) {
|
|
836
861
|
const file = files[i];
|
|
837
862
|
if (onProgress) {
|
|
838
863
|
onProgress(i + 1, files.length, file.relativePath);
|
|
839
864
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
865
|
+
const outcome = await buildDocument(file, embeddingOptions);
|
|
866
|
+
let failure;
|
|
867
|
+
if (outcome.ok) {
|
|
868
|
+
const owner = slugOwners.get(outcome.document.slug);
|
|
869
|
+
if (owner === void 0) {
|
|
870
|
+
slugOwners.set(outcome.document.slug, file.relativePath);
|
|
871
|
+
documents.push(outcome.document);
|
|
872
|
+
} else {
|
|
873
|
+
failure = {
|
|
874
|
+
file: file.relativePath,
|
|
875
|
+
stage: "parse",
|
|
876
|
+
error: new Error(
|
|
877
|
+
`Duplicate slug "${outcome.document.slug}": ${file.relativePath} collides with ${owner}`
|
|
878
|
+
)
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
} else {
|
|
882
|
+
failure = {
|
|
883
|
+
file: file.relativePath,
|
|
884
|
+
stage: outcome.stage,
|
|
885
|
+
error: outcome.error
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
if (failure === void 0) {
|
|
889
|
+
continue;
|
|
847
890
|
}
|
|
891
|
+
if (failurePolicy === "abort") {
|
|
892
|
+
throw new IndexingError(
|
|
893
|
+
`Failed to ${failure.stage} ${file.relativePath}: ${failure.error.message}. The existing index was left unchanged. Pass failurePolicy: 'skip' to rebuild from the remaining files.`,
|
|
894
|
+
"build",
|
|
895
|
+
[failure],
|
|
896
|
+
{ cause: failure.error }
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
console.error(`Skipping ${file.relativePath} (${failure.stage} failed):`, failure.error);
|
|
900
|
+
failures.push(failure);
|
|
848
901
|
}
|
|
849
|
-
|
|
902
|
+
if (files.length > 0 && documents.length === 0) {
|
|
903
|
+
throw new IndexingError(
|
|
904
|
+
`All ${files.length} source file(s) in ${contentPath} failed to index. The existing index was left unchanged.`,
|
|
905
|
+
"build",
|
|
906
|
+
failures,
|
|
907
|
+
{ cause: failures[0].error }
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
const success = documents.length;
|
|
911
|
+
await replaceIndex(client, quotedTableName, documents, failures);
|
|
912
|
+
return {
|
|
913
|
+
success,
|
|
914
|
+
failed: failures.length,
|
|
915
|
+
total: files.length,
|
|
916
|
+
replaced: true,
|
|
917
|
+
partial: failures.length > 0,
|
|
918
|
+
failures
|
|
919
|
+
};
|
|
850
920
|
}
|
|
851
921
|
async function findFiles(dir, baseDir, extensions, exclude) {
|
|
852
922
|
const files = [];
|
|
@@ -870,34 +940,113 @@ async function findFiles(dir, baseDir, extensions, exclude) {
|
|
|
870
940
|
}
|
|
871
941
|
return files;
|
|
872
942
|
}
|
|
873
|
-
async function
|
|
874
|
-
|
|
875
|
-
|
|
943
|
+
async function buildDocument(file, embeddingOptions) {
|
|
944
|
+
let raw;
|
|
945
|
+
try {
|
|
946
|
+
raw = await promises.readFile(file.fullPath, "utf-8");
|
|
947
|
+
} catch (error) {
|
|
948
|
+
return { ok: false, stage: "read", error: toError(error) };
|
|
949
|
+
}
|
|
950
|
+
let parsed;
|
|
951
|
+
try {
|
|
952
|
+
parsed = parseFile(file, raw);
|
|
953
|
+
} catch (error) {
|
|
954
|
+
return { ok: false, stage: "parse", error: toError(error) };
|
|
955
|
+
}
|
|
956
|
+
let embedding;
|
|
957
|
+
try {
|
|
958
|
+
embedding = await generateEmbedding(parsed.embeddingText, {
|
|
959
|
+
...embeddingOptions,
|
|
960
|
+
intent: embeddingOptions.intent ?? "document"
|
|
961
|
+
});
|
|
962
|
+
} catch (error) {
|
|
963
|
+
return { ok: false, stage: "embed", error: toError(error) };
|
|
964
|
+
}
|
|
965
|
+
return {
|
|
966
|
+
ok: true,
|
|
967
|
+
document: {
|
|
968
|
+
slug: parsed.slug,
|
|
969
|
+
title: parsed.title,
|
|
970
|
+
content: parsed.content,
|
|
971
|
+
folder: file.folder,
|
|
972
|
+
tags: parsed.tags,
|
|
973
|
+
serializedTags: parsed.serializedTags,
|
|
974
|
+
embedding,
|
|
975
|
+
metadata: parsed.metadata
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function parseFile(file, raw) {
|
|
980
|
+
const { data: frontMatter, content: markdown } = matter(raw);
|
|
876
981
|
const slug = file.relativePath.replace(/\.(md|markdown)$/, "").replace(/\\/g, "/");
|
|
877
|
-
const title = frontMatter.title
|
|
982
|
+
const title = resolveTitle(file, frontMatter.title);
|
|
878
983
|
const tags = Array.isArray(frontMatter.tags) ? frontMatter.tags : [];
|
|
984
|
+
const serializedTags = JSON.stringify(tags);
|
|
879
985
|
const embeddingText = prepareTextForEmbedding({
|
|
880
986
|
title,
|
|
881
987
|
description: frontMatter.description,
|
|
882
988
|
content: markdown,
|
|
883
989
|
tags
|
|
884
990
|
});
|
|
885
|
-
const embedding = await generateEmbedding(embeddingText, {
|
|
886
|
-
...embeddingOptions,
|
|
887
|
-
intent: embeddingOptions.intent ?? "document"
|
|
888
|
-
});
|
|
889
991
|
return {
|
|
890
992
|
slug,
|
|
891
993
|
title,
|
|
892
994
|
content: markdown,
|
|
893
|
-
folder: file.folder,
|
|
894
995
|
tags,
|
|
895
|
-
|
|
996
|
+
serializedTags,
|
|
997
|
+
embeddingText,
|
|
896
998
|
metadata: frontMatter
|
|
897
999
|
};
|
|
898
1000
|
}
|
|
899
|
-
|
|
900
|
-
|
|
1001
|
+
function resolveTitle(file, rawTitle) {
|
|
1002
|
+
if (!rawTitle) {
|
|
1003
|
+
return fallbackTitle(file);
|
|
1004
|
+
}
|
|
1005
|
+
if (typeof rawTitle === "string") {
|
|
1006
|
+
return rawTitle;
|
|
1007
|
+
}
|
|
1008
|
+
if (typeof rawTitle === "number" || typeof rawTitle === "bigint" || typeof rawTitle === "boolean") {
|
|
1009
|
+
return String(rawTitle);
|
|
1010
|
+
}
|
|
1011
|
+
if (rawTitle instanceof Date) {
|
|
1012
|
+
return rawTitle.toISOString();
|
|
1013
|
+
}
|
|
1014
|
+
throw new Error(
|
|
1015
|
+
`Unsupported frontmatter title of type ${describeType(rawTitle)}: expected a string`
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
function fallbackTitle(file) {
|
|
1019
|
+
return file.relativePath.split("/").pop()?.replace(/\.(md|markdown)$/, "").replace(/-/g, " ") || "Untitled";
|
|
1020
|
+
}
|
|
1021
|
+
function describeType(value) {
|
|
1022
|
+
return Array.isArray(value) ? "array" : typeof value;
|
|
1023
|
+
}
|
|
1024
|
+
async function replaceIndex(client, quotedTableName, documents, failures) {
|
|
1025
|
+
const statements = [`DELETE FROM ${quotedTableName}`];
|
|
1026
|
+
try {
|
|
1027
|
+
for (let i = 0; i < documents.length; i++) {
|
|
1028
|
+
const document = documents[i];
|
|
1029
|
+
if (document !== void 0) {
|
|
1030
|
+
statements.push(createInsertStatement(document, quotedTableName));
|
|
1031
|
+
documents[i] = void 0;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
} finally {
|
|
1035
|
+
documents.length = 0;
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
await client.batch(statements, "write");
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
throw new IndexingError(
|
|
1041
|
+
`Failed to replace the contents of ${quotedTableName}. The transaction was rolled back and the existing index was left unchanged.`,
|
|
1042
|
+
"replace",
|
|
1043
|
+
failures,
|
|
1044
|
+
{ cause: toError(error) }
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
function createInsertStatement(document, quotedTableName) {
|
|
1049
|
+
return {
|
|
901
1050
|
sql: `INSERT INTO ${quotedTableName}
|
|
902
1051
|
(slug, title, content, folder, tags, embedding, created_at, updated_at)
|
|
903
1052
|
VALUES (?, ?, ?, ?, ?, vector(?), datetime('now'), datetime('now'))`,
|
|
@@ -906,10 +1055,13 @@ async function insertDocument(client, document, quotedTableName) {
|
|
|
906
1055
|
document.title,
|
|
907
1056
|
document.content,
|
|
908
1057
|
document.folder,
|
|
909
|
-
|
|
1058
|
+
document.serializedTags,
|
|
910
1059
|
JSON.stringify(document.embedding)
|
|
911
1060
|
]
|
|
912
|
-
}
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function toError(error) {
|
|
1064
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
913
1065
|
}
|
|
914
1066
|
async function createTable(client, tableName = "articles", dimensions = 384) {
|
|
915
1067
|
const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
|
|
@@ -1059,6 +1211,7 @@ async function getFolders(client, tableName = "articles") {
|
|
|
1059
1211
|
return results.rows.map((row) => row.folder);
|
|
1060
1212
|
}
|
|
1061
1213
|
|
|
1214
|
+
exports.IndexingError = IndexingError;
|
|
1062
1215
|
exports.createEmbeddingProvider = createEmbeddingProvider;
|
|
1063
1216
|
exports.createTable = createTable;
|
|
1064
1217
|
exports.generateEmbedding = generateEmbedding;
|
package/dist/index.d.ts
CHANGED
|
@@ -82,6 +82,28 @@ declare function prepareTextForEmbedding(fields: {
|
|
|
82
82
|
* Content indexer for markdown and other formats
|
|
83
83
|
*/
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* How build-phase failures are handled.
|
|
87
|
+
*
|
|
88
|
+
* - `abort` (default) rejects the whole rebuild on the first failure
|
|
89
|
+
* - `skip` drops the failing file and rebuilds from the survivors
|
|
90
|
+
*/
|
|
91
|
+
type IndexFailurePolicy = 'abort' | 'skip';
|
|
92
|
+
/** The step that failed while turning a file into an indexable document. */
|
|
93
|
+
type IndexFailureStage = 'read' | 'parse' | 'embed';
|
|
94
|
+
/**
|
|
95
|
+
* The phase an {@link IndexingError} was raised in.
|
|
96
|
+
*
|
|
97
|
+
* - `build` means no database state was touched
|
|
98
|
+
* - `replace` means the replacement transaction failed and was rolled back
|
|
99
|
+
*/
|
|
100
|
+
type IndexingErrorPhase = 'build' | 'replace';
|
|
101
|
+
interface IndexFailure {
|
|
102
|
+
/** Path of the file relative to `contentPath`. */
|
|
103
|
+
file: string;
|
|
104
|
+
stage: IndexFailureStage;
|
|
105
|
+
error: Error;
|
|
106
|
+
}
|
|
85
107
|
interface IndexerOptions {
|
|
86
108
|
client: Client;
|
|
87
109
|
contentPath: string;
|
|
@@ -90,6 +112,23 @@ interface IndexerOptions {
|
|
|
90
112
|
exclude?: string[];
|
|
91
113
|
tableName?: string;
|
|
92
114
|
onProgress?: (current: number, total: number, file: string) => void;
|
|
115
|
+
/** Defaults to `abort`. */
|
|
116
|
+
failurePolicy?: IndexFailurePolicy;
|
|
117
|
+
/** Allow an empty source directory to empty the index. Defaults to `false`. */
|
|
118
|
+
allowEmptyIndex?: boolean;
|
|
119
|
+
}
|
|
120
|
+
interface IndexResult {
|
|
121
|
+
/** Documents written to the table. */
|
|
122
|
+
success: number;
|
|
123
|
+
/** Files that could not be indexed. */
|
|
124
|
+
failed: number;
|
|
125
|
+
/** Files discovered on disk. */
|
|
126
|
+
total: number;
|
|
127
|
+
/** Whether table contents were replaced by this call. */
|
|
128
|
+
replaced: boolean;
|
|
129
|
+
/** Replaced, but some files were skipped. */
|
|
130
|
+
partial: boolean;
|
|
131
|
+
failures: IndexFailure[];
|
|
93
132
|
}
|
|
94
133
|
interface IndexedDocument {
|
|
95
134
|
slug: string;
|
|
@@ -101,13 +140,22 @@ interface IndexedDocument {
|
|
|
101
140
|
metadata?: Record<string, any>;
|
|
102
141
|
}
|
|
103
142
|
/**
|
|
104
|
-
*
|
|
143
|
+
* Raised when a rebuild cannot complete. The previously indexed rows are always
|
|
144
|
+
* left exactly as they were.
|
|
105
145
|
*/
|
|
106
|
-
declare
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
146
|
+
declare class IndexingError extends Error {
|
|
147
|
+
readonly phase: IndexingErrorPhase;
|
|
148
|
+
readonly failures: IndexFailure[];
|
|
149
|
+
constructor(message: string, phase: IndexingErrorPhase, failures?: IndexFailure[], options?: ErrorOptions);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Index markdown content from a directory.
|
|
153
|
+
*
|
|
154
|
+
* Every document is read, parsed, and embedded in memory before any database
|
|
155
|
+
* state changes. The table is then replaced inside a single write transaction,
|
|
156
|
+
* so a failure at any point leaves the previous index intact.
|
|
157
|
+
*/
|
|
158
|
+
declare function indexContent(options: IndexerOptions): Promise<IndexResult>;
|
|
111
159
|
/**
|
|
112
160
|
* Create the articles table if it doesn't exist
|
|
113
161
|
*/
|
|
@@ -178,5 +226,5 @@ declare function getArticlesByFolder(client: Client, folder: string, tableName?:
|
|
|
178
226
|
*/
|
|
179
227
|
declare function getFolders(client: Client, tableName?: string): Promise<string[]>;
|
|
180
228
|
|
|
181
|
-
export { createEmbeddingProvider, createTable, generateEmbedding, generateEmbeddings, getAllArticles, getArticleBySlug, getArticlesByFolder, getEmbeddingProviderMetadata, getFolders, indexContent, padEmbedding, prepareTextForEmbedding, search, validateEmbeddingBatch };
|
|
182
|
-
export type { EmbeddingBatchBehavior, EmbeddingBatchItem, EmbeddingBatchItemResult, EmbeddingBatchMode, EmbeddingBatchResult, EmbeddingIntent, EmbeddingOptions, EmbeddingProvider, EmbeddingProviderClient, EmbeddingProviderMetadata, EmbeddingRequestOptions, IndexedDocument, IndexerOptions, SearchOptions, SearchResult };
|
|
229
|
+
export { IndexingError, createEmbeddingProvider, createTable, generateEmbedding, generateEmbeddings, getAllArticles, getArticleBySlug, getArticlesByFolder, getEmbeddingProviderMetadata, getFolders, indexContent, padEmbedding, prepareTextForEmbedding, search, validateEmbeddingBatch };
|
|
230
|
+
export type { EmbeddingBatchBehavior, EmbeddingBatchItem, EmbeddingBatchItemResult, EmbeddingBatchMode, EmbeddingBatchResult, EmbeddingIntent, EmbeddingOptions, EmbeddingProvider, EmbeddingProviderClient, EmbeddingProviderMetadata, EmbeddingRequestOptions, IndexFailure, IndexFailurePolicy, IndexFailureStage, IndexResult, IndexedDocument, IndexerOptions, IndexingErrorPhase, SearchOptions, SearchResult };
|