libsql-search 0.10.1 → 0.11.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 +18 -15
- package/dist/index.cjs +17 -127
- package/dist/index.d.ts +10 -10
- package/dist/index.esm.js +17 -127
- package/dist/turso.cjs +90 -7
- package/dist/turso.d.ts +29 -8
- package/dist/turso.esm.js +90 -7
- package/docs/API.md +22 -17
- package/docs/INDEXING.md +13 -7
- package/docs/INTEGRATIONS.md +18 -24
- package/docs/MIGRATIONS.md +10 -10
- package/docs/PROVIDERS.md +3 -23
- package/docs/README.md +1 -1
- package/docs/TESTING.md +13 -12
- package/docs/TROUBLESHOOTING.md +0 -6
- package/docs/TURSO.md +55 -16
- package/package.json +1 -2
- package/docs/TROUBLESHOOTING-SHARP.md +0 -65
package/docs/MIGRATIONS.md
CHANGED
|
@@ -27,7 +27,7 @@ References:
|
|
|
27
27
|
|
|
28
28
|
In practice, this means:
|
|
29
29
|
|
|
30
|
-
- `384
|
|
30
|
+
- a legacy `384` embedding space and `1024 Mistral` can never share a table
|
|
31
31
|
- `1024 Cloudflare` and `1024 Mistral` still need separate rebuilds because equal width does not make the vectors compatible
|
|
32
32
|
- a custom endpoint change at the same width still needs a new table because the model or serving stack may have changed
|
|
33
33
|
|
|
@@ -84,14 +84,14 @@ Re-running `createTable()` with the table's existing width is the fix. It is ide
|
|
|
84
84
|
|
|
85
85
|
```ts
|
|
86
86
|
// Same name and same width as the existing table
|
|
87
|
-
await createTable(client, "
|
|
87
|
+
await createTable(client, "articles_legacy_384", 384);
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
Equivalently, in SQL:
|
|
91
91
|
|
|
92
92
|
```sql
|
|
93
|
-
CREATE INDEX IF NOT EXISTS "
|
|
94
|
-
ON "
|
|
93
|
+
CREATE INDEX IF NOT EXISTS "articles_legacy_384_embedding_idx"
|
|
94
|
+
ON "articles_legacy_384"(libsql_vector_idx(embedding));
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Reindexing does not create the index; `indexContent()` only replaces rows. Any new table created by `createTable()` as part of a migration already has it, so this applies only to pre-existing tables you are carrying forward. Until the index exists, `search({ ..., exact: true })` keeps queries working on the exact full-scan path.
|
|
@@ -100,7 +100,7 @@ Reindexing does not create the index; `indexContent()` only replaces rows. Any n
|
|
|
100
100
|
|
|
101
101
|
| From | To | Why a rebuild is required | Recommended table move |
|
|
102
102
|
| --- | --- | --- | --- |
|
|
103
|
-
| Legacy
|
|
103
|
+
| Legacy in-process Transformers.js index | Any external provider | The runtime and provider were removed; every replacement service has its own embedding space | Build a parallel table named for the external provider/model, validate, then retire the legacy table |
|
|
104
104
|
| Any `768` space | Any `1024` space | Width changes from `F32_BLOB(768)` to `F32_BLOB(1024)` | Create a new `1024` table and reindex |
|
|
105
105
|
| Cloudflare `1024` | Mistral `1024` | Width stays the same, but provider/model space changes | Use a parallel `1024` table such as `articles_mistral_1024` |
|
|
106
106
|
| Mistral `1024` | Cloudflare `1024` | Same reason in reverse | Use a parallel `1024` table such as `articles_cf_bgem3_1024` |
|
|
@@ -111,21 +111,21 @@ Reindexing does not create the index; `indexContent()` only replaces rows. Any n
|
|
|
111
111
|
|
|
112
112
|
## Scenario Notes
|
|
113
113
|
|
|
114
|
-
### Legacy
|
|
114
|
+
### Legacy In-Process Embeddings To An External Service
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
Versions that exposed the in-process Transformers.js provider produced a separate embedding space that this release can no longer query. Choose an external provider or separately deployed OpenAI-compatible service and rebuild every vector into a new table.
|
|
117
117
|
|
|
118
118
|
Safe path:
|
|
119
119
|
|
|
120
120
|
```ts
|
|
121
|
-
await createTable(client, "
|
|
121
|
+
await createTable(client, "articles_bge_1024", 1024);
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
Reindex into `
|
|
124
|
+
Reindex into `articles_bge_1024` with the external service configuration; do not mix its vectors with the legacy table.
|
|
125
125
|
|
|
126
126
|
### `768` To `1024`
|
|
127
127
|
|
|
128
|
-
Any move from `768` dimensions to `1024` dimensions changes the schema width. Examples include a legacy
|
|
128
|
+
Any move from `768` dimensions to `1024` dimensions changes the schema width. Examples include a legacy table moving to Cloudflare, Mistral, or a 1024-dimensional OpenAI-compatible service.
|
|
129
129
|
|
|
130
130
|
```ts
|
|
131
131
|
await createTable(client, "articles_mistral_1024", 1024);
|
package/docs/PROVIDERS.md
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Use this page to choose an embedding provider, confirm the table width it needs, and understand what crosses a network boundary.
|
|
4
4
|
|
|
5
|
-
`libsql-search` supports these provider values:
|
|
5
|
+
`libsql-search` only talks to external embedding services; it never loads or hosts an embedding model in-process. It supports these provider values:
|
|
6
6
|
|
|
7
|
-
- `local`
|
|
8
7
|
- `cloudflare`
|
|
9
8
|
- `mistral`
|
|
10
9
|
- `gemini`
|
|
@@ -17,8 +16,7 @@ All providers share the same `EmbeddingOptions` surface:
|
|
|
17
16
|
|
|
18
17
|
```ts
|
|
19
18
|
interface EmbeddingOptions {
|
|
20
|
-
provider
|
|
21
|
-
| "local"
|
|
19
|
+
provider:
|
|
22
20
|
| "cloudflare"
|
|
23
21
|
| "mistral"
|
|
24
22
|
| "gemini"
|
|
@@ -40,7 +38,7 @@ interface EmbeddingOptions {
|
|
|
40
38
|
|
|
41
39
|
Shared defaults and rules:
|
|
42
40
|
|
|
43
|
-
- `provider`
|
|
41
|
+
- `provider` is required; there is no implicit embedding runtime or service
|
|
44
42
|
- `maxLength` defaults to `8000` code units
|
|
45
43
|
- `timeoutMs` defaults to `30000`
|
|
46
44
|
- `indexContent()` defaults to `intent: "document"`
|
|
@@ -59,7 +57,6 @@ The `model` option is only used by `openai-compatible`.
|
|
|
59
57
|
|
|
60
58
|
| Provider | Literal | Upstream model used by this adapter | Dimensions | Credentials | Batching | Network and privacy boundary | Cost and table planning |
|
|
61
59
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
62
|
-
| Local | `local` | `Xenova/all-MiniLM-L6-v2` | Fixed `384` | None | Sequential in-process | No hosted API call. First use may download model artifacts and cache them locally. | No hosted API bill. Table must be `F32_BLOB(384)`. |
|
|
63
60
|
| Cloudflare Workers AI | `cloudflare` | `@cf/baai/bge-m3` | Fixed `1024` | `accountId` and `apiToken`, or `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` | Native batch in one request | Indexed and queried text is sent to Cloudflare. | Check Cloudflare pricing before large rebuilds. Table must be `F32_BLOB(1024)`. |
|
|
64
61
|
| Mistral | `mistral` | `mistral-embed` | Fixed `1024` | `apiKey`, or `MISTRAL_API_KEY` | Native batch in one request | Indexed and queried text is sent to Mistral. | Check Mistral pricing before rebuilds. Table must be `F32_BLOB(1024)`. |
|
|
65
62
|
| Gemini | `gemini` | `gemini-embedding-2` | Default `3072`; allowed integers `128-3072` | `apiKey`, or `GEMINI_API_KEY` | Sequential SDK request per input | Indexed and queried text is sent to Google. The adapter currently rewrites payload text by intent. | Check Gemini pricing before rebuilds. Table width must match the chosen dimension count exactly. |
|
|
@@ -68,23 +65,6 @@ The `model` option is only used by `openai-compatible`.
|
|
|
68
65
|
|
|
69
66
|
## Provider Notes
|
|
70
67
|
|
|
71
|
-
### Local
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
embeddingOptions: {
|
|
75
|
-
provider: "local",
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
- fixed at `384` dimensions
|
|
80
|
-
- rejects any other `dimensions` value before loading the runtime
|
|
81
|
-
- uses `@huggingface/transformers` lazily and caches the local pipeline by model name
|
|
82
|
-
|
|
83
|
-
References:
|
|
84
|
-
|
|
85
|
-
- [Transformers.js in Node.js](https://huggingface.co/docs/transformers.js/en/tutorials/node)
|
|
86
|
-
- [Transformers.js environment and cache controls](https://huggingface.co/docs/transformers.js/en/api/env)
|
|
87
|
-
|
|
88
68
|
### Cloudflare Workers AI
|
|
89
69
|
|
|
90
70
|
```ts
|
package/docs/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This directory holds the longer-form reference material for `libsql-search`.
|
|
4
4
|
|
|
5
|
-
- [Provider selection and configuration](./PROVIDERS.md): compare
|
|
5
|
+
- [Provider selection and configuration](./PROVIDERS.md): compare external and custom embedding providers before you build an index
|
|
6
6
|
- [Integration examples](./INTEGRATIONS.md): reusable provider flow plus Astro and Next.js examples
|
|
7
7
|
- [Migration and reindexing guide](./MIGRATIONS.md): table-width changes, provider/model swaps, and safe cutovers
|
|
8
8
|
- [API reference](./API.md): exported functions, option shapes, and result data
|
package/docs/TESTING.md
CHANGED
|
@@ -9,30 +9,31 @@ Routine unit tests and CI should not make live embedding-provider calls and shou
|
|
|
9
9
|
- validate option handling and response parsing with mocks first
|
|
10
10
|
- assert failures happen before network calls when configuration is invalid
|
|
11
11
|
|
|
12
|
-
The current test suite follows that pattern in `tests/embeddings.test.ts` and [`tests/
|
|
12
|
+
The current test suite follows that pattern in `tests/embeddings.test.ts` and [`tests/embedding-service.mock.ts`](../tests/embedding-service.mock.ts).
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Shared Embedding Service Mock
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Indexer, search, and database tests use a deterministic OpenAI-compatible service mock. This keeps the tests on the same external-service boundary as production without making network calls.
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
embeddingServiceMock,
|
|
21
|
+
resetEmbeddingServiceMock,
|
|
22
|
+
TEST_EMBEDDING_OPTIONS,
|
|
23
|
+
} from "./embedding-service.mock.js";
|
|
23
24
|
|
|
24
25
|
beforeEach(() => {
|
|
25
|
-
|
|
26
|
+
resetEmbeddingServiceMock();
|
|
26
27
|
});
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
The
|
|
30
|
+
The example keeps the `.js` import suffix because this repo's ESM TypeScript source uses explicit `.js` relative imports that resolve after compilation.
|
|
30
31
|
|
|
31
32
|
Test the contract you care about:
|
|
32
33
|
|
|
33
|
-
- the
|
|
34
|
-
-
|
|
35
|
-
-
|
|
34
|
+
- indexing and querying use the same endpoint, model, and dimensions
|
|
35
|
+
- provider failures remain classified as build-stage failures
|
|
36
|
+
- queued deterministic vectors exercise exact ranking and tie behavior
|
|
36
37
|
|
|
37
38
|
## HTTP Provider Mocks
|
|
38
39
|
|
|
@@ -116,9 +117,9 @@ Apply the same pattern to `GEMINI_API_KEY`, `MISTRAL_API_KEY`, `CLOUDFLARE_ACCOU
|
|
|
116
117
|
Prefer tests that prove bad inputs fail locally:
|
|
117
118
|
|
|
118
119
|
- unknown provider
|
|
120
|
+
- missing provider
|
|
119
121
|
- missing provider credentials
|
|
120
122
|
- blank credentials where trimming is expected
|
|
121
|
-
- invalid local dimensions
|
|
122
123
|
- invalid Gemini dimensions
|
|
123
124
|
- invalid `openai-compatible` `baseUrl`
|
|
124
125
|
- invalid `openai-compatible` `batchSize`
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# Troubleshooting
|
|
2
2
|
|
|
3
|
-
Use the page that matches the failure mode:
|
|
4
|
-
|
|
5
|
-
- [Sharp native module issues](./TROUBLESHOOTING-SHARP.md)
|
|
6
|
-
|
|
7
3
|
Common operational checks:
|
|
8
4
|
|
|
9
5
|
- verify you called `createTable()` before indexing or searching
|
|
@@ -15,8 +11,6 @@ Common operational checks:
|
|
|
15
11
|
- after upgrading an existing Gemini index, fully re-embed with
|
|
16
12
|
`gemini-embedding-2`; for 3072-dimensional Gemini indexes, recreate the table
|
|
17
13
|
or use a new table name before rebuilding
|
|
18
|
-
- after upgrading an existing local 768-dimensional padded index, create or
|
|
19
|
-
recreate a 384-dimensional table and fully re-index before querying it
|
|
20
14
|
- if `search()` reports that the `<tableName>_embedding_idx` vector index could
|
|
21
15
|
not be used, the table has no embedding vector index: re-run `createTable()`
|
|
22
16
|
with the table's existing name and width to add it without touching rows, or
|
package/docs/TURSO.md
CHANGED
|
@@ -44,20 +44,32 @@ import { createTable, indexContent, search } from "libsql-search";
|
|
|
44
44
|
const database = await connect("./local.db");
|
|
45
45
|
const client = tursoAdapter(database);
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
const embeddingOptions = {
|
|
48
|
+
provider: "openai-compatible" as const,
|
|
49
|
+
baseUrl: process.env.EMBEDDING_BASE_URL!,
|
|
50
|
+
model: "bge-large-en-v1.5",
|
|
51
|
+
dimensions: 1024,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
await createTable(client, "articles", 1024);
|
|
48
55
|
|
|
49
56
|
await indexContent({
|
|
50
57
|
client,
|
|
51
58
|
contentPath: "./content",
|
|
52
|
-
embeddingOptions
|
|
59
|
+
embeddingOptions,
|
|
53
60
|
});
|
|
54
61
|
|
|
55
62
|
const results = await search({
|
|
56
63
|
client,
|
|
57
64
|
query: "how do I deploy my docs site",
|
|
58
65
|
limit: 5,
|
|
59
|
-
embeddingOptions
|
|
66
|
+
embeddingOptions,
|
|
60
67
|
});
|
|
68
|
+
|
|
69
|
+
// When this adapter's lifetime ends, drain and close its cached statements
|
|
70
|
+
// before closing the caller-owned database handle.
|
|
71
|
+
await client.dispose();
|
|
72
|
+
await database.close();
|
|
61
73
|
```
|
|
62
74
|
|
|
63
75
|
Use `":memory:"` instead of a file path for an ephemeral database.
|
|
@@ -67,6 +79,29 @@ Use `":memory:"` instead of a file path for an ephemeral database.
|
|
|
67
79
|
result shapes — `SearchOptions`, `SearchResult`, `IndexerOptions`,
|
|
68
80
|
`IndexResult` — are identical on both backends.
|
|
69
81
|
|
|
82
|
+
### Adapter lifetime and disposal
|
|
83
|
+
|
|
84
|
+
One adapter caches up to **32 query statements**, keyed by SQL and evicted in
|
|
85
|
+
least-recently-used order. The bound matters because public `tableName` options
|
|
86
|
+
are embedded in SQL: an unbounded cache would let a long-lived process retain a
|
|
87
|
+
new native statement for every valid table name it sees. Statements that are
|
|
88
|
+
currently running or queued are closed after they finish rather than being
|
|
89
|
+
evicted out from under a call.
|
|
90
|
+
|
|
91
|
+
Call `await client.dispose()` after all work using that adapter has settled.
|
|
92
|
+
Disposal drains queued query calls and closes every cached statement, but does
|
|
93
|
+
**not** close the database handle you supplied. It is terminal: later calls on
|
|
94
|
+
that adapter reject. Close the database separately, after disposal. If you omit
|
|
95
|
+
disposal, the reusable cache is still bounded at 32 entries and lives until the
|
|
96
|
+
underlying handle or process exits; statements evicted while in flight live
|
|
97
|
+
only until their queued calls finish.
|
|
98
|
+
|
|
99
|
+
The adapter serializes concurrent calls that share one cached statement. This
|
|
100
|
+
is required for correctness, not just memory use: the native statement mutates
|
|
101
|
+
its current bindings, so overlapping `all()` calls with different arguments can
|
|
102
|
+
otherwise return another caller's rows. Different cached SQL statements remain
|
|
103
|
+
independent.
|
|
104
|
+
|
|
70
105
|
## What is different on Turso
|
|
71
106
|
|
|
72
107
|
### There is no ANN vector index, so search is a full scan
|
|
@@ -185,13 +220,14 @@ point is still type-checked by `deno task check` so the claim above stays true.
|
|
|
185
220
|
unchanged, and the main entry point exports no new symbol and references no
|
|
186
221
|
Turso type.
|
|
187
222
|
|
|
188
|
-
`tursoAdapter()` returns a `
|
|
189
|
-
`libsql-search/turso` so you can
|
|
223
|
+
`tursoAdapter()` returns a `TursoAdapter`, which extends `DatabaseAdapter` with
|
|
224
|
+
the disposal hook. Both types are exported from `libsql-search/turso` so you can
|
|
225
|
+
name them:
|
|
190
226
|
|
|
191
227
|
```ts
|
|
192
|
-
import { tursoAdapter, type
|
|
228
|
+
import { tursoAdapter, type TursoAdapter } from "libsql-search/turso";
|
|
193
229
|
|
|
194
|
-
let client:
|
|
230
|
+
let client: TursoAdapter;
|
|
195
231
|
```
|
|
196
232
|
|
|
197
233
|
It is exported from the subpath only. The main entry point does not export it,
|
|
@@ -210,15 +246,18 @@ to prove the two stay interchangeable, and runs as part of
|
|
|
210
246
|
Three things in `src/turso.ts` look like noise and are not. Each has a
|
|
211
247
|
regression test; none of them fails loudly at runtime if removed.
|
|
212
248
|
|
|
213
|
-
**Prepared statements
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
249
|
+
**Prepared statements are bounded, serialized, and explicitly disposed.** A
|
|
250
|
+
statement holds native memory that the garbage collector cannot reclaim,
|
|
251
|
+
because it is not JavaScript heap. The query path therefore reuses a 32-entry
|
|
252
|
+
LRU instead of preparing on every request, serializes rebinds per entry, and
|
|
253
|
+
closes evictions only after their queued calls finish. `TursoAdapter.dispose()`
|
|
254
|
+
drains and closes the remaining cache. Measured through the built bundle,
|
|
255
|
+
60 000 `executeQuery()` calls grew RSS by ~570 MB when statements were never
|
|
256
|
+
closed, ~150 MB when each call prepared and closed, and ~7 MB when one statement
|
|
257
|
+
was reused. `search()` issues exactly one query, so the SSR-per-request path is
|
|
258
|
+
where reuse matters most. Inside `executeAtomicWrite()` the transaction-local
|
|
259
|
+
statements are still released only *after* `COMMIT` or `ROLLBACK` — a statement
|
|
260
|
+
stays bound to the transaction while it is open.
|
|
222
261
|
|
|
223
262
|
**`BEGIN IMMEDIATE` sits outside the `try`.** If it were inside, a `BEGIN` that
|
|
224
263
|
fails because another rebuild already holds the write lock would fall into the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libsql-search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Semantic search for static sites using libSQL/Turso with multi-provider embeddings",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.34.5",
|
|
@@ -75,7 +75,6 @@
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@huggingface/transformers": "4.2.0",
|
|
79
78
|
"gray-matter": "^4.0.3"
|
|
80
79
|
},
|
|
81
80
|
"devDependencies": {
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Troubleshooting: Transitive `sharp` Install Errors
|
|
2
|
-
|
|
3
|
-
`libsql-search` does not directly import `sharp`, but local embeddings use
|
|
4
|
-
`@huggingface/transformers`, which currently brings in `sharp` as a transitive
|
|
5
|
-
runtime dependency. If you see an install error mentioning `sharp`, it is
|
|
6
|
-
usually a native-package install or approval issue.
|
|
7
|
-
|
|
8
|
-
This page exists because the error can show up before your application reaches
|
|
9
|
-
any `libsql-search` code.
|
|
10
|
-
|
|
11
|
-
## Typical Error
|
|
12
|
-
|
|
13
|
-
```text
|
|
14
|
-
Cannot find module '../build/Release/sharp-*.node'
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Or:
|
|
18
|
-
|
|
19
|
-
```text
|
|
20
|
-
Error: Something went wrong installing the "sharp" module
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Why It Happens
|
|
24
|
-
|
|
25
|
-
With pnpm, native packages may need explicit build-script approval. If the
|
|
26
|
-
relevant install script is blocked, the native binary is never downloaded or
|
|
27
|
-
built.
|
|
28
|
-
|
|
29
|
-
## What To Do
|
|
30
|
-
|
|
31
|
-
First inspect which build scripts pnpm blocked:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pnpm ignored-builds
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Then approve the package that is actually failing and reinstall:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
pnpm approve-builds
|
|
41
|
-
pnpm install
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
In the interactive `pnpm approve-builds` prompt, select `sharp` if that is the
|
|
45
|
-
package reporting the native-module failure.
|
|
46
|
-
|
|
47
|
-
For a committed repository-level fix, you can also allow the package explicitly
|
|
48
|
-
in `pnpm-workspace.yaml` with `onlyBuiltDependencies`.
|
|
49
|
-
|
|
50
|
-
## Relation To `libsql-search`
|
|
51
|
-
|
|
52
|
-
- local embeddings use `@huggingface/transformers`
|
|
53
|
-
- the first local embedding run may download a model at runtime
|
|
54
|
-
- that runtime model download is separate from a pnpm native-module install
|
|
55
|
-
failure
|
|
56
|
-
|
|
57
|
-
## Verification
|
|
58
|
-
|
|
59
|
-
After reinstalling, rerun the command that originally failed. If your app uses
|
|
60
|
-
`sharp` directly, verify that import in your own project context.
|
|
61
|
-
|
|
62
|
-
## Additional Resources
|
|
63
|
-
|
|
64
|
-
- [pnpm approve-builds](https://pnpm.io/10.x/cli/approve-builds)
|
|
65
|
-
- [Sharp installation docs](https://sharp.pixelplumbing.com/install)
|