@libredb/libredb 0.0.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.
@@ -0,0 +1,44 @@
1
+ /**
2
+ * query/range.ts — the minimal query surface for the key-value lens.
3
+ *
4
+ * This is an edge, not the kernel (DESIGN.md section 5): the open,
5
+ * fast-contribution tier, holding no durability logic. It is deliberately thin
6
+ * — one inspectable value and the single piece of query logic the KV lens lacks.
7
+ *
8
+ * The kernel's native query is a range scan over its ordered keyspace, and the
9
+ * lens already exposes an explicit `[start, end)`. The one ordered-KV query that
10
+ * is both ubiquitous and easy to get wrong is the PREFIX scan ("every key under
11
+ * `user:`"). Its only subtlety is the exclusive upper bound, and the naive
12
+ * string trick (`prefix + "￿"`) is unsound here: the kernel sorts by
13
+ * unsigned BYTE order, not UTF-16 code units, and the two disagree above U+FFFF.
14
+ * So this module computes the bound on raw bytes, where it agrees with the
15
+ * kernel by construction. Working in bytes also keeps the surface shared: the
16
+ * document and relational lenses plan ranges over the same byte keyspace.
17
+ *
18
+ * A {@link KeyRange} is a plain value, not a builder: the query is visible
19
+ * (DESIGN.md principle 2 — query visible by default), so a caller can inspect
20
+ * the exact bounds a scan will use before running it.
21
+ */
22
+ /**
23
+ * A half-open key range `[start, end)`: `start` is included, `end` is excluded
24
+ * — the exact contract of the kernel's
25
+ * {@link import("../core.ts").Transaction.getRange}. Bounds are raw bytes, the
26
+ * unit the kernel orders on.
27
+ */
28
+ export interface KeyRange {
29
+ readonly start: Uint8Array;
30
+ readonly end: Uint8Array;
31
+ }
32
+ /**
33
+ * The half-open byte range that selects exactly the keys beginning with
34
+ * `prefix`: `[prefix, upperBound(prefix))`. The start is the prefix itself; the
35
+ * end is the smallest key that no longer carries the prefix (see
36
+ * {@link upperBound}).
37
+ *
38
+ * Throws if `prefix` has no finite upper bound — it is empty, or every byte is
39
+ * already `0xFF`. In that case no `end` key can exclude the next sibling, so a
40
+ * half-open range cannot express the scan; rejecting it loudly is honest
41
+ * (DESIGN.md principle 2) where silently scanning the wrong range would not be.
42
+ */
43
+ export declare function prefixRange(prefix: Uint8Array): KeyRange;
44
+ //# sourceMappingURL=range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range.d.ts","sourceRoot":"","sources":["../../src/query/range.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAMxD"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * query/range.ts — the minimal query surface for the key-value lens.
3
+ *
4
+ * This is an edge, not the kernel (DESIGN.md section 5): the open,
5
+ * fast-contribution tier, holding no durability logic. It is deliberately thin
6
+ * — one inspectable value and the single piece of query logic the KV lens lacks.
7
+ *
8
+ * The kernel's native query is a range scan over its ordered keyspace, and the
9
+ * lens already exposes an explicit `[start, end)`. The one ordered-KV query that
10
+ * is both ubiquitous and easy to get wrong is the PREFIX scan ("every key under
11
+ * `user:`"). Its only subtlety is the exclusive upper bound, and the naive
12
+ * string trick (`prefix + "￿"`) is unsound here: the kernel sorts by
13
+ * unsigned BYTE order, not UTF-16 code units, and the two disagree above U+FFFF.
14
+ * So this module computes the bound on raw bytes, where it agrees with the
15
+ * kernel by construction. Working in bytes also keeps the surface shared: the
16
+ * document and relational lenses plan ranges over the same byte keyspace.
17
+ *
18
+ * A {@link KeyRange} is a plain value, not a builder: the query is visible
19
+ * (DESIGN.md principle 2 — query visible by default), so a caller can inspect
20
+ * the exact bounds a scan will use before running it.
21
+ */
22
+ /**
23
+ * The half-open byte range that selects exactly the keys beginning with
24
+ * `prefix`: `[prefix, upperBound(prefix))`. The start is the prefix itself; the
25
+ * end is the smallest key that no longer carries the prefix (see
26
+ * {@link upperBound}).
27
+ *
28
+ * Throws if `prefix` has no finite upper bound — it is empty, or every byte is
29
+ * already `0xFF`. In that case no `end` key can exclude the next sibling, so a
30
+ * half-open range cannot express the scan; rejecting it loudly is honest
31
+ * (DESIGN.md principle 2) where silently scanning the wrong range would not be.
32
+ */
33
+ export function prefixRange(prefix) {
34
+ const end = upperBound(prefix);
35
+ if (end === undefined) {
36
+ throw new Error("libredb: prefix has no finite upper bound (empty or all-0xFF)");
37
+ }
38
+ return { start: prefix, end };
39
+ }
40
+ /**
41
+ * The smallest key strictly greater than every key beginning with `prefix`:
42
+ * drop the trailing `0xFF` bytes (they are already maximal and cannot be
43
+ * incremented), then add one to the last remaining byte. Returns `undefined`
44
+ * when no such key exists — the prefix is empty, or all of its bytes are `0xFF`.
45
+ *
46
+ * Computed on raw bytes so the result agrees with the kernel's unsigned
47
+ * byte-lexicographic order. A dropped trailing `0xFF` is why the bound can be
48
+ * shorter than the prefix (e.g. `[0x61, 0xFF]` yields `[0x62]`).
49
+ */
50
+ function upperBound(prefix) {
51
+ let length = prefix.length;
52
+ while (length > 0 && prefix[length - 1] === 0xff)
53
+ length--;
54
+ if (length === 0)
55
+ return undefined;
56
+ const bound = prefix.slice(0, length);
57
+ bound[length - 1] = bound[length - 1] + 1;
58
+ return bound;
59
+ }
60
+ //# sourceMappingURL=range.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range.js","sourceRoot":"","sources":["../../src/query/range.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,MAAkB;IAC5C,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,MAAkB;IACpC,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3B,OAAO,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI;QAAE,MAAM,EAAE,CAAC;IAC3D,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAY,GAAG,CAAC,CAAC;IACtD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@libredb/libredb",
3
+ "version": "0.0.1",
4
+ "description": "A small, readable, embeddable, multi-model database. One ordered key-value core, thin model lenses on top.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ }
21
+ },
22
+ "engines": {
23
+ "bun": ">=1.3.0"
24
+ },
25
+ "scripts": {
26
+ "typecheck": "tsc --noEmit",
27
+ "lint": "eslint .",
28
+ "test": "bun test --coverage",
29
+ "build": "tsc --project tsconfig.build.json",
30
+ "gate": "bun run typecheck && bun run lint && bun run test && bun run build"
31
+ },
32
+ "devDependencies": {
33
+ "@eslint/js": "^9.0.0",
34
+ "@types/bun": "latest",
35
+ "eslint": "^9.0.0",
36
+ "typescript": "^5.4.0",
37
+ "typescript-eslint": "^8.0.0"
38
+ }
39
+ }