@mailwoman/resolver-wof-wasm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/out/index.d.ts +9 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +8 -0
- package/out/index.js.map +1 -0
- package/out/loader.d.ts +55 -0
- package/out/loader.d.ts.map +1 -0
- package/out/loader.js +62 -0
- package/out/loader.js.map +1 -0
- package/out/lookup.d.ts +41 -0
- package/out/lookup.d.ts.map +1 -0
- package/out/lookup.js +255 -0
- package/out/lookup.js.map +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @mailwoman/resolver-wof-wasm
|
|
2
|
+
|
|
3
|
+
Browser-side WOF resolver backed by [`@sqlite.org/sqlite-wasm`](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm). Drop-in `PlaceLookup` implementation for the browser-side mailwoman demo (Phase B of the demo plan — see [sister-software/mailwoman#98](https://github.com/sister-software/mailwoman/issues/98)).
|
|
4
|
+
|
|
5
|
+
Pair with [`@mailwoman/resolver-wof-sqlite`](https://www.npmjs.com/package/@mailwoman/resolver-wof-sqlite)'s `mailwoman-wof-build-slim` CLI to produce a ~35 MB slim distribution suitable for static-asset deployment, then load it from a URL at runtime.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
**v0.1.0 — scaffold + minimal `findPlace`.** Supports text + placetype + country + limit. Full ranking surface (parentId descendant filter, proximity boost, bbox hard filter, population weighting) lands in v0.2.0 once the shared query builder is extracted from `@mailwoman/resolver-wof-sqlite`.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { loadSlimWofDatabase, WofWasmPlaceLookup } from "@mailwoman/resolver-wof-wasm"
|
|
15
|
+
|
|
16
|
+
// Load the slim DB. Either fetch from a URL or pass raw Uint8Array bytes.
|
|
17
|
+
const { db } = await loadSlimWofDatabase({
|
|
18
|
+
source: "/static/wof-hot.db", // or a Uint8Array from bundler import
|
|
19
|
+
wasmUrl: new URL("../node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm", import.meta.url).href,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const lookup = new WofWasmPlaceLookup({ db })
|
|
23
|
+
|
|
24
|
+
const matches = await lookup.findPlace({
|
|
25
|
+
text: "Springfield",
|
|
26
|
+
placetype: "locality",
|
|
27
|
+
country: "US",
|
|
28
|
+
limit: 5,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
for (const m of matches) {
|
|
32
|
+
console.log(m.id, m.name, m.lat, m.lon, "score:", m.score)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
lookup.close()
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Loading strategies
|
|
39
|
+
|
|
40
|
+
`loadSlimWofDatabase` currently fetches the whole DB and opens it in memory via `sqlite3_deserialize`. For the ~35 MB default slim build that's a one-RTT transfer + a one-shot in-memory open — typically sub-second on broadband, and after that every query is in-process WASM.
|
|
41
|
+
|
|
42
|
+
For larger DBs or low-bandwidth users, the future path is to swap the loader for an HTTP-VFS implementation (à la `sql.js-httpvfs`) so SQLite pages get fetched lazily via byte-range. The `WofWasmPlaceLookup` class is loader-agnostic — only the loader changes.
|
|
43
|
+
|
|
44
|
+
## Bundling
|
|
45
|
+
|
|
46
|
+
This package ships compiled TypeScript only. The `@sqlite.org/sqlite-wasm` runtime (`.wasm` + worker JS) is a peer asset your bundler needs to serve. For Vite:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import wasmUrl from "@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm?url"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For webpack: use `asset/resource` rules on the `.wasm` extension and pass the resolved URL via the `wasmUrl` option.
|
|
53
|
+
|
|
54
|
+
## Why not extend `WofSqlitePlaceLookup`?
|
|
55
|
+
|
|
56
|
+
`WofSqlitePlaceLookup` is hard-bound to `node:sqlite` (the Node 22+ built-in). Subclassing across the Node/WASM line means dragging Node-only types into a browser package. We chose composition over inheritance: both classes implement the same `PlaceLookup` interface and (v0.2.0+) call the same shared query builder, but stay independently importable.
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
AGPL-3.0-only (mirrors the rest of the mailwoman tree).
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
export { loadSlimWofDatabase, type LoadSlimOpts } from "./loader.js";
|
|
7
|
+
export { WofWasmPlaceLookup, type WofWasmPlaceLookupOpts } from "./lookup.js";
|
|
8
|
+
export type { FindPlaceQuery, GeoBbox, GeoPoint, PlaceCandidate, PlaceLookup, WofPlacetype, } from "@mailwoman/resolver-wof-sqlite";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAG7E,YAAY,EACX,cAAc,EACd,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACX,YAAY,GACZ,MAAM,gCAAgC,CAAA"}
|
package/out/index.js
ADDED
package/out/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAqB,MAAM,aAAa,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAA+B,MAAM,aAAa,CAAA"}
|
package/out/loader.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Loads a slim WOF SQLite distribution into an in-memory `@sqlite.org/sqlite-wasm` database.
|
|
7
|
+
*
|
|
8
|
+
* V1 strategy: fetch the whole file (~35 MB for the default top-1k US slim) and open it via the OO1
|
|
9
|
+
* API's "OPFS"-flavored constructor in transient mode. The full-fetch approach is fine for a
|
|
10
|
+
* bundle this size — the slim DB is what the browser holds in RAM for the duration of the session
|
|
11
|
+
* anyway, and HTTP/2 + gzip make the 35 MB transfer pay one RTT + transfer time, not the
|
|
12
|
+
* "hundreds of byte-range requests" cost a HTTP-VFS approach would incur.
|
|
13
|
+
*
|
|
14
|
+
* When we eventually want incremental loading (Phase B.x), this is the seam to swap — keep
|
|
15
|
+
* `WofWasmPlaceLookup` unchanged and replace the loader with a `sql.js-httpvfs`-style VFS.
|
|
16
|
+
*/
|
|
17
|
+
import { type Database, type Sqlite3Static } from "@sqlite.org/sqlite-wasm";
|
|
18
|
+
export interface LoadSlimOpts {
|
|
19
|
+
/**
|
|
20
|
+
* Either a URL to fetch the slim .db from, or a raw Uint8Array containing the file bytes.
|
|
21
|
+
*
|
|
22
|
+
* URL form is the public-demo path (load over HTTP). Uint8Array form is what tests use to skip
|
|
23
|
+
* the network entirely and is also useful if a caller wants to embed the DB in their own bundler
|
|
24
|
+
* output (Vite's `?url` / `?arraybuffer` imports both produce things that fit here).
|
|
25
|
+
*/
|
|
26
|
+
source: string | Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* Where the sqlite-wasm runtime can find its .wasm asset. Required in browser builds because the
|
|
29
|
+
* default URL is resolved relative to the worker script, which bundlers usually rewrite.
|
|
30
|
+
*
|
|
31
|
+
* Most bundlers will let you do `new
|
|
32
|
+
* URL("../node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm",
|
|
33
|
+
* import.meta.url).href`.
|
|
34
|
+
*
|
|
35
|
+
* Leave unset to use the runtime's defaults (works in Node + worker contexts where the path is
|
|
36
|
+
* resolvable directly).
|
|
37
|
+
*/
|
|
38
|
+
wasmUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Optional fetch implementation override. Defaults to `globalThis.fetch`. Useful in test
|
|
41
|
+
* harnesses that want to short-circuit network calls.
|
|
42
|
+
*/
|
|
43
|
+
fetchImpl?: typeof fetch;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Loads + opens the slim WOF DB. Returns `{ db, sqlite3 }` — `db` is the open Database; `sqlite3`
|
|
47
|
+
* is the runtime handle (in case the caller wants to call other OO1 APIs on it).
|
|
48
|
+
*
|
|
49
|
+
* Caller is responsible for `db.close()` when done.
|
|
50
|
+
*/
|
|
51
|
+
export declare function loadSlimWofDatabase(opts: LoadSlimOpts): Promise<{
|
|
52
|
+
db: Database;
|
|
53
|
+
sqlite3: Sqlite3Static;
|
|
54
|
+
}>;
|
|
55
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAA0B,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAE9F,MAAM,WAAW,YAAY;IAC5B;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;IAC3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,CAAC,CAuC/G"}
|
package/out/loader.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Loads a slim WOF SQLite distribution into an in-memory `@sqlite.org/sqlite-wasm` database.
|
|
7
|
+
*
|
|
8
|
+
* V1 strategy: fetch the whole file (~35 MB for the default top-1k US slim) and open it via the OO1
|
|
9
|
+
* API's "OPFS"-flavored constructor in transient mode. The full-fetch approach is fine for a
|
|
10
|
+
* bundle this size — the slim DB is what the browser holds in RAM for the duration of the session
|
|
11
|
+
* anyway, and HTTP/2 + gzip make the 35 MB transfer pay one RTT + transfer time, not the
|
|
12
|
+
* "hundreds of byte-range requests" cost a HTTP-VFS approach would incur.
|
|
13
|
+
*
|
|
14
|
+
* When we eventually want incremental loading (Phase B.x), this is the seam to swap — keep
|
|
15
|
+
* `WofWasmPlaceLookup` unchanged and replace the loader with a `sql.js-httpvfs`-style VFS.
|
|
16
|
+
*/
|
|
17
|
+
import sqlite3InitModule, {} from "@sqlite.org/sqlite-wasm";
|
|
18
|
+
/**
|
|
19
|
+
* Loads + opens the slim WOF DB. Returns `{ db, sqlite3 }` — `db` is the open Database; `sqlite3`
|
|
20
|
+
* is the runtime handle (in case the caller wants to call other OO1 APIs on it).
|
|
21
|
+
*
|
|
22
|
+
* Caller is responsible for `db.close()` when done.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadSlimWofDatabase(opts) {
|
|
25
|
+
const bytes = typeof opts.source === "string" ? await fetchBytes(opts.source, opts.fetchImpl) : opts.source;
|
|
26
|
+
// sqlite3InitModule's TS signature lies about its options bag — the runtime DOES accept the
|
|
27
|
+
// Emscripten-style {print, printErr, locateFile} options shown in the upstream docs. Cast to
|
|
28
|
+
// `any` for the call site rather than shadowing the typed wrapper for the entire file.
|
|
29
|
+
const sqlite3 = await sqlite3InitModule({
|
|
30
|
+
print: () => { }, // suppress stdout from the WASM runtime
|
|
31
|
+
printErr: (msg) => console.error("[sqlite-wasm]", msg),
|
|
32
|
+
...(opts.wasmUrl ? { locateFile: (name) => (name.endsWith(".wasm") ? opts.wasmUrl : name) } : {}),
|
|
33
|
+
});
|
|
34
|
+
// OO1 transient-DB constructor: opens an in-memory DB then we restore the file bytes into it
|
|
35
|
+
// via `sqlite3.capi.sqlite3_deserialize`. This is the official way to "open a Uint8Array as a
|
|
36
|
+
// database" — `new DB(":memory:")` followed by deserialize is faster than CREATE TABLE +
|
|
37
|
+
// INSERT-from-dump and preserves the on-disk b-tree pages directly.
|
|
38
|
+
const db = new sqlite3.oo1.DB(":memory:", "ct");
|
|
39
|
+
// `allocFromTypedArray` has shape constraints across sqlite-wasm versions; the
|
|
40
|
+
// explicit alloc + HEAPU8.set pattern is the lowest-common-denominator path and
|
|
41
|
+
// avoids the "expecting 8/16/32/64" heap-shape mismatch seen on Node builds.
|
|
42
|
+
const p = sqlite3.wasm.alloc(bytes.byteLength);
|
|
43
|
+
const heap = sqlite3.wasm.heap8u();
|
|
44
|
+
heap.set(bytes, p);
|
|
45
|
+
const rc = sqlite3.capi.sqlite3_deserialize(db.pointer, "main", p, bytes.byteLength, bytes.byteLength, sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE | sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE);
|
|
46
|
+
if (rc !== sqlite3.capi.SQLITE_OK) {
|
|
47
|
+
sqlite3.wasm.dealloc(p);
|
|
48
|
+
db.close();
|
|
49
|
+
throw new Error(`sqlite3_deserialize failed: rc=${rc}`);
|
|
50
|
+
}
|
|
51
|
+
return { db, sqlite3 };
|
|
52
|
+
}
|
|
53
|
+
async function fetchBytes(url, fetchImpl) {
|
|
54
|
+
const f = fetchImpl ?? globalThis.fetch;
|
|
55
|
+
if (!f)
|
|
56
|
+
throw new Error("no fetch implementation available — pass fetchImpl in non-fetch environments");
|
|
57
|
+
const res = await f(url);
|
|
58
|
+
if (!res.ok)
|
|
59
|
+
throw new Error(`fetch ${url} failed: ${res.status} ${res.statusText}`);
|
|
60
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,iBAAiB,EAAE,EAAqC,MAAM,yBAAyB,CAAA;AA8B9F;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAkB;IAC3D,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAA;IAE3G,4FAA4F;IAC5F,6FAA6F;IAC7F,uFAAuF;IACvF,MAAM,OAAO,GAAG,MAAO,iBAA0F,CAAC;QACjH,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,wCAAwC;QACzD,QAAQ,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1G,CAAC,CAAA;IAEF,6FAA6F;IAC7F,8FAA8F;IAC9F,yFAAyF;IACzF,oEAAoE;IACpE,MAAM,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;IAE/C,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAA;IAClC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IAClB,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAC1C,EAAE,CAAC,OAAQ,EACX,MAAM,EACN,CAAC,EACD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,UAAU,EAChB,OAAO,CAAC,IAAI,CAAC,8BAA8B,GAAG,OAAO,CAAC,IAAI,CAAC,6BAA6B,CACxF,CAAA;IACD,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QACvB,EAAE,CAAC,KAAK,EAAE,CAAA;QACV,MAAM,IAAI,KAAK,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAA;IACxD,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAA;AACvB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,SAAwB;IAC9D,MAAM,CAAC,GAAG,SAAS,IAAI,UAAU,CAAC,KAAK,CAAA;IACvC,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;IACvG,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAA;IACxB,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;IACpF,OAAO,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;AAC/C,CAAC"}
|
package/out/lookup.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* `WofWasmPlaceLookup` — browser-side `PlaceLookup` backed by `@sqlite.org/sqlite-wasm`.
|
|
7
|
+
*
|
|
8
|
+
* V1 scope: text + placetype + limit + country. The full ranking surface from
|
|
9
|
+
* `WofSqlitePlaceLookup` (parentId descendant filter, near-proximity boost, bbox hard filter,
|
|
10
|
+
* population-weighted ordering) is queued for v2 in the same PR series — see Phase B tracking
|
|
11
|
+
* issue #98. The v1 scope is the minimum that lets the public demo answer "type a US city /
|
|
12
|
+
* postcode, get a hit".
|
|
13
|
+
*
|
|
14
|
+
* Internally this is a thin facade over the OO1 DB returned by `loadSlimWofDatabase`. The SQL we
|
|
15
|
+
* issue is the same SQLite dialect the Node implementation uses — once we extract the SQL
|
|
16
|
+
* building into a shared helper (planned: `@mailwoman/resolver-wof-sqlite/query-builder`), both
|
|
17
|
+
* implementations will call into the same builder and the parity guarantee becomes mechanical
|
|
18
|
+
* rather than convention-driven.
|
|
19
|
+
*/
|
|
20
|
+
import type { Database } from "@sqlite.org/sqlite-wasm";
|
|
21
|
+
import { type CoincidentLocality } from "@mailwoman/resolver";
|
|
22
|
+
import type { FindPlaceQuery, PlaceCandidate, PlaceLookup } from "@mailwoman/resolver-wof-sqlite";
|
|
23
|
+
export interface WofWasmPlaceLookupOpts {
|
|
24
|
+
/** Open `@sqlite.org/sqlite-wasm` Database (from `loadSlimWofDatabase`). */
|
|
25
|
+
db: Database;
|
|
26
|
+
}
|
|
27
|
+
export declare class WofWasmPlaceLookup implements PlaceLookup {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(opts: WofWasmPlaceLookupOpts);
|
|
30
|
+
findPlace(query: FindPlaceQuery): Promise<PlaceCandidate[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Dual-role localities coincident with an admin id, from the `coincident_roles` relation (#403)
|
|
33
|
+
* carried into the slim DB by build-slim. Backs the resolver's hierarchy completion (on by
|
|
34
|
+
* default) in the browser — mirrors `WofSqlitePlaceLookup.coincidentLocalitiesFor`. Returns `[]`
|
|
35
|
+
* when the slim DB predates the relation. Loaded once + memoized (the relation is ~hundreds of
|
|
36
|
+
* rows).
|
|
37
|
+
*/
|
|
38
|
+
coincidentLocalitiesFor(adminId: number | string): CoincidentLocality[];
|
|
39
|
+
close(): void;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvD,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAgB,MAAM,gCAAgC,CAAA;AAK/G,MAAM,WAAW,sBAAsB;IACtC,4EAA4E;IAC5E,EAAE,EAAE,QAAQ,CAAA;CACZ;AAiBD,qBAAa,kBAAmB,YAAW,WAAW;;gBAUzC,IAAI,EAAE,sBAAsB;IAqClC,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoIjE;;;;;;OAMG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,kBAAkB,EAAE;IAgDvE,KAAK,IAAI,IAAI;CAGb"}
|
package/out/lookup.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* `WofWasmPlaceLookup` — browser-side `PlaceLookup` backed by `@sqlite.org/sqlite-wasm`.
|
|
7
|
+
*
|
|
8
|
+
* V1 scope: text + placetype + limit + country. The full ranking surface from
|
|
9
|
+
* `WofSqlitePlaceLookup` (parentId descendant filter, near-proximity boost, bbox hard filter,
|
|
10
|
+
* population-weighted ordering) is queued for v2 in the same PR series — see Phase B tracking
|
|
11
|
+
* issue #98. The v1 scope is the minimum that lets the public demo answer "type a US city /
|
|
12
|
+
* postcode, get a hit".
|
|
13
|
+
*
|
|
14
|
+
* Internally this is a thin facade over the OO1 DB returned by `loadSlimWofDatabase`. The SQL we
|
|
15
|
+
* issue is the same SQLite dialect the Node implementation uses — once we extract the SQL
|
|
16
|
+
* building into a shared helper (planned: `@mailwoman/resolver-wof-sqlite/query-builder`), both
|
|
17
|
+
* implementations will call into the same builder and the parity guarantee becomes mechanical
|
|
18
|
+
* rather than convention-driven.
|
|
19
|
+
*/
|
|
20
|
+
import { expandPlacetypeFilter } from "@mailwoman/resolver";
|
|
21
|
+
// Browser-safe subpath import (fts.ts's only node:sqlite import is type-only) — the shared
|
|
22
|
+
// alias-bag parser keeps this backend's exact tier byte-identical to the Node resolver's.
|
|
23
|
+
import { aliasBagExactMatch } from "@mailwoman/resolver-wof-sqlite/fts";
|
|
24
|
+
/**
|
|
25
|
+
* Population-boost tunables, mirroring `resolver-wof-sqlite/lookup.ts` defaults. The boost is
|
|
26
|
+
* `POPULATION_BOOST * min(1, log10(1 + pop) / POPULATION_SCALE_LOG10)`, subtracted from bm25 (lower
|
|
27
|
+
* = better, matching SQLite's convention). A 1M-population city earns the full boost — enough to
|
|
28
|
+
* surface the famous same-name place ("New York" over "West New York") without steamrolling a
|
|
29
|
+
* clearly-better text match, because exact-name tiering is consulted FIRST.
|
|
30
|
+
*/
|
|
31
|
+
const POPULATION_BOOST = 4.0;
|
|
32
|
+
const POPULATION_SCALE_LOG10 = 6.0;
|
|
33
|
+
/** Normalize a name/query for exact-match tiering: lowercase, trim, collapse internal whitespace. */
|
|
34
|
+
function normalizeName(s) {
|
|
35
|
+
return s.toLowerCase().trim().replace(/\s+/g, " ");
|
|
36
|
+
}
|
|
37
|
+
export class WofWasmPlaceLookup {
|
|
38
|
+
#db;
|
|
39
|
+
#hasPopulationCache;
|
|
40
|
+
#hasPlaceAbbrCache;
|
|
41
|
+
/**
|
|
42
|
+
* Lazily-built `admin_id → coincident localities` map from the #403 relation (the slim DB carries
|
|
43
|
+
* it).
|
|
44
|
+
*/
|
|
45
|
+
#coincidentRolesCache;
|
|
46
|
+
constructor(opts) {
|
|
47
|
+
this.#db = opts.db;
|
|
48
|
+
}
|
|
49
|
+
/** Lazily probe (once) whether the slim DB carries the `place_population` aux table. */
|
|
50
|
+
#hasPopulation() {
|
|
51
|
+
if (this.#hasPopulationCache === undefined) {
|
|
52
|
+
const r = this.#db.selectObjects(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='place_population' LIMIT 1`);
|
|
53
|
+
this.#hasPopulationCache = r.length > 0;
|
|
54
|
+
}
|
|
55
|
+
return this.#hasPopulationCache;
|
|
56
|
+
}
|
|
57
|
+
/** Lazily probe (once) whether the slim DB carries the `place_abbr` aux table (build-slim ≥ #189). */
|
|
58
|
+
#hasPlaceAbbr() {
|
|
59
|
+
if (this.#hasPlaceAbbrCache === undefined) {
|
|
60
|
+
const r = this.#db.selectObjects(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='place_abbr' LIMIT 1`);
|
|
61
|
+
this.#hasPlaceAbbrCache = r.length > 0;
|
|
62
|
+
}
|
|
63
|
+
return this.#hasPlaceAbbrCache;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Ids whose region abbreviation exactly equals `text` (case-insensitive), from `place_abbr`. The
|
|
67
|
+
* exact-abbrev tier signal — see the `findPlace` call site. Empty on slim DBs without the table.
|
|
68
|
+
*/
|
|
69
|
+
#abbrExactIds(text) {
|
|
70
|
+
const t = text.trim();
|
|
71
|
+
if (!t || !this.#hasPlaceAbbr())
|
|
72
|
+
return new Set();
|
|
73
|
+
const rows = this.#db.selectObjects(`SELECT id FROM place_abbr WHERE abbr = ? COLLATE NOCASE`, [t]);
|
|
74
|
+
return new Set(rows.map((r) => Number(r.id)));
|
|
75
|
+
}
|
|
76
|
+
async findPlace(query) {
|
|
77
|
+
const text = (query.text ?? "").trim();
|
|
78
|
+
if (!text)
|
|
79
|
+
return [];
|
|
80
|
+
const ftsQuery = sanitizeFtsQuery(text);
|
|
81
|
+
if (!ftsQuery)
|
|
82
|
+
return [];
|
|
83
|
+
const limit = Math.max(1, query.limit ?? 10);
|
|
84
|
+
// FTS5 MATCH on place_search joined to spr. Placetype + country filters are pushed into the
|
|
85
|
+
// WHERE clause because they reduce candidate count cheaply.
|
|
86
|
+
const conditions = ["place_search MATCH ?", "spr.is_current != 0", "spr.is_deprecated = 0"];
|
|
87
|
+
const params = [ftsQuery];
|
|
88
|
+
// Shared placetype-equivalence expansion (core/resolver): a `locality` query must also reach
|
|
89
|
+
// `borough` / `localadmin` rows. Without it, Brooklyn-the-borough (pop 2.5M, an EXACT name
|
|
90
|
+
// match) was unreachable and the fuzzy "Brooklyn Park, MN" won. Same table the Node resolver
|
|
91
|
+
// uses — the two backends can't drift.
|
|
92
|
+
const placetypes = expandPlacetypeFilter(normalizePlacetypes(query.placetype));
|
|
93
|
+
if (placetypes && placetypes.length > 0) {
|
|
94
|
+
conditions.push(`spr.placetype IN (${placetypes.map(() => "?").join(",")})`);
|
|
95
|
+
params.push(...placetypes);
|
|
96
|
+
}
|
|
97
|
+
if (query.country) {
|
|
98
|
+
conditions.push("spr.country = ?");
|
|
99
|
+
params.push(query.country.toUpperCase());
|
|
100
|
+
}
|
|
101
|
+
// Point-in-bbox filter. Used to constrain a locality lookup to a parsed region/state's bounds
|
|
102
|
+
// (e.g. "Roseville, Michigan" → only the Roseville whose centroid sits in Michigan's bbox),
|
|
103
|
+
// which the broken-in-the-slim-DB parent_id chain can't do via descendant filtering.
|
|
104
|
+
if (query.bbox) {
|
|
105
|
+
conditions.push("spr.latitude BETWEEN ? AND ?", "spr.longitude BETWEEN ? AND ?");
|
|
106
|
+
params.push(query.bbox.minLat, query.bbox.maxLat, query.bbox.minLon, query.bbox.maxLon);
|
|
107
|
+
}
|
|
108
|
+
// Over-fetch a pool ordered by raw BM25, then re-rank in JS (exact-name tier, then
|
|
109
|
+
// population-weighted bm25). The over-fetch is essential: a famous place can sit a few rows
|
|
110
|
+
// below a tiny same-name town on raw BM25 ("New York" loses to "West New York" by a hair), so a
|
|
111
|
+
// tight LIMIT on bm25 alone would truncate it before the re-rank could pull it up. This mirrors
|
|
112
|
+
// the post-scoring tier + population boost in resolver-wof-sqlite/lookup.ts. (v1 issued pure
|
|
113
|
+
// bm25, which is why the demo targeted West New York for "New York, NY".)
|
|
114
|
+
const hasPop = this.#hasPopulation();
|
|
115
|
+
const pool = Math.max(limit, 50);
|
|
116
|
+
const sql = `SELECT spr.id, spr.name, spr.placetype, spr.country, spr.latitude, spr.longitude, spr.parent_id, ` +
|
|
117
|
+
`spr.min_latitude, spr.max_latitude, spr.min_longitude, spr.max_longitude, ` +
|
|
118
|
+
`place_search.alt_names AS alt_names, ` +
|
|
119
|
+
`${hasPop ? "pp.population" : "NULL"} AS population, bm25(place_search) AS bm25 ` +
|
|
120
|
+
`FROM place_search JOIN spr ON spr.id = place_search.wof_id ` +
|
|
121
|
+
`${hasPop ? "LEFT JOIN place_population pp ON pp.id = spr.id " : ""}` +
|
|
122
|
+
`WHERE ${conditions.join(" AND ")} ` +
|
|
123
|
+
`ORDER BY bm25(place_search) ASC ` +
|
|
124
|
+
`LIMIT ?`;
|
|
125
|
+
params.push(pool);
|
|
126
|
+
const rows = this.#db.selectObjects(sql, params);
|
|
127
|
+
const normQuery = normalizeName(text);
|
|
128
|
+
// Exact-abbreviation ids: region/state abbreviations live in the slim DB's `place_abbr` table
|
|
129
|
+
// (carried by build-slim before `names` is dropped). A candidate whose abbreviation equals the
|
|
130
|
+
// query is an EXACT match — same tier as an exact name match — so "VT" → Vermont outranks a
|
|
131
|
+
// foreign region that merely token-matches "VT" via a multilingual name fragment. No-op on slim
|
|
132
|
+
// DBs built before place_abbr (the table is absent → empty set). This is the data-driven
|
|
133
|
+
// replacement for the demo's hardcoded `expandUsRegion` map; it also generalizes beyond US.
|
|
134
|
+
const abbrIds = this.#abbrExactIds(text);
|
|
135
|
+
// Strict exact = canonical name or region abbreviation equals the query. Computed for the whole
|
|
136
|
+
// pool FIRST because the ALIAS tier below only engages when no strict exact exists.
|
|
137
|
+
const strictExact = (row) => normalizeName(row.name) === normQuery || abbrIds.has(row.id);
|
|
138
|
+
const anyStrictExact = rows.some(strictExact);
|
|
139
|
+
return rows
|
|
140
|
+
.map((row) => {
|
|
141
|
+
// Alias tier: `alt_names` is the FTS row's alias bag (the slim DB's only surviving alias
|
|
142
|
+
// source), aliases joined on the boundary-preserving ALIAS_SEPARATOR (#523). The shared
|
|
143
|
+
// parser does a true per-alias equality check, ungated; on a LEGACY bag (pre-#523 slim
|
|
144
|
+
// artifact, boundaries lost) it falls back to padded containment gated on "no strictly
|
|
145
|
+
// exact candidate" so interior fragments ("York" inside "New York City") can't be
|
|
146
|
+
// false-promoted. Mirrors the Node resolver's alias tier
|
|
147
|
+
// (`WofSqlitePlaceLookup.#exactMatchIds`).
|
|
148
|
+
const aliasExact = aliasBagExactMatch(row.alt_names, normQuery, anyStrictExact);
|
|
149
|
+
const exactTier = strictExact(row) || aliasExact ? 0 : 1;
|
|
150
|
+
const popBoost = row.population && row.population > 0
|
|
151
|
+
? POPULATION_BOOST * Math.min(1, Math.log10(1 + row.population) / POPULATION_SCALE_LOG10)
|
|
152
|
+
: 0;
|
|
153
|
+
// Lower adjScore = better, matching SQLite's bm25 convention (more negative = better).
|
|
154
|
+
const adjScore = row.bm25 - popBoost;
|
|
155
|
+
return { row, exactTier, adjScore };
|
|
156
|
+
})
|
|
157
|
+
.sort((a, b) => a.exactTier - b.exactTier || a.adjScore - b.adjScore)
|
|
158
|
+
.slice(0, limit)
|
|
159
|
+
.map(({ row, adjScore, exactTier }) => ({
|
|
160
|
+
id: row.id,
|
|
161
|
+
name: row.name,
|
|
162
|
+
placetype: row.placetype,
|
|
163
|
+
country: row.country,
|
|
164
|
+
lat: row.latitude,
|
|
165
|
+
lon: row.longitude,
|
|
166
|
+
parent_id: row.parent_id ?? undefined,
|
|
167
|
+
// Surface the exact-match tier so a downstream country re-rank (#369) can keep the country
|
|
168
|
+
// pin from crossing it — parity with `WofSqlitePlaceLookup`. See ResolvedPlace.exactMatch.
|
|
169
|
+
exactMatch: exactTier === 0,
|
|
170
|
+
bbox: row.min_latitude != null && row.max_latitude != null && row.min_longitude != null && row.max_longitude != null
|
|
171
|
+
? {
|
|
172
|
+
minLat: row.min_latitude,
|
|
173
|
+
maxLat: row.max_latitude,
|
|
174
|
+
minLon: row.min_longitude,
|
|
175
|
+
maxLon: row.max_longitude,
|
|
176
|
+
}
|
|
177
|
+
: undefined,
|
|
178
|
+
// Flip sign so higher = better (PlaceLookup contract). The adjusted, population-aware
|
|
179
|
+
// score is what we sorted by, so callers see the same ordering they're shown.
|
|
180
|
+
score: -adjScore,
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Dual-role localities coincident with an admin id, from the `coincident_roles` relation (#403)
|
|
185
|
+
* carried into the slim DB by build-slim. Backs the resolver's hierarchy completion (on by
|
|
186
|
+
* default) in the browser — mirrors `WofSqlitePlaceLookup.coincidentLocalitiesFor`. Returns `[]`
|
|
187
|
+
* when the slim DB predates the relation. Loaded once + memoized (the relation is ~hundreds of
|
|
188
|
+
* rows).
|
|
189
|
+
*/
|
|
190
|
+
coincidentLocalitiesFor(adminId) {
|
|
191
|
+
const id = typeof adminId === "number" ? adminId : Number(adminId);
|
|
192
|
+
if (!Number.isFinite(id))
|
|
193
|
+
return [];
|
|
194
|
+
if (!this.#coincidentRolesCache) {
|
|
195
|
+
const map = new Map();
|
|
196
|
+
const exists = this.#db.selectObjects(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='coincident_roles' LIMIT 1`);
|
|
197
|
+
if (exists.length > 0) {
|
|
198
|
+
const rows = this.#db.selectObjects(`SELECT cr.admin_id AS adminId, s.id AS id, s.name AS name, s.country AS country,
|
|
199
|
+
s.latitude AS lat, s.longitude AS lon, cr.relationship_type AS relationshipType,
|
|
200
|
+
cr.locality_population AS population, cr.distance_km AS distanceKm
|
|
201
|
+
FROM coincident_roles cr JOIN spr s ON s.id = cr.locality_id`);
|
|
202
|
+
for (const r of rows) {
|
|
203
|
+
const candidate = {
|
|
204
|
+
id: r.id,
|
|
205
|
+
name: r.name,
|
|
206
|
+
placetype: "locality",
|
|
207
|
+
country: r.country,
|
|
208
|
+
lat: r.lat,
|
|
209
|
+
lon: r.lon,
|
|
210
|
+
score: 0,
|
|
211
|
+
relationshipType: r.relationshipType,
|
|
212
|
+
population: r.population,
|
|
213
|
+
distanceKm: r.distanceKm,
|
|
214
|
+
};
|
|
215
|
+
const list = map.get(r.adminId);
|
|
216
|
+
if (list)
|
|
217
|
+
list.push(candidate);
|
|
218
|
+
else
|
|
219
|
+
map.set(r.adminId, [candidate]);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
this.#coincidentRolesCache = map;
|
|
223
|
+
}
|
|
224
|
+
return this.#coincidentRolesCache.get(id) ?? [];
|
|
225
|
+
}
|
|
226
|
+
close() {
|
|
227
|
+
this.#db.close();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Trim raw user input into something FTS5 will accept. Preserves trailing `*` as the FTS5 prefix
|
|
232
|
+
* operator (matching the resolver-wof-sqlite implementation). Strips characters FTS5 treats as
|
|
233
|
+
* punctuation or operators so a user typing `Paris's` or `St. (Petersburg)` doesn't trigger an
|
|
234
|
+
* "fts5: syntax error" inside the WASM runtime.
|
|
235
|
+
*/
|
|
236
|
+
function sanitizeFtsQuery(text) {
|
|
237
|
+
const out = [];
|
|
238
|
+
for (const rawToken of text.normalize("NFKC").split(/\s+/u)) {
|
|
239
|
+
const trimmed = rawToken.trim();
|
|
240
|
+
if (!trimmed)
|
|
241
|
+
continue;
|
|
242
|
+
const hasPrefixStar = trimmed.endsWith("*");
|
|
243
|
+
const body = trimmed.replace(/[^\p{L}\p{N}]/gu, "");
|
|
244
|
+
if (!body)
|
|
245
|
+
continue;
|
|
246
|
+
out.push(hasPrefixStar ? `${body}*` : `"${body.replace(/"/g, '""')}"`);
|
|
247
|
+
}
|
|
248
|
+
return out.join(" ");
|
|
249
|
+
}
|
|
250
|
+
function normalizePlacetypes(input) {
|
|
251
|
+
if (!input)
|
|
252
|
+
return null;
|
|
253
|
+
return Array.isArray(input) ? input : [input];
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAE,qBAAqB,EAA2B,MAAM,qBAAqB,CAAA;AAEpF,2FAA2F;AAC3F,0FAA0F;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAOvE;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAC5B,MAAM,sBAAsB,GAAG,GAAG,CAAA;AAElC,qGAAqG;AACrG,SAAS,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AACnD,CAAC;AAED,MAAM,OAAO,kBAAkB;IACrB,GAAG,CAAU;IACtB,mBAAmB,CAAU;IAC7B,kBAAkB,CAAU;IAC5B;;;OAGG;IACH,qBAAqB,CAAoC;IAEzD,YAAY,IAA4B;QACvC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAA;IACnB,CAAC;IAED,wFAAwF;IACxF,cAAc;QACb,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAC/B,oFAAoF,CACpF,CAAA;YACD,IAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;QACxC,CAAC;QACD,OAAO,IAAI,CAAC,mBAAmB,CAAA;IAChC,CAAC;IAED,sGAAsG;IACtG,aAAa;QACZ,IAAI,IAAI,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;YAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,8EAA8E,CAAC,CAAA;YAChH,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;QACvC,CAAC;QACD,OAAO,IAAI,CAAC,kBAAkB,CAAA;IAC/B,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,IAAY;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACrB,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;YAAE,OAAO,IAAI,GAAG,EAAE,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,yDAAyD,EAAE,CAAC,CAAC,CAAC,CAEhG,CAAA;QACF,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAqB;QACpC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACtC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAA;QAEpB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAA;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;QAE5C,4FAA4F;QAC5F,4DAA4D;QAC5D,MAAM,UAAU,GAAa,CAAC,sBAAsB,EAAE,qBAAqB,EAAE,uBAAuB,CAAC,CAAA;QACrG,MAAM,MAAM,GAA2B,CAAC,QAAQ,CAAC,CAAA;QAEjD,6FAA6F;QAC7F,2FAA2F;QAC3F,6FAA6F;QAC7F,uCAAuC;QACvC,MAAM,UAAU,GAAG,qBAAqB,CAAC,mBAAmB,CAAC,KAAK,CAAC,SAAS,CAAC,CAA0B,CAAA;QACvG,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,UAAU,CAAC,IAAI,CAAC,qBAAqB,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC5E,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAA;QAC3B,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;YAClC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;QACzC,CAAC;QACD,8FAA8F;QAC9F,4FAA4F;QAC5F,qFAAqF;QACrF,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChB,UAAU,CAAC,IAAI,CAAC,8BAA8B,EAAE,+BAA+B,CAAC,CAAA;YAChF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACxF,CAAC;QAED,mFAAmF;QACnF,4FAA4F;QAC5F,gGAAgG;QAChG,gGAAgG;QAChG,6FAA6F;QAC7F,0EAA0E;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAChC,MAAM,GAAG,GACR,mGAAmG;YACnG,4EAA4E;YAC5E,uCAAuC;YACvC,GAAG,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,6CAA6C;YACjF,6DAA6D;YAC7D,GAAG,MAAM,CAAC,CAAC,CAAC,kDAAkD,CAAC,CAAC,CAAC,EAAE,EAAE;YACrE,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG;YACpC,kCAAkC;YAClC,SAAS,CAAA;QACV,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEjB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAe7C,CAAA;QAEF,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;QACrC,8FAA8F;QAC9F,+FAA+F;QAC/F,4FAA4F;QAC5F,gGAAgG;QAChG,yFAAyF;QACzF,4FAA4F;QAC5F,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;QACxC,gGAAgG;QAChG,oFAAoF;QACpF,MAAM,WAAW,GAAG,CAAC,GAAiC,EAAW,EAAE,CAClE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC7D,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC7C,OAAO,IAAI;aACT,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACZ,yFAAyF;YACzF,wFAAwF;YACxF,uFAAuF;YACvF,uFAAuF;YACvF,kFAAkF;YAClF,yDAAyD;YACzD,2CAA2C;YAC3C,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,CAAA;YAC/E,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACxD,MAAM,QAAQ,GACb,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC;gBACnC,CAAC,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,sBAAsB,CAAC;gBACzF,CAAC,CAAC,CAAC,CAAA;YACL,uFAAuF;YACvF,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAA;YACpC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;QACpC,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;aACpE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;aACf,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;YACvC,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,SAAS,EAAE,GAAG,CAAC,SAAyB;YACxC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,GAAG,EAAE,GAAG,CAAC,QAAQ;YACjB,GAAG,EAAE,GAAG,CAAC,SAAS;YAClB,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;YACrC,2FAA2F;YAC3F,2FAA2F;YAC3F,UAAU,EAAE,SAAS,KAAK,CAAC;YAC3B,IAAI,EACH,GAAG,CAAC,YAAY,IAAI,IAAI,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,IAAI,GAAG,CAAC,aAAa,IAAI,IAAI,IAAI,GAAG,CAAC,aAAa,IAAI,IAAI;gBAC7G,CAAC,CAAC;oBACA,MAAM,EAAE,GAAG,CAAC,YAAY;oBACxB,MAAM,EAAE,GAAG,CAAC,YAAY;oBACxB,MAAM,EAAE,GAAG,CAAC,aAAa;oBACzB,MAAM,EAAE,GAAG,CAAC,aAAa;iBACzB;gBACF,CAAC,CAAC,SAAS;YACb,sFAAsF;YACtF,8EAA8E;YAC9E,KAAK,EAAE,CAAC,QAAQ;SAChB,CAAC,CAAC,CAAA;IACL,CAAC;IAED;;;;;;OAMG;IACH,uBAAuB,CAAC,OAAwB;QAC/C,MAAM,EAAE,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAClE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,CAAA;QACnC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAgC,CAAA;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CACpC,oFAAoF,CACpF,CAAA;YACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAClC;;;kEAG6D,CAW5D,CAAA;gBACF,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;oBACtB,MAAM,SAAS,GAAuB;wBACrC,EAAE,EAAE,CAAC,CAAC,EAAE;wBACR,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,SAAS,EAAE,UAAU;wBACrB,OAAO,EAAE,CAAC,CAAC,OAAO;wBAClB,GAAG,EAAE,CAAC,CAAC,GAAG;wBACV,GAAG,EAAE,CAAC,CAAC,GAAG;wBACV,KAAK,EAAE,CAAC;wBACR,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;wBACpC,UAAU,EAAE,CAAC,CAAC,UAAU;wBACxB,UAAU,EAAE,CAAC,CAAC,UAAU;qBACxB,CAAA;oBACD,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;oBAC/B,IAAI,IAAI;wBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;;wBACzB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAA;gBACrC,CAAC;YACF,CAAC;YACD,IAAI,CAAC,qBAAqB,GAAG,GAAG,CAAA;QACjC,CAAC;QACD,OAAO,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;IAChD,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;IACjB,CAAC;CACD;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,IAAY;IACrC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC/B,IAAI,CAAC,OAAO;YAAE,SAAQ;QACtB,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAA;QACnD,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACvE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACrB,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAkC;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AAC9C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mailwoman/resolver-wof-wasm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Who's On First WASM resolver — browser-side PlaceLookup backed by @sqlite.org/sqlite-wasm. Drop-in replacement for @mailwoman/resolver-wof-sqlite when you need a static-asset deploy (Phase B of the demo plan).",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/sister-software/mailwoman.git",
|
|
9
|
+
"directory": "resolver-wof-wasm"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": "./out/index.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@mailwoman/core": "4.14.0",
|
|
18
|
+
"@mailwoman/resolver": "4.14.0",
|
|
19
|
+
"@mailwoman/resolver-wof-sqlite": "4.14.0",
|
|
20
|
+
"@sqlite.org/sqlite-wasm": "^3.53.0-build1"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"out/**/*.js",
|
|
24
|
+
"out/**/*.js.map",
|
|
25
|
+
"out/**/*.d.ts",
|
|
26
|
+
"out/**/*.d.ts.map",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
}
|
|
32
|
+
}
|