@orbitdb/ordered-keyvalue-db
Version:
Ordered keyvalue database type for orbit-db.
138 lines • 4.42 kB
JavaScript
import { Database, } from "@orbitdb/core";
import itAll from "it-all";
import { getScalePosition } from "./utils.js";
const type = "ordered-keyvalue";
const OrderedKeyValue = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate, }) => {
const database = await Database({
ipfs,
identity,
address,
name,
access,
directory,
meta,
headsStorage,
entryStorage,
indexStorage,
referencesCount,
syncAutomatically,
onUpdate,
});
const { put, set, del, move, get, iterator, all } = OrderedKeyValueApi({
database,
});
return {
...database,
type,
put,
set,
del,
move,
get,
iterator,
all,
};
};
OrderedKeyValue.type = type;
export const OrderedKeyValueApi = ({ database, }) => {
const put = async (key, value, position) => {
// Somewhat inefficient, I suppose, but we need to know which entries are already present.
const entries = await itAll(iterator());
// Avoid overwriting existing position; default to end of list (findIndex gives -1)
let scaledPosition = undefined;
if (position === undefined) {
scaledPosition = entries.find((e) => e.key === key)?.position;
}
if (scaledPosition === undefined) {
scaledPosition = await getScalePosition({
entries,
key,
position: position ?? -1,
});
}
const entryValue = {
value,
position: scaledPosition,
};
return database.addOperation({ op: "PUT", key, value: entryValue });
};
const move = async (key, position) => {
// Somewhat inefficient, I suppose, but we need to know which entries are already present.
const entries = await itAll(iterator());
position = await getScalePosition({ entries, key, position });
await database.addOperation({ op: "MOVE", key, value: position });
};
const del = async (key) => {
return database.addOperation({ op: "DEL", key, value: null });
};
const get = async (key) => {
for await (const entry of database.log.traverse()) {
const { op, key: k, value } = entry.payload;
if (op === "PUT" && k === key) {
return value.value;
}
else if (op === "DEL" && k === key) {
return undefined;
}
}
return undefined;
};
const iterator = async function* ({ amount, } = {}) {
let count = 0;
// `true` indicates a `PUT` operation; `number` indicates a `MOVE` operation
const keys = {};
for await (const entry of database.log.traverse()) {
const { op, key, value } = entry.payload;
if (typeof key !== "string")
continue;
if (op === "PUT" && keys[key] !== true) {
const hash = entry.hash;
const putValue = value;
const position = typeof keys[key] === "number"
? keys[key]
: putValue.position;
keys[key] = true;
count++;
yield {
key,
value: putValue.value,
position,
hash,
};
}
else if (op === "MOVE" && !keys[key]) {
keys[key] = value;
}
else if (op === "DEL") {
keys[key] = true;
}
if (amount !== undefined && count >= amount) {
break;
}
}
};
const all = async () => {
const entries = [];
for await (const entry of iterator()) {
entries.push(entry);
}
return entries
.sort((a, b) => a.position - b.position)
.map((e) => ({
key: e.key,
value: e.value,
hash: e.hash,
}));
};
return {
get,
set: put, // Alias for put()
put,
move,
del,
iterator,
all,
};
};
export default OrderedKeyValue;
//# sourceMappingURL=ordered-keyvalue.js.map