@x5e/gink
Version:
an eventually consistent database
114 lines • 4.73 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Bundler = void 0;
const builders_1 = require("./builders");
const utils_1 = require("./utils");
class Bundler {
constructor(pendingComment, preAssignedMedallion) {
this.pendingComment = pendingComment;
this.preAssignedMedallion = preAssignedMedallion;
// note: this class is unit tested as part of Store.test.ts
this.bundleInfo = undefined;
this.bundleBytes = undefined;
this.bundleBuilder = new builders_1.BundleBuilder();
this.countItems = 0;
}
requireNotSealed() {
if (this.bundleInfo)
throw new Error("This Bundler has already been sealed.");
}
get info() {
return (0, utils_1.ensure)(this.bundleInfo, "not yet sealed");
}
get bytes() {
return (0, utils_1.ensure)(this.bundleBytes, "not yet sealed!");
}
get builder() {
if (!this.bundleInfo)
throw new Error("Bundle not yet sealed.");
return this.bundleBuilder;
}
set comment(value) {
this.requireNotSealed();
this.pendingComment = value;
}
get comment() {
var _a;
return this.pendingComment || ((_a = this.bundleInfo) === null || _a === void 0 ? void 0 : _a.comment);
}
get medallion() {
var _a;
return this.preAssignedMedallion || ((_a = this.bundleInfo) === null || _a === void 0 ? void 0 : _a.medallion);
}
get timestamp() {
var _a;
return (_a = this.bundleInfo) === null || _a === void 0 ? void 0 : _a.timestamp;
}
addEntry(entryBuilder) {
return this.addChange(new builders_1.ChangeBuilder().setEntry(entryBuilder));
}
addContainer(containerBuilder) {
return this.addChange(new builders_1.ChangeBuilder().setContainer(containerBuilder));
}
/**
*
* @param changeBuilder a protobuf Change ready to be serialized
* @returns an Address who's offset is immediately available and whose medallion and
* timestamp become defined when this Bundle is sealed.
*/
addChange(changeBuilder) {
this.requireNotSealed();
const offset = ++this.countItems;
this.bundleBuilder.getChangesList().push(changeBuilder);
// Using an anonymous class here because I only need the interface of Address,
// but I need some non-trivial behavior: the timestamp and possibly medallion
// are undefined until the associated bundle is finalized, then all the
// components of the address become well-defined.
return new (class {
constructor(bundler, offset) {
this.bundler = bundler;
this.offset = offset;
}
get medallion() {
return this.bundler.medallion;
}
get timestamp() {
return this.bundler.timestamp;
}
})(this, offset);
}
/**
* Intended to be called by a Database to finalize a bundle.
* @param bundleInfo the bundle metadata to add when serializing
* @returns serialized
*/
seal(bundleInfo, keyPair, priorHash, identity) {
this.requireNotSealed();
if (this.preAssignedMedallion &&
this.preAssignedMedallion !== bundleInfo.medallion) {
throw new Error("specified bundleInfo doesn't match pre-assigned medallion");
}
this.bundleInfo = Object.assign({}, bundleInfo);
this.bundleInfo.comment = this.pendingComment;
this.bundleBuilder.setComment(this.pendingComment);
this.bundleBuilder.setTimestamp(bundleInfo.timestamp);
this.bundleBuilder.setPrevious(bundleInfo.priorTime);
this.bundleBuilder.setChainStart(bundleInfo.chainStart);
this.bundleBuilder.setMedallion(bundleInfo.medallion);
this.bundleBuilder.setComment(this.bundleInfo.comment);
if (bundleInfo.chainStart === bundleInfo.timestamp) {
(0, utils_1.ensure)(identity, "identity required for chain-start bundles");
this.bundleBuilder.setIdentity(identity);
this.bundleBuilder.setVerifyKey(keyPair.publicKey);
}
else {
(0, utils_1.ensure)(priorHash && priorHash.length == 32, "need prior_hash");
(0, utils_1.ensure)(!identity, "identity not allowed for non-chain-start bundles");
this.bundleBuilder.setPriorHash(priorHash);
}
this.bundleBytes = (0, utils_1.signBundle)(this.bundleBuilder.serializeBinary(), keyPair.secretKey);
this.bundleInfo.hashCode = (0, utils_1.digest)(this.bundleBytes);
}
}
exports.Bundler = Bundler;
//# sourceMappingURL=Bundler.js.map