@baqhub/sdk
Version:
The official JavaScript SDK for the BAQ federated app platform.
261 lines (260 loc) • 9.02 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Record = exports.RNoContentRecord = exports.AnyRecord = exports.subscriptionRecordContainer = exports.NoContentRecordAction = exports.RecordMode = exports.RecordSource = void 0;
const tslib_1 = require("tslib");
const IO = tslib_1.__importStar(require("../../helpers/io.js"));
const uuid_js_1 = require("../../helpers/uuid.js");
const entityLink_js_1 = require("../links/entityLink.js");
const recordPermissions_js_1 = require("./recordPermissions.js");
const recordType_js_1 = require("./recordType.js");
var RecordSource;
(function (RecordSource) {
RecordSource["SELF"] = "self";
RecordSource["RESOLUTION"] = "resolution";
RecordSource["NOTIFICATION"] = "notification";
RecordSource["NOTIFICATION_UNKNOWN"] = "notification_unknown";
RecordSource["SUBSCRIPTION"] = "subscription";
RecordSource["PROXY"] = "proxy";
})(RecordSource || (exports.RecordSource = RecordSource = {}));
var RecordMode;
(function (RecordMode) {
RecordMode["SYNCED"] = "synced";
RecordMode["LOCAL"] = "local";
})(RecordMode || (exports.RecordMode = RecordMode = {}));
var NoContentRecordAction;
(function (NoContentRecordAction) {
NoContentRecordAction["DELETE"] = "delete";
NoContentRecordAction["LOCAL"] = "local";
NoContentRecordAction["REPORT"] = "report";
NoContentRecordAction["LEAVE"] = "leave";
})(NoContentRecordAction || (exports.NoContentRecordAction = NoContentRecordAction = {}));
exports.subscriptionRecordContainer = {
SubscriptionRecord: null,
};
//
// Runtime model.
//
const RRecordSource = IO.weakEnumeration(RecordSource);
const RRecordMode = IO.weakEnumeration(RecordMode);
const RRecordVersion = IO.intersection([
IO.object({
author: entityLink_js_1.EntityLink.io(),
hash: IO.union([IO.undefined, IO.string]),
createdAt: IO.isoDate,
receivedAt: IO.union([IO.undefined, IO.isoDate]),
}),
IO.partialObject({
parentHash: IO.string,
}),
]);
class RecordClassBase extends IO.Type {
RType;
RContent;
constructor(RType, RContent) {
const model = IO.object({
author: entityLink_js_1.EntityLink.io(),
id: IO.string,
source: IO.defaultValue(RRecordSource, RecordSource.PROXY),
createdAt: IO.isoDate,
receivedAt: IO.union([IO.undefined, IO.isoDate]),
version: IO.union([IO.undefined, RRecordVersion]),
permissions: recordPermissions_js_1.RRecordPermissions,
type: RType,
content: RContent,
mode: IO.defaultValue(RRecordMode, RecordMode.SYNCED),
});
super("Record", model.is, model.validate, model.encode);
this.RType = RType;
this.RContent = RContent;
}
}
class RecordClass extends RecordClassBase {
type;
constructor(type, RType, RContent) {
super(RType, RContent);
this.type = type;
this.link = {
entity: type.entity,
recordId: type.recordId,
};
}
link;
new(entity, content, options) {
return buildRecord(this, entity, this.type, content, options);
}
update(entity, record, content, options) {
return buildRecordUpdate(this, entity, record, content, options);
}
subscribe(entity, publisherEntity, options) {
return buildSubscriptionRecord(entity, publisherEntity, this.type, options);
}
}
function record(type, RType, RContent) {
return new RecordClass(type, RType, RContent);
}
function cleanRecord(RRecord) {
return RRecord;
}
exports.AnyRecord = new RecordClassBase(IO.any, IO.any);
const RNoContentRecordAction = IO.weakEnumeration(NoContentRecordAction);
exports.RNoContentRecord = IO.object({
author: entityLink_js_1.EntityLink.io(),
id: IO.string,
source: RRecordSource,
createdAt: IO.isoDate,
receivedAt: IO.union([IO.undefined, IO.isoDate]),
version: IO.union([IO.undefined, RRecordVersion]),
permissions: recordPermissions_js_1.RRecordPermissions,
type: recordType_js_1.RAnyRecordType,
noContent: IO.dualObject({ action: RNoContentRecordAction }, { links: IO.unknown }),
mode: IO.defaultValue(RRecordMode, RecordMode.SYNCED),
});
function buildRecord(model, entity, type, content, { id, permissions, mode } = {}) {
const record = {
author: { entity },
id: id || uuid_js_1.Uuid.new(),
source: "self",
createdAt: new Date(),
receivedAt: undefined,
version: undefined,
permissions: permissions || recordPermissions_js_1.RecordPermissions.private,
type,
content,
mode: mode || RecordMode.SYNCED,
};
return IO.validate(model, record);
}
function buildRecordUpdate(model, entity, record, content, { permissions } = {}) {
if (record.source === "proxy") {
throw new Error("Cannot update proxied record.");
}
// Make sure the new date is higher.
// TODO: bound this and keep local server offset.
const versionCreatedAt = record.version?.createdAt;
const now = new Date();
const newVersionCreatedAt = versionCreatedAt && versionCreatedAt > now
? new Date(versionCreatedAt.getTime() + 1)
: now;
const recordUpdate = {
author: { entity: record.author.entity },
id: record.id,
source: "self",
createdAt: record.createdAt,
receivedAt: record.receivedAt,
version: {
author: { entity },
hash: undefined,
createdAt: newVersionCreatedAt,
receivedAt: undefined,
parentHash: record.version?.hash,
},
permissions: permissions || record.permissions,
type: record.type,
content,
mode: record.mode,
};
return IO.validate(model, recordUpdate);
}
function buildNoContentRecord(entity, record, action = NoContentRecordAction.DELETE) {
if (record.source === "proxy") {
throw new Error("Cannot delete proxied record.");
}
// Make sure the new date is higher.
// TODO: bound this and keep local server offset.
const versionCreatedAt = record.version?.createdAt;
const now = new Date();
const newVersionCreatedAt = versionCreatedAt && versionCreatedAt > now
? new Date(versionCreatedAt.getTime() + 1)
: now;
return {
author: { entity: record.author.entity },
id: record.id,
source: "self",
createdAt: record.createdAt,
receivedAt: undefined,
version: {
author: { entity },
hash: undefined,
createdAt: newVersionCreatedAt,
receivedAt: undefined,
parentHash: record.version?.hash,
},
permissions: record.permissions,
type: record.type,
noContent: { action },
mode: record.mode,
};
}
function buildSubscriptionRecord(entity, publisherEntity, type, { id, readPermissions } = {}) {
return buildRecord(exports.subscriptionRecordContainer.SubscriptionRecord, entity, exports.subscriptionRecordContainer.SubscriptionRecord.type, {
publisher: { entity: publisherEntity },
recordType: {
entity: type.entity,
recordId: type.recordId,
},
}, {
id,
permissions: {
read: readPermissions,
notify: [{ entity: publisherEntity }],
},
});
}
//
// Helpers.
//
function isSameRecord(record1, record2) {
return (record1.author.entity === record2.author.entity && record1.id === record2.id);
}
function isPublicRecord(record) {
return record.permissions.read === "public";
}
function recordHasType(record, type) {
return (record.type.entity === type.type.entity &&
record.type.recordId === type.type.recordId);
}
function updateToSynced(record) {
return { ...record, mode: RecordMode.SYNCED };
}
function updateToSelf(record) {
return { ...record, source: RecordSource.SELF };
}
function updateToResolution(record) {
return { ...record, source: "resolution" };
}
function recordToKey(record) {
return `${record.author.entity}+${record.id}`;
}
function recordToLink(record) {
return {
entity: record.author.entity,
recordId: record.id,
};
}
function recordToVersionHash(record) {
if (!record.version?.hash) {
throw new Error("This record does not have a version.");
}
return record.version.hash;
}
function noContentRecordToLink(record) {
return {
entity: record.author.entity,
recordId: record.id,
};
}
exports.Record = {
io: record,
ioClean: cleanRecord,
isSame: isSameRecord,
isPublic: isPublicRecord,
hasType: recordHasType,
toSynced: updateToSynced,
toSelf: updateToSelf,
toResolution: updateToResolution,
toKey: recordToKey,
toLink: recordToLink,
toVersionHash: recordToVersionHash,
noContentToLink: noContentRecordToLink,
delete: buildNoContentRecord,
};