albex 0.6.0 → 0.6.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/README.md +30 -19
  3. package/dist/albex-worker.d.ts +65 -2
  4. package/dist/albex-worker.d.ts.map +1 -1
  5. package/dist/albex-worker.js +97 -20
  6. package/dist/albex-worker.js.map +1 -1
  7. package/dist/albex.d.ts +206 -42
  8. package/dist/albex.d.ts.map +1 -1
  9. package/dist/albex.js +384 -103
  10. package/dist/albex.js.map +1 -1
  11. package/dist/errors.d.ts +35 -4
  12. package/dist/errors.d.ts.map +1 -1
  13. package/dist/errors.js +37 -2
  14. package/dist/errors.js.map +1 -1
  15. package/dist/persistence.js +1 -1
  16. package/dist/pool/coordinator.d.ts +14 -6
  17. package/dist/pool/coordinator.d.ts.map +1 -1
  18. package/dist/pool/coordinator.js +65 -28
  19. package/dist/pool/coordinator.js.map +1 -1
  20. package/dist/profile.js +1 -1
  21. package/dist/resource-manager.js +1 -1
  22. package/dist/tiered-store.js +1 -1
  23. package/dist/wasm-bindings.d.ts +50 -1
  24. package/dist/wasm-bindings.d.ts.map +1 -1
  25. package/dist/wasm-bindings.js +19 -11
  26. package/dist/wasm-bindings.js.map +1 -1
  27. package/dist/worker-protocol.d.ts +23 -2
  28. package/dist/worker-protocol.d.ts.map +1 -1
  29. package/dist/worker-protocol.js +1 -1
  30. package/dist/worker-runtime.js +16 -1
  31. package/dist/worker-runtime.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/albex-worker.ts +103 -18
  34. package/src/albex.ts +2937 -2524
  35. package/src/errors.ts +49 -4
  36. package/src/pool/coordinator.ts +61 -34
  37. package/src/wasm-bindings.ts +78 -12
  38. package/src/worker-protocol.ts +12 -2
  39. package/src/worker-runtime.ts +16 -1
  40. package/wasm/pkg/albex_pdf.wasm +0 -0
  41. package/wasm/pkg/albex_wasm.wasm +0 -0
  42. package/wasm/pkg/albex_wasm_bg.wasm +0 -0
  43. package/wasm/pkg/albex_wasm_simd.wasm +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,197 @@ All notable changes to Albex are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and Albex follows [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Changed
11
+
12
+ - **IDF-aware relevance scoring — exact matches no longer saturate.** The old
13
+ `rich_score` set `base = 1000 − avg_errors·200`, so every 0-error hit hit the
14
+ 1000 ceiling and `.min(1000)` discarded *all* the relevance bonuses (term
15
+ frequency, proximity, location, word-boundary AND idf). The practical effect:
16
+ on a real corpus every exact match tied at 1000 and the final ordering among
17
+ them was arbitrary — the chunk holding the rare, decisive query term was no
18
+ more likely to rank first than dozens that merely shared a common word.
19
+ Scores are now laid out as **error tiers with relevance ranking inside each
20
+ tier** (0 errors → `[750,1000]`, 1 → `[500,750]`, 2 → `[250,500]`, 3 →
21
+ `[0,250]`): a cleaner match still always beats a noisier one, but within a
22
+ tier the **IDF bonus (now the largest, up to 150)** lifts rare-term matches
23
+ decisively above common-term ones. Measured downstream (the Noesis hybrid-RAG
24
+ eval over a 293-doc / 12.4k-chunk corpus): lexical-leg MRR **0.19 → 0.40**,
25
+ R@5 **23% → 53%**, and end-to-end hybrid MRR **0.42 → 0.53** — with no change
26
+ on small corpora. `SearchResult.score` keeps its `0..=1000` range, so
27
+ `setThreshold` is unaffected for typical values; very weak 3-error fuzzy
28
+ matches (bonus-starved, score < the default threshold 250) are now filtered
29
+ where they previously slipped through.
30
+ - **Fixed a latent result-ordering bug** exposed by the scoring change. The
31
+ heap-sort already left `RESULTS` in descending order; a trailing reverse then
32
+ flipped it to *ascending*. It was invisible while all exact scores tied at
33
+ 1000 and was masked from API consumers by the TypeScript-side re-sort, but the
34
+ WASM ABI now returns correctly descending results on its own.
35
+
36
+ ### Added
37
+
38
+ - **Dynamic capacity (ABI 7, decision A16 executed).** The compile-time
39
+ capacity tiers are gone for good: the engine pools (chunk table, trigram
40
+ signatures, text pool, doc table, content hashes, name pool, tombstone
41
+ bitset, GPU candidate mask) moved from BSS `static mut` arrays to heap
42
+ allocations sized at runtime by the new export
43
+ `initWithCapacity(maxDocs, maxChunks, textPoolBytes, namePoolBytes) → 1|0`.
44
+ - `init()` is now a wrapper over `initWithCapacity` with the historical
45
+ std defaults (128 docs · 100k chunks · 16 MB text · 32 KB names) —
46
+ default behaviour is identical to every previous release.
47
+ - Hard ceilings (validated, return 0 — never trap): 65 536 docs, 4 M
48
+ chunks, 1 GiB text, 16 MiB names; allocation failure (memory.grow
49
+ refused) also returns 0 cleanly with the engine in a safe empty state.
50
+ - Re-init with the same capacities is a plain reset (no realloc);
51
+ different capacities free + re-allocate (no leak; the linear-memory
52
+ high-water mark stays, as WASM memory never shrinks).
53
+ - **Snapshots are admitted by CONTENT, not by writer capacity**: the
54
+ restore gate compares the snapshot's counters against the live runtime
55
+ capacities, so a snapshot saved by a `'large'` engine loads into a
56
+ `'std'` engine whenever its contents fit — and fails CLEANLY (previous
57
+ index intact) when they don't. v4 headers additionally record the
58
+ writer's capacities in the previously-reserved bytes [40..56], for
59
+ diagnostics only.
60
+ - **TS option `capacity`**: `'std'` (default) · `'large'`
61
+ (1 024 docs / 800k chunks / 128 MB text / 256 KB names — the old "pro"
62
+ tier) · custom `{ maxDocs?, maxChunks?, textPoolBytes?, namePoolBytes? }`
63
+ (partials completed from std scaled by std's ratios: chunks = docs×782,
64
+ text = chunks×168 B, names = docs×256 B, with floors; explicit
65
+ maxChunks below maxDocs is clamped up). Forwarded to workers and pool
66
+ shards (structured-clone-safe). `reset()` re-inits with the CONFIGURED
67
+ capacity, not the std defaults.
68
+ - `AlbexCapacityError` gains `max` — the runtime numeric limit of the
69
+ pool named by `limit` (e.g. `4` for `capacity: { maxDocs: 4 }`); it
70
+ survives the worker boundary.
71
+ - New large-scale bench (`bench/large.bench.ts`): 1 000 docs / 200k
72
+ chunks / 15.8 MB text in a `'large'` engine — search 141-769 ms
73
+ (rare-token → fuzzy), snapshot save 24 ms / restore 175 ms for a
74
+ ~20 MB snapshot. Numbers in `bench/README.md`. Binary cost of heap
75
+ pools: baseline 43 396 → 47 446 B (gate 48 KB still green).
76
+
77
+ - **Batch frontier reads (ABI 6).** Three groups of exports collapse the
78
+ JS↔WASM call count on the hot read paths:
79
+ - `getResultsPtr()` + `getResultStride()`: `_collectResults` now reads every
80
+ numeric field of all results with ONE `DataView` pass over the
81
+ `#[repr(C)]` `RESULTS` array (layout documented in the export's
82
+ doc-comment and compile-time asserted with `offset_of!`) instead of
83
+ ~12-15 frontier calls per result. Doc names are resolved once per
84
+ distinct document via its table slot — the per-result
85
+ `getResultDocName` (O(doc_count) inside WASM) is no longer called.
86
+ - `getPatternBloomLo()/Hi()`: the query Bloom the GPU pre-filter needs is
87
+ now computed in `setPattern` through the exact pipeline `searchBegin`
88
+ uses (split → optional Spanish stemming → fold). The third TypeScript
89
+ copy of the fold (`computePatternBloom`) is deleted; with stemming on it
90
+ silently diverged from the CPU pattern.
91
+ - `listChunksBatch(slot, startOrd, maxChunks)`: packs consecutive chunk
92
+ records `[u32 text_len][u32 location][text]` into the scratchpad;
93
+ `listChunks` now makes ~1 frontier call per 64 KB batch instead of 2-3
94
+ per chunk (an embeddings pipeline over 100k chunks drops from ~300k
95
+ calls to ~1.3k).
96
+ `abiVersion()` bumped 5 → 6; host pin updated. Snapshot format unchanged.
97
+ - **UTF-16 spans: `SearchResult.snippetStart/snippetEnd`.** The existing
98
+ `matchStart/matchEnd`/`matches[]` are UTF-8 BYTE offsets (now documented as
99
+ such) and mis-highlight as soon as the snippet contains accents. The new
100
+ fields are the primary span as UTF-16 code-unit indices over the decoded
101
+ snippet — safe for `snippet.slice()`. Byte fields are unchanged for
102
+ backwards compatibility.
103
+ - **Worker parity: `replaceDocument` + `takeDiagnostics`** added to the
104
+ worker protocol and `AlbexEngineWorker`. The class JSDoc now documents that
105
+ `attachOcr` cannot exist in the worker (functions don't cross postMessage)
106
+ and that scanned PDFs index with 0 chunks there — with the diagnostic
107
+ explaining why readable through `takeDiagnostics`, and the recommendation
108
+ to OCR on the main-thread engine and `save()`/`load()` the snapshot.
109
+ - **CI with teeth:** `cargo clippy -D warnings` for every crate (host pass
110
+ for core/ingest, wasm32 pass for the wasm and pdf crates), an advisory
111
+ `cargo fmt --check`, a hard 48 KB size gate on the baseline `.wasm`, a
112
+ build of the SIMD variant, and a manual-only (`workflow_dispatch`) bench
113
+ job with no thresholds.
114
+
115
+ ### Removed
116
+
117
+ - **`tier` option and tier plumbing (breaking, pre-1.0).** The deprecated
118
+ `AlbexOptions.tier` (ignored since 0.5.0), `AlbexEngine.tier`,
119
+ `AlbexPool.tier`, `EngineStats.tier` and the WASM `getTier` export are
120
+ gone — superseded by the runtime `capacity` option (ABI 7, decision
121
+ A16). `EngineStats` now reports the real runtime capacities (`maxDocs`,
122
+ `maxChunks`, `textCapacity`, plus new `namePoolBytes`). The deprecated
123
+ `pickTier`/`Tier` re-exports from `profile.ts` remain for source
124
+ compatibility. The `tier-mini`/`tier-std`/`tier-pro` Cargo features are
125
+ deleted; the only build-time switch left is `simd`.
126
+
127
+ ### Changed
128
+
129
+ - **Trigram windows no longer cross word boundaries.** `build_sig` /
130
+ `token_trigram_bits` only extend the sliding window on code points that
131
+ fold to an ASCII letter or digit; whitespace/punctuation/symbols break it
132
+ on BOTH the index and query sides (previously the fold passed ASCII
133
+ whitespace through, so signatures contained cross-word trigrams that
134
+ diluted selectivity — the doc-comment claimed otherwise). Sound under
135
+ fuzzy matching (same ≤3-windows-per-edit bound); signatures are rebuilt
136
+ on every restore/compact, so existing snapshots are unaffected.
137
+ - **`wee_alloc` replaced (RUSTSEC-2022-0054).** The main no_std module now
138
+ uses `dlmalloc` (`global` feature) — baseline binary 36 732 → 42 654 bytes
139
+ (+5.8 KB, under the 48 KB gate). `pdf-wasm` drops its custom allocator and
140
+ uses std's default (also dlmalloc): 1 193 074 → 1 199 910 bytes (+6.8 KB on
141
+ 1.2 MB). In pdf-wasm the allocator serves all of lopdf, so the known
142
+ wee_alloc leak surface was real there.
143
+
144
+ ### Fixed (this pass)
145
+
146
+ - **`getSnippetWindow` can no longer open or close mid code point.** When the
147
+ word-boundary snap found no space within 20 bytes, the window edge could
148
+ land on a UTF-8 continuation byte → U+FFFD at the snippet border and every
149
+ span visually off. Both edges now back off continuation bytes to the
150
+ code-point lead (same pattern as the chunker's hard cut).
151
+
152
+ ### Added (previous pass)
153
+
154
+ - **Authoritative chunk enumeration (ABI 4).** New `AlbexEngine.listChunks(docId)`
155
+ (and `AlbexEngineWorker.listChunks`) returns the exact chunks Albex indexed for
156
+ a document — `{ docId, location, ord, sub, text, byteLen, id }` — so a host can
157
+ mirror Albex's chunking for a parallel index (e.g. embeddings) keyed on the
158
+ `compact()`-stable id `"<docId>::<ord>"`. Backed by four read-only
159
+ WASM exports (`getDocChunkBase`, `getChunkLocationAt`, `getChunkByteLenAt`,
160
+ `getChunkTextAt`); `abiVersion()` bumped 3 → 4. No change to search, scoring or
161
+ snapshot formats; v3 snapshots still load.
162
+ - **`SearchResult` now carries `docId` and `chunkId`.** `chunkId` (`"<docId>::<ord>"`)
163
+ is identical to the matching `AuthoritativeChunk.id`, so a host can fuse search
164
+ hits with a parallel index on a single stable key — no `(name, location)`
165
+ heuristics, and unambiguous when a long paragraph splits into sub-chunks.
166
+ - **`maxFileBytes` option (default 256 MiB).** `indexFile` now rejects oversized
167
+ inputs with a typed `AlbexCapacityError` (`limit: 'file'`) by checking
168
+ `File.size` BEFORE reading — a 2 GB file no longer gets fully buffered and
169
+ hashed just to fail later. Enforced by the engine, the worker wrapper and the
170
+ pool coordinator.
171
+
172
+ ### Fixed
173
+
174
+ - **GPU pre-filter no longer corrupts the search pattern.** `setCandidateMask`
175
+ pushes the candidate bitset through the scratchpad, overwriting the pattern
176
+ staged by `selectQueryBranch`; `searchBegin` then compiled garbage tokens out
177
+ of the mask bytes, so every GPU-assisted cooperative search silently returned
178
+ wrong results. The active branch is now re-selected after the pre-filter.
179
+ - **GPU bloom upload invalidation is now mutation-driven.** The upload used to
180
+ be skipped when the chunk count matched the last upload — but `compact()` can
181
+ reorder chunks while keeping the count identical, leaving the GPU mask
182
+ filtering the wrong chunks (silent false negatives). A dirty flag set by every
183
+ index mutation (index/remove/compact/reset/load) now forces the re-upload.
184
+ - **`AlbexEngineWorker.init` forwards all engine options.** Previously only
185
+ `wasmUrl` and `pdfWasmUrl` crossed the worker boundary; `wasmBaseUrl`, `simd`,
186
+ `gpu`, `gpuThreshold` and `maxFileBytes` were silently dropped, making the
187
+ SIMD variant and the GPU policy unreachable in workers.
188
+ - **OR-branch dedup keyed on `chunkId`.** The previous
189
+ `(doc, location, matchStart)` key collided when two sub-chunks of the same
190
+ location hit at the same relative offset, dropping a legitimate result.
191
+ - **`AlbexPool.search` now actually caps and dedups after the merge.** The doc
192
+ promised "capped to setMaxResults AFTER merge" but the coordinator returned
193
+ the raw flattened buckets. Results are now deduped, sorted and capped to the
194
+ last `setMaxResults` value (default 50); per-shard search stats are captured
195
+ race-free in the same posting batch as the search itself.
196
+ - Corrected the `listChunks` docstring (and the entry above): the canonical
197
+ chunk id format is `"<docId>::<ord>"`, not `"<docId>::<location>.<sub>"`.
198
+
8
199
  ## [0.6.0] — 2026-05-31
9
200
 
10
201
  Release de auditoría algorítmica + robustez. Cierra los hallazgos #1–#8 de
package/README.md CHANGED
@@ -81,10 +81,11 @@ That's the entire onboarding. Read on for what else the engine can do.
81
81
  - **WebGPU pre-filter** — experimental, opt-in (`gpu: 'auto'`). Implemented for corpora over 20 k chunks; no reproducible speedup number yet — the bench in this repo runs on a 200-document synthetic corpus only.
82
82
  - **SIMD opportunistic** — picks a SIMD-accelerated variant when the host supports v128.
83
83
  - **Tiered storage** — `TieredStore` keeps recent docs hot, evicts cold ones to OPFS, promotes on demand.
84
- - **Capacity-safe** — when a pool fills (`docs`/`chunks`/`text`/`names`), `indexFile` throws `AlbexCapacityError` with a `limit` field instead of silently truncating the corpus.
84
+ - **Runtime capacity** — one binary, pools sized at init: `capacity: 'std'` (default, 128 docs / 100k chunks / 16 MB text), `'large'` (1 024 docs / 800k chunks / 128 MB text) or a custom `{ maxDocs, maxChunks, textPoolBytes, namePoolBytes }`.
85
+ - **Capacity-safe** — when a pool fills (`docs`/`chunks`/`text`/`names`), `indexFile` throws `AlbexCapacityError` with `limit` (which pool) and `max` (the runtime limit) instead of silently truncating the corpus.
85
86
  - **Re-entrancy-safe** — async operations on one engine serialize; sync `search`/`compact`/`reset` refuse to run mid-operation (`AlbexError` kind `busy`) rather than corrupting the shared WASM state. Use `searchCooperative` for overlapping search-as-you-type.
86
87
  - **Typed errors** — `AlbexParseError`, `AlbexUnsupportedFormatError`, `AlbexCapacityError`, `AlbexInitError`. All extend `AlbexError`.
87
- - **Tiny core** — main WASM 33 KB (37 KB SIMD). PDF module (~1.2 MB) loads on demand. The OCR companion (`@albex/ocr`) is a separate package and pulls Tesseract.js (~3.5 MB) only when you call `enableOcr()`.
88
+ - **Tiny core** — main WASM ~47 KB (~19 KB gzipped); the SIMD build is ~54 KB (~21 KB gzipped). PDF module (~1.2 MB, ~510 KB gzipped) loads on demand. The OCR companion (`@albex/ocr`) is a separate package and pulls Tesseract.js (~3.5 MB) only when you call `enableOcr()`.
88
89
 
89
90
  ---
90
91
 
@@ -199,7 +200,7 @@ const results = await pool.search('contrato'); // map-reduce
199
200
 
200
201
  ## Big corpora — tiered storage
201
202
 
202
- For workloads that exceed the tier's RAM capacity:
203
+ For workloads that exceed the engine's RAM capacity:
203
204
 
204
205
  ```ts
205
206
  import { AlbexEngine, TieredStore } from 'albex';
@@ -223,29 +224,40 @@ Hot tier = engine. Warm tier = original files in OPFS. LRU eviction is automatic
223
224
  `new AlbexEngine()` covers the default case. The options below address
224
225
  specific deployment needs:
225
226
 
226
- ### Tier auto-selection (`mini` / `std` / `pro` based on `deviceMemory`)
227
+ ### Capacity (runtime, single binary)
227
228
 
228
- Albex ships **six** WASM variants of the main engine (3 tiers × baseline/SIMD).
229
- By default it loads the std-baseline binary that comes with the npm package.
230
- If you want runtime tier auto-selection, serve the variants yourself and
231
- pass `wasmBaseUrl`:
229
+ Capacity is a **runtime** parameter there is one engine binary (plus its
230
+ SIMD variant) and the pools are heap-allocated at `init()` to the size you
231
+ ask for. The old compile-time tiers (and the `tier` option) are gone:
232
232
 
233
233
  ```ts
234
234
  const engine = new AlbexEngine({
235
- wasmBaseUrl: '/assets', // directory containing the 6 .wasm files
236
- tier: 'auto', // picks mini/std/pro by deviceMemory
235
+ capacity: 'large', // or 'std' (default), or a custom object
237
236
  simd: 'auto', // picks baseline/simd by WASM probe
238
237
  gpu: 'auto', // engages WebGPU when corpus > 20k chunks
239
238
  });
240
239
  ```
241
240
 
242
- Tier capacities:
241
+ Presets and cost (≈ `maxChunks × 64 B + textPool + namePool`; WASM memory
242
+ never shrinks, so the largest capacity initialised stays committed):
243
243
 
244
- | Tier | Max docs | Max chunks | Max text | Working set |
245
- |-------|---------:|-----------:|---------:|------------:|
246
- | mini | 32 | 25 000 | 4 MB | ~5 MB |
247
- | std | 128 | 100 000 | 16 MB | ~20 MB |
248
- | pro | 1 024 | 800 000 | 128 MB | ~160 MB |
244
+ | Capacity | Max docs | Max chunks | Max text | Working set |
245
+ |-----------|---------:|-----------:|---------:|------------:|
246
+ | `'std'` | 128 | 100 000 | 16 MB | ~22 MB |
247
+ | `'large'` | 1 024 | 800 000 | 128 MB | ~180 MB |
248
+ | custom | 65 536 | 4 M | 1 GiB | as configured |
249
+
250
+ Custom objects may be partial — missing fields are completed from the std
251
+ ratios (`maxChunks = maxDocs × 782`, `textPoolBytes = maxChunks × 168 B`,
252
+ `namePoolBytes = maxDocs × 256 B`, with sane floors):
253
+
254
+ ```ts
255
+ const tiny = new AlbexEngine({ capacity: { maxDocs: 16 } });
256
+ ```
257
+
258
+ Snapshots are admitted by **content**: a snapshot saved with `'large'`
259
+ loads into a `'std'` engine whenever its counters fit, and fails cleanly
260
+ (previous index intact) when they don't.
249
261
 
250
262
  ### Custom CDN
251
263
 
@@ -299,14 +311,13 @@ PDF support requires `albex_pdf.wasm` to be served with MIME type `application/w
299
311
  rustup target add wasm32-unknown-unknown
300
312
 
301
313
  npm install
302
- npm run build:all # 6 main variants + PDF + TypeScript
314
+ npm run build:all # main (baseline + SIMD) + PDF + TypeScript
303
315
  ```
304
316
 
305
317
  Partial builds:
306
318
 
307
319
  ```bash
308
- npm run build:wasm # std baseline only
309
- npm run build:wasm:tiers # all 6 variants
320
+ npm run build:wasm # main module (baseline + SIMD)
310
321
  npm run build:pdf-wasm # PDF module
311
322
  npm run build # TypeScript only
312
323
  ```
@@ -20,8 +20,24 @@
20
20
  * call is in flight at a time. This matches the actual `static mut` model
21
21
  * inside the .wasm and is fine for an interactive search UI (each keystroke
22
22
  * replaces the previous query).
23
+ *
24
+ * ## OCR is NOT available in the worker
25
+ *
26
+ * `AlbexEngineWorker` has no `attachOcr`: an OCR adapter is an object with
27
+ * functions, and functions cannot cross the `postMessage` boundary (the
28
+ * structured-clone algorithm rejects them). Consequences:
29
+ *
30
+ * - **Scanned (image-only) PDFs index with 0 chunks, silently.** The
31
+ * engine records a diagnostic explaining why — read it with
32
+ * {@link takeDiagnostics} after `indexFile`.
33
+ * - If your corpus contains scanned PDFs and you need their text, index
34
+ * them with the synchronous main-thread `AlbexEngine` plus the OCR
35
+ * adapter (`engine.attachOcr(...)` / `@albex/ocr`'s `enableOcr`), then
36
+ * `save()` the snapshot and `load()` it from the worker engine.
37
+ * - A future protocol iteration could proxy OCR over a dedicated
38
+ * `MessagePort`; until then the main-thread engine is the OCR path.
23
39
  */
24
- import type { AlbexOptions, IndexedDocument, SearchOptions, SearchResult, EngineStats, SearchStats } from './albex.js';
40
+ import type { AlbexDiagnostic, AlbexOptions, AuthoritativeChunk, IndexedDocument, SearchOptions, SearchResult, EngineStats, SearchStats } from './albex.js';
25
41
  export interface AlbexWorkerOptions extends AlbexOptions {
26
42
  /** URL to the bundled worker runtime script (worker-runtime.js). */
27
43
  workerUrl: string | URL;
@@ -31,12 +47,38 @@ export declare class AlbexEngineWorker {
31
47
  private _worker;
32
48
  private _nextId;
33
49
  private _pending;
34
- private _docsCache;
35
50
  constructor(opts: AlbexWorkerOptions);
51
+ /**
52
+ * Spawn the worker and initialise the engine inside it.
53
+ *
54
+ * Every serializable engine option is forwarded across the worker
55
+ * boundary (`wasmUrl`, `wasmBaseUrl`, `pdfWasmUrl`, `capacity`, `simd`,
56
+ * `gpu`, `gpuThreshold`, `maxFileBytes`) — only `workerUrl`, which is
57
+ * consumed on this side, is stripped. Notes on what applies in a worker:
58
+ *
59
+ * - `capacity`: fully honoured — both the `'std'`/`'large'` presets
60
+ * (plain strings) and a custom object are structured-clone-safe, so
61
+ * the worker-side engine sizes its pools exactly like a main-thread
62
+ * engine would. Mind the memory cost (`'large'` ≈ 180 MB) lives in
63
+ * the worker's heap.
64
+ * - `wasmBaseUrl` + `simd`: fully honoured — the worker-side engine can
65
+ * load the `_simd.wasm` variant.
66
+ * - `gpu` / `gpuThreshold`: honoured where the worker runtime exposes
67
+ * WebGPU. `navigator.gpu` is available in dedicated workers in
68
+ * Chromium-based browsers (compute needs no canvas); elsewhere the
69
+ * engine's GPU probe fails gracefully and searches use the CPU
70
+ * pre-filter, exactly as on the main thread.
71
+ */
36
72
  init(): Promise<void>;
37
73
  private _send;
38
74
  indexFile(file: File): Promise<IndexedDocument>;
39
75
  search(query: string, opts?: SearchOptions): Promise<SearchResult[]>;
76
+ /**
77
+ * Enumerate the authoritative chunks Albex indexed for `docId`
78
+ * (`IndexedDocument.docId` from {@link indexFile}). Mirrors
79
+ * `AlbexEngine.listChunks` across the worker boundary.
80
+ */
81
+ listChunks(docId: number): Promise<AuthoritativeChunk[]>;
40
82
  /**
41
83
  * Cooperative variant of `search`. Today the wire still sends a single
42
84
  * batch — the result array is fetched in one round-trip from the worker
@@ -51,6 +93,27 @@ export declare class AlbexEngineWorker {
51
93
  */
52
94
  searchStream(query: string, opts?: SearchOptions): AsyncIterable<SearchResult>;
53
95
  removeDocument(id: string): Promise<boolean>;
96
+ /**
97
+ * Replace a previously indexed document with new content. Mirrors
98
+ * `AlbexEngine.replaceDocument`: equivalent to `removeDocument(name)` +
99
+ * `indexFile(newFile)` without tripping the idempotency check, plus an
100
+ * opportunistic compact under text-pool pressure — all inside the
101
+ * worker-side engine's lock. The file bytes are transferred (zero-copy),
102
+ * like `indexFile`.
103
+ */
104
+ replaceDocument(name: string, newFile: File): Promise<IndexedDocument>;
105
+ /**
106
+ * Drain and return the diagnostics collected by the worker-side engine
107
+ * since the last call. Mirrors `AlbexEngine.takeDiagnostics` — consult it
108
+ * after `indexFile`/`load` to surface recoverable issues (PDF fallbacks,
109
+ * skipped content, persistence warnings). The worker-side buffer is
110
+ * cleared on each call.
111
+ *
112
+ * Particularly important in a worker: scanned PDFs index with **0 chunks**
113
+ * (no OCR available — see the note on OCR below), and the diagnostic
114
+ * explaining why is only visible through this method.
115
+ */
116
+ takeDiagnostics(): Promise<AlbexDiagnostic[]>;
54
117
  compact(): Promise<void>;
55
118
  reset(): Promise<void>;
56
119
  getStats(): Promise<EngineStats>;
@@ -1 +1 @@
1
- {"version":3,"file":"albex-worker.d.ts","sourceRoot":"","sources":["../src/albex-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACZ,MAAM,YAAY,CAAC;AAcpB,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AASD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,UAAU,CAAyB;gBAE/B,IAAI,EAAE,kBAAkB;IAI9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,OAAO,CAAC,KAAK;IASP,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC;IAWrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIxE;;;;;;;OAOG;IACI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,aAAa,CAAC,YAAY,CAAC;IAK9F;;OAEG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,aAAa,CAAC,YAAY,CAAC;IAQnF,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,QAAQ,IAAa,OAAO,CAAC,WAAW,CAAC;IACzC,kBAAkB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IACjD,YAAY,IAAU,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAEnD,YAAY,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7C,YAAY,CAAC,CAAC,EAAE,MAAM,GAAS,OAAO,CAAC,IAAI,CAAC;IAC5C,aAAa,CAAC,CAAC,EAAE,MAAM,GAAQ,OAAO,CAAC,IAAI,CAAC;IAC5C,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAG9C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAa,OAAO,CAAC,IAAI,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAa,OAAO,CAAC,OAAO,CAAC;IAC9C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAO,OAAO,CAAC,OAAO,CAAC;IAC9C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3C,aAAa,IAAiB,OAAO,CAAC,MAAM,EAAE,CAAC;IAErD,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAMzB"}
1
+ {"version":3,"file":"albex-worker.d.ts","sourceRoot":"","sources":["../src/albex-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACZ,MAAM,YAAY,CAAC;AAepB,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AASD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAA8B;gBAElC,IAAI,EAAE,kBAAkB;IAIpC;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B3B,OAAO,CAAC,KAAK;IASP,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC;IAarD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIxE;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAIxD;;;;;;;OAOG;IACI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,aAAa,CAAC,YAAY,CAAC;IAK9F;;OAEG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,aAAa,CAAC,YAAY,CAAC;IAQnF,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD;;;;;;;OAOG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC;IAS5E;;;;;;;;;;OAUG;IACH,eAAe,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAIvC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAE5B,QAAQ,IAAa,OAAO,CAAC,WAAW,CAAC;IACzC,kBAAkB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IACjD,YAAY,IAAU,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAEnD,YAAY,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7C,YAAY,CAAC,CAAC,EAAE,MAAM,GAAS,OAAO,CAAC,IAAI,CAAC;IAC5C,aAAa,CAAC,CAAC,EAAE,MAAM,GAAQ,OAAO,CAAC,IAAI,CAAC;IAC5C,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAG9C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAa,OAAO,CAAC,IAAI,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAa,OAAO,CAAC,OAAO,CAAC;IAC9C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAO,OAAO,CAAC,OAAO,CAAC;IAC9C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3C,aAAa,IAAiB,OAAO,CAAC,MAAM,EAAE,CAAC;IAErD,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAKzB"}
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * albex v0.6.0
2
+ * albex v0.6.1
3
3
  * Zero-config local full-text search for documents — runs entirely in the browser, no server, no upload.
4
4
  * (c) 2026 RafaCalRob
5
5
  * @license MIT
@@ -27,18 +27,54 @@
27
27
  * call is in flight at a time. This matches the actual `static mut` model
28
28
  * inside the .wasm and is fine for an interactive search UI (each keystroke
29
29
  * replaces the previous query).
30
+ *
31
+ * ## OCR is NOT available in the worker
32
+ *
33
+ * `AlbexEngineWorker` has no `attachOcr`: an OCR adapter is an object with
34
+ * functions, and functions cannot cross the `postMessage` boundary (the
35
+ * structured-clone algorithm rejects them). Consequences:
36
+ *
37
+ * - **Scanned (image-only) PDFs index with 0 chunks, silently.** The
38
+ * engine records a diagnostic explaining why — read it with
39
+ * {@link takeDiagnostics} after `indexFile`.
40
+ * - If your corpus contains scanned PDFs and you need their text, index
41
+ * them with the synchronous main-thread `AlbexEngine` plus the OCR
42
+ * adapter (`engine.attachOcr(...)` / `@albex/ocr`'s `enableOcr`), then
43
+ * `save()` the snapshot and `load()` it from the worker engine.
44
+ * - A future protocol iteration could proxy OCR over a dedicated
45
+ * `MessagePort`; until then the main-thread engine is the OCR path.
30
46
  */
31
- import { AlbexError, AlbexInitError, AlbexUnsupportedFormatError, AlbexParseError, AlbexCapacityError, } from './errors.js';
47
+ import { AlbexError, AlbexInitError, AlbexUnsupportedFormatError, AlbexParseError, AlbexCapacityError, assertFileSizeWithinLimit, } from './errors.js';
32
48
  let _workerSearchStreamWarned = false;
33
49
  export class AlbexEngineWorker {
34
50
  _opts;
35
51
  _worker;
36
52
  _nextId = 1;
37
53
  _pending = new Map();
38
- _docsCache = [];
39
54
  constructor(opts) {
40
55
  this._opts = opts;
41
56
  }
57
+ /**
58
+ * Spawn the worker and initialise the engine inside it.
59
+ *
60
+ * Every serializable engine option is forwarded across the worker
61
+ * boundary (`wasmUrl`, `wasmBaseUrl`, `pdfWasmUrl`, `capacity`, `simd`,
62
+ * `gpu`, `gpuThreshold`, `maxFileBytes`) — only `workerUrl`, which is
63
+ * consumed on this side, is stripped. Notes on what applies in a worker:
64
+ *
65
+ * - `capacity`: fully honoured — both the `'std'`/`'large'` presets
66
+ * (plain strings) and a custom object are structured-clone-safe, so
67
+ * the worker-side engine sizes its pools exactly like a main-thread
68
+ * engine would. Mind the memory cost (`'large'` ≈ 180 MB) lives in
69
+ * the worker's heap.
70
+ * - `wasmBaseUrl` + `simd`: fully honoured — the worker-side engine can
71
+ * load the `_simd.wasm` variant.
72
+ * - `gpu` / `gpuThreshold`: honoured where the worker runtime exposes
73
+ * WebGPU. `navigator.gpu` is available in dedicated workers in
74
+ * Chromium-based browsers (compute needs no canvas); elsewhere the
75
+ * engine's GPU probe fails gracefully and searches use the CPU
76
+ * pre-filter, exactly as on the main thread.
77
+ */
42
78
  async init() {
43
79
  this._worker = new Worker(this._opts.workerUrl, { type: 'module' });
44
80
  this._worker.onmessage = (ev) => {
@@ -59,10 +95,18 @@ export class AlbexEngineWorker {
59
95
  p.reject(err);
60
96
  this._pending.clear();
61
97
  };
62
- await this._send({ kind: 'init', opts: {
63
- wasmUrl: this._opts.wasmUrl,
64
- pdfWasmUrl: this._opts.pdfWasmUrl,
65
- } });
98
+ // Forward every serializable engine option. AlbexOptions is data-only
99
+ // (strings/numbers/booleans), but filter defensively so a future
100
+ // non-clonable option (function, DOM handle) cannot break postMessage.
101
+ const opts = {};
102
+ for (const [k, v] of Object.entries(this._opts)) {
103
+ if (k === 'workerUrl')
104
+ continue; // consumed on this side
105
+ if (v === undefined || typeof v === 'function')
106
+ continue;
107
+ opts[k] = v;
108
+ }
109
+ await this._send({ kind: 'init', opts });
66
110
  }
67
111
  _send(op, transfer = []) {
68
112
  const id = this._nextId++;
@@ -73,15 +117,25 @@ export class AlbexEngineWorker {
73
117
  });
74
118
  }
75
119
  async indexFile(file) {
120
+ // Size guard BEFORE reading: the worker-side engine enforces the same
121
+ // limit, but checking here avoids buffering an oversized file on the
122
+ // main thread just to have the worker reject it.
123
+ assertFileSizeWithinLimit(file, this._opts.maxFileBytes);
76
124
  const buffer = await file.arrayBuffer();
77
125
  // Transfer the buffer to avoid a copy.
78
- const doc = await this._send({ kind: 'indexFile', name: file.name, buffer }, [buffer]);
79
- this._docsCache.push(doc);
80
- return doc;
126
+ return this._send({ kind: 'indexFile', name: file.name, buffer }, [buffer]);
81
127
  }
82
128
  search(query, opts = {}) {
83
129
  return this._send({ kind: 'search', query, options: opts });
84
130
  }
131
+ /**
132
+ * Enumerate the authoritative chunks Albex indexed for `docId`
133
+ * (`IndexedDocument.docId` from {@link indexFile}). Mirrors
134
+ * `AlbexEngine.listChunks` across the worker boundary.
135
+ */
136
+ listChunks(docId) {
137
+ return this._send({ kind: 'listChunks', docId });
138
+ }
85
139
  /**
86
140
  * Cooperative variant of `search`. Today the wire still sends a single
87
141
  * batch — the result array is fetched in one round-trip from the worker
@@ -106,16 +160,37 @@ export class AlbexEngineWorker {
106
160
  yield* this.searchCooperative(query, opts);
107
161
  }
108
162
  async removeDocument(id) {
109
- const ok = await this._send({ kind: 'removeDocument', id });
110
- if (ok)
111
- this._docsCache = this._docsCache.filter(d => d.name !== id && d.contentHash !== id);
112
- return ok;
163
+ return this._send({ kind: 'removeDocument', id });
113
164
  }
114
- async compact() { await this._send({ kind: 'compact' }); }
115
- async reset() {
116
- await this._send({ kind: 'reset' });
117
- this._docsCache = [];
165
+ /**
166
+ * Replace a previously indexed document with new content. Mirrors
167
+ * `AlbexEngine.replaceDocument`: equivalent to `removeDocument(name)` +
168
+ * `indexFile(newFile)` without tripping the idempotency check, plus an
169
+ * opportunistic compact under text-pool pressure — all inside the
170
+ * worker-side engine's lock. The file bytes are transferred (zero-copy),
171
+ * like `indexFile`.
172
+ */
173
+ async replaceDocument(name, newFile) {
174
+ assertFileSizeWithinLimit(newFile, this._opts.maxFileBytes);
175
+ const buffer = await newFile.arrayBuffer();
176
+ return this._send({ kind: 'replaceDocument', name, fileName: newFile.name, buffer }, [buffer]);
118
177
  }
178
+ /**
179
+ * Drain and return the diagnostics collected by the worker-side engine
180
+ * since the last call. Mirrors `AlbexEngine.takeDiagnostics` — consult it
181
+ * after `indexFile`/`load` to surface recoverable issues (PDF fallbacks,
182
+ * skipped content, persistence warnings). The worker-side buffer is
183
+ * cleared on each call.
184
+ *
185
+ * Particularly important in a worker: scanned PDFs index with **0 chunks**
186
+ * (no OCR available — see the note on OCR below), and the diagnostic
187
+ * explaining why is only visible through this method.
188
+ */
189
+ takeDiagnostics() {
190
+ return this._send({ kind: 'takeDiagnostics' });
191
+ }
192
+ async compact() { await this._send({ kind: 'compact' }); }
193
+ async reset() { await this._send({ kind: 'reset' }); }
119
194
  getStats() { return this._send({ kind: 'getStats' }); }
120
195
  getLastSearchStats() { return this._send({ kind: 'getLastSearchStats' }); }
121
196
  getDocuments() { return this._send({ kind: 'getDocuments' }); }
@@ -134,7 +209,6 @@ export class AlbexEngineWorker {
134
209
  p.reject(new AlbexError('disposed', 'Engine disposed'));
135
210
  this._pending.clear();
136
211
  this._worker?.terminate();
137
- this._docsCache = [];
138
212
  }
139
213
  }
140
214
  function rehydrateError(e) {
@@ -142,7 +216,10 @@ function rehydrateError(e) {
142
216
  case 'init': return new AlbexInitError(e.message);
143
217
  case 'unsupported_format': return new AlbexUnsupportedFormatError(e.message.replace(/^Unsupported format: \./, ''));
144
218
  case 'parse': return new AlbexParseError('unknown', e.message);
145
- case 'capacity': return new AlbexCapacityError(e.message);
219
+ // `limit`/`max` survive the wire (worker-runtime serialises them) so
220
+ // the rehydrated error still reports the runtime capacity that
221
+ // overflowed inside the worker-side engine.
222
+ case 'capacity': return new AlbexCapacityError(e.message, e.limit, e.max);
146
223
  default: {
147
224
  const err = new Error(e.message);
148
225
  err.name = e.name;
@@ -1 +1 @@
1
- {"version":3,"file":"albex-worker.js","sourceRoot":"","sources":["../src/albex-worker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"albex-worker.js","sourceRoot":"","sources":["../src/albex-worker.ts"],"names":[],"mappings":"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"}