zonemapdb 0.8.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/block-fetch.d.ts +11 -0
  4. package/dist/block-fetch.d.ts.map +1 -0
  5. package/dist/block-fetch.js +29 -0
  6. package/dist/block-fetch.js.map +1 -0
  7. package/dist/client.d.ts +3 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +584 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/errors.d.ts +40 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +24 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/fetch-file.d.ts +39 -0
  16. package/dist/fetch-file.d.ts.map +1 -0
  17. package/dist/fetch-file.js +179 -0
  18. package/dist/fetch-file.js.map +1 -0
  19. package/dist/filter.d.ts +9 -0
  20. package/dist/filter.d.ts.map +1 -0
  21. package/dist/filter.js +104 -0
  22. package/dist/filter.js.map +1 -0
  23. package/dist/index-fetch.d.ts +3 -0
  24. package/dist/index-fetch.d.ts.map +1 -0
  25. package/dist/index-fetch.js +5 -0
  26. package/dist/index-fetch.js.map +1 -0
  27. package/dist/index.d.ts +6 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +5 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/manifest.d.ts +127 -0
  32. package/dist/manifest.d.ts.map +1 -0
  33. package/dist/manifest.js +86 -0
  34. package/dist/manifest.js.map +1 -0
  35. package/dist/normalize.d.ts +25 -0
  36. package/dist/normalize.d.ts.map +1 -0
  37. package/dist/normalize.js +43 -0
  38. package/dist/normalize.js.map +1 -0
  39. package/dist/secondary-index.d.ts +32 -0
  40. package/dist/secondary-index.d.ts.map +1 -0
  41. package/dist/secondary-index.js +80 -0
  42. package/dist/secondary-index.js.map +1 -0
  43. package/dist/types.d.ts +319 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +164 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/version.d.ts +9 -0
  48. package/dist/version.d.ts.map +1 -0
  49. package/dist/version.js +9 -0
  50. package/dist/version.js.map +1 -0
  51. package/dist/zonemap-fetch.d.ts +9 -0
  52. package/dist/zonemap-fetch.d.ts.map +1 -0
  53. package/dist/zonemap-fetch.js +11 -0
  54. package/dist/zonemap-fetch.js.map +1 -0
  55. package/dist/zonemap.d.ts +35 -0
  56. package/dist/zonemap.d.ts.map +1 -0
  57. package/dist/zonemap.js +139 -0
  58. package/dist/zonemap.js.map +1 -0
  59. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Emil Elgaard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # zonemapdb
