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.
- package/CHANGELOG.md +191 -0
- package/README.md +30 -19
- package/dist/albex-worker.d.ts +65 -2
- package/dist/albex-worker.d.ts.map +1 -1
- package/dist/albex-worker.js +97 -20
- package/dist/albex-worker.js.map +1 -1
- package/dist/albex.d.ts +206 -42
- package/dist/albex.d.ts.map +1 -1
- package/dist/albex.js +384 -103
- package/dist/albex.js.map +1 -1
- package/dist/errors.d.ts +35 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +37 -2
- package/dist/errors.js.map +1 -1
- package/dist/persistence.js +1 -1
- package/dist/pool/coordinator.d.ts +14 -6
- package/dist/pool/coordinator.d.ts.map +1 -1
- package/dist/pool/coordinator.js +65 -28
- package/dist/pool/coordinator.js.map +1 -1
- package/dist/profile.js +1 -1
- package/dist/resource-manager.js +1 -1
- package/dist/tiered-store.js +1 -1
- package/dist/wasm-bindings.d.ts +50 -1
- package/dist/wasm-bindings.d.ts.map +1 -1
- package/dist/wasm-bindings.js +19 -11
- package/dist/wasm-bindings.js.map +1 -1
- package/dist/worker-protocol.d.ts +23 -2
- package/dist/worker-protocol.d.ts.map +1 -1
- package/dist/worker-protocol.js +1 -1
- package/dist/worker-runtime.js +16 -1
- package/dist/worker-runtime.js.map +1 -1
- package/package.json +1 -1
- package/src/albex-worker.ts +103 -18
- package/src/albex.ts +2937 -2524
- package/src/errors.ts +49 -4
- package/src/pool/coordinator.ts +61 -34
- package/src/wasm-bindings.ts +78 -12
- package/src/worker-protocol.ts +12 -2
- package/src/worker-runtime.ts +16 -1
- 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
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
|
-
- **
|
|
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
|
|
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
|
|
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
|
-
###
|
|
227
|
+
### Capacity (runtime, single binary)
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
241
|
+
Presets and cost (≈ `maxChunks × 64 B + textPool + namePool`; WASM memory
|
|
242
|
+
never shrinks, so the largest capacity initialised stays committed):
|
|
243
243
|
|
|
244
|
-
|
|
|
245
|
-
|
|
246
|
-
|
|
|
247
|
-
|
|
|
248
|
-
|
|
|
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 #
|
|
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 #
|
|
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
|
```
|
package/dist/albex-worker.d.ts
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/albex-worker.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* albex v0.6.
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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;
|
package/dist/albex-worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"albex-worker.js","sourceRoot":"","sources":["../src/albex-worker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"albex-worker.js","sourceRoot":"","sources":["../src/albex-worker.ts"],"names":[],"mappings":"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"}
|