keri-ts 2.0.0 → 2.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/esm/keri/npm/src/acdc/chaining.js +223 -89
- package/esm/keri/npm/src/acdc/observing.js +2 -2
- package/esm/keri/npm/src/acdc/regbasing.js +20 -29
- package/esm/keri/npm/src/acdc/regengine.js +4 -6
- package/esm/keri/npm/src/acdc/registering.js +13 -10
- package/esm/keri/npm/src/acdc/registraring.js +4 -9
- package/esm/keri/npm/src/acdc/salting.js +127 -25
- package/esm/keri/npm/src/acdc/unblinding.js +2 -4
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +1 -1
- package/esm/keri/npm/src/app/credentials/root-replicating.js +123 -0
- package/esm/keri/npm/src/app/credentials/verifying.js +21 -3
- package/esm/keri/npm/src/app/delivery/forwarding.js +49 -7
- package/esm/keri/npm/src/app/exchanges/exchanging.js +11 -12
- package/esm/keri/npm/src/app/identity/configing.js +17 -43
- package/esm/keri/npm/src/app/identity/habbing.js +54 -35
- package/esm/keri/npm/src/app/identity/keeping.js +1 -1
- package/esm/keri/npm/src/app/identity/lmdb-habery.js +12 -0
- package/esm/keri/npm/src/app/ipex/ipex-graph.js +7 -70
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +8 -5
- package/esm/keri/npm/src/app/ipex/ipex-v2.js +21 -5
- package/esm/keri/npm/src/app/ipex/ipexing.js +1 -1
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +5 -9
- package/esm/keri/npm/src/app/multisig/group-members.js +14 -0
- package/esm/keri/npm/src/app/multisig/grouping.js +4 -3
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +7 -2
- package/esm/keri/npm/src/app/notifications/notifying.js +5 -9
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +35 -3
- package/esm/keri/npm/src/app/queries/witness-inquisitor.js +2 -2
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +7 -10
- package/esm/keri/npm/src/app/transport/byte-source.js +6 -42
- package/esm/keri/npm/src/app/transport/cesr-egress.js +77 -13
- package/esm/keri/npm/src/app/transport/file-bytes.js +41 -0
- package/esm/keri/npm/src/app/transport/http-messenger.js +14 -9
- package/esm/keri/npm/src/app/transport/message-source.js +1 -1
- package/esm/keri/npm/src/app/transport/messengering.js +3 -4
- package/esm/keri/npm/src/app/transport/node-http-client.js +21 -0
- package/esm/keri/npm/src/app/transport/tcp-dialer.js +14 -0
- package/esm/keri/npm/src/app/witness/receipt-client.js +2 -2
- package/esm/keri/npm/src/cli/common/context.js +2 -2
- package/esm/keri/npm/src/cli/common/existing.js +3 -5
- package/esm/keri/npm/src/cli/credentials/ipex.js +2 -1
- package/esm/keri/npm/src/cli/endpoints/oobi-clean.js +4 -3
- package/esm/keri/npm/src/cli/endpoints/oobi.js +2 -2
- package/esm/keri/npm/src/cli/identity/challenge.js +2 -2
- package/esm/keri/npm/src/cli/identity/incept.js +2 -2
- package/esm/keri/npm/src/cli/identity/init.js +4 -3
- package/esm/keri/npm/src/cli/identity/interact.js +2 -2
- package/esm/keri/npm/src/cli/identity/rotate.js +2 -2
- package/esm/keri/npm/src/cli/messaging/exchange.js +2 -2
- package/esm/keri/npm/src/cli/messaging/multisig.js +61 -3
- package/esm/keri/npm/src/cli/tools/annotate.js +2 -1
- package/esm/keri/npm/src/cli/tools/benchmark.js +2 -1
- package/esm/keri/npm/src/cli/tools/db-dump.js +1 -4
- package/esm/keri/npm/src/core/bytes.js +9 -0
- package/esm/keri/npm/src/core/kel/delegation-ancestry.js +92 -0
- package/esm/keri/npm/src/core/kel/eventing.js +1 -2
- package/esm/keri/npm/src/core/kel/kever.js +11 -9
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +2 -3
- package/esm/keri/npm/src/core/mailbox/kram-pruning.js +15 -22
- package/esm/keri/npm/src/core/mailbox/kram-staging.js +2 -2
- package/esm/keri/npm/src/core/routing.js +2 -2
- package/esm/keri/npm/src/db/basing.js +149 -203
- package/esm/keri/npm/src/db/core/dber.js +1062 -0
- package/esm/keri/npm/src/db/core/index.js +4 -2
- package/esm/keri/npm/src/db/core/lmdb-opener.js +30 -0
- package/esm/keri/npm/src/db/core/lmdber.js +199 -1067
- package/esm/keri/npm/src/db/core/memstore.js +532 -0
- package/esm/keri/npm/src/db/core/path-manager.js +109 -223
- package/esm/keri/npm/src/db/core/store-owner.js +104 -0
- package/esm/keri/npm/src/db/core/webdber.js +252 -0
- package/esm/keri/npm/src/db/escrowing.js +2 -2
- package/esm/keri/npm/src/db/exchange/exchange-nests.js +3 -2
- package/esm/keri/npm/src/db/keeping.js +27 -121
- package/esm/keri/npm/src/db/koming.js +1 -1
- package/esm/keri/npm/src/db/lmdb-stores.js +68 -0
- package/esm/keri/npm/src/db/lmdb.js +6 -0
- package/esm/keri/npm/src/db/mailbox/mailboxing.js +12 -65
- package/esm/keri/npm/src/db/noting.js +11 -55
- package/esm/keri/npm/src/db/reger.js +43 -105
- package/esm/keri/npm/src/db/replay/witness-replay.js +2 -20
- package/esm/keri/npm/src/db/subing.js +1 -1
- package/esm/keri/npm/src/npm/db-lmdb.js +3 -0
- package/esm/keri/npm/src/npm/runtime-node.js +3 -0
- package/esm/keri/npm/src/runtime/index.js +9 -10
- package/esm/keri/npm/src/runtime/node.js +14 -0
- package/esm/keri/npm/src/vdr/eventing.js +1 -1
- package/esm/keri/npm/src/vdr/tel-publication.js +3 -3
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +11 -4
- package/types/acdc/chaining.d.ts +56 -6
- package/types/acdc/chaining.d.ts.map +1 -1
- package/types/acdc/regbasing.d.ts +7 -6
- package/types/acdc/regbasing.d.ts.map +1 -1
- package/types/acdc/regengine.d.ts.map +1 -1
- package/types/acdc/registering.d.ts +8 -4
- package/types/acdc/registering.d.ts.map +1 -1
- package/types/acdc/registraring.d.ts.map +1 -1
- package/types/acdc/salting.d.ts +53 -6
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/credentials/root-replicating.d.ts +85 -0
- package/types/app/credentials/root-replicating.d.ts.map +1 -0
- package/types/app/credentials/verifying.d.ts +2 -1
- package/types/app/credentials/verifying.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +10 -0
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts +2 -0
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/configing.d.ts +7 -5
- package/types/app/identity/configing.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +23 -5
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +1 -1
- package/types/app/identity/lmdb-habery.d.ts +6 -0
- package/types/app/identity/lmdb-habery.d.ts.map +1 -0
- package/types/app/ipex/ipex-graph.d.ts +4 -4
- package/types/app/ipex/ipex-graph.d.ts.map +1 -1
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/ipex-v2.d.ts +11 -2
- package/types/app/ipex/ipex-v2.d.ts.map +1 -1
- package/types/app/ipex/ipexing.d.ts +3 -0
- package/types/app/ipex/ipexing.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +1 -1
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/group-members.d.ts +9 -0
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +2 -1
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/notifications/notifying.d.ts +1 -1
- package/types/app/notifications/notifying.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +3 -1
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +2 -0
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/runtime/agent-runtime.d.ts +7 -0
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/transport/byte-source.d.ts +13 -10
- package/types/app/transport/byte-source.d.ts.map +1 -1
- package/types/app/transport/cesr-egress.d.ts +21 -7
- package/types/app/transport/cesr-egress.d.ts.map +1 -1
- package/types/app/transport/file-bytes.d.ts +11 -0
- package/types/app/transport/file-bytes.d.ts.map +1 -0
- package/types/app/transport/http-client.d.ts +3 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/app/transport/http-messenger.d.ts +15 -10
- package/types/app/transport/http-messenger.d.ts.map +1 -1
- package/types/app/transport/message-source.d.ts +1 -1
- package/types/app/transport/messengering.d.ts +2 -2
- package/types/app/transport/messengering.d.ts.map +1 -1
- package/types/app/transport/node-http-client.d.ts +10 -0
- package/types/app/transport/node-http-client.d.ts.map +1 -0
- package/types/app/transport/tcp-dialer.d.ts +21 -0
- package/types/app/transport/tcp-dialer.d.ts.map +1 -0
- package/types/cli/common/existing.d.ts.map +1 -1
- package/types/cli/credentials/ipex.d.ts.map +1 -1
- package/types/cli/endpoints/oobi-clean.d.ts.map +1 -1
- package/types/cli/identity/init.d.ts.map +1 -1
- package/types/cli/index.d.ts +1 -1
- package/types/cli/index.d.ts.map +1 -1
- package/types/cli/messaging/multisig.d.ts +10 -1
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/cli/tools/annotate.d.ts.map +1 -1
- package/types/cli/tools/benchmark.d.ts.map +1 -1
- package/types/cli/tools/db-dump.d.ts.map +1 -1
- package/types/core/bytes.d.ts +2 -0
- package/types/core/bytes.d.ts.map +1 -1
- package/types/core/kel/delegation-ancestry.d.ts +41 -0
- package/types/core/kel/delegation-ancestry.d.ts.map +1 -0
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/kel/kever.d.ts +5 -4
- package/types/core/kel/kever.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kram-pruning.d.ts +1 -3
- package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
- package/types/db/basing.d.ts +14 -23
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/dber.d.ts +505 -0
- package/types/db/core/dber.d.ts.map +1 -0
- package/types/db/core/index.d.ts +4 -2
- package/types/db/core/index.d.ts.map +1 -1
- package/types/db/core/lmdb-opener.d.ts +17 -0
- package/types/db/core/lmdb-opener.d.ts.map +1 -0
- package/types/db/core/lmdber.d.ts +60 -383
- package/types/db/core/lmdber.d.ts.map +1 -1
- package/types/db/core/memstore.d.ts +129 -0
- package/types/db/core/memstore.d.ts.map +1 -0
- package/types/db/core/path-manager.d.ts +22 -45
- package/types/db/core/path-manager.d.ts.map +1 -1
- package/types/db/core/store-owner.d.ts +59 -0
- package/types/db/core/store-owner.d.ts.map +1 -0
- package/types/db/core/webdber.d.ts +127 -0
- package/types/db/core/webdber.d.ts.map +1 -0
- package/types/db/escrowing.d.ts +3 -3
- package/types/db/escrowing.d.ts.map +1 -1
- package/types/db/exchange/exchange-nests.d.ts.map +1 -1
- package/types/db/keeping.d.ts +9 -23
- package/types/db/keeping.d.ts.map +1 -1
- package/types/db/koming.d.ts +7 -8
- package/types/db/koming.d.ts.map +1 -1
- package/types/db/lmdb-stores.d.ts +45 -0
- package/types/db/lmdb-stores.d.ts.map +1 -0
- package/types/db/lmdb.d.ts +7 -0
- package/types/db/lmdb.d.ts.map +1 -0
- package/types/db/mailbox/mailboxing.d.ts +7 -21
- package/types/db/mailbox/mailboxing.d.ts.map +1 -1
- package/types/db/noting.d.ts +7 -12
- package/types/db/noting.d.ts.map +1 -1
- package/types/db/reger.d.ts +7 -11
- package/types/db/reger.d.ts.map +1 -1
- package/types/db/replay/witness-replay.d.ts +0 -2
- package/types/db/replay/witness-replay.d.ts.map +1 -1
- package/types/db/subing.d.ts +24 -25
- package/types/db/subing.d.ts.map +1 -1
- package/types/npm/db-lmdb.d.ts +4 -0
- package/types/npm/db-lmdb.d.ts.map +1 -0
- package/types/npm/runtime-node.d.ts +4 -0
- package/types/npm/runtime-node.d.ts.map +1 -0
- package/types/runtime/index.d.ts +9 -10
- package/types/runtime/index.d.ts.map +1 -1
- package/types/runtime/node.d.ts +15 -0
- package/types/runtime/node.d.ts.map +1 -0
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/acdc/relating.js +0 -59
- package/esm/keri/npm/src/db/baser-defaults.js +0 -11
- package/types/acdc/relating.d.ts +0 -21
- package/types/acdc/relating.d.ts.map +0 -1
- package/types/db/baser-defaults.d.ts +0 -5
- package/types/db/baser-defaults.d.ts.map +0 -1
|
@@ -2,46 +2,60 @@
|
|
|
2
2
|
* Core LMDB manager used by higher-level DB abstractions.
|
|
3
3
|
*/
|
|
4
4
|
import * as dntShim from "../../_dnt.shims.js";
|
|
5
|
-
import { b,
|
|
5
|
+
import { b, bytesHex, t, toBytes } from "cesr-ts";
|
|
6
6
|
import { call, ensure } from "effection";
|
|
7
7
|
import { open } from "lmdb";
|
|
8
|
-
import { startsWith } from "../../core/bytes.js";
|
|
8
|
+
import { compareBytes, startsWith } from "../../core/bytes.js";
|
|
9
9
|
import { DatabaseKeyError, DatabaseNotOpenError, DatabaseOperationError } from "../../core/errors.js";
|
|
10
10
|
import { consoleLogger } from "../../core/logger.js";
|
|
11
|
-
import {
|
|
11
|
+
import { BaseDBer, MAX_KEY_BYTES } from "./dber.js";
|
|
12
12
|
import { PathManager } from "./path-manager.js";
|
|
13
|
-
/** Default separator used by ordinal/suffix key helpers (`onKey`, `suffix`). */
|
|
14
|
-
const DOT_SEP = b(".");
|
|
15
13
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Reach the native database behind a handle this backend issued.
|
|
15
|
+
*
|
|
16
|
+
* Only LMDB-specific code may use this; backend-neutral code must stay on the
|
|
17
|
+
* `DBer` contract.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
export function lmdbTable(table) {
|
|
20
|
+
return table;
|
|
21
|
+
}
|
|
22
|
+
/** Clone native LMDB bytes so callers never retain addon-owned buffer views. */
|
|
23
|
+
function copyDbBytes(value) {
|
|
24
|
+
return new Uint8Array(toBytes(value));
|
|
25
|
+
}
|
|
26
|
+
/** Reject a row ADR-0015 forbids with `DatabaseKeyError`, as every backend does, rather than a raw native error. */
|
|
27
|
+
function assertRowFits(table, key, val) {
|
|
28
|
+
if (key.length === 0 || key.length > MAX_KEY_BYTES) {
|
|
29
|
+
throw new DatabaseKeyError(`Key is empty, too big, or wrong size: ${key.length} bytes`);
|
|
30
|
+
}
|
|
31
|
+
if (lmdbTable(table).dupSort && (val.length === 0 || val.length > MAX_KEY_BYTES)) {
|
|
32
|
+
throw new DatabaseKeyError(`Duplicate value is empty or larger than ${MAX_KEY_BYTES} bytes: ${val.length} bytes`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
20
35
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
36
|
+
* A key no row can have. lmdb-js reads an empty one as the table's first key, and a
|
|
37
|
+
* rejected over-long one can leave a read transaction open, so duplicate reads skip both.
|
|
23
38
|
*/
|
|
24
|
-
|
|
39
|
+
function isAbsentKey(key) {
|
|
40
|
+
return key.length === 0 || key.length > MAX_KEY_BYTES;
|
|
41
|
+
}
|
|
25
42
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
43
|
+
* Build the lmdb-js range for a scan. lmdb-js writes into the options it is given
|
|
44
|
+
* (`getKeys` sets `values: false`), so the caller's object must never be passed:
|
|
45
|
+
* reusing it for a later `scan` would yield rows with no key or value. It also
|
|
46
|
+
* refuses an `end` over the key limit, which is applied by comparison instead.
|
|
28
47
|
*/
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
seen.add(key);
|
|
38
|
-
out.push(val);
|
|
39
|
-
}
|
|
40
|
-
return out;
|
|
48
|
+
function nativeRange(options) {
|
|
49
|
+
const range = { ...options };
|
|
50
|
+
const long = range.end !== undefined && range.end.length > MAX_KEY_BYTES;
|
|
51
|
+
if (long)
|
|
52
|
+
delete range.end;
|
|
53
|
+
return { range, end: options.end, long };
|
|
41
54
|
}
|
|
42
|
-
/**
|
|
43
|
-
function
|
|
44
|
-
|
|
55
|
+
/** True once `key` reaches the exclusive `end` bound in scan direction. */
|
|
56
|
+
function pastEnd(key, end, reverse = false) {
|
|
57
|
+
const order = compareBytes(key, end);
|
|
58
|
+
return reverse ? order <= 0 : order >= 0;
|
|
45
59
|
}
|
|
46
60
|
/**
|
|
47
61
|
* Build one LMDB-js range option object for a KERIpy-style branch scan.
|
|
@@ -51,37 +65,12 @@ function copyDbBytes(value) {
|
|
|
51
65
|
* stores, but dupsort iterators reject that option, so those stay on the
|
|
52
66
|
* default path until we replace them with a lower-level cursor implementation.
|
|
53
67
|
*/
|
|
54
|
-
function branchScanOptions(
|
|
68
|
+
function branchScanOptions(table, start) {
|
|
69
|
+
const db = lmdbTable(table);
|
|
55
70
|
return db.dupSort
|
|
56
71
|
? { start }
|
|
57
72
|
: { start, snapshot: false };
|
|
58
73
|
}
|
|
59
|
-
/** Build IoDup ordering proem bytes (`000...00a.` for index `10`). */
|
|
60
|
-
function iDupProem(index) {
|
|
61
|
-
return b(`${index.toString(16).padStart(IODUP_PROEM_HEX_SIZE, "0")}.`);
|
|
62
|
-
}
|
|
63
|
-
/** Prefix value bytes with IoDup ordering proem for insertion-order semantics. */
|
|
64
|
-
function withIoDupProem(index, val) {
|
|
65
|
-
const proem = iDupProem(index);
|
|
66
|
-
const out = new Uint8Array(proem.length + val.length);
|
|
67
|
-
out.set(proem, 0);
|
|
68
|
-
out.set(val, proem.length);
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
/** Strip IoDup ordering proem from a stored duplicate value. */
|
|
72
|
-
function stripIoDupProem(val) {
|
|
73
|
-
return val.length >= IODUP_PROEM_SIZE
|
|
74
|
-
? val.slice(IODUP_PROEM_SIZE)
|
|
75
|
-
: new Uint8Array(0);
|
|
76
|
-
}
|
|
77
|
-
/** Parse IoDup insertion ordinal from proem-prefixed stored value. */
|
|
78
|
-
function parseIoDupOrdinal(val) {
|
|
79
|
-
if (val.length < IODUP_PROEM_HEX_SIZE) {
|
|
80
|
-
return 0;
|
|
81
|
-
}
|
|
82
|
-
const parsed = Number.parseInt(t(val.slice(0, IODUP_PROEM_HEX_SIZE)), 16);
|
|
83
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
84
|
-
}
|
|
85
74
|
/** Default LMDB/path settings used when callers do not override environment wiring. */
|
|
86
75
|
export const LMDBER_DEFAULTS = {
|
|
87
76
|
headDirPath: "/usr/local/var",
|
|
@@ -90,13 +79,12 @@ export const LMDBER_DEFAULTS = {
|
|
|
90
79
|
altHeadDirPath: "~",
|
|
91
80
|
altTailDirPath: ".tufa/db",
|
|
92
81
|
altCleanTailDirPath: ".tufa/clean/db",
|
|
93
|
-
tempHeadDir: "/tmp",
|
|
94
82
|
tempPrefix: "keri_lmdb_",
|
|
95
83
|
tempSuffix: "_test",
|
|
96
84
|
perm: 0o1700,
|
|
97
85
|
mode: "r+",
|
|
98
86
|
fext: "text",
|
|
99
|
-
maxNamedDBs:
|
|
87
|
+
maxNamedDBs: 128,
|
|
100
88
|
// 10 GiB default so common KERIpy deployments (e.g. KERI_BASER_MAP_SIZE=10GiB)
|
|
101
89
|
// open without an explicit CLI/env override. Existing environments still
|
|
102
90
|
// auto-raise to at least the map size stamped in `data.mdb` when larger.
|
|
@@ -208,12 +196,13 @@ const DEFAULT_DB_VERSION = "1.0.0";
|
|
|
208
196
|
* - includes TypeScript-only `OnIoSet*` helpers and a composition-based
|
|
209
197
|
* `PathManager` lifecycle instead of inheriting KERIpy's exact structure
|
|
210
198
|
*/
|
|
211
|
-
export class LMDBer {
|
|
199
|
+
export class LMDBer extends BaseDBer {
|
|
212
200
|
/** Expose only a usable environment, never one whose shutdown has started or failed. */
|
|
213
201
|
get env() {
|
|
214
202
|
return this.closing ? null : this.root;
|
|
215
203
|
}
|
|
216
204
|
constructor(options = {}, defaults) {
|
|
205
|
+
super();
|
|
217
206
|
Object.defineProperty(this, "pathManager", {
|
|
218
207
|
enumerable: true,
|
|
219
208
|
configurable: true,
|
|
@@ -276,7 +265,6 @@ export class LMDBer {
|
|
|
276
265
|
altHeadDirPath: this.defaults.altHeadDirPath,
|
|
277
266
|
altTailDirPath: this.defaults.altTailDirPath,
|
|
278
267
|
altCleanTailDirPath: this.defaults.altCleanTailDirPath,
|
|
279
|
-
tempHeadDir: this.defaults.tempHeadDir,
|
|
280
268
|
tempPrefix: this.defaults.tempPrefix,
|
|
281
269
|
tempSuffix: this.defaults.tempSuffix,
|
|
282
270
|
perm: this.defaults.perm,
|
|
@@ -444,6 +432,11 @@ export class LMDBer {
|
|
|
444
432
|
const message = error instanceof Error ? error.message : String(error);
|
|
445
433
|
throw new DatabaseOperationError(`Failed to open LMDB environment: ${message}`, { cause: error });
|
|
446
434
|
}
|
|
435
|
+
// KERIpy passes its directory permissions to `lmdb.open`, which strips execute bits.
|
|
436
|
+
if (!dbExists && dntShim.Deno.build.os !== "windows") {
|
|
437
|
+
for (const file of ["data.mdb", "lock.mdb"])
|
|
438
|
+
dntShim.Deno.chmodSync(`${dbPath}/${file}`, this.pathManager.perm & ~0o111);
|
|
439
|
+
}
|
|
447
440
|
}
|
|
448
441
|
/** True if the current env path already has a `data.mdb` file. */
|
|
449
442
|
*checkDatabaseExists() {
|
|
@@ -522,6 +515,106 @@ export class LMDBer {
|
|
|
522
515
|
throw new DatabaseOperationError(`Database not found: ${name}`);
|
|
523
516
|
return table;
|
|
524
517
|
}
|
|
518
|
+
/** Bind a named table through the backend-neutral contract. */
|
|
519
|
+
openTable(name, options = {}) {
|
|
520
|
+
return this.openDB(name, options.dupsort ?? false);
|
|
521
|
+
}
|
|
522
|
+
/** Run `fn` in one native synchronous write transaction; nesting is a child transaction. */
|
|
523
|
+
transaction(fn) {
|
|
524
|
+
return this.requireEnv().transactionSync(fn);
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Yield caller-owned copies of native rows; closing the iterator closes the cursor.
|
|
528
|
+
*
|
|
529
|
+
* Scan bounds are unvalidated (ADR-0015), including an `end` over the key limit.
|
|
530
|
+
*/
|
|
531
|
+
*scan(table, options = {}) {
|
|
532
|
+
this.requireEnv();
|
|
533
|
+
const { range, end, long } = nativeRange(options);
|
|
534
|
+
for (const entry of lmdbTable(table).getRange(range)) {
|
|
535
|
+
const key = copyDbBytes(entry.key);
|
|
536
|
+
if (long && pastEnd(key, end, options.reverse))
|
|
537
|
+
return;
|
|
538
|
+
yield { key, value: copyDbBytes(entry.value) };
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/** LMDB's committed transaction ID, which also counts other processes' commits. */
|
|
542
|
+
get writeVersion() {
|
|
543
|
+
const version = this.requireEnv().getStats().lastTxnId;
|
|
544
|
+
if (!Number.isSafeInteger(version) || version < 0) {
|
|
545
|
+
throw new DatabaseOperationError("Invalid LMDB transaction ID");
|
|
546
|
+
}
|
|
547
|
+
return version;
|
|
548
|
+
}
|
|
549
|
+
/** Join native flush completion, including when the waiter is halted. */
|
|
550
|
+
*flushed() {
|
|
551
|
+
const root = this.requireEnv();
|
|
552
|
+
const pending = Promise.resolve(root.flushed);
|
|
553
|
+
yield* call(function* () {
|
|
554
|
+
yield* ensure(() => call(() => pending));
|
|
555
|
+
yield* call(() => pending);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
/** Release the shared read transaction so later reads see other commits. */
|
|
559
|
+
refreshRead() {
|
|
560
|
+
this.requireEnv().resetReadTxn();
|
|
561
|
+
}
|
|
562
|
+
/** Yield caller-owned key copies; native dupsort iteration already yields each key once. */
|
|
563
|
+
*scanKeys(table, options = {}) {
|
|
564
|
+
this.requireEnv();
|
|
565
|
+
const { range, end, long } = nativeRange(options);
|
|
566
|
+
for (const native of lmdbTable(table).getKeys(range)) {
|
|
567
|
+
const key = copyDbBytes(native);
|
|
568
|
+
if (long && pastEnd(key, end, options.reverse))
|
|
569
|
+
return;
|
|
570
|
+
yield key;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
/** Named tables are root entries; native lmdb-js may keep its C-string terminator in the key. */
|
|
574
|
+
hasTable(name) {
|
|
575
|
+
const root = this.requireEnv();
|
|
576
|
+
return root.getBinary(b(name)) !== undefined
|
|
577
|
+
|| root.getBinary(b(`${name}\0`)) !== undefined;
|
|
578
|
+
}
|
|
579
|
+
/** Copy one duplicate through the pinned binary seek branch, closing its cursor before return. */
|
|
580
|
+
seekVal(table, key, start) {
|
|
581
|
+
this.requireEnv();
|
|
582
|
+
const db = lmdbTable(table);
|
|
583
|
+
if (db.encoding !== "binary")
|
|
584
|
+
throw new DatabaseOperationError("Duplicate seek requires binary rows");
|
|
585
|
+
if (isAbsentKey(key))
|
|
586
|
+
return null;
|
|
587
|
+
// lmdb-js refuses a start over the key limit; no stored value can reach one by seeking, so compare.
|
|
588
|
+
if (start && start.length > MAX_KEY_BYTES) {
|
|
589
|
+
for (const val of db.getValues(key)) {
|
|
590
|
+
if (compareBytes(copyDbBytes(val), start) >= 0)
|
|
591
|
+
return copyDbBytes(val);
|
|
592
|
+
}
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
// Pinned lmdb-js 3.5.3 dereferences encoder.writeKey before its binary-value seek branch.
|
|
596
|
+
// This private read facade supplies that missing object without mutating the table.
|
|
597
|
+
const seek = Object.create(db);
|
|
598
|
+
seek.encoder = db.encoder ?? {};
|
|
599
|
+
const rows = seek.getValues(key, { start, limit: 1 })[Symbol.iterator]();
|
|
600
|
+
try {
|
|
601
|
+
const row = rows.next();
|
|
602
|
+
return row.done ? null : copyDbBytes(row.value);
|
|
603
|
+
}
|
|
604
|
+
finally {
|
|
605
|
+
rows.return?.();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
assertOpen() {
|
|
609
|
+
this.requireEnv();
|
|
610
|
+
}
|
|
611
|
+
putRow(table, key, val) {
|
|
612
|
+
assertRowFits(table, key, val);
|
|
613
|
+
lmdbTable(table).putSync(key, val);
|
|
614
|
+
}
|
|
615
|
+
removeRow(table, key, val) {
|
|
616
|
+
return val === undefined ? lmdbTable(table).removeSync(key) : lmdbTable(table).removeSync(key, val);
|
|
617
|
+
}
|
|
525
618
|
/*
|
|
526
619
|
* Plain key/value family (`dupsort=false` parity surface)
|
|
527
620
|
* Logical shape: one physical key -> one value.
|
|
@@ -535,7 +628,8 @@ export class LMDBer {
|
|
|
535
628
|
* Plain key/value storage with no duplicate semantics. Returns `false` when
|
|
536
629
|
* an entry already exists at `key`. Mirrors KERIpy `putVal`.
|
|
537
630
|
*/
|
|
538
|
-
putVal(
|
|
631
|
+
putVal(table, key, val) {
|
|
632
|
+
const db = lmdbTable(table);
|
|
539
633
|
const env = this.requireEnv();
|
|
540
634
|
try {
|
|
541
635
|
const result = env.transactionSync(() => {
|
|
@@ -543,6 +637,7 @@ export class LMDBer {
|
|
|
543
637
|
if (existing !== null && existing !== undefined) {
|
|
544
638
|
return false;
|
|
545
639
|
}
|
|
640
|
+
assertRowFits(db, key, val);
|
|
546
641
|
db.put(key, val);
|
|
547
642
|
return true;
|
|
548
643
|
});
|
|
@@ -558,10 +653,12 @@ export class LMDBer {
|
|
|
558
653
|
* Plain key/value upsert. Returns `true` on successful write. Mirrors KERIpy
|
|
559
654
|
* `setVal`.
|
|
560
655
|
*/
|
|
561
|
-
setVal(
|
|
656
|
+
setVal(table, key, val) {
|
|
657
|
+
const db = lmdbTable(table);
|
|
562
658
|
const env = this.requireEnv();
|
|
563
659
|
try {
|
|
564
660
|
env.transactionSync(() => {
|
|
661
|
+
assertRowFits(db, key, val);
|
|
565
662
|
db.put(key, val);
|
|
566
663
|
});
|
|
567
664
|
return true;
|
|
@@ -576,7 +673,8 @@ export class LMDBer {
|
|
|
576
673
|
* Plain key/value lookup. Returns `null` when the key is missing. Mirrors
|
|
577
674
|
* KERIpy `getVal`.
|
|
578
675
|
*/
|
|
579
|
-
getVal(
|
|
676
|
+
getVal(table, key) {
|
|
677
|
+
const db = lmdbTable(table);
|
|
580
678
|
this.requireEnv();
|
|
581
679
|
try {
|
|
582
680
|
const val = db.get(key);
|
|
@@ -598,11 +696,18 @@ export class LMDBer {
|
|
|
598
696
|
* against a dupsort DB, this removes just that duplicate value. The plain K/V
|
|
599
697
|
* behavior mirrors KERIpy `delVal`.
|
|
600
698
|
*/
|
|
601
|
-
delVal(
|
|
699
|
+
delVal(table, key, val) {
|
|
700
|
+
const db = lmdbTable(table);
|
|
602
701
|
const env = this.requireEnv();
|
|
603
702
|
try {
|
|
604
703
|
const result = env.transactionSync(() => {
|
|
605
704
|
if (val !== undefined) {
|
|
705
|
+
// An empty key must still fail, but an over-long one can only be absent.
|
|
706
|
+
if (key.length > MAX_KEY_BYTES)
|
|
707
|
+
return false;
|
|
708
|
+
// An empty duplicate can never be stored, so it is always absent.
|
|
709
|
+
if (val.length === 0 && db.dupSort)
|
|
710
|
+
return false;
|
|
606
711
|
return db.removeSync(key, val);
|
|
607
712
|
}
|
|
608
713
|
const exists = db.get(key) !== null && db.get(key) !== undefined;
|
|
@@ -623,7 +728,8 @@ export class LMDBer {
|
|
|
623
728
|
* For dupsort DBs, duplicate values are included in the count because LMDB
|
|
624
729
|
* stores them as separate entries. Mirrors KERIpy `cnt`.
|
|
625
730
|
*/
|
|
626
|
-
cnt(
|
|
731
|
+
cnt(table) {
|
|
732
|
+
const db = lmdbTable(table);
|
|
627
733
|
this.requireEnv();
|
|
628
734
|
try {
|
|
629
735
|
let count = 0;
|
|
@@ -652,7 +758,8 @@ export class LMDBer {
|
|
|
652
758
|
* included. TypeScript-only convenience alongside KERIpy `getTopItemIter`
|
|
653
759
|
* and `delTopVal`.
|
|
654
760
|
*/
|
|
655
|
-
cntTop(
|
|
761
|
+
cntTop(table, top = new Uint8Array(0)) {
|
|
762
|
+
const db = lmdbTable(table);
|
|
656
763
|
this.requireEnv();
|
|
657
764
|
try {
|
|
658
765
|
let count = 0;
|
|
@@ -676,7 +783,8 @@ export class LMDBer {
|
|
|
676
783
|
*
|
|
677
784
|
* This counts the whole DB, including duplicate entries in dupsort DBs.
|
|
678
785
|
*/
|
|
679
|
-
cntAll(
|
|
786
|
+
cntAll(table) {
|
|
787
|
+
const db = lmdbTable(table);
|
|
680
788
|
return this.cnt(db);
|
|
681
789
|
}
|
|
682
790
|
/**
|
|
@@ -685,7 +793,8 @@ export class LMDBer {
|
|
|
685
793
|
* This is a lexicographic key-prefix delete, not a separate storage family.
|
|
686
794
|
* Empty `top` deletes the whole DB. Mirrors KERIpy `delTopVal`.
|
|
687
795
|
*/
|
|
688
|
-
delTop(
|
|
796
|
+
delTop(table, top = new Uint8Array(0)) {
|
|
797
|
+
const db = lmdbTable(table);
|
|
689
798
|
const env = this.requireEnv();
|
|
690
799
|
try {
|
|
691
800
|
const keys = [];
|
|
@@ -719,7 +828,8 @@ export class LMDBer {
|
|
|
719
828
|
* iterates the whole DB. Returned keys are the full stored keys, not trimmed
|
|
720
829
|
* branch-relative suffixes. Mirrors KERIpy `getTopItemIter`.
|
|
721
830
|
*/
|
|
722
|
-
*getTopItemIter(
|
|
831
|
+
*getTopItemIter(table, top = new Uint8Array(0)) {
|
|
832
|
+
const db = lmdbTable(table);
|
|
723
833
|
this.requireEnv();
|
|
724
834
|
try {
|
|
725
835
|
// Use getRange with start position at top key
|
|
@@ -741,726 +851,6 @@ export class LMDBer {
|
|
|
741
851
|
throw new DatabaseOperationError(`Failed to iterate database branch: ${message}`, { cause: error });
|
|
742
852
|
}
|
|
743
853
|
}
|
|
744
|
-
/*
|
|
745
|
-
* Ordinal-key family (`On*`)
|
|
746
|
-
* Logical shape: one logical key -> many values via fixed-width ordinal in key.
|
|
747
|
-
* Multiplicity: synthetic, stored in keyspace as `key.<on>`.
|
|
748
|
-
* Ordering: lexicographic key order matches numeric ordinal order.
|
|
749
|
-
* Parity status: direct KERIpy analogs plus TS rename conveniences.
|
|
750
|
-
*/
|
|
751
|
-
/**
|
|
752
|
-
* Write `val` at the ordinal key `onKey(key, on)` only when absent.
|
|
753
|
-
*
|
|
754
|
-
* The ordinal is encoded into the physical key as a fixed-width 32-hex suffix
|
|
755
|
-
* so LMDB key order matches ordinal order. Mirrors KERIpy `putOnVal`.
|
|
756
|
-
*/
|
|
757
|
-
putOnVal(db, key, on = 0, val = null, sep = DOT_SEP) {
|
|
758
|
-
if (val === null) {
|
|
759
|
-
return false;
|
|
760
|
-
}
|
|
761
|
-
return this.putVal(db, onKey(key, on, sep), val);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Write `val` at `onKey(key, on)`, overwriting any existing value.
|
|
765
|
-
*
|
|
766
|
-
* TypeScript-local rename of KERIpy `setOnVal`.
|
|
767
|
-
*/
|
|
768
|
-
pinOnVal(db, key, on = 0, val = null, sep = DOT_SEP) {
|
|
769
|
-
if (!key.length || val === null) {
|
|
770
|
-
return false;
|
|
771
|
-
}
|
|
772
|
-
return this.setVal(db, onKey(key, on, sep), val);
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Append `val` at the next ordinal slot for `key`.
|
|
776
|
-
*
|
|
777
|
-
* This scans backward from the `key.MaxON` boundary to find the current tail,
|
|
778
|
-
* then writes at the next ordinal. Returns the appended ordinal. Mirrors
|
|
779
|
-
* KERIpy `appendOnVal`.
|
|
780
|
-
*/
|
|
781
|
-
appendOnVal(db, key, val, sep = DOT_SEP) {
|
|
782
|
-
this.requireEnv();
|
|
783
|
-
if (!key.length || val === null) {
|
|
784
|
-
throw new Error(`Bad append parameter: key=${Array.from(key)} val=${val}`);
|
|
785
|
-
}
|
|
786
|
-
// KERIpy parity: seek from key.MaxON boundary and step back to the tail
|
|
787
|
-
// key for this prefix (if any), rather than scanning key.000... upward.
|
|
788
|
-
const maxOnSuffix = b("f".repeat(32));
|
|
789
|
-
const start = new Uint8Array(key.length + sep.length + maxOnSuffix.length);
|
|
790
|
-
start.set(key, 0);
|
|
791
|
-
start.set(sep, key.length);
|
|
792
|
-
start.set(maxOnSuffix, key.length + sep.length);
|
|
793
|
-
let nextOn = 0;
|
|
794
|
-
for (const entry of db.getRange({ start, reverse: true, limit: 1 })) {
|
|
795
|
-
const tailOnKey = copyDbBytes(entry.key);
|
|
796
|
-
const [ckey, cn] = splitOnKey(tailOnKey, sep);
|
|
797
|
-
if (!bytesEqual(ckey, key)) {
|
|
798
|
-
break;
|
|
799
|
-
}
|
|
800
|
-
if (bytesEqual(tailOnKey, start)) {
|
|
801
|
-
throw new Error(`Number part cn=${cn} for key part ckey=${Array.from(ckey)} exceeds maximum size.`);
|
|
802
|
-
}
|
|
803
|
-
nextOn = cn + 1;
|
|
804
|
-
break;
|
|
805
|
-
}
|
|
806
|
-
const targetKey = onKey(key, nextOn, sep);
|
|
807
|
-
const added = this.putVal(db, targetKey, val);
|
|
808
|
-
if (added) {
|
|
809
|
-
return nextOn;
|
|
810
|
-
}
|
|
811
|
-
// Some compat/interop flows still surface LMDB-js range behavior where the
|
|
812
|
-
// reverse tail probe fails to observe the existing highest ordinal entry.
|
|
813
|
-
// Fall back to counting the live branch and append at that count instead of
|
|
814
|
-
// failing a logically valid append.
|
|
815
|
-
const fallbackOn = this.cntOnAll(db, key, 0, sep);
|
|
816
|
-
if (fallbackOn !== nextOn) {
|
|
817
|
-
const fallbackAdded = this.putVal(db, onKey(key, fallbackOn, sep), val);
|
|
818
|
-
if (fallbackAdded) {
|
|
819
|
-
return fallbackOn;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
throw new Error(`Failed appending value at key=${Array.from(key)}.`);
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* TypeScript-only convenience returning the exact `(key, on, val)` triple.
|
|
826
|
-
*
|
|
827
|
-
* Returns `null` when that exact ordinal entry is missing.
|
|
828
|
-
*/
|
|
829
|
-
getOnItem(db, key, on = 0, sep = DOT_SEP) {
|
|
830
|
-
const val = this.getOnVal(db, key, on, sep);
|
|
831
|
-
return val ? [key, on, val] : null;
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Fetch the value stored exactly at `onKey(key, on)`.
|
|
835
|
-
*
|
|
836
|
-
* Returns `null` when the exact ordinal entry is missing. Empty logical keys
|
|
837
|
-
* are treated as absent. Mirrors KERIpy `getOnVal`.
|
|
838
|
-
*/
|
|
839
|
-
getOnVal(db, key, on = 0, sep = DOT_SEP) {
|
|
840
|
-
if (!key.length) {
|
|
841
|
-
return null;
|
|
842
|
-
}
|
|
843
|
-
return this.getVal(db, onKey(key, on, sep));
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Remove the value stored exactly at `onKey(key, on)`.
|
|
847
|
-
*
|
|
848
|
-
* Empty logical keys return `false`. TypeScript-local rename of KERIpy
|
|
849
|
-
* `delOnVal`.
|
|
850
|
-
*/
|
|
851
|
-
remOn(db, key, on = 0, sep = DOT_SEP) {
|
|
852
|
-
if (!key.length) {
|
|
853
|
-
return false;
|
|
854
|
-
}
|
|
855
|
-
return this.delVal(db, onKey(key, on, sep));
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Remove all ordinal entries for `key` starting at ordinal `on`.
|
|
859
|
-
*
|
|
860
|
-
* Empty `key` removes the whole DB branch. TypeScript-only convenience built
|
|
861
|
-
* on the same ordinal-key semantics as KERIpy `delOnVal`.
|
|
862
|
-
*/
|
|
863
|
-
remOnAll(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
864
|
-
if (!key.length) {
|
|
865
|
-
return this.delTop(db, new Uint8Array(0));
|
|
866
|
-
}
|
|
867
|
-
const env = this.requireEnv();
|
|
868
|
-
const start = onKey(key, on, sep);
|
|
869
|
-
const keys = [];
|
|
870
|
-
for (const entry of db.getRange({ start })) {
|
|
871
|
-
const ckey = copyDbBytes(entry.key);
|
|
872
|
-
const [top] = splitOnKey(ckey, sep);
|
|
873
|
-
if (!bytesEqual(top, key)) {
|
|
874
|
-
break;
|
|
875
|
-
}
|
|
876
|
-
keys.push(new Uint8Array(ckey));
|
|
877
|
-
}
|
|
878
|
-
if (!keys.length) {
|
|
879
|
-
return false;
|
|
880
|
-
}
|
|
881
|
-
env.transactionSync(() => {
|
|
882
|
-
for (const k of keys) {
|
|
883
|
-
db.removeSync(k);
|
|
884
|
-
}
|
|
885
|
-
});
|
|
886
|
-
return true;
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* Count ordinal-keyed entries for `key` starting at ordinal `on`.
|
|
890
|
-
*
|
|
891
|
-
* Empty `key` counts the whole DB. TypeScript-local rename of KERIpy
|
|
892
|
-
* `cntOnVals`.
|
|
893
|
-
*/
|
|
894
|
-
cntOnAll(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
895
|
-
this.requireEnv();
|
|
896
|
-
const start = key.length ? onKey(key, on, sep) : new Uint8Array(0);
|
|
897
|
-
let count = 0;
|
|
898
|
-
for (const entry of db.getRange({ start })) {
|
|
899
|
-
const ckey = copyDbBytes(entry.key);
|
|
900
|
-
const [top] = splitOnKey(ckey, sep);
|
|
901
|
-
if (key.length && !bytesEqual(top, key)) {
|
|
902
|
-
break;
|
|
903
|
-
}
|
|
904
|
-
count += 1;
|
|
905
|
-
}
|
|
906
|
-
return count;
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Iterate `(key, on, val)` triples whose physical keys begin with branch `top`.
|
|
910
|
-
*
|
|
911
|
-
* This is the ordinal-key version of `getTopItemIter`, so branch membership is
|
|
912
|
-
* still a lexicographic key-prefix concept. TypeScript-only convenience.
|
|
913
|
-
*/
|
|
914
|
-
*getOnTopItemIter(db, top = new Uint8Array(0), sep = DOT_SEP) {
|
|
915
|
-
for (const [onkey, val] of this.getTopItemIter(db, top)) {
|
|
916
|
-
const [key, on] = splitOnKey(onkey, sep);
|
|
917
|
-
yield [key, on, val];
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
/**
|
|
921
|
-
* Iterate `(key, on, val)` triples for ordinals `>= on`.
|
|
922
|
-
*
|
|
923
|
-
* Empty `key` iterates the whole DB. Ordering follows the fixed-width ordinal
|
|
924
|
-
* encoded in the key. TypeScript-local rename of KERIpy `getOnItemIter`.
|
|
925
|
-
*/
|
|
926
|
-
*getOnAllItemIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
927
|
-
this.requireEnv();
|
|
928
|
-
const start = key.length ? onKey(key, on, sep) : new Uint8Array(0);
|
|
929
|
-
for (const entry of db.getRange({ start })) {
|
|
930
|
-
const ckey = copyDbBytes(entry.key);
|
|
931
|
-
const cval = copyDbBytes(entry.value);
|
|
932
|
-
const [top, cn] = splitOnKey(ckey, sep);
|
|
933
|
-
if (key.length && !bytesEqual(top, key)) {
|
|
934
|
-
break;
|
|
935
|
-
}
|
|
936
|
-
yield [top, cn, cval];
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
/*
|
|
940
|
-
* Synthetic insertion-ordered set family (`IoSet*`)
|
|
941
|
-
* Logical shape: one logical key -> many values without dupsort.
|
|
942
|
-
* Multiplicity: synthetic, stored in keyspace as `key.<ion>`.
|
|
943
|
-
* Ordering: hidden fixed-width insertion suffix in the physical key.
|
|
944
|
-
* Parity status: direct KERIpy analogs with TS naming tweaks.
|
|
945
|
-
*/
|
|
946
|
-
/**
|
|
947
|
-
* Add each unique value in `vals` to the insertion-ordered set at `key`.
|
|
948
|
-
*
|
|
949
|
-
* This emulates duplicate-like behavior in keyspace, not native LMDB dupsort:
|
|
950
|
-
* values are stored under hidden suffixed keys `key.<ion>`. Existing logical
|
|
951
|
-
* members are skipped, new members append at the next suffix. Mirrors KERIpy
|
|
952
|
-
* `putIoSetVals`.
|
|
953
|
-
*/
|
|
954
|
-
putIoSetVals(db, key, vals, sep = DOT_SEP) {
|
|
955
|
-
if (!key.length || vals === null) {
|
|
956
|
-
return false;
|
|
957
|
-
}
|
|
958
|
-
const uniqueVals = asUniqueVals(vals);
|
|
959
|
-
if (!uniqueVals.length) {
|
|
960
|
-
return false;
|
|
961
|
-
}
|
|
962
|
-
const existing = new Set();
|
|
963
|
-
let ion = 0;
|
|
964
|
-
const start = suffix(key, 0, sep);
|
|
965
|
-
for (const entry of db.getRange({ start })) {
|
|
966
|
-
const iokey = copyDbBytes(entry.key);
|
|
967
|
-
const cval = copyDbBytes(entry.value);
|
|
968
|
-
const [ckey, cion] = unsuffix(iokey, sep);
|
|
969
|
-
if (!bytesEqual(ckey, key)) {
|
|
970
|
-
break;
|
|
971
|
-
}
|
|
972
|
-
existing.add(bytesHex(cval));
|
|
973
|
-
ion = cion + 1;
|
|
974
|
-
}
|
|
975
|
-
const insert = uniqueVals.filter((val) => !existing.has(bytesHex(val)));
|
|
976
|
-
if (!insert.length) {
|
|
977
|
-
return false;
|
|
978
|
-
}
|
|
979
|
-
const env = this.requireEnv();
|
|
980
|
-
env.transactionSync(() => {
|
|
981
|
-
for (const [index, val] of insert.entries()) {
|
|
982
|
-
db.putSync(suffix(key, ion + index, sep), val);
|
|
983
|
-
}
|
|
984
|
-
});
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Replace the insertion-ordered set at `key` with the provided unique values.
|
|
989
|
-
*
|
|
990
|
-
* This clears the existing synthetic key run for `key`, then rewrites fresh
|
|
991
|
-
* hidden suffixes from zero. TypeScript-local rename of KERIpy `setIoSetVals`.
|
|
992
|
-
*/
|
|
993
|
-
pinIoSetVals(db, key, vals, sep = DOT_SEP) {
|
|
994
|
-
if (!key.length || vals === null) {
|
|
995
|
-
return false;
|
|
996
|
-
}
|
|
997
|
-
const uniqueVals = asUniqueVals(vals);
|
|
998
|
-
if (!uniqueVals.length) {
|
|
999
|
-
return false;
|
|
1000
|
-
}
|
|
1001
|
-
this.remIoSet(db, key, sep);
|
|
1002
|
-
const env = this.requireEnv();
|
|
1003
|
-
env.transactionSync(() => {
|
|
1004
|
-
for (const [index, val] of uniqueVals.entries()) {
|
|
1005
|
-
db.putSync(suffix(key, index, sep), val);
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1008
|
-
return true;
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Add one value to the insertion-ordered set at `key` if it is not present.
|
|
1012
|
-
*
|
|
1013
|
-
* Membership is checked by scanning the synthetic key run for `key`; new
|
|
1014
|
-
* values append at the next hidden suffix. Mirrors KERIpy `addIoSetVal`.
|
|
1015
|
-
*/
|
|
1016
|
-
addIoSetVal(db, key, val, sep = DOT_SEP) {
|
|
1017
|
-
this.requireEnv();
|
|
1018
|
-
if (!key.length || val === null) {
|
|
1019
|
-
return false;
|
|
1020
|
-
}
|
|
1021
|
-
let ion = 0;
|
|
1022
|
-
const valHex = bytesHex(val);
|
|
1023
|
-
const start = suffix(key, 0, sep);
|
|
1024
|
-
for (const entry of db.getRange({ start })) {
|
|
1025
|
-
const iokey = copyDbBytes(entry.key);
|
|
1026
|
-
const cval = copyDbBytes(entry.value);
|
|
1027
|
-
const [ckey, cion] = unsuffix(iokey, sep);
|
|
1028
|
-
if (!bytesEqual(ckey, key)) {
|
|
1029
|
-
break;
|
|
1030
|
-
}
|
|
1031
|
-
if (bytesHex(cval) === valHex) {
|
|
1032
|
-
return false;
|
|
1033
|
-
}
|
|
1034
|
-
ion = cion + 1;
|
|
1035
|
-
}
|
|
1036
|
-
return this.putVal(db, suffix(key, ion, sep), val);
|
|
1037
|
-
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Iterate `(key, val)` pairs for the insertion-ordered set at `key`.
|
|
1040
|
-
*
|
|
1041
|
-
* Iteration starts at hidden insertion ordinal `ion`. Returned keys are the
|
|
1042
|
-
* logical effective key with the hidden suffix removed. TypeScript-local item
|
|
1043
|
-
* variant corresponding to KERIpy `getIoSetValsIter`.
|
|
1044
|
-
*/
|
|
1045
|
-
*getIoSetItemIter(db, key, ion = 0, sep = DOT_SEP) {
|
|
1046
|
-
this.requireEnv();
|
|
1047
|
-
if (!key.length) {
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
|
-
const start = suffix(key, ion, sep);
|
|
1051
|
-
for (const entry of db.getRange({ start })) {
|
|
1052
|
-
const iokey = copyDbBytes(entry.key);
|
|
1053
|
-
const cval = copyDbBytes(entry.value);
|
|
1054
|
-
const [ckey] = unsuffix(iokey, sep);
|
|
1055
|
-
if (!bytesEqual(ckey, key)) {
|
|
1056
|
-
break;
|
|
1057
|
-
}
|
|
1058
|
-
yield [ckey, cval];
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
/**
|
|
1062
|
-
* Return the last logical set member at `key`, or `null`.
|
|
1063
|
-
*
|
|
1064
|
-
* "Last" means the entry stored under the greatest hidden insertion suffix,
|
|
1065
|
-
* not the lexicographically greatest value bytes. TypeScript-local item
|
|
1066
|
-
* variant corresponding to KERIpy `getIoSetValLast`.
|
|
1067
|
-
*/
|
|
1068
|
-
getIoSetLastItem(db, key, sep = DOT_SEP) {
|
|
1069
|
-
let last = null;
|
|
1070
|
-
for (const item of this.getIoSetItemIter(db, key, 0, sep)) {
|
|
1071
|
-
last = item;
|
|
1072
|
-
}
|
|
1073
|
-
return last;
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Remove all insertion-ordered set members at `key`.
|
|
1077
|
-
*
|
|
1078
|
-
* This deletes the full synthetic key run for the effective key. TypeScript-
|
|
1079
|
-
* local rename of KERIpy `delIoSetVals`.
|
|
1080
|
-
*/
|
|
1081
|
-
remIoSet(db, key, sep = DOT_SEP) {
|
|
1082
|
-
if (!key.length) {
|
|
1083
|
-
return false;
|
|
1084
|
-
}
|
|
1085
|
-
const env = this.requireEnv();
|
|
1086
|
-
const keys = [];
|
|
1087
|
-
const start = suffix(key, 0, sep);
|
|
1088
|
-
for (const entry of db.getRange({ start })) {
|
|
1089
|
-
const iokey = copyDbBytes(entry.key);
|
|
1090
|
-
const [ckey] = unsuffix(iokey, sep);
|
|
1091
|
-
if (!bytesEqual(ckey, key)) {
|
|
1092
|
-
break;
|
|
1093
|
-
}
|
|
1094
|
-
keys.push(new Uint8Array(iokey));
|
|
1095
|
-
}
|
|
1096
|
-
if (!keys.length) {
|
|
1097
|
-
return false;
|
|
1098
|
-
}
|
|
1099
|
-
env.transactionSync(() => {
|
|
1100
|
-
for (const iokey of keys) {
|
|
1101
|
-
db.removeSync(iokey);
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
return true;
|
|
1105
|
-
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Remove one logical set member at `key`, or all members when `val` is `null`.
|
|
1108
|
-
*
|
|
1109
|
-
* Delete-by-value requires a linear search because the caller does not know
|
|
1110
|
-
* the hidden insertion suffix. As in KERIpy, suffix ordinals can grow
|
|
1111
|
-
* monotonically over time after deletes and reinserts. TypeScript-local rename
|
|
1112
|
-
* of KERIpy `delIoSetVal`.
|
|
1113
|
-
*/
|
|
1114
|
-
remIoSetVal(db, key, val = null, sep = DOT_SEP) {
|
|
1115
|
-
if (val === null) {
|
|
1116
|
-
return this.remIoSet(db, key, sep);
|
|
1117
|
-
}
|
|
1118
|
-
if (!key.length) {
|
|
1119
|
-
return false;
|
|
1120
|
-
}
|
|
1121
|
-
const env = this.requireEnv();
|
|
1122
|
-
const valHex = bytesHex(val);
|
|
1123
|
-
let removeKey = null;
|
|
1124
|
-
const start = suffix(key, 0, sep);
|
|
1125
|
-
for (const entry of db.getRange({ start })) {
|
|
1126
|
-
const iokey = copyDbBytes(entry.key);
|
|
1127
|
-
const cval = copyDbBytes(entry.value);
|
|
1128
|
-
const [ckey] = unsuffix(iokey, sep);
|
|
1129
|
-
if (!bytesEqual(ckey, key)) {
|
|
1130
|
-
break;
|
|
1131
|
-
}
|
|
1132
|
-
if (bytesHex(cval) === valHex) {
|
|
1133
|
-
removeKey = new Uint8Array(iokey);
|
|
1134
|
-
break;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
if (!removeKey) {
|
|
1138
|
-
return false;
|
|
1139
|
-
}
|
|
1140
|
-
env.transactionSync(() => {
|
|
1141
|
-
db.removeSync(removeKey);
|
|
1142
|
-
});
|
|
1143
|
-
return true;
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Count logical set members at `key` starting from hidden insertion ordinal `ion`.
|
|
1147
|
-
*
|
|
1148
|
-
* TypeScript-local item-count variant corresponding to KERIpy `cntIoSetVals`.
|
|
1149
|
-
*/
|
|
1150
|
-
cntIoSet(db, key, ion = 0, sep = DOT_SEP) {
|
|
1151
|
-
if (!key.length) {
|
|
1152
|
-
return 0;
|
|
1153
|
-
}
|
|
1154
|
-
let count = 0;
|
|
1155
|
-
for (const _ of this.getIoSetItemIter(db, key, ion, sep)) {
|
|
1156
|
-
count += 1;
|
|
1157
|
-
}
|
|
1158
|
-
return count;
|
|
1159
|
-
}
|
|
1160
|
-
/**
|
|
1161
|
-
* Iterate `(key, val)` pairs for all synthetic-set entries in branch `top`.
|
|
1162
|
-
*
|
|
1163
|
-
* Branch membership is still a lexicographic key-prefix scan over the
|
|
1164
|
-
* physical suffixed keys; returned keys have the hidden insertion suffix
|
|
1165
|
-
* removed. Mirrors KERIpy `getTopIoSetItemIter`.
|
|
1166
|
-
*/
|
|
1167
|
-
*getTopIoSetItemIter(db, top = new Uint8Array(0), sep = DOT_SEP) {
|
|
1168
|
-
for (const [iokey, val] of this.getTopItemIter(db, top)) {
|
|
1169
|
-
const [key] = unsuffix(iokey, sep);
|
|
1170
|
-
yield [key, val];
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
/**
|
|
1174
|
-
* Iterate the last `(key, val)` member for each effective io-set key.
|
|
1175
|
-
*
|
|
1176
|
-
* Empty `key` iterates the whole DB. "Last" is determined by hidden suffix
|
|
1177
|
-
* order. TypeScript-local item variant built from KERIpy `getIoSetValLast`
|
|
1178
|
-
* and branch iteration semantics.
|
|
1179
|
-
*/
|
|
1180
|
-
*getIoSetLastItemIterAll(db, key = new Uint8Array(0), sep = DOT_SEP) {
|
|
1181
|
-
this.requireEnv();
|
|
1182
|
-
const start = key.length ? suffix(key, 0, sep) : undefined;
|
|
1183
|
-
let last = null;
|
|
1184
|
-
let lkey = null;
|
|
1185
|
-
for (const entry of db.getRange({ start })) {
|
|
1186
|
-
const iokey = copyDbBytes(entry.key);
|
|
1187
|
-
const cval = copyDbBytes(entry.value);
|
|
1188
|
-
const [ckey] = unsuffix(iokey, sep);
|
|
1189
|
-
if (lkey === null) {
|
|
1190
|
-
lkey = ckey;
|
|
1191
|
-
}
|
|
1192
|
-
if (!bytesEqual(ckey, lkey)) {
|
|
1193
|
-
if (last) {
|
|
1194
|
-
yield last;
|
|
1195
|
-
}
|
|
1196
|
-
lkey = ckey;
|
|
1197
|
-
}
|
|
1198
|
-
last = [ckey, cval];
|
|
1199
|
-
}
|
|
1200
|
-
if (last) {
|
|
1201
|
-
yield last;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Iterate only the last logical values for each effective io-set key `>= key`.
|
|
1206
|
-
*
|
|
1207
|
-
* TypeScript-only convenience built on `getIoSetLastItemIterAll`.
|
|
1208
|
-
*/
|
|
1209
|
-
*getIoSetLastIterAll(db, key = new Uint8Array(0), sep = DOT_SEP) {
|
|
1210
|
-
for (const [_, val] of this.getIoSetLastItemIterAll(db, key, sep)) {
|
|
1211
|
-
yield val;
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
/*
|
|
1215
|
-
* Ordinal synthetic insertion-ordered set family (`OnIoSet*`)
|
|
1216
|
-
* Logical shape: ordinal buckets, each bucket holding an insertion-ordered set.
|
|
1217
|
-
* Multiplicity: synthetic, stored in keyspace as `key.<on>.<ion>`.
|
|
1218
|
-
* Ordering: exposed fixed-width ordinal in the key, then hidden insertion suffix.
|
|
1219
|
-
* Parity status: TypeScript-only extension. KERIpy exposes `IoSet*` and
|
|
1220
|
-
* `OnIoDup*`, but not `OnIoSet*`. This exists as extension surface for
|
|
1221
|
-
* ordinal buckets + insertion-ordered set semantics without dupsort
|
|
1222
|
-
* constraints, and currently has no production callers.
|
|
1223
|
-
*/
|
|
1224
|
-
/**
|
|
1225
|
-
* TypeScript-only extension. Add set members at ordinal effective key `onKey(key, on)`.
|
|
1226
|
-
*
|
|
1227
|
-
* This is the `On` + `IoSet` composition: exposed ordinal in keyspace, hidden
|
|
1228
|
-
* insertion suffix inside that ordinal bucket.
|
|
1229
|
-
*/
|
|
1230
|
-
putOnIoSetVals(db, key, on = 0, vals = null, sep = DOT_SEP) {
|
|
1231
|
-
if (!key.length) {
|
|
1232
|
-
return false;
|
|
1233
|
-
}
|
|
1234
|
-
return this.putIoSetVals(db, onKey(key, on, sep), vals, sep);
|
|
1235
|
-
}
|
|
1236
|
-
/**
|
|
1237
|
-
* TypeScript-only extension. Replace set members at ordinal effective key.
|
|
1238
|
-
*
|
|
1239
|
-
* This is the ordinal-bucketed counterpart to `pinIoSetVals`.
|
|
1240
|
-
*/
|
|
1241
|
-
pinOnIoSetVals(db, key, on = 0, vals = null, sep = DOT_SEP) {
|
|
1242
|
-
if (!key.length) {
|
|
1243
|
-
return false;
|
|
1244
|
-
}
|
|
1245
|
-
return this.pinIoSetVals(db, onKey(key, on, sep), vals, sep);
|
|
1246
|
-
}
|
|
1247
|
-
/**
|
|
1248
|
-
* TypeScript-only extension. Append a new ordinal bucket for `key`.
|
|
1249
|
-
*
|
|
1250
|
-
* Returns the newly appended exposed ordinal. Within that bucket, the values
|
|
1251
|
-
* are stored as an `IoSet` using hidden insertion suffixes.
|
|
1252
|
-
*/
|
|
1253
|
-
appendOnIoSetVals(db, key, vals, sep = DOT_SEP) {
|
|
1254
|
-
this.requireEnv();
|
|
1255
|
-
if (!key.length || vals === null) {
|
|
1256
|
-
throw new Error(`Bad append parameter: key=${Array.from(key)} vals=${vals}`);
|
|
1257
|
-
}
|
|
1258
|
-
let on = 0;
|
|
1259
|
-
const start = suffix(onKey(key, 0, sep), 0, sep);
|
|
1260
|
-
for (const entry of db.getRange({ start })) {
|
|
1261
|
-
const iokey = copyDbBytes(entry.key);
|
|
1262
|
-
const [onkey] = unsuffix(iokey, sep);
|
|
1263
|
-
const [ckey, con] = splitOnKey(onkey, sep);
|
|
1264
|
-
if (!bytesEqual(ckey, key)) {
|
|
1265
|
-
break;
|
|
1266
|
-
}
|
|
1267
|
-
on = con + 1;
|
|
1268
|
-
}
|
|
1269
|
-
if (!this.putOnIoSetVals(db, key, on, vals, sep)) {
|
|
1270
|
-
throw new Error(`Failed appending set values at key=${Array.from(key)}.`);
|
|
1271
|
-
}
|
|
1272
|
-
return on;
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* TypeScript-only extension. Add one set member inside an ordinal bucket.
|
|
1276
|
-
*
|
|
1277
|
-
* This is the ordinal-bucketed counterpart to `addIoSetVal`.
|
|
1278
|
-
*/
|
|
1279
|
-
addOnIoSetVal(db, key, on = 0, val = null, sep = DOT_SEP) {
|
|
1280
|
-
return this.addIoSetVal(db, onKey(key, on, sep), val, sep);
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* TypeScript-only extension. Iterate `(key, on, val)` members at one exact ordinal.
|
|
1284
|
-
*
|
|
1285
|
-
* Ordering inside the bucket follows the hidden insertion suffix.
|
|
1286
|
-
*/
|
|
1287
|
-
*getOnIoSetItemIter(db, key, on = 0, ion = 0, sep = DOT_SEP) {
|
|
1288
|
-
for (const [onkey, val] of this.getIoSetItemIter(db, onKey(key, on, sep), ion, sep)) {
|
|
1289
|
-
const [k, o] = splitOnKey(onkey, sep);
|
|
1290
|
-
yield [k, o, val];
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
/**
|
|
1294
|
-
* TypeScript-only extension. Fetch the last member in one ordinal bucket.
|
|
1295
|
-
*
|
|
1296
|
-
* Returns `null` when that exact ordinal bucket is empty or missing.
|
|
1297
|
-
*/
|
|
1298
|
-
getOnIoSetLastItem(db, key, on = 0, sep = DOT_SEP) {
|
|
1299
|
-
const last = this.getIoSetLastItem(db, onKey(key, on, sep), sep);
|
|
1300
|
-
if (!last) {
|
|
1301
|
-
return null;
|
|
1302
|
-
}
|
|
1303
|
-
const [onkey, val] = last;
|
|
1304
|
-
const [k, o] = splitOnKey(onkey, sep);
|
|
1305
|
-
return [k, o, val];
|
|
1306
|
-
}
|
|
1307
|
-
/**
|
|
1308
|
-
* TypeScript-only extension. Remove one member from an ordinal bucket.
|
|
1309
|
-
*
|
|
1310
|
-
* When `val` is `null`, removes the whole ordinal bucket.
|
|
1311
|
-
*/
|
|
1312
|
-
remOnIoSetVal(db, key, on = 0, val = null, sep = DOT_SEP) {
|
|
1313
|
-
return this.remIoSetVal(db, onKey(key, on, sep), val, sep);
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* TypeScript-only extension. Remove all ordinal buckets for `key` from `on` onward.
|
|
1317
|
-
*
|
|
1318
|
-
* Empty `key` removes the whole DB branch.
|
|
1319
|
-
*/
|
|
1320
|
-
remOnAllIoSet(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1321
|
-
if (!key.length) {
|
|
1322
|
-
return this.delTop(db, new Uint8Array(0));
|
|
1323
|
-
}
|
|
1324
|
-
const env = this.requireEnv();
|
|
1325
|
-
const start = suffix(onKey(key, on, sep), 0, sep);
|
|
1326
|
-
const keys = [];
|
|
1327
|
-
for (const entry of db.getRange({ start })) {
|
|
1328
|
-
const iokey = copyDbBytes(entry.key);
|
|
1329
|
-
const [onkey] = unsuffix(iokey, sep);
|
|
1330
|
-
const [ckey] = splitOnKey(onkey, sep);
|
|
1331
|
-
if (!bytesEqual(ckey, key)) {
|
|
1332
|
-
break;
|
|
1333
|
-
}
|
|
1334
|
-
keys.push(new Uint8Array(iokey));
|
|
1335
|
-
}
|
|
1336
|
-
if (!keys.length) {
|
|
1337
|
-
return false;
|
|
1338
|
-
}
|
|
1339
|
-
env.transactionSync(() => {
|
|
1340
|
-
for (const iokey of keys) {
|
|
1341
|
-
db.removeSync(iokey);
|
|
1342
|
-
}
|
|
1343
|
-
});
|
|
1344
|
-
return true;
|
|
1345
|
-
}
|
|
1346
|
-
/**
|
|
1347
|
-
* TypeScript-only extension. Count members in one ordinal bucket from insertion ordinal `ion`.
|
|
1348
|
-
*/
|
|
1349
|
-
cntOnIoSet(db, key, on = 0, ion = 0, sep = DOT_SEP) {
|
|
1350
|
-
return this.cntIoSet(db, onKey(key, on, sep), ion, sep);
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
|
-
* TypeScript-only extension. Count all members across ordinal buckets for `key` from `on` onward.
|
|
1354
|
-
*/
|
|
1355
|
-
cntOnAllIoSet(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1356
|
-
this.requireEnv();
|
|
1357
|
-
if (!key.length) {
|
|
1358
|
-
return this.cntAll(db);
|
|
1359
|
-
}
|
|
1360
|
-
let count = 0;
|
|
1361
|
-
const start = suffix(onKey(key, on, sep), 0, sep);
|
|
1362
|
-
for (const entry of db.getRange({ start })) {
|
|
1363
|
-
const [onkey] = unsuffix(copyDbBytes(entry.key), sep);
|
|
1364
|
-
const [ckey] = splitOnKey(onkey, sep);
|
|
1365
|
-
if (!bytesEqual(ckey, key)) {
|
|
1366
|
-
break;
|
|
1367
|
-
}
|
|
1368
|
-
count += 1;
|
|
1369
|
-
}
|
|
1370
|
-
return count;
|
|
1371
|
-
}
|
|
1372
|
-
/**
|
|
1373
|
-
* TypeScript-only extension. Iterate `(key, on, val)` members in branch `top`.
|
|
1374
|
-
*
|
|
1375
|
-
* This is the branch-scan form of `OnIoSet*`, still based on lexicographic
|
|
1376
|
-
* physical key-prefix traversal.
|
|
1377
|
-
*/
|
|
1378
|
-
*getOnTopIoSetItemIter(db, top = new Uint8Array(0), sep = DOT_SEP) {
|
|
1379
|
-
for (const [onkey, val] of this.getTopIoSetItemIter(db, top, sep)) {
|
|
1380
|
-
const [key, on] = splitOnKey(onkey, sep);
|
|
1381
|
-
yield [key, on, val];
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* TypeScript-only extension. Iterate `(key, on, val)` members for ordinals `>= on`.
|
|
1386
|
-
*
|
|
1387
|
-
* Empty `key` iterates the whole DB.
|
|
1388
|
-
*/
|
|
1389
|
-
*getOnAllIoSetItemIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1390
|
-
this.requireEnv();
|
|
1391
|
-
if (!key.length) {
|
|
1392
|
-
yield* this.getOnTopIoSetItemIter(db, new Uint8Array(0), sep);
|
|
1393
|
-
return;
|
|
1394
|
-
}
|
|
1395
|
-
const start = suffix(onKey(key, on, sep), 0, sep);
|
|
1396
|
-
for (const entry of db.getRange({ start })) {
|
|
1397
|
-
const [onkey] = unsuffix(copyDbBytes(entry.key), sep);
|
|
1398
|
-
const cval = copyDbBytes(entry.value);
|
|
1399
|
-
const [ckey, con] = splitOnKey(onkey, sep);
|
|
1400
|
-
if (!bytesEqual(ckey, key)) {
|
|
1401
|
-
break;
|
|
1402
|
-
}
|
|
1403
|
-
yield [ckey, con, cval];
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
/**
|
|
1407
|
-
* TypeScript-only extension. Iterate the last member for each ordinal bucket.
|
|
1408
|
-
*
|
|
1409
|
-
* Empty `key` iterates all logical keys and ordinals in the DB.
|
|
1410
|
-
*/
|
|
1411
|
-
*getOnAllIoSetLastItemIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1412
|
-
if (!key.length) {
|
|
1413
|
-
for (const [onkey, val] of this.getIoSetLastItemIterAll(db, new Uint8Array(0), sep)) {
|
|
1414
|
-
const [k, o] = splitOnKey(onkey, sep);
|
|
1415
|
-
yield [k, o, val];
|
|
1416
|
-
}
|
|
1417
|
-
return;
|
|
1418
|
-
}
|
|
1419
|
-
let last = null;
|
|
1420
|
-
for (const [ckey, con, cval] of this.getOnAllIoSetItemIter(db, key, on, sep)) {
|
|
1421
|
-
if (last === null) {
|
|
1422
|
-
last = [ckey, con, cval];
|
|
1423
|
-
continue;
|
|
1424
|
-
}
|
|
1425
|
-
if (con !== last[1]) {
|
|
1426
|
-
yield last;
|
|
1427
|
-
}
|
|
1428
|
-
last = [ckey, con, cval];
|
|
1429
|
-
}
|
|
1430
|
-
if (last) {
|
|
1431
|
-
yield last;
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
/**
|
|
1435
|
-
* TypeScript-only extension. Iterate backward over `(key, on, val)` members.
|
|
1436
|
-
*
|
|
1437
|
-
* Backward order is materialized from the forward iterator, then reversed.
|
|
1438
|
-
*/
|
|
1439
|
-
*getOnAllIoSetItemBackIter(db, key = new Uint8Array(0), on = null, sep = DOT_SEP) {
|
|
1440
|
-
const items = key.length
|
|
1441
|
-
? [...this.getOnAllIoSetItemIter(db, key, 0, sep)]
|
|
1442
|
-
: [...this.getOnTopIoSetItemIter(db, new Uint8Array(0), sep)];
|
|
1443
|
-
for (let i = items.length - 1; i >= 0; i--) {
|
|
1444
|
-
const item = items[i];
|
|
1445
|
-
if (key.length && on !== null && item[1] > on) {
|
|
1446
|
-
continue;
|
|
1447
|
-
}
|
|
1448
|
-
yield item;
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* TypeScript-only extension. Iterate backward over the last member per ordinal bucket.
|
|
1453
|
-
*/
|
|
1454
|
-
*getOnAllIoSetLastItemBackIter(db, key = new Uint8Array(0), on = null, sep = DOT_SEP) {
|
|
1455
|
-
const items = [...this.getOnAllIoSetLastItemIter(db, key, 0, sep)];
|
|
1456
|
-
for (let i = items.length - 1; i >= 0; i--) {
|
|
1457
|
-
const item = items[i];
|
|
1458
|
-
if (key.length && on !== null && item[1] > on) {
|
|
1459
|
-
continue;
|
|
1460
|
-
}
|
|
1461
|
-
yield item;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
854
|
/*
|
|
1465
855
|
* Native dupsort duplicate family (`Dup*`)
|
|
1466
856
|
* Logical shape: one physical key -> many native LMDB duplicate values.
|
|
@@ -1475,7 +865,8 @@ export class LMDBer {
|
|
|
1475
865
|
* by insertion order. Returns `false` if any supplied value already existed.
|
|
1476
866
|
* Mirrors KERIpy `putVals`.
|
|
1477
867
|
*/
|
|
1478
|
-
putVals(
|
|
868
|
+
putVals(table, key, vals) {
|
|
869
|
+
const db = lmdbTable(table);
|
|
1479
870
|
let result = true;
|
|
1480
871
|
const existing = new Set(this.getVals(db, key).map((val) => bytesHex(val)));
|
|
1481
872
|
const env = this.requireEnv();
|
|
@@ -1486,6 +877,7 @@ export class LMDBer {
|
|
|
1486
877
|
result = false;
|
|
1487
878
|
continue;
|
|
1488
879
|
}
|
|
880
|
+
assertRowFits(db, key, val);
|
|
1489
881
|
db.putSync(key, val);
|
|
1490
882
|
existing.add(vh);
|
|
1491
883
|
}
|
|
@@ -1498,17 +890,20 @@ export class LMDBer {
|
|
|
1498
890
|
* Duplicate ordering is still lexicographic by stored value bytes. Mirrors
|
|
1499
891
|
* KERIpy `addVal`.
|
|
1500
892
|
*/
|
|
1501
|
-
addVal(
|
|
1502
|
-
const
|
|
1503
|
-
return this.putVals(db, key,
|
|
893
|
+
addVal(table, key, val) {
|
|
894
|
+
const db = lmdbTable(table);
|
|
895
|
+
return this.putVals(db, key, [val]);
|
|
1504
896
|
}
|
|
1505
897
|
/**
|
|
1506
898
|
* Get duplicate values at `key` in lexicographic stored-value order.
|
|
1507
899
|
*
|
|
1508
900
|
* Returns an empty list when `key` is missing. Mirrors KERIpy `getVals`.
|
|
1509
901
|
*/
|
|
1510
|
-
getVals(
|
|
902
|
+
getVals(table, key) {
|
|
903
|
+
const db = lmdbTable(table);
|
|
1511
904
|
this.requireEnv();
|
|
905
|
+
if (isAbsentKey(key))
|
|
906
|
+
return [];
|
|
1512
907
|
return [...db.getValues(key)].map((val) => copyDbBytes(val));
|
|
1513
908
|
}
|
|
1514
909
|
/**
|
|
@@ -1517,7 +912,8 @@ export class LMDBer {
|
|
|
1517
912
|
* "Last" here means LMDB dupsort order, not most-recent insertion. Returns
|
|
1518
913
|
* `null` when `key` is missing. Mirrors KERIpy `getValLast`.
|
|
1519
914
|
*/
|
|
1520
|
-
getValLast(
|
|
915
|
+
getValLast(table, key) {
|
|
916
|
+
const db = lmdbTable(table);
|
|
1521
917
|
const vals = this.getVals(db, key);
|
|
1522
918
|
return vals.length ? vals[vals.length - 1] : null;
|
|
1523
919
|
}
|
|
@@ -1526,288 +922,24 @@ export class LMDBer {
|
|
|
1526
922
|
*
|
|
1527
923
|
* Yields nothing when `key` is missing. Mirrors KERIpy `getValsIter`.
|
|
1528
924
|
*/
|
|
1529
|
-
*getValsIter(
|
|
925
|
+
*getValsIter(table, key) {
|
|
926
|
+
const db = lmdbTable(table);
|
|
1530
927
|
this.requireEnv();
|
|
928
|
+
if (isAbsentKey(key))
|
|
929
|
+
return;
|
|
1531
930
|
for (const val of db.getValues(key)) {
|
|
1532
931
|
yield copyDbBytes(val);
|
|
1533
932
|
}
|
|
1534
933
|
}
|
|
1535
934
|
/** Count native dupsort values at `key`. Mirrors KERIpy `cntVals`. */
|
|
1536
|
-
cntVals(
|
|
935
|
+
cntVals(table, key) {
|
|
936
|
+
const db = lmdbTable(table);
|
|
1537
937
|
this.requireEnv();
|
|
1538
|
-
return db.getValuesCount(key);
|
|
938
|
+
return isAbsentKey(key) ? 0 : db.getValuesCount(key);
|
|
1539
939
|
}
|
|
1540
940
|
/** Delete all duplicate values at `key`. Mirrors KERIpy `delVals(key)`. */
|
|
1541
|
-
delVals(
|
|
1542
|
-
this.
|
|
1543
|
-
return db.removeSync(key);
|
|
1544
|
-
}
|
|
1545
|
-
/*
|
|
1546
|
-
* Insertion-ordered duplicate family (`IoDup*`)
|
|
1547
|
-
* Logical shape: one physical key -> many native dupsort values.
|
|
1548
|
-
* Multiplicity: native LMDB dupsort values.
|
|
1549
|
-
* Ordering: hidden 33-byte value proem makes dupsort order equal insertion order.
|
|
1550
|
-
* Parity status: direct KERIpy analogs with TS naming tweaks.
|
|
1551
|
-
*
|
|
1552
|
-
* Use this when dupsort size constraints are acceptable; use `IoSet*` when
|
|
1553
|
-
* values may be too large for dupsort-backed storage.
|
|
1554
|
-
*/
|
|
1555
|
-
/**
|
|
1556
|
-
* Add insertion-ordered duplicate values at `key`.
|
|
1557
|
-
*
|
|
1558
|
-
* Each stored value gets a hidden 33-byte proem (`32 hex chars + '.'`) so
|
|
1559
|
-
* LMDB's lexicographic duplicate ordering becomes logical insertion order.
|
|
1560
|
-
* Returned values later strip that proem away. Mirrors KERIpy `putIoDupVals`.
|
|
1561
|
-
*/
|
|
1562
|
-
putIoDupVals(db, key, vals) {
|
|
1563
|
-
let result = false;
|
|
1564
|
-
const existing = new Set(this.getIoDupVals(db, key).map((val) => bytesHex(val)));
|
|
1565
|
-
let idx = 0;
|
|
1566
|
-
const last = this.getValLast(db, key);
|
|
1567
|
-
if (last) {
|
|
1568
|
-
idx = parseIoDupOrdinal(last) + 1;
|
|
1569
|
-
}
|
|
1570
|
-
const env = this.requireEnv();
|
|
1571
|
-
env.transactionSync(() => {
|
|
1572
|
-
for (const val of vals) {
|
|
1573
|
-
const vh = bytesHex(val);
|
|
1574
|
-
if (existing.has(vh)) {
|
|
1575
|
-
continue;
|
|
1576
|
-
}
|
|
1577
|
-
db.putSync(key, withIoDupProem(idx, val));
|
|
1578
|
-
idx += 1;
|
|
1579
|
-
existing.add(vh);
|
|
1580
|
-
result = true;
|
|
1581
|
-
}
|
|
1582
|
-
});
|
|
1583
|
-
return result;
|
|
1584
|
-
}
|
|
1585
|
-
/**
|
|
1586
|
-
* Add one insertion-ordered duplicate value at `key` if absent.
|
|
1587
|
-
*
|
|
1588
|
-
* Mirrors KERIpy `addIoDupVal`.
|
|
1589
|
-
*/
|
|
1590
|
-
addIoDupVal(db, key, val) {
|
|
1591
|
-
const single = val === null ? [new Uint8Array(0)] : [val];
|
|
1592
|
-
return this.putIoDupVals(db, key, single);
|
|
1593
|
-
}
|
|
1594
|
-
/**
|
|
1595
|
-
* Get insertion-ordered duplicate values at `key`, with the hidden proem removed.
|
|
1596
|
-
*
|
|
1597
|
-
* Ordering is logical insertion order because the hidden proem controls LMDB
|
|
1598
|
-
* dupsort ordering. Mirrors KERIpy `getIoDupVals`.
|
|
1599
|
-
*/
|
|
1600
|
-
getIoDupVals(db, key) {
|
|
1601
|
-
return this.getVals(db, key).map((val) => stripIoDupProem(val));
|
|
1602
|
-
}
|
|
1603
|
-
/**
|
|
1604
|
-
* Iterate insertion-ordered duplicate values at `key`, with the hidden proem removed.
|
|
1605
|
-
*
|
|
1606
|
-
* Mirrors KERIpy `getIoDupValsIter`.
|
|
1607
|
-
*/
|
|
1608
|
-
*getIoDupValsIter(db, key) {
|
|
1609
|
-
for (const val of this.getValsIter(db, key)) {
|
|
1610
|
-
yield stripIoDupProem(val);
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Get the most recently inserted logical duplicate value at `key`.
|
|
1615
|
-
*
|
|
1616
|
-
* This uses the lexicographically last stored proem-prefixed duplicate and
|
|
1617
|
-
* strips the hidden proem before returning. Mirrors KERIpy `getIoDupValLast`.
|
|
1618
|
-
*/
|
|
1619
|
-
getIoDupValLast(db, key) {
|
|
1620
|
-
const last = this.getValLast(db, key);
|
|
1621
|
-
return last ? stripIoDupProem(last) : null;
|
|
1622
|
-
}
|
|
1623
|
-
/** Delete all insertion-ordered duplicates at `key`. Mirrors KERIpy `delIoDupVals`. */
|
|
1624
|
-
delIoDupVals(db, key) {
|
|
1625
|
-
return this.delVals(db, key);
|
|
1626
|
-
}
|
|
1627
|
-
/**
|
|
1628
|
-
* Delete one insertion-ordered duplicate value at `key`, matching the stripped logical value.
|
|
1629
|
-
*
|
|
1630
|
-
* This performs a linear search over stored proem-prefixed duplicates. Mirrors
|
|
1631
|
-
* KERIpy `delIoDupVal`.
|
|
1632
|
-
*/
|
|
1633
|
-
delIoDupVal(db, key, val) {
|
|
1634
|
-
this.requireEnv();
|
|
1635
|
-
for (const proVal of this.getVals(db, key)) {
|
|
1636
|
-
if (bytesEqual(stripIoDupProem(proVal), val)) {
|
|
1637
|
-
return db.removeSync(key, proVal);
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
return false;
|
|
1641
|
-
}
|
|
1642
|
-
/** Count insertion-ordered duplicates at `key`. Mirrors KERIpy `cntIoDupVals`. */
|
|
1643
|
-
cntIoDups(db, key) {
|
|
1644
|
-
return this.cntVals(db, key);
|
|
1645
|
-
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Iterate `(key, val)` over branch `top`, stripping the hidden IoDup proem from each value.
|
|
1648
|
-
*
|
|
1649
|
-
* Mirrors KERIpy `getTopIoDupItemIter`.
|
|
1650
|
-
*/
|
|
1651
|
-
*getTopIoDupItemIter(db, top = new Uint8Array(0)) {
|
|
1652
|
-
for (const [key, val] of this.getTopItemIter(db, top)) {
|
|
1653
|
-
yield [key, stripIoDupProem(val)];
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
/*
|
|
1657
|
-
* Ordinal insertion-ordered duplicate family (`OnIoDup*`)
|
|
1658
|
-
* Logical shape: ordinal buckets, each bucket holding native dupsort duplicates.
|
|
1659
|
-
* Multiplicity: native LMDB dupsort values within each exposed ordinal key.
|
|
1660
|
-
* Ordering: exposed fixed-width ordinal in the key, hidden 33-byte proem in each duplicate value.
|
|
1661
|
-
* Parity status: direct KERIpy analogs plus a few TS-only convenience methods.
|
|
1662
|
-
*/
|
|
1663
|
-
/**
|
|
1664
|
-
* TypeScript-only convenience adding insertion-ordered duplicates at one exact ordinal key.
|
|
1665
|
-
*
|
|
1666
|
-
* This is the ordinal-bucketed counterpart to `putIoDupVals`. KERIpy exposes
|
|
1667
|
-
* `addOnIoDupVal` and iterator-based accessors instead of this bulk helper.
|
|
1668
|
-
*/
|
|
1669
|
-
putOnIoDupVals(db, key, on = 0, vals, sep = DOT_SEP) {
|
|
1670
|
-
return this.putIoDupVals(db, onKey(key, on, sep), vals);
|
|
1671
|
-
}
|
|
1672
|
-
/** Add one insertion-ordered duplicate at ordinal effective key. Mirrors KERIpy `addOnIoDupVal`. */
|
|
1673
|
-
addOnIoDupVal(db, key, on = 0, val = null, sep = DOT_SEP) {
|
|
1674
|
-
return this.addIoDupVal(db, onKey(key, on, sep), val);
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Append one insertion-ordered duplicate value at the next ordinal bucket for `key`.
|
|
1678
|
-
*
|
|
1679
|
-
* Mirrors KERIpy `appendOnIoDupVal`.
|
|
1680
|
-
*/
|
|
1681
|
-
appendOnIoDupVal(db, key, val, sep = DOT_SEP) {
|
|
1682
|
-
return this.appendOnVal(db, key, withIoDupProem(0, val), sep);
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
|
-
* TypeScript-only convenience returning all logical duplicates at one exact ordinal.
|
|
1686
|
-
*
|
|
1687
|
-
* KERIpy exposes scan-style iterators across ordinals rather than this exact
|
|
1688
|
-
* per-ordinal list helper.
|
|
1689
|
-
*/
|
|
1690
|
-
getOnIoDupVals(db, key, on = 0, sep = DOT_SEP) {
|
|
1691
|
-
if (!key.length) {
|
|
1692
|
-
return [];
|
|
1693
|
-
}
|
|
1694
|
-
return this.getIoDupVals(db, onKey(key, on, sep));
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* TypeScript-only convenience iterating logical duplicates at one exact ordinal.
|
|
1698
|
-
*
|
|
1699
|
-
* No direct KERIpy equivalent: KERIpy `getOnIoDupValIter` scans all ordinals
|
|
1700
|
-
* from `on` onward, which maps to `getOnIoDupIterAll` here.
|
|
1701
|
-
*/
|
|
1702
|
-
*getOnIoDupValsIter(db, key, on = 0, sep = DOT_SEP) {
|
|
1703
|
-
if (!key.length) {
|
|
1704
|
-
return;
|
|
1705
|
-
}
|
|
1706
|
-
yield* this.getIoDupValsIter(db, onKey(key, on, sep));
|
|
1707
|
-
}
|
|
1708
|
-
/**
|
|
1709
|
-
* TypeScript-only convenience fetching the last logical duplicate at one exact ordinal.
|
|
1710
|
-
*/
|
|
1711
|
-
getOnIoDupLast(db, key, on = 0, sep = DOT_SEP) {
|
|
1712
|
-
return this.getIoDupValLast(db, onKey(key, on, sep));
|
|
1713
|
-
}
|
|
1714
|
-
/** Iterate the last logical duplicate value per ordinal bucket. Mirrors KERIpy `getOnIoDupLastValIter`. */
|
|
1715
|
-
*getOnIoDupLastValIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1716
|
-
for (const [_, __, val] of this.getOnIoDupLastItemIter(db, key, on, sep)) {
|
|
1717
|
-
yield val;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
/** Iterate `(key, on, val)` where `val` is the last logical duplicate per ordinal bucket. Mirrors KERIpy `getOnIoDupLastItemIter`. */
|
|
1721
|
-
*getOnIoDupLastItemIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1722
|
-
this.requireEnv();
|
|
1723
|
-
const start = key.length ? onKey(key, on, sep) : new Uint8Array(0);
|
|
1724
|
-
let lonkey = null;
|
|
1725
|
-
let lkey = null;
|
|
1726
|
-
let lon = 0;
|
|
1727
|
-
let last = null;
|
|
1728
|
-
for (const entry of db.getRange({ start })) {
|
|
1729
|
-
const onkeyBytes = copyDbBytes(entry.key);
|
|
1730
|
-
const cval = copyDbBytes(entry.value);
|
|
1731
|
-
const [ckey, con] = splitOnKey(onkeyBytes, sep);
|
|
1732
|
-
if (key.length && !bytesEqual(ckey, key)) {
|
|
1733
|
-
break;
|
|
1734
|
-
}
|
|
1735
|
-
if (lonkey === null || !bytesEqual(lonkey, onkeyBytes)) {
|
|
1736
|
-
if (lonkey !== null && lkey && last) {
|
|
1737
|
-
yield [lkey, lon, stripIoDupProem(last)];
|
|
1738
|
-
}
|
|
1739
|
-
lonkey = new Uint8Array(onkeyBytes);
|
|
1740
|
-
lkey = ckey;
|
|
1741
|
-
lon = con;
|
|
1742
|
-
}
|
|
1743
|
-
last = cval;
|
|
1744
|
-
}
|
|
1745
|
-
if (lonkey !== null && lkey && last) {
|
|
1746
|
-
yield [lkey, lon, stripIoDupProem(last)];
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
/**
|
|
1750
|
-
* Iterate `(key, on, val)` logical duplicates across a top branch.
|
|
1751
|
-
*
|
|
1752
|
-
* This mirrors the KERIpy helper added for normalized `OnIoDupSuber`
|
|
1753
|
-
* branch iteration and strips the hidden insertion-order proem from each
|
|
1754
|
-
* duplicate value before yielding it.
|
|
1755
|
-
*/
|
|
1756
|
-
*getOnTopIoDupItemIter(db, top = new Uint8Array(0), sep = DOT_SEP) {
|
|
1757
|
-
for (const [key, on, val] of this.getOnTopItemIter(db, top, sep)) {
|
|
1758
|
-
yield [key, on, stripIoDupProem(val)];
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
/** Delete all insertion-ordered duplicates at one ordinal effective key. TypeScript-local rename of KERIpy `delOnIoDupVals`. */
|
|
1762
|
-
delOnIoDups(db, key, on = 0, sep = DOT_SEP) {
|
|
1763
|
-
return this.delIoDupVals(db, onKey(key, on, sep));
|
|
1764
|
-
}
|
|
1765
|
-
/** Delete one insertion-ordered duplicate at one ordinal effective key. Mirrors KERIpy `delOnIoDupVal`. */
|
|
1766
|
-
delOnIoDupVal(db, key, on = 0, val = new Uint8Array(0), sep = DOT_SEP) {
|
|
1767
|
-
return this.delIoDupVal(db, onKey(key, on, sep), val);
|
|
1768
|
-
}
|
|
1769
|
-
/**
|
|
1770
|
-
* TypeScript-only convenience counting logical duplicates at one exact ordinal.
|
|
1771
|
-
*
|
|
1772
|
-
* KERIpy does not expose this exact count helper on `LMDBer`.
|
|
1773
|
-
*/
|
|
1774
|
-
cntOnIoDups(db, key, on = 0, sep = DOT_SEP) {
|
|
1775
|
-
return this.cntIoDups(db, onKey(key, on, sep));
|
|
1776
|
-
}
|
|
1777
|
-
/** Iterate backwards over logical duplicate values. Mirrors KERIpy `getOnIoDupValBackIter`. */
|
|
1778
|
-
*getOnIoDupValBackIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1779
|
-
for (const [_, __, val] of this.getOnIoDupItemBackIter(db, key, on, sep)) {
|
|
1780
|
-
yield val;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
/** Iterate backwards over `(key, on, val)` logical duplicates. Mirrors KERIpy `getOnIoDupItemBackIter`. */
|
|
1784
|
-
*getOnIoDupItemBackIter(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1785
|
-
const items = key.length
|
|
1786
|
-
? [...this.getOnIoDupItemIterAll(db, key, 0, sep)].filter((item) => item[1] <= on)
|
|
1787
|
-
: [...this.getOnIoDupItemIterAll(db, new Uint8Array(0), 0, sep)];
|
|
1788
|
-
for (let i = items.length - 1; i >= 0; i--) {
|
|
1789
|
-
yield items[i];
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
/**
|
|
1793
|
-
* Iterate logical duplicate values for ordinals `>= on`.
|
|
1794
|
-
*
|
|
1795
|
-
* TypeScript-local name for KERIpy `getOnIoDupValIter`.
|
|
1796
|
-
*/
|
|
1797
|
-
*getOnIoDupIterAll(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1798
|
-
for (const [_, __, val] of this.getOnIoDupItemIterAll(db, key, on, sep)) {
|
|
1799
|
-
yield val;
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
/**
|
|
1803
|
-
* Iterate `(key, on, val)` logical duplicates for ordinals `>= on`.
|
|
1804
|
-
*
|
|
1805
|
-
* TypeScript-local name for KERIpy `getOnIoDupItemIter`.
|
|
1806
|
-
*/
|
|
1807
|
-
*getOnIoDupItemIterAll(db, key = new Uint8Array(0), on = 0, sep = DOT_SEP) {
|
|
1808
|
-
for (const [ckey, con, cval] of this.getOnAllItemIter(db, key, on, sep)) {
|
|
1809
|
-
yield [ckey, con, stripIoDupProem(cval)];
|
|
1810
|
-
}
|
|
941
|
+
delVals(table, key) {
|
|
942
|
+
return this.delVal(table, key);
|
|
1811
943
|
}
|
|
1812
944
|
}
|
|
1813
945
|
// Class constants
|