2
+
3
+ Query large datasets from any static host: no backend, no WASM, no HTTP Range requests.
4
+
5
+ This is the **runtime** package: a zero-third-party-dependency, ESM-only browser client that fetches a manifest and the handful of small block/index files a query actually needs, and returns fully-typed records. It has no `bin` and does no building — pair it with [`zonemapdb-cli`](https://www.npmjs.com/package/zonemapdb-cli) (a devDependency) to partition your data and generate the typed client this package powers.
6
+
7
+ See the [project README](https://github.com/shivan2418/blockdb#readme) for the full pitch, design, and alternatives comparison.
8
+
9
+ ## Quickstart
10
+
11
+ ```bash
12
+ pnpm add zonemapdb && pnpm add -D zonemapdb-cli
13
+ npx zonemapdb init data/movies.ndjson # a guided wizard reads your data, recommends
14
+ # what to index, and writes zonemapdb.config.json
15
+ npx zonemapdb build # → public/zonemapdb/ (deploy this) + src/zonemapdb/ (commit this)
16
+ ```
17
+
18
+ ```ts
19
+ import { connect } from "./zonemapdb/client";
20
+
21
+ const db = connect();
22
+ const { records, hasMore } = await db.movies.findMany({
23
+ where: { year: { gte: 2000 }, rating: { gt: 8 } },
24
+ orderBy: { rating: "desc" },
25
+ limit: 20,
26
+ });
27
+ ```
28
+
29
+ `db.<collection>` is a real, named member with go-to-definition and intellisense on both the field and its available operators — the type system offers exactly the operators each field's type allows, and rejects a query none of whose filters can narrow which files are read (see [Riders](https://github.com/shivan2418/blockdb/blob/master/docs/query-guide.md#riders-filters-that-dont-narrow-the-read)). See [`examples/`](https://github.com/shivan2418/blockdb/tree/master/examples) in the repo for two complete, working example apps (movie catalog, product lookup) that build → deploy → query in a real browser.
30
+
31
+ ## Querying
32
+
33
+ The full reference, with every operator, sorting, pagination, counting, errors and what each query costs, is the **[query guide](https://github.com/shivan2418/blockdb/blob/master/docs/query-guide.md)**. Two things worth knowing up front:
34
+
35
+ ### List fields
36
+
37
+ A multi-valued field (`"multi": true`) takes list operators instead of scalar ones:
38
+
39
+ ```ts
40
+ await db.books.findMany({ where: { tags: { some: "poetry" } } }); // any tag is poetry
41
+ await db.books.findMany({ where: { tags: { hasEvery: ["poetry", "travel"] } } }); // has both
42
+ await db.books.findMany({ where: { tags: { every: { in: ["poetry", "travel"] } } } }); // no other tags; [] passes
43
+ await db.books.findMany({ where: { tags: { isEmpty: true } } }); // []
44
+ // Operators on one field AND together, so exactly [poetry, travel] is:
45
+ await db.books.findMany({ where: { tags: { hasEvery: ["poetry", "travel"], every: { in: ["poetry", "travel"] } } } });
46
+ ```
47
+
48
+ A record whose list is missing or `null` matches none of them, including `isEmpty`.
49
+
50
+ ### Case-insensitive search: fold at build time
51
+
52
+ Every string operator compares **exactly**, against an index built from the stored values, so on Title Case data `contains: "atlas"` finds nothing. Folding only the query can't fix that. Instead, add a **derived field** that the build computes with the `fold` normalizer (lowercase, accents stripped):
53
+
54
+ ```json
55
+ "title_fold": { "kind": "string", "indexed": true, "contains": true, "derive": { "from": "title", "using": "fold" } }
56
+ ```
57
+
58
+ Then fold the query the same way with the exported `normalize`, which is the same function the build uses:
59
+
60
+ ```ts
61
+ import { normalize } from "zonemapdb";
62
+
63
+ const q = normalize("fold", input) ?? "";
64
+ await db.books.findMany({ where: { title_fold: { contains: q } } }); // "cafe" finds "Café Atlas"
65
+ ```
66
+
67
+ `title` itself is untouched, so you still display it, sort by it and match it exactly. The other normalizers are `lowercase`, `trim` and `numeric`.
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,11 @@
1
+ import { type Compression } from "./types.js";
2
+ /**
3
+ * The served path of a block file, given the TOTAL block count (`manifest.blocks.length`) and
4
+ * whether the deploy gzips block payloads (`manifest.dataset.gzip`). Exported (rather than kept
5
+ * module-private) solely so the CLI's own test suite can assert this stays byte-for-byte
6
+ * identical to `block.ts`'s build-side `blockRelPath` — the two are independent implementations
7
+ * with no shared module, so an equivalence test is the only thing that catches drift between them.
8
+ */
9
+ export declare function blockRelPath(hash: string, blockCount: number, compression: Compression): string;
10
+ export declare function fetchBlockRecords(basePath: string, hash: string, blockCount: number, compression: Compression, fetchImpl: typeof fetch, signal?: AbortSignal): Promise<Record<string, unknown>[]>;
11
+ //# sourceMappingURL=block-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-fetch.d.ts","sourceRoot":"","sources":["../src/block-fetch.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAMtF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,CAG/F;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,OAAO,KAAK,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAcpC"}
@@ -0,0 +1,29 @@
1
+ import { fetchCompressedText, fetchText, parseCorruptible } from "./fetch-file.js";
2
+ import { compressionSuffix, decompressionFormat } from "./types.js";
3
+ /** Past this many blocks, files nest under a 2-hex-char prefix subdir (ADR-0002 §8) — must match `blockRelPath` in the CLI's `block.ts` exactly, since no manifest field records which layout a deploy used. */
4
+ const HASH_PREFIX_THRESHOLD = 1000;
5
+ const HASH_PREFIX_LEN = 2;
6
+ /**
7
+ * The served path of a block file, given the TOTAL block count (`manifest.blocks.length`) and
8
+ * whether the deploy gzips block payloads (`manifest.dataset.gzip`). Exported (rather than kept
9
+ * module-private) solely so the CLI's own test suite can assert this stays byte-for-byte
10
+ * identical to `block.ts`'s build-side `blockRelPath` — the two are independent implementations
11
+ * with no shared module, so an equivalence test is the only thing that catches drift between them.
12
+ */
13
+ export function blockRelPath(hash, blockCount, compression) {
14
+ const filename = `${hash}.ndjson${compressionSuffix(compression)}`;
15
+ return blockCount > HASH_PREFIX_THRESHOLD ? `blocks/${hash.slice(0, HASH_PREFIX_LEN)}/${filename}` : `blocks/${filename}`;
16
+ }
17
+ export async function fetchBlockRecords(basePath, hash, blockCount, compression, fetchImpl, signal) {
18
+ const url = `${basePath}/${blockRelPath(hash, blockCount, compression)}`;
19
+ const format = decompressionFormat(compression);
20
+ const text = format === undefined
21
+ ? await fetchText(url, "referenced", fetchImpl, signal)
22
+ : await fetchCompressedText(url, "referenced", format, fetchImpl, signal);
23
+ return parseCorruptible(url, () => text
24
+ .split("\n")
25
+ .map((line) => line.trim())
26
+ .filter((line) => line.length > 0)
27
+ .map((line) => JSON.parse(line)));
28
+ }
29
+ //# sourceMappingURL=block-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-fetch.js","sourceRoot":"","sources":["../src/block-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAoB,MAAM,YAAY,CAAC;AAEtF,gNAAgN;AAChN,MAAM,qBAAqB,GAAG,IAAI,CAAC;AACnC,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,UAAkB,EAAE,WAAwB;IACrF,MAAM,QAAQ,GAAG,GAAG,IAAI,UAAU,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;IACnE,OAAO,UAAU,GAAG,qBAAqB,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,UAAU,QAAQ,EAAE,CAAC;AAC5H,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,IAAY,EACZ,UAAkB,EAClB,WAAwB,EACxB,SAAuB,EACvB,MAAoB;IAEpB,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC;IACzE,MAAM,MAAM,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,IAAI,GACR,MAAM,KAAK,SAAS;QAClB,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC;QACvD,CAAC,CAAC,MAAM,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC9E,OAAO,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,CAChC,IAAI;SACD,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC,CAC9D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type ClientOptions, type GenericClient, type SchemaMeta } from "./types.js";
2
+ export declare function createClient<S extends SchemaMeta, Records>(schema: S, opts: ClientOptions): GenericClient<S, Records>;
3
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,aAAa,EAIlB,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAqkBpB,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EACxD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,aAAa,GAClB,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAyH3B"}