vecito 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeka Kiselyov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # vecito
2
+
3
+ Tiny **hybrid semantic search** for Node and the browser — dense embeddings
4
+ ([`@huggingface/transformers`](https://github.com/huggingface/transformers.js); default
5
+ `Xenova/all-MiniLM-L6-v2`, but any feature-extraction model works) fused with **BM25** sparse
6
+ lexical scoring over an [`altor-vec`](https://github.com/altor-lab/altor-vec) WASM HNSW index. No server,
7
+ no API keys.
8
+
9
+ **Where to build the index.** Building a snapshot means embedding every document and constructing the HNSW graph — the expensive part. It's usually best to do this once on the server or with the CLI (`vecito index`), then serve the resulting `.vecito` file and load it in the browser with `Vecito.loadFromUrl()`, which restores the pre-built graph in milliseconds. Building, indexing, and adding documents directly in the browser is fully supported too — it just runs that same per-document embedding client-side, which is slow for large corpora.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install vecito
15
+ ```
16
+
17
+ The first run downloads the embedding model to the Hugging Face cache. By default it loads
18
+ **quantized** weights (`dtype: 'q8'`) — about **22 MB** for the default model. Pass
19
+ `dtype: 'fp32'` for full precision (~87 MB) if you need maximum quality.
20
+
21
+ ## Library usage
22
+
23
+ The core library indexes **any data** — plain strings or raw JSON objects — with no
24
+ pre-formatting. By default the searchable text is the item's flattened string values, and the
25
+ whole object comes back as metadata on each hit.
26
+
27
+ ```js
28
+ import { Vecito } from 'vecito';
29
+
30
+ const v = new Vecito();
31
+ await v.addDocuments([
32
+ { id: 'a', title: 'Animals', body: 'The quick brown fox jumps over the lazy dog.' },
33
+ { id: 'b', title: 'Search', body: 'BM25 ranks documents by term frequency and rarity.' },
34
+ { id: 'c', title: 'Botany', body: 'Photosynthesis converts sunlight into chemical energy.' },
35
+ ]);
36
+
37
+ const hits = await v.search('how do plants make food?', { mode: 'hybrid', top: 3 });
38
+ // → [{ score, metadata: { id, title, body }, dense_rank?, sparse_rank? }, ...]
39
+ ```
40
+
41
+ Plain strings work too (`addDocuments(['some text', ...])`). For custom shapes, pass extractor
42
+ functions:
43
+
44
+ ```js
45
+ await v.addDocuments(rows, {
46
+ text: r => `${r.headline}\n${r.summary}`, // what gets embedded + BM25-scored
47
+ metadata: r => ({ id: r.id }), // what's returned with hits
48
+ });
49
+ ```
50
+
51
+ `mode` is `'hybrid'` (default, reciprocal-rank fusion), `'dense'` (vectors only), or `'sparse'`
52
+ (BM25-weighted). All modes support a `filter` predicate to post-filter results by metadata.
53
+ If the query has no in-vocabulary terms, hybrid/sparse automatically fall back to dense.
54
+
55
+ ### Options & models
56
+
57
+ ```js
58
+ new Vecito({ model, dtype, k1, b });
59
+ ```
60
+
61
+ Pass any transformers.js feature-extraction model as `model` — the embedding width is detected
62
+ automatically (e.g. 384 for MiniLM/BGE-small, 768 for MPNet/GTE-base). `dtype` picks the weight
63
+ precision: `'q8'` (quantized, the default, ~4× smaller download) or `'fp32'` (full precision);
64
+ the chosen dtype is stored in the snapshot so loads stay consistent. `k1` / `b` tune BM25.
65
+ `v.model`, `v.dtype`, `v.dimensions`, and `v.count` expose index state.
66
+
67
+ `addDocuments` fits BM25 on the **first** call, then freezes it, so you can add more documents
68
+ later (including to a loaded snapshot — see below). Dense search covers new documents fully;
69
+ sparse scoring only sees terms already in the frozen vocabulary, so pass your whole corpus up
70
+ front for best lexical recall.
71
+
72
+ ### Persistence
73
+
74
+ ```js
75
+ // Node — single self-contained file (vectors + metadata + sparse + BM25 + model)
76
+ await v.save('data.vecito');
77
+ const loaded = await Vecito.load('data.vecito');
78
+
79
+ // Universal — in-memory bytes (use in the browser, or to store anywhere)
80
+ const bytes = v.exportBytes(); // Uint8Array
81
+ const fromBytes = await Vecito.loadFromBytes(bytes);
82
+ const fromUrl = await Vecito.loadFromUrl('https://example.com/data.vecito');
83
+ ```
84
+
85
+ A snapshot is self-describing — it stores the model it was built with, so `load` always searches
86
+ with the right embedder. You can keep extending a loaded index and re-save it:
87
+
88
+ ```js
89
+ const loaded = await Vecito.load('data.vecito');
90
+ await loaded.addDocuments(moreItems);
91
+ await loaded.save('data.vecito');
92
+ ```
93
+
94
+ The primitives are exported too if you want to wire them yourself:
95
+ `import { Embedder, BM25, VecStore } from 'vecito'`.
96
+
97
+ ## File indexing (`vecito/file`)
98
+
99
+ A thin Node-only layer on top of the core turns files and directories into indexed documents.
100
+ It's a separate subpath import, so the core stays browser-safe.
101
+
102
+ ```js
103
+ import { indexDirectory } from 'vecito/file';
104
+
105
+ const v = await indexDirectory('./docs'); // → a ready Vecito
106
+ await v.save('docs.vecito');
107
+ const hits = await v.search('renewable energy sources', { top: 5 });
108
+ ```
109
+
110
+ Each file becomes one document: `.json`/`.jsonl` are parsed to objects (then flattened),
111
+ everything else is indexed as raw text; metadata is `{ path, name }`. Options: `ext` (extension
112
+ allowlist), `hidden` (include dotfiles, off by default), `limit`, `model`. Also exported:
113
+ `indexFiles(paths, opts)`, `walk(dir, opts)`, and `DEFAULT_EXTENSIONS`.
114
+
115
+ ## Browser
116
+
117
+ The core (`Embedder` + `BM25` + `VecStore` + `Vecito`) runs in the browser unchanged — it only
118
+ needs a dev server that resolves bare imports and serves the two WASM deps. A ready-to-run smoke
119
+ test lives in [`browser/index.html`](browser/index.html):
120
+
121
+ ```bash
122
+ pnpm install
123
+ pnpm dev:browser # vite — opens browser/index.html
124
+ ```
125
+
126
+ It imports `vecito`, loads pre-built snapshots or embeds documents from scratch in the altor-vec
127
+ WASM HNSW store, and runs hybrid search entirely client-side (the only network call is the
128
+ one-time ~22 MB quantized model download from the Hugging Face CDN). Don't import `vecito/file`
129
+ in the browser — it's the Node-only layer.
130
+
131
+ The included [`vite.config.js`](vite.config.js) keeps `@huggingface/transformers` and `altor-vec`
132
+ out of dependency pre-bundling (`optimizeDeps.exclude`) so their `import.meta.url`-relative
133
+ `.wasm` URLs resolve correctly. Any bundler works as long as it does the same.
134
+
135
+ ## CLI
136
+
137
+ Install globally to get the `vecito` command on your `PATH`:
138
+
139
+ ```bash
140
+ pnpm add -g vecito
141
+ ```
142
+
143
+ Or run it without installing via `pnpm dlx vecito …`.
144
+
145
+ ```bash
146
+ # Index the current directory into ./data.vecito
147
+ vecito index
148
+
149
+ # …or a specific directory / output file
150
+ vecito index ./docs -o docs.vecito
151
+
152
+ # Search (path is optional; defaults to data.vecito in the current directory)
153
+ vecito search "renewable energy sources" --mode hybrid --top 5
154
+ vecito search "renewable energy sources" docs.vecito --top 5
155
+ ```
156
+
157
+ `index` recursively walks the directory, indexing a broad set of text/data/code extensions
158
+ (`.md`, `.txt`, `.json`, `.yaml`, `.js`, `.py`, … — override with `--ext .md,.txt`).
159
+ **Dotfiles and dot-directories are skipped by default** (pass `--hidden` to include them).
160
+ `.json` files are flattened to their string values before indexing.
161
+
162
+ The trailing path is optional and **defaults to the current directory** — `index` scans cwd, and
163
+ `search` loads `data.vecito` from cwd (a directory path resolves to `data.vecito` inside it).
164
+
165
+ ```
166
+ vecito index [dir] [-o data.vecito] [--ext .md,.txt,...] [--hidden] [--limit N]
167
+ vecito search <query> [path] [--mode dense|sparse|hybrid] [--top N] [--filter <expr>]
168
+ ```
169
+
170
+ ## Sample data
171
+
172
+ The pre-built snapshots in `sampledata/` are derived from the [Books Dataset](https://www.kaggle.com/datasets/saurabhbagchi/books-dataset/data) on Kaggle.
173
+
174
+ ## License
175
+
176
+ MIT © Jeka Kiselyov
package/bin/cli.js ADDED
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, statSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { Vecito } from '../lib/vecito.js';
5
+ import { walk, indexFiles } from '../lib/file-index.js';
6
+
7
+ /** Default index filename, used for both output and directory-relative lookup. */
8
+ const DEFAULT_INDEX = 'data.vecito';
9
+
10
+ /** Flags that consume the following argv token as their value. */
11
+ const VALUE_FLAGS = new Set(['-o', '--out', '--ext', '--limit', '--mode', '--top', '--filter']);
12
+
13
+ /**
14
+ * Read the value following a flag in argv.
15
+ * @param {string} name Flag, e.g. '--top'.
16
+ * @param {*} fallback Value returned when the flag is absent.
17
+ * @returns {string|*} The argument after the flag, or `fallback`.
18
+ */
19
+ function flag(name, fallback) {
20
+ const i = process.argv.indexOf(name);
21
+ return i !== -1 ? process.argv[i + 1] : fallback;
22
+ }
23
+
24
+ /**
25
+ * Whether a boolean flag is present in argv.
26
+ * @param {string} name Flag, e.g. '--hidden'.
27
+ * @returns {boolean}
28
+ */
29
+ function hasFlag(name) {
30
+ return process.argv.includes(name);
31
+ }
32
+
33
+ /**
34
+ * Extract positional arguments for the current command, skipping flags and the
35
+ * values they consume.
36
+ * @returns {string[]} Positional args in order (after the command word).
37
+ */
38
+ function positionals() {
39
+ const args = process.argv.slice(3); // after `node cli.js <command>`
40
+ const out = [];
41
+ for (let i = 0; i < args.length; i++) {
42
+ const a = args[i];
43
+ if (a.startsWith('-')) {
44
+ if (VALUE_FLAGS.has(a)) i++; // skip this flag's value
45
+ continue;
46
+ }
47
+ out.push(a);
48
+ }
49
+ return out;
50
+ }
51
+
52
+ /**
53
+ * Resolve a search path to an index file: a directory (or the cwd default)
54
+ * resolves to `data.vecito` inside it; a file path is used as-is.
55
+ * @param {string} [p] Path argument; defaults to the current directory.
56
+ * @returns {string} Path to the index file.
57
+ */
58
+ function resolveIndexPath(p) {
59
+ const target = p || '.';
60
+ if (existsSync(target) && statSync(target).isDirectory()) return join(target, DEFAULT_INDEX);
61
+ return target;
62
+ }
63
+
64
+ /**
65
+ * Print CLI usage to stderr.
66
+ * @returns {void}
67
+ */
68
+ function usage() {
69
+ console.error(`vecito — hybrid (dense + BM25) semantic search
70
+
71
+ Usage:
72
+ vecito index [dir] [-o data.vecito] [--mode dense|hybrid] [--ext .md,.txt,...] [--hidden] [--limit N]
73
+ vecito search <query> [path] [--mode dense|sparse|hybrid] [--top N] [--filter <expr>]
74
+
75
+ The trailing path is optional and defaults to the current directory.
76
+
77
+ Index options:
78
+ -o, --out <file> Output index file (default: data.vecito)
79
+ --mode <m> Index mode: hybrid (default, dense+BM25) or dense (vectors only, smaller file)
80
+ --ext <list> Comma-separated extensions to index (default: broad text set)
81
+ --hidden Include dotfiles and dot-directories (skipped by default)
82
+ --limit <n> Index at most n files
83
+
84
+ Search options:
85
+ --mode <m> Search mode: hybrid (default), dense, or sparse
86
+ --top <n> Number of results (default: 10)
87
+ --filter <expr> JS expression over metadata, e.g. 'meta.category === "science"'`);
88
+ }
89
+
90
+ /**
91
+ * `vecito index [dir]` — walk a directory (default: cwd) and write a
92
+ * self-contained index. Thin wrapper over the file layer (lib/file-index.js).
93
+ * @returns {Promise<void>}
94
+ */
95
+ async function cmdIndex() {
96
+ const dir = positionals().pop() || '.';
97
+ const out = flag('-o', flag('--out', DEFAULT_INDEX));
98
+ const extArg = flag('--ext', null);
99
+ const ext = extArg ? extArg.split(',') : undefined;
100
+ const hidden = hasFlag('--hidden');
101
+ const limitArg = flag('--limit', null);
102
+ const limit = limitArg ? parseInt(limitArg, 10) : undefined;
103
+ const mode = flag('--mode', 'hybrid');
104
+
105
+ if (mode !== 'hybrid' && mode !== 'dense') {
106
+ console.error(`Unknown index mode "${mode}". Use hybrid (default) or dense.`);
107
+ process.exit(1);
108
+ }
109
+
110
+ const files = walk(dir, { ext, hidden, limit });
111
+ console.log(`Found ${files.length} file(s) to index in ${dir}`);
112
+
113
+ const label = mode === 'dense' ? 'Embedding (dense only)...' : 'Embedding + fitting BM25...';
114
+ console.log(label);
115
+ const vecito = await indexFiles(files, { base: dir, mode });
116
+ await vecito.save(out);
117
+ console.log(`Indexed ${vecito.count} document(s) [${vecito.indexMode}] → ${out}`);
118
+ }
119
+
120
+ /**
121
+ * `vecito search <query> [path]` — load an index (default: data.vecito in the
122
+ * current directory) and print ranked results.
123
+ * @returns {Promise<void>}
124
+ */
125
+ async function cmdSearch() {
126
+ const pos = positionals();
127
+ const query = pos[0];
128
+ if (!query) { usage(); process.exit(1); }
129
+ const indexFile = resolveIndexPath(pos[1]);
130
+
131
+ const mode = flag('--mode', 'hybrid');
132
+ const top = parseInt(flag('--top', '10'), 10);
133
+ const filterExpr = flag('--filter', undefined);
134
+ let filter;
135
+ if (filterExpr) {
136
+ try {
137
+ filter = new Function('meta', `return (${filterExpr})`);
138
+ } catch (e) {
139
+ console.error(`Invalid --filter expression: ${e.message}`);
140
+ process.exit(1);
141
+ }
142
+ }
143
+
144
+ if (!existsSync(indexFile)) {
145
+ console.error(`Index not found: ${indexFile}`);
146
+ process.exit(1);
147
+ }
148
+
149
+ const vecito = await Vecito.load(indexFile);
150
+ const effectiveMode = vecito.indexMode === 'dense' ? 'dense' : mode;
151
+ console.log(`Loaded ${vecito.count} doc(s) from ${indexFile} [index: ${vecito.indexMode}, search: ${effectiveMode}]\n`);
152
+ console.log(`Results for "${query}"${filterExpr ? ` (filter: ${filterExpr})` : ''}:\n`);
153
+
154
+ const results = await vecito.search(query, { mode: effectiveMode, top, filter });
155
+ if (results.length === 0) {
156
+ console.log('(no results)');
157
+ return;
158
+ }
159
+
160
+ for (let i = 0; i < results.length; i++) {
161
+ const r = results[i];
162
+ const m = r.metadata || {};
163
+ const score = r.score?.toFixed(4) ?? '-';
164
+ const ranks = [];
165
+ if (r.dense_rank) ranks.push(`dense:#${r.dense_rank}`);
166
+ if (r.sparse_rank) ranks.push(`sparse:#${r.sparse_rank}`);
167
+ const rankStr = ranks.length ? ` (${ranks.join(', ')})` : '';
168
+ console.log(`${i + 1}. [${score}] ${m.name || m.path || '?'}${rankStr}`);
169
+ if (m.path && m.path !== m.name) console.log(` ${m.path}`);
170
+ }
171
+ }
172
+
173
+ const cmd = process.argv[2];
174
+ if (cmd === 'index') {
175
+ await cmdIndex();
176
+ } else if (cmd === 'search') {
177
+ await cmdSearch();
178
+ } else {
179
+ usage();
180
+ process.exit(1);
181
+ }
package/file.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { Vecito } from './index';
2
+
3
+ export const DEFAULT_EXTENSIONS: string[];
4
+
5
+ export interface WalkOptions {
6
+ ext?: string[];
7
+ hidden?: boolean;
8
+ limit?: number;
9
+ }
10
+
11
+ export interface IndexOptions extends WalkOptions {
12
+ model?: string;
13
+ dtype?: string;
14
+ mode?: 'hybrid' | 'dense';
15
+ }
16
+
17
+ /** Recursively collect matching file paths, skipping dotfiles by default. */
18
+ export function walk(dir: string, opts?: WalkOptions): string[];
19
+
20
+ /** Index an explicit list of files into a fresh Vecito. */
21
+ export function indexFiles(
22
+ paths: string[],
23
+ opts?: { model?: string; dtype?: string; mode?: 'hybrid' | 'dense'; base?: string }
24
+ ): Promise<Vecito>;
25
+
26
+ /** Walk a directory and index every matching file into a fresh Vecito. */
27
+ export function indexDirectory(dir: string, opts?: IndexOptions): Promise<Vecito>;
package/index.d.ts ADDED
@@ -0,0 +1,113 @@
1
+ export interface SparseVector {
2
+ indices: Uint32Array;
3
+ values: Float32Array;
4
+ dim: number;
5
+ }
6
+
7
+ export interface SearchResult {
8
+ id?: number;
9
+ score?: number;
10
+ dense_rank?: number;
11
+ sparse_rank?: number;
12
+ metadata: Record<string, any>;
13
+ /** BM25-matched (hybrid) or tokenized (dense) terms. Present only when search was called with `{ matchedTerms: true }`. */
14
+ matchedTerms?: string[];
15
+ }
16
+
17
+ export class Highlighter {
18
+ /** Escape HTML special characters in a plain-text string. */
19
+ static escape(s: string): string;
20
+ /** Tokenize a query string for dense-mode fallback highlighting. */
21
+ static tokenize(text: string): string[];
22
+ /** Wrap occurrences of `terms` in `text` with `<mark>` tags. Matching is stem-aware: "run" matches "running", "adventure" matches "adventures". */
23
+ static highlight(text: string, terms: string[] | Set<string>): string;
24
+ /** Extract a snippet centred on the first stem match (plain text — pass to highlight for markup). */
25
+ static snippet(text: string, terms: string[] | Set<string>, maxLen?: number): string;
26
+ }
27
+
28
+ export class Embedder {
29
+ constructor(opts?: { model?: string; dtype?: string });
30
+ init(): Promise<void>;
31
+ embed(text: string): Promise<Float32Array>;
32
+ embedBatch(texts: string[], opts?: { batchSize?: number }): Promise<Float32Array[]>;
33
+ get dimensions(): number;
34
+ get dtype(): string;
35
+ get model(): string;
36
+ }
37
+
38
+ export class BM25 {
39
+ constructor(opts?: { k1?: number; b?: number });
40
+ fit(texts: string[]): void;
41
+ score(text: string): SparseVector;
42
+ scoreAll(texts: string[]): SparseVector[];
43
+ /** Map a query string to the in-vocabulary term ids it contains. */
44
+ scoreQuery(queryText: string): { indices: number[]; vocabSize: number };
45
+ querySparse(queryText: string): SparseVector;
46
+ /** Map vocabulary term ids back to their original term strings (unknown ids omitted). */
47
+ termsForIndices(indices: Uint32Array | number[]): string[];
48
+ toJSON(): Record<string, any>;
49
+ static fromJSON(data: Record<string, any>): BM25;
50
+ get vocabSize(): number;
51
+ }
52
+
53
+ export class VecStore {
54
+ constructor(opts: { dimensions: number });
55
+ init(): Promise<void>;
56
+ insert(vector: Float32Array | number[], metadata?: Record<string, any>): number;
57
+ initSparse(): void;
58
+ insertSparse(sparse: SparseVector): void;
59
+ search(query: Float32Array, k?: number): Promise<SearchResult[]>;
60
+ /** Post-filters HNSW candidates with a JS predicate over metadata objects. */
61
+ searchWithFilter(query: Float32Array, filter: (meta: Record<string, any>) => boolean, k?: number): Promise<SearchResult[]>;
62
+ hybridSearch(
63
+ denseQuery: Float32Array,
64
+ sparse: SparseVector,
65
+ k?: number,
66
+ opts?: { dense_k?: number; sparse_k?: number; fusion?: any }
67
+ ): SearchResult[];
68
+ save(filePath: string): Promise<void>;
69
+ /** Alias for {@link VecStore.save}. */
70
+ exportToFile(filePath: string): Promise<void>;
71
+ exportBytes(): Uint8Array;
72
+ static load(filePath: string): Promise<VecStore>;
73
+ static loadFromBytes(bytes: Uint8Array): Promise<VecStore>;
74
+ static loadFromUrl(url: string): Promise<VecStore>;
75
+ get count(): number;
76
+ /** Dense vector width of the index. */
77
+ get dimensions(): number;
78
+ }
79
+
80
+ export interface AddOptions {
81
+ /** Extract searchable text from an item (default: flatten string values). */
82
+ text?: (item: any) => string;
83
+ /** Extract metadata returned with hits (default: the object itself). */
84
+ metadata?: (item: any) => Record<string, any>;
85
+ }
86
+
87
+ export interface VecitoSearchOptions {
88
+ mode?: 'hybrid' | 'dense' | 'sparse';
89
+ top?: number;
90
+ /** JS predicate over metadata — post-filters results in any mode, over-fetching to preserve the requested count. */
91
+ filter?: (meta: Record<string, any>) => boolean;
92
+ /** When true, each result includes `matchedTerms` for use with `Highlighter.highlight`. */
93
+ matchedTerms?: boolean;
94
+ }
95
+
96
+ export class Vecito {
97
+ constructor(opts?: { model?: string; dtype?: string; embedder?: Embedder; mode?: 'hybrid' | 'dense'; k1?: number; b?: number });
98
+ addDocuments(items: any | any[], opts?: AddOptions): Promise<this>;
99
+ search(query: string, opts?: VecitoSearchOptions): Promise<SearchResult[]>;
100
+ exportBytes(): Uint8Array;
101
+ save(path: string): Promise<void>;
102
+ static load(path: string): Promise<Vecito>;
103
+ static loadFromBytes(bytes: Uint8Array | ArrayBuffer): Promise<Vecito>;
104
+ static loadFromUrl(url: string): Promise<Vecito>;
105
+ get count(): number;
106
+ get model(): string;
107
+ /** Weight precision the embedder loads (e.g. 'q8', 'fp32'). */
108
+ get dtype(): string;
109
+ /** Dense vector width of the index, or null before anything is indexed. */
110
+ get dimensions(): number | null;
111
+ /** Index mode this instance was built with ('hybrid' or 'dense'). */
112
+ get indexMode(): 'hybrid' | 'dense';
113
+ }
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { Embedder } from './lib/embedder.js';
2
+ export { BM25 } from './lib/bm25.js';
3
+ export { VecStore } from './lib/vec-store.js';
4
+ export { Vecito } from './lib/vecito.js';
5
+ export { Highlighter } from './lib/highlight.js';