libsql-search 0.1.3 → 0.1.5
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 +79 -431
- package/dist/index.cjs +354 -0
- package/dist/index.d.ts +0 -4
- package/dist/index.esm.js +249 -295
- package/docs/API.md +167 -0
- package/docs/INDEXING.md +68 -0
- package/docs/INTEGRATIONS.md +143 -0
- package/docs/PROVIDERS.md +100 -0
- package/docs/README.md +16 -0
- package/docs/RELEASING.md +91 -0
- package/docs/TROUBLESHOOTING-SHARP.md +64 -0
- package/docs/TROUBLESHOOTING.md +12 -0
- package/package.json +31 -19
- package/dist/index.js +0 -400
package/README.md
CHANGED
|
@@ -1,477 +1,125 @@
|
|
|
1
1
|
# libsql-search
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/libsql-search)
|
|
4
|
+
[](https://jsr.io/@logan/libsql-search)
|
|
5
|
+
[](https://github.com/llbbl/libsql-search/actions/workflows/ci.yml)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
`libsql-search` adds semantic search to Markdown-backed sites using libSQL/Turso.
|
|
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.
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
Use it when you want:
|
|
8
13
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- 🎯 **Type-Safe** - Full TypeScript support
|
|
14
|
-
- 🆓 **Free Tier Friendly** - Local embeddings require no API keys
|
|
14
|
+
- a small TypeScript library instead of a hosted search product
|
|
15
|
+
- one search index shared across static-site builds and app routes
|
|
16
|
+
- local or API-based embeddings behind the same indexing/search API
|
|
17
|
+
- direct control over table names, dimensions, content shape, and deployment
|
|
15
18
|
|
|
16
|
-
##
|
|
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 that exist in the code today: local
|
|
24
|
+
`Xenova/all-MiniLM-L6-v2`, Google Gemini `text-embedding-004`, and OpenAI
|
|
25
|
+
`text-embedding-3-small` and `text-embedding-3-large`
|
|
26
|
+
- npm distribution plus JSR publishing
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
`@libsql/client` is a peer dependency.
|
|
17
31
|
|
|
18
|
-
**npm:**
|
|
19
32
|
```bash
|
|
20
|
-
|
|
33
|
+
pnpm add libsql-search @libsql/client
|
|
21
34
|
```
|
|
22
35
|
|
|
23
|
-
**pnpm:**
|
|
24
36
|
```bash
|
|
25
|
-
|
|
37
|
+
npm install libsql-search @libsql/client
|
|
26
38
|
```
|
|
27
39
|
|
|
28
|
-
**JSR:**
|
|
29
40
|
```bash
|
|
30
|
-
deno add
|
|
41
|
+
deno add jsr:@logan/libsql-search npm:@libsql/client
|
|
31
42
|
```
|
|
32
43
|
|
|
44
|
+
For npm usage, the package requires Node `>=22.12.0`.
|
|
45
|
+
Node examples in this README import from `libsql-search` and `@libsql/client`.
|
|
46
|
+
In Deno, after `deno add`, import from `@logan/libsql-search` and
|
|
47
|
+
`@libsql/client`.
|
|
48
|
+
|
|
33
49
|
## Quick Start
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
The shortest working flow is:
|
|
52
|
+
|
|
53
|
+
1. create a libSQL client
|
|
54
|
+
2. create the search table
|
|
55
|
+
3. index a Markdown directory
|
|
56
|
+
4. query it with the same embedding provider and dimensions
|
|
36
57
|
|
|
37
|
-
```
|
|
38
|
-
import { createClient } from
|
|
39
|
-
import { createTable } from
|
|
58
|
+
```ts
|
|
59
|
+
import { createClient } from "@libsql/client";
|
|
60
|
+
import { createTable, indexContent, search } from "libsql-search";
|
|
40
61
|
|
|
41
62
|
const client = createClient({
|
|
42
|
-
url:
|
|
43
|
-
authToken:
|
|
63
|
+
url: "libsql://your-db.turso.io",
|
|
64
|
+
authToken: "your-auth-token",
|
|
44
65
|
});
|
|
45
66
|
|
|
46
|
-
|
|
47
|
-
await createTable(client, 'articles', 768);
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 2. Index Your Content
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
import { indexContent } from 'libsql-search';
|
|
67
|
+
await createTable(client, "articles", 768);
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
await indexContent({
|
|
56
70
|
client,
|
|
57
|
-
contentPath:
|
|
71
|
+
contentPath: "./content",
|
|
58
72
|
embeddingOptions: {
|
|
59
|
-
provider:
|
|
60
|
-
dimensions: 768
|
|
73
|
+
provider: "local",
|
|
74
|
+
dimensions: 768,
|
|
61
75
|
},
|
|
62
|
-
onProgress: (current, total, file) => {
|
|
63
|
-
console.log(`[${current}/${total}] Indexing: ${file}`);
|
|
64
|
-
}
|
|
65
76
|
});
|
|
66
77
|
|
|
67
|
-
console.log(`Indexed ${result.success}/${result.total} documents`);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 3. Search Your Content
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { search } from 'libsql-search';
|
|
74
|
-
|
|
75
78
|
const results = await search({
|
|
76
79
|
client,
|
|
77
|
-
query:
|
|
80
|
+
query: "how do I deploy my docs site",
|
|
78
81
|
limit: 5,
|
|
79
82
|
embeddingOptions: {
|
|
80
|
-
provider:
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
results.forEach(result => {
|
|
85
|
-
console.log(`${result.title} (${result.distance})`);
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Embedding Providers
|
|
90
|
-
|
|
91
|
-
### Local (Xenova/Transformers.js)
|
|
92
|
-
|
|
93
|
-
**Free, no API key required**. Runs `all-MiniLM-L6-v2` in Node.js using ONNX.
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
embeddingOptions: {
|
|
97
|
-
provider: 'local',
|
|
98
|
-
dimensions: 768 // 384 native, padded to 768
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
**Pros:**
|
|
103
|
-
- ✅ No API costs
|
|
104
|
-
- ✅ No rate limits
|
|
105
|
-
- ✅ Works offline
|
|
106
|
-
- ✅ Privacy-friendly
|
|
107
|
-
|
|
108
|
-
**Cons:**
|
|
109
|
-
- ⚠️ First run downloads model (~50MB)
|
|
110
|
-
- ⚠️ Slower than API-based options
|
|
111
|
-
- ⚠️ Lower quality than large models
|
|
112
|
-
|
|
113
|
-
### Google Gemini
|
|
114
|
-
|
|
115
|
-
**Free tier: 1,500 requests/day**. Uses `text-embedding-004` model.
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
embeddingOptions: {
|
|
119
|
-
provider: 'gemini',
|
|
120
|
-
apiKey: process.env.GEMINI_API_KEY,
|
|
121
|
-
dimensions: 768 // native
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**Pros:**
|
|
126
|
-
- ✅ Generous free tier
|
|
127
|
-
- ✅ High quality embeddings
|
|
128
|
-
- ✅ Fast
|
|
129
|
-
|
|
130
|
-
**Cons:**
|
|
131
|
-
- ⚠️ Requires API key
|
|
132
|
-
- ⚠️ Rate limited
|
|
133
|
-
|
|
134
|
-
### OpenAI
|
|
135
|
-
|
|
136
|
-
**Paid only**. Uses `text-embedding-3-small` or `text-embedding-3-large`.
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
embeddingOptions: {
|
|
140
|
-
provider: 'openai',
|
|
141
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
142
|
-
dimensions: 1536 // or 3072 for large
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
**Pros:**
|
|
147
|
-
- ✅ Highest quality
|
|
148
|
-
- ✅ Very fast
|
|
149
|
-
- ✅ Configurable dimensions
|
|
150
|
-
|
|
151
|
-
**Cons:**
|
|
152
|
-
- ⚠️ Costs money ($0.02 per 1M tokens)
|
|
153
|
-
- ⚠️ Requires API key
|
|
154
|
-
|
|
155
|
-
## API Reference
|
|
156
|
-
|
|
157
|
-
### Indexing
|
|
158
|
-
|
|
159
|
-
#### `indexContent(options)`
|
|
160
|
-
|
|
161
|
-
Index markdown files from a directory.
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
interface IndexerOptions {
|
|
165
|
-
client: Client; // libSQL client
|
|
166
|
-
contentPath: string; // Path to content directory
|
|
167
|
-
embeddingOptions?: EmbeddingOptions;
|
|
168
|
-
fileExtensions?: string[]; // Default: ['.md', '.markdown']
|
|
169
|
-
exclude?: string[]; // Default: ['node_modules', '.git']
|
|
170
|
-
tableName?: string; // Default: 'articles'
|
|
171
|
-
onProgress?: (current, total, file) => void;
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
#### `createTable(client, tableName?, dimensions?)`
|
|
176
|
-
|
|
177
|
-
Create the articles table with vector index.
|
|
178
|
-
|
|
179
|
-
### Searching
|
|
180
|
-
|
|
181
|
-
#### `search(options)`
|
|
182
|
-
|
|
183
|
-
Perform semantic search.
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
interface SearchOptions {
|
|
187
|
-
client: Client;
|
|
188
|
-
query: string;
|
|
189
|
-
limit?: number; // Default: 10
|
|
190
|
-
tableName?: string; // Default: 'articles'
|
|
191
|
-
embeddingOptions?: EmbeddingOptions;
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
Returns `SearchResult[]`:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
interface SearchResult {
|
|
199
|
-
id: number;
|
|
200
|
-
slug: string;
|
|
201
|
-
title: string;
|
|
202
|
-
content: string;
|
|
203
|
-
folder: string;
|
|
204
|
-
tags: string[];
|
|
205
|
-
distance: number; // Lower is better
|
|
206
|
-
created_at: string;
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
#### `getAllArticles(client, tableName?)`
|
|
211
|
-
|
|
212
|
-
Get all articles (useful for building static pages).
|
|
213
|
-
|
|
214
|
-
#### `getArticleBySlug(client, slug, tableName?)`
|
|
215
|
-
|
|
216
|
-
Get a single article by slug.
|
|
217
|
-
|
|
218
|
-
#### `getArticlesByFolder(client, folder, tableName?)`
|
|
219
|
-
|
|
220
|
-
Get all articles in a folder.
|
|
221
|
-
|
|
222
|
-
#### `getFolders(client, tableName?)`
|
|
223
|
-
|
|
224
|
-
Get all unique folders.
|
|
225
|
-
|
|
226
|
-
### Embeddings
|
|
227
|
-
|
|
228
|
-
#### `generateEmbedding(text, options?)`
|
|
229
|
-
|
|
230
|
-
Generate embeddings for arbitrary text.
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
interface EmbeddingOptions {
|
|
234
|
-
provider?: 'local' | 'gemini' | 'openai';
|
|
235
|
-
apiKey?: string;
|
|
236
|
-
dimensions?: number;
|
|
237
|
-
maxLength?: number; // Default: 8000
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
#### `prepareTextForEmbedding(fields)`
|
|
242
|
-
|
|
243
|
-
Combine multiple fields into embedding text.
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
const text = prepareTextForEmbedding({
|
|
247
|
-
title: 'My Article',
|
|
248
|
-
description: 'A description',
|
|
249
|
-
content: '# Content here',
|
|
250
|
-
tags: ['astro', 'turso']
|
|
251
|
-
});
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Framework Integration
|
|
255
|
-
|
|
256
|
-
### Astro
|
|
257
|
-
|
|
258
|
-
**Search API Endpoint** (`src/pages/api/search.json.ts`):
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
import type { APIRoute } from 'astro';
|
|
262
|
-
import { createClient } from '@libsql/client';
|
|
263
|
-
import { search } from 'libsql-search';
|
|
264
|
-
|
|
265
|
-
export const prerender = false;
|
|
266
|
-
|
|
267
|
-
const client = createClient({
|
|
268
|
-
url: import.meta.env.TURSO_DB_URL,
|
|
269
|
-
authToken: import.meta.env.TURSO_AUTH_TOKEN
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
273
|
-
const { query, limit = 10 } = await request.json();
|
|
274
|
-
|
|
275
|
-
const results = await search({
|
|
276
|
-
client,
|
|
277
|
-
query,
|
|
278
|
-
limit,
|
|
279
|
-
embeddingOptions: { provider: 'local' }
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
return new Response(JSON.stringify({ results }), {
|
|
283
|
-
headers: { 'Content-Type': 'application/json' }
|
|
284
|
-
});
|
|
285
|
-
};
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
**Static Page Generation** (`src/pages/[...slug].astro`):
|
|
289
|
-
|
|
290
|
-
```astro
|
|
291
|
-
---
|
|
292
|
-
import { createClient } from '@libsql/client';
|
|
293
|
-
import { getAllArticles, getArticleBySlug } from 'libsql-search';
|
|
294
|
-
|
|
295
|
-
export const prerender = true;
|
|
296
|
-
|
|
297
|
-
const client = createClient({
|
|
298
|
-
url: import.meta.env.TURSO_DB_URL,
|
|
299
|
-
authToken: import.meta.env.TURSO_AUTH_TOKEN
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
export async function getStaticPaths() {
|
|
303
|
-
const articles = await getAllArticles(client);
|
|
304
|
-
return articles.map(article => ({
|
|
305
|
-
params: { slug: article.slug }
|
|
306
|
-
}));
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const { slug } = Astro.params;
|
|
310
|
-
const article = await getArticleBySlug(client, slug);
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
<article>
|
|
314
|
-
<h1>{article.title}</h1>
|
|
315
|
-
<div set:html={article.content} />
|
|
316
|
-
</article>
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Next.js
|
|
320
|
-
|
|
321
|
-
**API Route** (`app/api/search/route.ts`):
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
import { createClient } from '@libsql/client';
|
|
325
|
-
import { search } from 'libsql-search';
|
|
326
|
-
import { NextRequest } from 'next/server';
|
|
327
|
-
|
|
328
|
-
const client = createClient({
|
|
329
|
-
url: process.env.TURSO_DB_URL!,
|
|
330
|
-
authToken: process.env.TURSO_AUTH_TOKEN!
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
export async function POST(request: NextRequest) {
|
|
334
|
-
const { query, limit = 10 } = await request.json();
|
|
335
|
-
|
|
336
|
-
const results = await search({
|
|
337
|
-
client,
|
|
338
|
-
query,
|
|
339
|
-
limit,
|
|
340
|
-
embeddingOptions: { provider: 'local' }
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
return Response.json({ results });
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
**Static Generation** (`app/[slug]/page.tsx`):
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
import { createClient } from '@libsql/client';
|
|
351
|
-
import { getAllArticles, getArticleBySlug } from 'libsql-search';
|
|
352
|
-
|
|
353
|
-
const client = createClient({
|
|
354
|
-
url: process.env.TURSO_DB_URL!,
|
|
355
|
-
authToken: process.env.TURSO_AUTH_TOKEN!
|
|
83
|
+
provider: "local",
|
|
84
|
+
dimensions: 768,
|
|
85
|
+
},
|
|
356
86
|
});
|
|
357
87
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export default async function Page({ params }: { params: { slug: string } }) {
|
|
366
|
-
const article = await getArticleBySlug(client, params.slug);
|
|
367
|
-
|
|
368
|
-
return (
|
|
369
|
-
<article>
|
|
370
|
-
<h1>{article.title}</h1>
|
|
371
|
-
<div dangerouslySetInnerHTML={{ __html: article.content }} />
|
|
372
|
-
</article>
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
## Best Practices
|
|
378
|
-
|
|
379
|
-
### Embedding Dimensions
|
|
380
|
-
|
|
381
|
-
- Use **768 dimensions** for best compatibility
|
|
382
|
-
- Local model outputs 384, automatically padded to 768
|
|
383
|
-
- Gemini outputs 768 natively
|
|
384
|
-
- OpenAI supports custom dimensions
|
|
385
|
-
|
|
386
|
-
### Index Updates
|
|
387
|
-
|
|
388
|
-
Create a script to re-index content:
|
|
389
|
-
|
|
390
|
-
```json
|
|
391
|
-
{
|
|
392
|
-
"scripts": {
|
|
393
|
-
"index": "node scripts/index.js",
|
|
394
|
-
"build": "npm run index && astro build"
|
|
395
|
-
}
|
|
396
|
-
}
|
|
88
|
+
console.log(results.map((result) => ({
|
|
89
|
+
slug: result.slug,
|
|
90
|
+
title: result.title,
|
|
91
|
+
distance: result.distance,
|
|
92
|
+
})));
|
|
397
93
|
```
|
|
398
94
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
Improve search results:
|
|
402
|
-
|
|
403
|
-
1. **Include relevant fields** in embedding text (title, description, tags)
|
|
404
|
-
2. **Truncate long content** to avoid noise
|
|
405
|
-
3. **Use the same provider** for indexing and search
|
|
406
|
-
4. **Experiment with distance thresholds** (lower is better)
|
|
407
|
-
|
|
408
|
-
### Performance
|
|
409
|
-
|
|
410
|
-
- **Cache the embedding model** (done automatically)
|
|
411
|
-
- **Use edge databases** (Turso) for low latency
|
|
412
|
-
- **Implement search debouncing** in the UI
|
|
413
|
-
- **Limit result count** to 5-10 for best UX
|
|
414
|
-
|
|
415
|
-
## Examples
|
|
416
|
-
|
|
417
|
-
See the `/examples` directory for complete implementations:
|
|
418
|
-
|
|
419
|
-
- [Astro Documentation Site](./examples/astro-docs)
|
|
420
|
-
- [Next.js Blog](./examples/nextjs-blog)
|
|
421
|
-
- [CLI Indexer](./examples/cli-indexer)
|
|
95
|
+
Important behavior:
|
|
422
96
|
|
|
423
|
-
|
|
97
|
+
- Call `createTable()` before indexing or searching.
|
|
98
|
+
- Keep dimensions aligned across table creation, indexing, and search queries.
|
|
99
|
+
- `indexContent()` clears existing rows before rebuilding the index.
|
|
424
100
|
|
|
425
|
-
|
|
101
|
+
## Core API
|
|
426
102
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
103
|
+
- `createTable(client, tableName?, dimensions?)`
|
|
104
|
+
- `indexContent(options)`
|
|
105
|
+
- `search(options)`
|
|
106
|
+
- `getAllArticles(client, tableName?)`
|
|
107
|
+
- `getArticleBySlug(client, slug, tableName?)`
|
|
108
|
+
- `getArticlesByFolder(client, folder, tableName?)`
|
|
109
|
+
- `getFolders(client, tableName?)`
|
|
110
|
+
- `generateEmbedding(text, options?)`
|
|
111
|
+
- `prepareTextForEmbedding(fields)`
|
|
431
112
|
|
|
432
|
-
|
|
433
|
-
url: process.env.TURSO_DB_URL,
|
|
434
|
-
authToken: process.env.TURSO_AUTH_TOKEN
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
await createTable(client);
|
|
438
|
-
|
|
439
|
-
const result = await indexContent({
|
|
440
|
-
client,
|
|
441
|
-
contentPath: './content',
|
|
442
|
-
embeddingOptions: {
|
|
443
|
-
provider: process.env.EMBEDDING_PROVIDER || 'local'
|
|
444
|
-
},
|
|
445
|
-
onProgress: (current, total, file) => {
|
|
446
|
-
console.log(`[${current}/${total}] ${file}`);
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
console.log(`✅ Indexed ${result.success} documents`);
|
|
451
|
-
```
|
|
113
|
+
## Docs
|
|
452
114
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
115
|
+
- [Docs index](./docs/README.md)
|
|
116
|
+
- [Provider guide](./docs/PROVIDERS.md)
|
|
117
|
+
- [API reference](./docs/API.md)
|
|
118
|
+
- [Integration examples](./docs/INTEGRATIONS.md)
|
|
119
|
+
- [Indexing and operations](./docs/INDEXING.md)
|
|
120
|
+
- [Troubleshooting](./docs/TROUBLESHOOTING.md)
|
|
121
|
+
- [Release workflow](./docs/RELEASING.md)
|
|
457
122
|
|
|
458
123
|
## License
|
|
459
124
|
|
|
460
125
|
MIT
|
|
461
|
-
|
|
462
|
-
## Contributing
|
|
463
|
-
|
|
464
|
-
Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/llbbl/libsql-search).
|
|
465
|
-
|
|
466
|
-
## Related Projects
|
|
467
|
-
|
|
468
|
-
- [Turso](https://turso.tech) - Edge SQLite database
|
|
469
|
-
- [libSQL](https://github.com/tursodatabase/libsql) - Open source SQLite fork
|
|
470
|
-
- [Astro](https://astro.build) - Static site framework
|
|
471
|
-
- [Transformers.js](https://huggingface.co/docs/transformers.js) - ML models in JavaScript
|
|
472
|
-
|
|
473
|
-
## Support
|
|
474
|
-
|
|
475
|
-
- [Documentation](https://github.com/llbbl/libsql-search)
|
|
476
|
-
- [Issues](https://github.com/llbbl/libsql-search/issues)
|
|
477
|
-
- [Discussions](https://github.com/llbbl/libsql-search/discussions)
|