albex 0.1.0 → 0.6.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/CHANGELOG.md +416 -0
- package/README.md +244 -112
- package/dist/albex-worker.d.ts +70 -0
- package/dist/albex-worker.d.ts.map +1 -0
- package/dist/albex-worker.js +153 -0
- package/dist/albex-worker.js.map +1 -0
- package/dist/albex.d.ts +508 -6
- package/dist/albex.d.ts.map +1 -1
- package/dist/albex.js +1911 -141
- package/dist/albex.js.map +1 -1
- package/dist/errors.d.ts +52 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +66 -0
- package/dist/errors.js.map +1 -0
- package/dist/gpu/bloom-runtime.d.ts +60 -0
- package/dist/gpu/bloom-runtime.d.ts.map +1 -0
- package/dist/gpu/bloom-runtime.js +176 -0
- package/dist/gpu/bloom-runtime.js.map +1 -0
- package/dist/gpu/bloom-shader.wgsl.d.ts +19 -0
- package/dist/gpu/bloom-shader.wgsl.d.ts.map +1 -0
- package/dist/gpu/bloom-shader.wgsl.js +49 -0
- package/dist/gpu/bloom-shader.wgsl.js.map +1 -0
- package/dist/persistence.d.ts +21 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +174 -0
- package/dist/persistence.js.map +1 -0
- package/dist/pool/coordinator.d.ts +98 -0
- package/dist/pool/coordinator.d.ts.map +1 -0
- package/dist/pool/coordinator.js +247 -0
- package/dist/pool/coordinator.js.map +1 -0
- package/dist/profile.d.ts +100 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +200 -0
- package/dist/profile.js.map +1 -0
- package/dist/resource-manager.d.ts +56 -0
- package/dist/resource-manager.d.ts.map +1 -0
- package/dist/resource-manager.js +138 -0
- package/dist/resource-manager.js.map +1 -0
- package/dist/tiered-store.d.ts +98 -0
- package/dist/tiered-store.d.ts.map +1 -0
- package/dist/tiered-store.js +238 -0
- package/dist/tiered-store.js.map +1 -0
- package/dist/wasm-bindings.d.ts +180 -0
- package/dist/wasm-bindings.d.ts.map +1 -0
- package/dist/wasm-bindings.js +128 -0
- package/dist/wasm-bindings.js.map +1 -0
- package/dist/worker-protocol.d.ts +86 -0
- package/dist/worker-protocol.d.ts.map +1 -0
- package/dist/worker-protocol.js +20 -0
- package/dist/worker-protocol.js.map +1 -0
- package/dist/worker-runtime.d.ts +14 -0
- package/dist/worker-runtime.d.ts.map +1 -0
- package/dist/worker-runtime.js +109 -0
- package/dist/worker-runtime.js.map +1 -0
- package/package.json +60 -13
- package/src/albex-worker.ts +187 -0
- package/src/albex.ts +2136 -189
- package/src/errors.ts +76 -0
- package/src/gpu/bloom-runtime.ts +229 -0
- package/src/gpu/bloom-shader.wgsl.ts +48 -0
- package/src/persistence.ts +175 -0
- package/src/pool/coordinator.ts +324 -0
- package/src/profile.ts +280 -0
- package/src/resource-manager.ts +167 -0
- package/src/tiered-store.ts +259 -0
- package/src/wasm-bindings.ts +349 -0
- package/src/worker-protocol.ts +48 -0
- package/src/worker-runtime.ts +106 -0
- package/wasm/pkg/albex_pdf.wasm +0 -0
- package/wasm/pkg/albex_wasm.wasm +0 -0
- package/wasm/pkg/albex_wasm_bg.wasm +0 -0
- package/wasm/pkg/albex_wasm_simd.wasm +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* albex v0.6.0
|
|
3
|
+
* Zero-config local full-text search for documents — runs entirely in the browser, no server, no upload.
|
|
4
|
+
* (c) 2026 RafaCalRob
|
|
5
|
+
* @license MIT
|
|
6
|
+
* https://github.com/RafaCalRob/Albex#readme
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Tiered storage layer for Albex.
|
|
10
|
+
*
|
|
11
|
+
* The base engine keeps every indexed document in memory inside the BSS
|
|
12
|
+
* arrays. That works beautifully up to the tier's capacity (4 MB to 128 MB
|
|
13
|
+
* of indexed text) but breaks when the user wants to search across more.
|
|
14
|
+
*
|
|
15
|
+
* `TieredStore` adds two memory tiers behind the engine:
|
|
16
|
+
*
|
|
17
|
+
* HOT — already-indexed documents living in the WASM arrays.
|
|
18
|
+
* WARM — original file blobs serialised in OPFS, NOT in the engine.
|
|
19
|
+
*
|
|
20
|
+
* Eviction happens when text capacity climbs above a configurable
|
|
21
|
+
* threshold (default 85 %). The least-recently-accessed HOT document is
|
|
22
|
+
* removed from the engine; its blob stays in OPFS so we can promote it
|
|
23
|
+
* back later without asking the user to re-pick the file.
|
|
24
|
+
*
|
|
25
|
+
* Promotion happens explicitly: callers tell the store "I want these
|
|
26
|
+
* names searchable again" and we re-feed them through `engine.indexFile`.
|
|
27
|
+
*
|
|
28
|
+
* **Trade-off vs storing the internal representation in OPFS:** promoting
|
|
29
|
+
* a doc means re-parsing it (DOCX XML decode, etc.). For typical document
|
|
30
|
+
* sizes this is 20-200 ms — negligible for an explicit "search across the
|
|
31
|
+
* archive" action. The win is huge: the persistence format is just the
|
|
32
|
+
* source files, so it survives engine version bumps without any migration.
|
|
33
|
+
*/
|
|
34
|
+
const OPFS_DIR = 'albex-tiered';
|
|
35
|
+
export class TieredStore {
|
|
36
|
+
_engine;
|
|
37
|
+
_entries = new Map();
|
|
38
|
+
_dir = null;
|
|
39
|
+
_opts;
|
|
40
|
+
constructor(engine, opts = {}) {
|
|
41
|
+
this._engine = engine;
|
|
42
|
+
this._opts = {
|
|
43
|
+
evictThreshold: opts.evictThreshold ?? 0.85,
|
|
44
|
+
hotFloor: opts.hotFloor ?? 1,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Ensure the OPFS directory exists. Idempotent. Tolerated when OPFS is unavailable. */
|
|
48
|
+
async init() {
|
|
49
|
+
try {
|
|
50
|
+
const root = await navigator.storage.getDirectory();
|
|
51
|
+
this._dir = await root.getDirectoryHandle(OPFS_DIR, { create: true });
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// No OPFS — warm tier disabled; everything stays hot.
|
|
55
|
+
this._dir = null;
|
|
56
|
+
}
|
|
57
|
+
await this._rehydrateIndex();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Index a file AND register it in the warm tier so it survives across
|
|
61
|
+
* sessions. Equivalent to `engine.indexFile(file)` but with the extra
|
|
62
|
+
* persistence guarantee.
|
|
63
|
+
*/
|
|
64
|
+
async indexFile(file) {
|
|
65
|
+
const doc = await this._engine.indexFile(file);
|
|
66
|
+
// Persist the original blob in OPFS so we can promote it back later
|
|
67
|
+
// without asking the user to re-pick the file.
|
|
68
|
+
await this._writeBlob(file);
|
|
69
|
+
this._entries.set(doc.name, {
|
|
70
|
+
name: doc.name,
|
|
71
|
+
ext: doc.ext,
|
|
72
|
+
lastAccessedMs: Date.now(),
|
|
73
|
+
hot: true,
|
|
74
|
+
byteSize: file.size,
|
|
75
|
+
});
|
|
76
|
+
await this._enforceCapacity();
|
|
77
|
+
return doc;
|
|
78
|
+
}
|
|
79
|
+
/** Touch an entry to mark it recently used (for LRU). */
|
|
80
|
+
touch(name) {
|
|
81
|
+
const e = this._entries.get(name);
|
|
82
|
+
if (e)
|
|
83
|
+
e.lastAccessedMs = Date.now();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Evict the least-recently-used HOT documents until `textUsed` falls
|
|
87
|
+
* below the configured threshold. Respects `hotFloor` (never evicts
|
|
88
|
+
* the last N docs).
|
|
89
|
+
*/
|
|
90
|
+
async _enforceCapacity() {
|
|
91
|
+
const stats = this._engine.getStats();
|
|
92
|
+
const threshold = stats.textCapacity * this._opts.evictThreshold;
|
|
93
|
+
if (stats.textUsed <= threshold)
|
|
94
|
+
return;
|
|
95
|
+
const hot = [...this._entries.values()]
|
|
96
|
+
.filter(e => e.hot)
|
|
97
|
+
.sort((a, b) => a.lastAccessedMs - b.lastAccessedMs);
|
|
98
|
+
while (hot.length > this._opts.hotFloor) {
|
|
99
|
+
const victim = hot.shift();
|
|
100
|
+
this._engine.removeDocument(victim.name);
|
|
101
|
+
victim.hot = false;
|
|
102
|
+
// Reclaim storage now so subsequent index calls see real headroom.
|
|
103
|
+
this._engine.compact();
|
|
104
|
+
const after = this._engine.getStats();
|
|
105
|
+
if (after.textUsed <= threshold)
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Bring a warm document back into the engine. No-op if already hot.
|
|
111
|
+
* Returns the resulting `IndexedDocument` or `null` if the doc isn't known.
|
|
112
|
+
*/
|
|
113
|
+
async promote(name) {
|
|
114
|
+
const e = this._entries.get(name);
|
|
115
|
+
if (!e)
|
|
116
|
+
return null;
|
|
117
|
+
if (e.hot) {
|
|
118
|
+
this.touch(name);
|
|
119
|
+
const stats = this._engine.getStats();
|
|
120
|
+
void stats;
|
|
121
|
+
return this._engine.documents.find(d => d.name === name) ?? null;
|
|
122
|
+
}
|
|
123
|
+
const blob = await this._readBlob(name);
|
|
124
|
+
if (!blob)
|
|
125
|
+
return null;
|
|
126
|
+
// Re-create the File so `engine.indexFile` sees the original metadata.
|
|
127
|
+
const file = new File([blob], name);
|
|
128
|
+
const doc = await this._engine.indexFile(file);
|
|
129
|
+
e.hot = true;
|
|
130
|
+
e.lastAccessedMs = Date.now();
|
|
131
|
+
return doc;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Forget a document entirely: remove from engine and delete its OPFS blob.
|
|
135
|
+
* Returns whether the entry existed.
|
|
136
|
+
*/
|
|
137
|
+
async forget(name) {
|
|
138
|
+
const had = this._entries.has(name);
|
|
139
|
+
if (this._entries.get(name)?.hot)
|
|
140
|
+
this._engine.removeDocument(name);
|
|
141
|
+
this._entries.delete(name);
|
|
142
|
+
await this._deleteBlob(name);
|
|
143
|
+
return had;
|
|
144
|
+
}
|
|
145
|
+
/** Names of all known documents, hot or warm. */
|
|
146
|
+
list() {
|
|
147
|
+
return [...this._entries.values()].map(e => ({
|
|
148
|
+
name: e.name, hot: e.hot, byteSize: e.byteSize,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
/** Aggregate storage stats. */
|
|
152
|
+
getTierStats() {
|
|
153
|
+
let hot = 0, warm = 0, totalBytes = 0;
|
|
154
|
+
for (const e of this._entries.values()) {
|
|
155
|
+
if (e.hot)
|
|
156
|
+
hot++;
|
|
157
|
+
else
|
|
158
|
+
warm++;
|
|
159
|
+
totalBytes += e.byteSize;
|
|
160
|
+
}
|
|
161
|
+
return { hot, warm, totalBytes };
|
|
162
|
+
}
|
|
163
|
+
// ── OPFS plumbing ─────────────────────────────────────────────────────
|
|
164
|
+
_safeName(name) {
|
|
165
|
+
// Strip path separators just in case; OPFS requires plain file names.
|
|
166
|
+
return name.replace(/[/\\]/g, '_');
|
|
167
|
+
}
|
|
168
|
+
async _writeBlob(file) {
|
|
169
|
+
if (!this._dir)
|
|
170
|
+
return;
|
|
171
|
+
try {
|
|
172
|
+
const handle = await this._dir.getFileHandle(this._safeName(file.name), { create: true });
|
|
173
|
+
const w = await handle.createWritable();
|
|
174
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
175
|
+
const plain = new Uint8Array(bytes.byteLength);
|
|
176
|
+
plain.set(bytes);
|
|
177
|
+
await w.write(plain);
|
|
178
|
+
await w.close();
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
console.warn(`[albex] failed to persist blob for ${file.name}:`, e);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async _readBlob(name) {
|
|
185
|
+
if (!this._dir)
|
|
186
|
+
return null;
|
|
187
|
+
try {
|
|
188
|
+
const handle = await this._dir.getFileHandle(this._safeName(name));
|
|
189
|
+
return await handle.getFile();
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async _deleteBlob(name) {
|
|
196
|
+
if (!this._dir)
|
|
197
|
+
return;
|
|
198
|
+
try {
|
|
199
|
+
await this._dir.removeEntry(this._safeName(name));
|
|
200
|
+
}
|
|
201
|
+
catch { /* not found */ }
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* TC39 explicit-resource-management hook. Drops the in-memory index and
|
|
205
|
+
* the OPFS directory handle. The underlying OPFS blobs are NOT deleted —
|
|
206
|
+
* disposal only frees JS-side state. Use `forget()` per-doc or
|
|
207
|
+
* `deleteOpfsAll()` if you want to wipe persisted data.
|
|
208
|
+
*/
|
|
209
|
+
[Symbol.dispose]() {
|
|
210
|
+
this._entries.clear();
|
|
211
|
+
this._dir = null;
|
|
212
|
+
}
|
|
213
|
+
async _rehydrateIndex() {
|
|
214
|
+
if (!this._dir)
|
|
215
|
+
return;
|
|
216
|
+
try {
|
|
217
|
+
// @ts-expect-error async-iterable on FileSystemDirectoryHandle
|
|
218
|
+
for await (const [name, handle] of this._dir.entries()) {
|
|
219
|
+
if (handle.kind !== 'file')
|
|
220
|
+
continue;
|
|
221
|
+
const file = await handle.getFile();
|
|
222
|
+
if (this._entries.has(name))
|
|
223
|
+
continue;
|
|
224
|
+
this._entries.set(name, {
|
|
225
|
+
name,
|
|
226
|
+
ext: (name.split('.').pop() ?? '').toLowerCase(),
|
|
227
|
+
lastAccessedMs: 0, // never accessed in this session yet
|
|
228
|
+
hot: false,
|
|
229
|
+
byteSize: file.size,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
console.warn('[albex] could not rehydrate tiered index:', e);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=tiered-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiered-store.js","sourceRoot":"","sources":["../src/tiered-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed interfaces for the two WASM modules Albex ships with.
|
|
3
|
+
*
|
|
4
|
+
* These types replace ad-hoc `as Function` casts and give us:
|
|
5
|
+
* - argument/return type checking at call sites,
|
|
6
|
+
* - autocompletion in IDEs,
|
|
7
|
+
* - safe refactors when an export name or signature changes.
|
|
8
|
+
*
|
|
9
|
+
* The interfaces mirror the `#[no_mangle] pub extern "C" fn` exports
|
|
10
|
+
* in `wasm/src/lib.rs` and `pdf-wasm/src/lib.rs`.
|
|
11
|
+
*/
|
|
12
|
+
export interface AlbexWasmExports {
|
|
13
|
+
readonly memory: WebAssembly.Memory;
|
|
14
|
+
abiVersion(): number;
|
|
15
|
+
getBuffer(size: number): number;
|
|
16
|
+
init(): void;
|
|
17
|
+
/** Reset the streaming FNV-1a 64-bit hash state. Optional on the first
|
|
18
|
+
* hash of a session because the static initialiser is also FNV_OFFSET. */
|
|
19
|
+
hashBegin(): void;
|
|
20
|
+
/** Fold `len` bytes of scratchpad into the streaming hash. May be
|
|
21
|
+
* called repeatedly for files larger than SCRATCHPAD_SIZE. */
|
|
22
|
+
hashFeed(len: number): void;
|
|
23
|
+
/** Write the final 8 raw big-endian bytes at scratchpad[0..8] and
|
|
24
|
+
* reset the state so the next hash can start without an explicit Begin. */
|
|
25
|
+
hashFinish(): void;
|
|
26
|
+
setDocumentName(len: number): void;
|
|
27
|
+
beginDocument(): number;
|
|
28
|
+
feedXmlBytes(len: number): void;
|
|
29
|
+
endDocument(): number;
|
|
30
|
+
beginXlsx(): number;
|
|
31
|
+
feedXlsxBytes(len: number): void;
|
|
32
|
+
feedText(len: number): void;
|
|
33
|
+
flushParagraph(): void;
|
|
34
|
+
setMaxErrors(errors: number): void;
|
|
35
|
+
setThreshold(threshold: number): void;
|
|
36
|
+
setMaxResults(max: number): void;
|
|
37
|
+
prepareQuery(len: number): number;
|
|
38
|
+
getQueryKind(): number;
|
|
39
|
+
getQueryBranchCount(): number;
|
|
40
|
+
getQueryBranchPattern(i: number): number;
|
|
41
|
+
selectQueryBranch(i: number): number;
|
|
42
|
+
setPattern(len: number): number;
|
|
43
|
+
search(): number;
|
|
44
|
+
searchBegin(): number;
|
|
45
|
+
searchSlice(maxChunks: number): number;
|
|
46
|
+
getSearchCursor(): number;
|
|
47
|
+
getSearchTotal(): number;
|
|
48
|
+
getResultCount(): number;
|
|
49
|
+
getResultDocId(i: number): number;
|
|
50
|
+
getResultLocation(i: number): number;
|
|
51
|
+
getResultScore(i: number): number;
|
|
52
|
+
getResultStart(i: number): number;
|
|
53
|
+
getResultEnd(i: number): number;
|
|
54
|
+
getResultChunkIdx(i: number): number;
|
|
55
|
+
getResultDocName(i: number): number;
|
|
56
|
+
getResultMatchCount(i: number): number;
|
|
57
|
+
getResultMatchStartAt(i: number, k: number): number;
|
|
58
|
+
getResultMatchEndAt(i: number, k: number): number;
|
|
59
|
+
getSnippet(i: number): number;
|
|
60
|
+
getSnippetWindow(i: number, before: number, after: number): number;
|
|
61
|
+
getSnippetWindowOffset(): number;
|
|
62
|
+
getStatBloomTested(): number;
|
|
63
|
+
getStatBloomPassed(): number;
|
|
64
|
+
getStatBitapMatched(): number;
|
|
65
|
+
getChunkCount(): number;
|
|
66
|
+
getDocCount(): number;
|
|
67
|
+
getTextUsed(): number;
|
|
68
|
+
getTextCapacity(): number;
|
|
69
|
+
/** Bitflags of capacity limits hit during the most recent
|
|
70
|
+
* begin..endDocument cycle: 1 = chunks, 2 = text, 4 = docs, 8 = names.
|
|
71
|
+
* 0 = everything fit. Read by the host right after endDocument to raise a
|
|
72
|
+
* typed AlbexCapacityError instead of silently truncating the corpus. */
|
|
73
|
+
getLastIndexOverflow(): number;
|
|
74
|
+
snapshotSize(): number;
|
|
75
|
+
snapshotChunk(offset: number, maxLen: number): number;
|
|
76
|
+
/** Validate header. For v3 also reserves the staging buffer; state is
|
|
77
|
+
* NOT touched until restoreCommit succeeds. For v1/v2 (legacy) state is
|
|
78
|
+
* reset and counters are written immediately. */
|
|
79
|
+
restoreBegin(): number;
|
|
80
|
+
/** Feed payload bytes. For v3 they accumulate into staging; for v1/v2
|
|
81
|
+
* they are written straight to the state arrays as before. */
|
|
82
|
+
restoreFeed(len: number): number;
|
|
83
|
+
/** Atomic commit for v3 snapshots. Returns 1 if the staged payload was
|
|
84
|
+
* complete and decoded successfully; 0 otherwise — and in the 0 case
|
|
85
|
+
* the previous engine state is preserved. For v1/v2 this is a no-op
|
|
86
|
+
* that always returns 1. */
|
|
87
|
+
restoreCommit(): number;
|
|
88
|
+
getDocId(index: number): number;
|
|
89
|
+
getDocChunkCount(index: number): number;
|
|
90
|
+
getDocName(index: number): number;
|
|
91
|
+
isDocDeleted(index: number): number;
|
|
92
|
+
removeDocument(docId: number): number;
|
|
93
|
+
compact(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Per-document content hash (snapshot v2). Returns a pointer to 8 bytes
|
|
96
|
+
* holding the FNV-1a 64-bit hash of the original file bytes, or 0 if the
|
|
97
|
+
* doc index is out of range. All-zero bytes mean "hash not available"
|
|
98
|
+
* — either the host never called setDocumentContentHash for this doc
|
|
99
|
+
* (legacy code path) or it was restored from a v1 snapshot.
|
|
100
|
+
*/
|
|
101
|
+
getDocContentHashPtr(index: number): number;
|
|
102
|
+
/** Always returns 8. Useful as a runtime feature-detect: older binaries
|
|
103
|
+
* without snapshot v2 will not export this function at all. */
|
|
104
|
+
getDocContentHashLen(): number;
|
|
105
|
+
/** Copy a content hash (up to 8 bytes from the scratchpad) into the
|
|
106
|
+
* pending slot. endDocument() then writes it into doc_hashes[]. */
|
|
107
|
+
setDocumentContentHash(len: number): void;
|
|
108
|
+
setLanguage(lang: number): void;
|
|
109
|
+
getTier(): number;
|
|
110
|
+
getMaxChunks(): number;
|
|
111
|
+
getMaxDocs(): number;
|
|
112
|
+
getNameCapacity(): number;
|
|
113
|
+
getChunksPtr(): number;
|
|
114
|
+
getChunkStructSize(): number;
|
|
115
|
+
setCandidateMask(byteLen: number): void;
|
|
116
|
+
clearCandidateMask(): void;
|
|
117
|
+
}
|
|
118
|
+
export interface AlbexPdfExports {
|
|
119
|
+
readonly memory: WebAssembly.Memory;
|
|
120
|
+
/** ABI version of the PDF module. The host loader refuses any binary
|
|
121
|
+
* whose abiVersion is outside the supported range. */
|
|
122
|
+
abiVersion(): number;
|
|
123
|
+
/** Reserve `len` bytes inside the PDF module and return a pointer. */
|
|
124
|
+
allocInput(len: number): number;
|
|
125
|
+
/**
|
|
126
|
+
* Parse the PDF. Returns:
|
|
127
|
+
* N ≥ 0 — page count,
|
|
128
|
+
* -1 — parse error (read with getErrorPtr/getErrorLen),
|
|
129
|
+
* -2 — image-only / no extractable text.
|
|
130
|
+
*
|
|
131
|
+
* When `-2` is returned, the host can fall through to the scanned-PDF
|
|
132
|
+
* path via `getPageCount` + `extractPageImages`.
|
|
133
|
+
*/
|
|
134
|
+
extractPdf(len: number): number;
|
|
135
|
+
getPageLen(page: number): number;
|
|
136
|
+
getPagePtr(page: number): number;
|
|
137
|
+
getErrorLen(): number;
|
|
138
|
+
getErrorPtr(): number;
|
|
139
|
+
/** Total page count of the loaded PDF; 0 if the input cannot be parsed. */
|
|
140
|
+
getPageCount(): number;
|
|
141
|
+
/**
|
|
142
|
+
* Extract every supported image XObject on page `page` (0-based) into the
|
|
143
|
+
* module's internal buffer.
|
|
144
|
+
*
|
|
145
|
+
* Returns:
|
|
146
|
+
* N ≥ 0 — number of images extracted on this page,
|
|
147
|
+
* -1 — parse error or page index out of range.
|
|
148
|
+
*
|
|
149
|
+
* Each extracted image is one of:
|
|
150
|
+
* * JPEG (kind = 1, from a `/DCTDecode` filter), or
|
|
151
|
+
* * JPEG2000 (kind = 2, from a `/JPXDecode` filter).
|
|
152
|
+
*
|
|
153
|
+
* Filters that require Rust-side reconstruction (`FlateDecode`,
|
|
154
|
+
* `CCITTFaxDecode`, `JBIG2Decode`) are intentionally skipped — they
|
|
155
|
+
* would roughly double the binary size for ~5 % more coverage.
|
|
156
|
+
*/
|
|
157
|
+
extractPageImages(page: number): number;
|
|
158
|
+
/** Byte length of extracted image `i` (from the last `extractPageImages`). */
|
|
159
|
+
getPageImageLen(i: number): number;
|
|
160
|
+
/** Pointer to extracted image `i`'s raw bytes. */
|
|
161
|
+
getPageImagePtr(i: number): number;
|
|
162
|
+
/** Format tag for extracted image `i`: 1 = JPEG, 2 = JPEG2000, 0 = none. */
|
|
163
|
+
getPageImageKind(i: number): number;
|
|
164
|
+
}
|
|
165
|
+
/** Thrown when an instantiated WASM module fails the ABI contract. */
|
|
166
|
+
export declare class AlbexAbiMismatchError extends Error {
|
|
167
|
+
readonly module: 'main' | 'pdf';
|
|
168
|
+
readonly missing?: readonly string[];
|
|
169
|
+
readonly version?: number;
|
|
170
|
+
constructor(module: 'main' | 'pdf', message: string, opts?: {
|
|
171
|
+
missing?: readonly string[];
|
|
172
|
+
version?: number;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** Validate and narrow `WebAssembly.Exports` to the typed Albex main
|
|
176
|
+
* interface. Throws `AlbexAbiMismatchError` if the contract is broken. */
|
|
177
|
+
export declare function asAlbexExports(exports: WebAssembly.Exports): AlbexWasmExports;
|
|
178
|
+
/** Validate and narrow `WebAssembly.Exports` to the typed PDF interface. */
|
|
179
|
+
export declare function asAlbexPdfExports(exports: WebAssembly.Exports): AlbexPdfExports;
|
|
180
|
+
//# sourceMappingURL=wasm-bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-bindings.d.ts","sourceRoot":"","sources":["../src/wasm-bindings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* albex v0.6.0
|
|
3
|
+
* Zero-config local full-text search for documents — runs entirely in the browser, no server, no upload.
|
|
4
|
+
* (c) 2026 RafaCalRob
|
|
5
|
+
* @license MIT
|
|
6
|
+
* https://github.com/RafaCalRob/Albex#readme
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Typed interfaces for the two WASM modules Albex ships with.
|
|
10
|
+
*
|
|
11
|
+
* These types replace ad-hoc `as Function` casts and give us:
|
|
12
|
+
* - argument/return type checking at call sites,
|
|
13
|
+
* - autocompletion in IDEs,
|
|
14
|
+
* - safe refactors when an export name or signature changes.
|
|
15
|
+
*
|
|
16
|
+
* The interfaces mirror the `#[no_mangle] pub extern "C" fn` exports
|
|
17
|
+
* in `wasm/src/lib.rs` and `pdf-wasm/src/lib.rs`.
|
|
18
|
+
*/
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// Runtime validators
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
//
|
|
23
|
+
// These replace the pre-0.5.0 `as unknown as` casts. They check three
|
|
24
|
+
// things at instantiation time:
|
|
25
|
+
// 1. memory is a WebAssembly.Memory instance.
|
|
26
|
+
// 2. abiVersion() returns a number inside the supported range.
|
|
27
|
+
// 3. every required export exists and is a function.
|
|
28
|
+
//
|
|
29
|
+
// If any of these fails, the loader throws a typed error before the
|
|
30
|
+
// engine returns from init(). This eliminates the audit 3.2 issue:
|
|
31
|
+
// previously a missing export only surfaced when its call site ran.
|
|
32
|
+
/** Range of ABI versions this host code understands for the main module.
|
|
33
|
+
* Update both ends together with the Rust `abiVersion()` constant when
|
|
34
|
+
* the export surface changes. */
|
|
35
|
+
// 0.6.0 requires ABI 3 (trigram pre-filter + getLastIndexOverflow). The
|
|
36
|
+
// required-exports list below already makes any older binary fail the
|
|
37
|
+
// missing-exports check, so a tolerant lower bound was dead code — the range
|
|
38
|
+
// is pinned to the one ABI this host actually speaks (audit 0.6.0, finding #7).
|
|
39
|
+
const MAIN_ABI_MIN = 3;
|
|
40
|
+
const MAIN_ABI_MAX = 3;
|
|
41
|
+
/** Range of ABI versions for the PDF module. */
|
|
42
|
+
const PDF_ABI_MIN = 1;
|
|
43
|
+
const PDF_ABI_MAX = 3;
|
|
44
|
+
/** Required function names on the main WASM. Adding a new one here forces
|
|
45
|
+
* the validator to check it; removing one is a breaking ABI bump. */
|
|
46
|
+
const MAIN_REQUIRED = [
|
|
47
|
+
'abiVersion', 'getBuffer', 'init',
|
|
48
|
+
'setDocumentName', 'beginDocument', 'feedXmlBytes', 'endDocument',
|
|
49
|
+
'beginXlsx', 'feedXlsxBytes',
|
|
50
|
+
'feedText', 'flushParagraph',
|
|
51
|
+
'setMaxErrors', 'setThreshold', 'setMaxResults',
|
|
52
|
+
'prepareQuery', 'getQueryKind', 'getQueryBranchCount',
|
|
53
|
+
'getQueryBranchPattern', 'selectQueryBranch',
|
|
54
|
+
'setPattern', 'search',
|
|
55
|
+
'searchBegin', 'searchSlice', 'getSearchCursor', 'getSearchTotal',
|
|
56
|
+
'getResultCount',
|
|
57
|
+
'getResultDocId', 'getResultLocation', 'getResultScore',
|
|
58
|
+
'getResultStart', 'getResultEnd', 'getResultChunkIdx',
|
|
59
|
+
'getResultDocName', 'getResultMatchCount',
|
|
60
|
+
'getResultMatchStartAt', 'getResultMatchEndAt',
|
|
61
|
+
'getSnippet', 'getSnippetWindow', 'getSnippetWindowOffset',
|
|
62
|
+
'getStatBloomTested', 'getStatBloomPassed', 'getStatBitapMatched',
|
|
63
|
+
'getChunkCount', 'getDocCount', 'getTextUsed', 'getTextCapacity',
|
|
64
|
+
'getLastIndexOverflow',
|
|
65
|
+
'snapshotSize', 'snapshotChunk',
|
|
66
|
+
'restoreBegin', 'restoreFeed', 'restoreCommit',
|
|
67
|
+
'getDocId', 'getDocChunkCount', 'getDocName', 'isDocDeleted',
|
|
68
|
+
'removeDocument', 'compact',
|
|
69
|
+
'setLanguage',
|
|
70
|
+
'getTier', 'getMaxChunks', 'getMaxDocs', 'getNameCapacity',
|
|
71
|
+
'getChunksPtr', 'getChunkStructSize',
|
|
72
|
+
'setCandidateMask', 'clearCandidateMask',
|
|
73
|
+
'getDocContentHashPtr', 'getDocContentHashLen', 'setDocumentContentHash',
|
|
74
|
+
'hashBegin', 'hashFeed', 'hashFinish',
|
|
75
|
+
];
|
|
76
|
+
const PDF_REQUIRED = [
|
|
77
|
+
'abiVersion', 'allocInput', 'extractPdf',
|
|
78
|
+
'getPageLen', 'getPagePtr', 'getErrorLen', 'getErrorPtr',
|
|
79
|
+
'getPageCount', 'extractPageImages',
|
|
80
|
+
'getPageImageLen', 'getPageImagePtr', 'getPageImageKind',
|
|
81
|
+
];
|
|
82
|
+
/** Thrown when an instantiated WASM module fails the ABI contract. */
|
|
83
|
+
export class AlbexAbiMismatchError extends Error {
|
|
84
|
+
module;
|
|
85
|
+
missing;
|
|
86
|
+
version;
|
|
87
|
+
constructor(module, message, opts) {
|
|
88
|
+
super(message);
|
|
89
|
+
this.name = 'AlbexAbiMismatchError';
|
|
90
|
+
this.module = module;
|
|
91
|
+
if (opts?.missing)
|
|
92
|
+
this.missing = opts.missing;
|
|
93
|
+
if (opts?.version !== undefined)
|
|
94
|
+
this.version = opts.version;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function validateExports(exports, required, module, abiMin, abiMax) {
|
|
98
|
+
const mem = exports['memory'];
|
|
99
|
+
if (!(mem instanceof WebAssembly.Memory)) {
|
|
100
|
+
throw new AlbexAbiMismatchError(module, `${module}: \`memory\` is missing or not a WebAssembly.Memory instance.`);
|
|
101
|
+
}
|
|
102
|
+
const missing = [];
|
|
103
|
+
for (const name of required) {
|
|
104
|
+
if (typeof exports[name] !== 'function')
|
|
105
|
+
missing.push(name);
|
|
106
|
+
}
|
|
107
|
+
if (missing.length) {
|
|
108
|
+
throw new AlbexAbiMismatchError(module, `${module}: WASM binary missing required exports: ${missing.join(', ')}. ` +
|
|
109
|
+
`The .wasm was built with an incompatible source — rebuild with the current toolchain.`, { missing });
|
|
110
|
+
}
|
|
111
|
+
const version = exports['abiVersion']();
|
|
112
|
+
if (version < abiMin || version > abiMax) {
|
|
113
|
+
throw new AlbexAbiMismatchError(module, `${module}: abiVersion ${version} outside supported range [${abiMin}..${abiMax}]. ` +
|
|
114
|
+
`The host TypeScript expects a different binary — upgrade albex or rebuild the WASM.`, { version });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Validate and narrow `WebAssembly.Exports` to the typed Albex main
|
|
118
|
+
* interface. Throws `AlbexAbiMismatchError` if the contract is broken. */
|
|
119
|
+
export function asAlbexExports(exports) {
|
|
120
|
+
validateExports(exports, MAIN_REQUIRED, 'main', MAIN_ABI_MIN, MAIN_ABI_MAX);
|
|
121
|
+
return exports;
|
|
122
|
+
}
|
|
123
|
+
/** Validate and narrow `WebAssembly.Exports` to the typed PDF interface. */
|
|
124
|
+
export function asAlbexPdfExports(exports) {
|
|
125
|
+
validateExports(exports, PDF_REQUIRED, 'pdf', PDF_ABI_MIN, PDF_ABI_MAX);
|
|
126
|
+
return exports;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=wasm-bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-bindings.js","sourceRoot":"","sources":["../src/wasm-bindings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire protocol between the main thread and the AlbexEngineWorker runtime.
|
|
3
|
+
*
|
|
4
|
+
* One request/response pair per call, identified by `id`. The runtime is
|
|
5
|
+
* single-threaded so we serialise requests on the main side (one in-flight
|
|
6
|
+
* call at a time per worker) — keeps the protocol trivial and matches the
|
|
7
|
+
* actual constraint of `static mut` WASM state.
|
|
8
|
+
*
|
|
9
|
+
* `Transferable` is opt-in per op; we use it for `indexFile` to avoid
|
|
10
|
+
* copying the file bytes into the worker.
|
|
11
|
+
*/
|
|
12
|
+
import type { AlbexOptions, IndexedDocument, SearchOptions, SearchResult, EngineStats, SearchStats } from './albex.js';
|
|
13
|
+
export type WorkerOp = {
|
|
14
|
+
kind: 'init';
|
|
15
|
+
opts: AlbexOptions;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'indexFile';
|
|
18
|
+
name: string;
|
|
19
|
+
buffer: ArrayBuffer;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'search';
|
|
22
|
+
query: string;
|
|
23
|
+
options: SearchOptions;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'removeDocument';
|
|
26
|
+
id: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'compact';
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'reset';
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'getStats';
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'getLastSearchStats';
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'getDocuments';
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'setMaxErrors';
|
|
39
|
+
n: 0 | 1 | 2 | 3;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'setThreshold';
|
|
42
|
+
n: number;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'setMaxResults';
|
|
45
|
+
n: number;
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'setLanguage';
|
|
48
|
+
lang: 'off' | 'es';
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'save';
|
|
51
|
+
name: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'load';
|
|
54
|
+
name: string;
|
|
55
|
+
} | {
|
|
56
|
+
kind: 'loadOrInit';
|
|
57
|
+
name: string;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'deleteSnapshot';
|
|
60
|
+
name: string;
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'listSnapshots';
|
|
63
|
+
};
|
|
64
|
+
export interface WorkerRequest {
|
|
65
|
+
id: number;
|
|
66
|
+
op: WorkerOp;
|
|
67
|
+
}
|
|
68
|
+
export type WorkerResponse = {
|
|
69
|
+
id: number;
|
|
70
|
+
ok: true;
|
|
71
|
+
result: unknown;
|
|
72
|
+
} | {
|
|
73
|
+
id: number;
|
|
74
|
+
ok: false;
|
|
75
|
+
error: {
|
|
76
|
+
name: string;
|
|
77
|
+
kind?: string;
|
|
78
|
+
message: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
export type IndexFileResult = IndexedDocument;
|
|
82
|
+
export type SearchResultArr = SearchResult[];
|
|
83
|
+
export type StatsResult = EngineStats;
|
|
84
|
+
export type SearchStatsRes = SearchStats | null;
|
|
85
|
+
export type DocsResult = readonly IndexedDocument[];
|
|
86
|
+
//# sourceMappingURL=worker-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-protocol.d.ts","sourceRoot":"","sources":["../src/worker-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvH,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAY,IAAI,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAQ,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAW,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAM,IAAI,EAAE,KAAK,GAAG,IAAI,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAa,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAa,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAO,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAG,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE9B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;CACd;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAC1C;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEvF,MAAM,MAAM,eAAe,GAAG,eAAe,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,YAAY,EAAE,CAAC;AAC7C,MAAM,MAAM,WAAW,GAAO,WAAW,CAAC;AAC1C,MAAM,MAAM,cAAc,GAAI,WAAW,GAAG,IAAI,CAAC;AACjD,MAAM,MAAM,UAAU,GAAQ,SAAS,eAAe,EAAE,CAAC"}
|