@x5e/gink
Version:
an eventually consistent database
206 lines • 8.59 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Group = void 0;
const Database_1 = require("./Database");
const Container_1 = require("./Container");
const utils_1 = require("./utils");
const factories_1 = require("./factories");
const builders_1 = require("./builders");
class Group extends Container_1.Container {
constructor(database, address) {
super(database, address, builders_1.Behavior.GROUP);
}
static get(database, muid) {
database = database || Database_1.Database.recent;
if (!muid) {
muid = { timestamp: -1, medallion: -1, offset: builders_1.Behavior.GROUP };
}
return new Group(database, muid);
}
static async create(database, meta) {
database = database || Database_1.Database.recent;
const muid = await Container_1.Container.addContainer({
behavior: builders_1.Behavior.GROUP,
database,
meta,
});
return new Group(database, muid);
}
/**
* Includes a Muid or Container in the group.
* @param key either a container or a Muid to include
* @param change an optional bundler to put this change into
* @returns a promise that resolves to the Muid for the inclusion
*/
async include(key, meta) {
return await this.addEntry(key, Container_1.Container.INCLUSION, meta);
}
/**
* Excludes a Muid or Container from the group.
* @param key either a Muid or container to exclude
* @param change an optional bundler to put this in
* @returns a promise that resolves to the Muid for the exclusion
*/
async exclude(key, meta) {
return await this.addEntry(key, Container_1.Container.DELETION, meta);
}
/**
* This returns the number of inclusions only, NOT exclusions.
* @returns how many containers are included in the group
*/
async size() {
return (await this.includedAsArray()).length;
}
/**
* Whether or not the given key is explicitly included in the group.
* @param key either a Muid or container to check if it is included
* @param asOf optional timestamp to look back to
* @returns a promise that resolves to a boolean stating whether the key is explicitly included
*/
async isIncluded(key, asOf) {
if ("address" in key) {
key = key.address;
}
const entry = await this.database.store.getEntryByKey(this.address, key, asOf);
if (entry && !entry.deletion) {
return true;
}
return false;
}
/**
* Function to iterate over the containers in the group.
* @param asOf optional timestamp to look back to
* @returns an async iterator across all containers in the group
*/
getMembers(asOf) {
const thisGroup = this;
let container;
return (async function* () {
const entries = await thisGroup.database.store.getKeyedEntries(thisGroup.address, asOf);
for (const [key, entry] of entries) {
container = await (0, factories_1.interpret)(entry, thisGroup.database);
if ("behavior" in container) {
yield container;
}
}
})();
}
/**
* Dumps the contents of this group to a javascript array.Only includes explicitly included members.
* useful for debugging and could also be used to export data by walking the tree
* @param asOf effective time to get the dump for: leave undefined to get data as of the present
* @returns an array containing Values (e.g. numbers, strings) and Containers (e.g. other Lists, Boxes, Directories)
*/
async includedAsArray(asOf) {
const thisList = this;
let toArray = [];
let container;
const entries = await thisList.database.store.getKeyedEntries(thisList.address, asOf);
for (const [key, entry] of entries) {
container = await (0, factories_1.interpret)(entry, thisList.database);
if ("behavior" in container) {
toArray.push(container);
}
else {
throw Error("All entries should be containers - something is broken");
}
}
return toArray;
}
async reset(toTime, recurse, meta) {
if (recurse)
throw new Error("recurse not implemented for Group");
const bundler = await this.database.startBundle(meta);
if (!toTime) {
// If no time is specified, we are resetting to epoch, which is just a clear
this.clear(false, { bundler });
}
else {
const union = new Set();
const entriesThen = await this.database.store.getKeyedEntries(this.address, toTime);
const entriesNow = await this.database.store.getKeyedEntries(this.address);
for (const [key, entry] of entriesThen) {
const storageKey = entry.storageKey;
union.add(storageKey);
}
for (const [key, entry] of entriesNow) {
const storageKey = entry.storageKey;
union.add(storageKey);
}
for (const key of union) {
const genericKey = (0, utils_1.fromStorageKey)(key);
const thenEntry = await this.database.store.getEntryByKey(this.address, genericKey, toTime);
const nowEntry = await this.database.store.getEntryByKey(this.address, genericKey);
(0, utils_1.ensure)(nowEntry || thenEntry, "both then and now undefined?");
if (!nowEntry) {
// This key was present then, but not now, so we need to add it back
(0, utils_1.ensure)(thenEntry, "missing then entry?");
await this.addEntry(genericKey, thenEntry.value, {
bundler,
});
}
else if (!thenEntry) {
// This key is present now, but not then, so we need to delete it
(0, utils_1.ensure)(nowEntry, "missing now entry?");
await this.addEntry(genericKey, Container_1.Container.DELETION, {
bundler,
});
}
else if (nowEntry.deletion !== thenEntry.deletion) {
if (nowEntry.deletion) {
// Present then, deleted now. Need to revive.
await this.addEntry(genericKey, Container_1.Container.INCLUSION, {
bundler,
});
}
else if (thenEntry.deletion) {
// Present now, deleted then. Need to delete.
await this.addEntry(genericKey, Container_1.Container.DELETION, {
bundler,
});
}
}
else {
(0, utils_1.ensure)(nowEntry.deletion === thenEntry.deletion, "last case should be same entry");
}
}
}
if (!meta?.bundler) {
await bundler.commit();
}
}
/**
* Generates a JSON representation of the data in the group.
* Mostly intended for demo/debug purposes.
* @param indent true to pretty print (not yet implemented)
* @param asOf optional timestamp to look back to
* @param seen (internal use only! This prevents cycles from breaking things)
* @returns a JSON string
*/
async toJson(indent = false, asOf, seen) {
//TODO(https://github.com/google/gink/issues/62): add indentation
(0, utils_1.ensure)(indent === false, "indent not implemented");
if (seen === undefined)
seen = new Set();
const mySig = (0, utils_1.muidToString)(this.address);
if (seen.has(mySig))
return "null";
seen.add(mySig);
const asArray = await this.includedAsArray(asOf);
let returning = "[";
let first = true;
for (const container of asArray) {
if (first) {
first = false;
}
else {
returning += ",";
}
returning += await (0, factories_1.toJson)(container, indent === false ? false : +indent + 1, asOf, seen);
}
returning += "]";
return returning;
}
}
exports.Group = Group;
//# sourceMappingURL=Group.js.map