jinaga
Version:
Data management for web and mobile applications.
225 lines • 11.9 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IndexedDBStore = exports.getPredecessors = void 0;
const hydrate_1 = require("../fact/hydrate");
const sorter_1 = require("../fact/sorter");
const specification_runner_1 = require("../specification/specification-runner");
const fn_1 = require("../util/fn");
const driver_1 = require("./driver");
function getPredecessors(fact, role) {
if (!fact) {
return [];
}
const predecessors = fact.predecessors[role];
if (predecessors) {
if (Array.isArray(predecessors)) {
return predecessors;
}
else {
return [predecessors];
}
}
else {
return [];
}
}
exports.getPredecessors = getPredecessors;
function findAllAncestors(facts) {
const sorter = new sorter_1.TopologicalSorter();
const ancestorSets = sorter.sort(facts, (predecessors, fact) => ({
key: (0, driver_1.factKey)(fact),
ancestors: (0, fn_1.flatten)(predecessors, p => p.ancestors)
.filter(fn_1.distinct)
.concat([(0, driver_1.factKey)(fact)])
}));
if (!sorter.finished()) {
throw new Error(`Not all ancestors have been provided: ${JSON.stringify(facts, null, 2)}`);
}
return ancestorSets.reduce((obj, ancestorSet) => (Object.assign(Object.assign({}, obj), { [ancestorSet.key]: ancestorSet.ancestors })), {});
}
function saveFact(factObjectStore, ancestorObjectStore, edgeObjectStore, ancestorMap, fact) {
return __awaiter(this, void 0, void 0, function* () {
const key = (0, driver_1.factKey)(fact);
const edges = (0, fn_1.flatten)(Object.getOwnPropertyNames(fact.predecessors), role => getPredecessors(fact, role)
.map(p => ({ successor: key, predecessor: (0, driver_1.factKey)(p), role })));
if (edges.length) {
const edgeCount = yield ((0, driver_1.execRequest)(edgeObjectStore.index('all').count(key)));
if (edgeCount !== edges.length) {
yield Promise.all(edges.map(edge => (0, driver_1.execRequest)(edgeObjectStore.add(edge))));
}
}
const ancestorCount = yield (0, driver_1.execRequest)(ancestorObjectStore.count(key));
if (ancestorCount === 0) {
yield (0, driver_1.execRequest)(ancestorObjectStore.add(ancestorMap[key], key));
}
const factCount = yield (0, driver_1.execRequest)(factObjectStore.count(key));
if (factCount === 0) {
yield (0, driver_1.execRequest)(factObjectStore.add(fact, key));
return fact;
}
return null;
});
}
class IndexedDBStore {
constructor(indexName) {
this.indexName = indexName;
}
close() {
return Promise.resolve();
}
save(envelopes) {
const ancestorMap = findAllAncestors(envelopes.map(e => e.fact));
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['fact', 'ancestor', 'edge'], 'readwrite', (tx) => __awaiter(this, void 0, void 0, function* () {
const factObjectStore = tx.objectStore('fact');
const ancestorObjectStore = tx.objectStore('ancestor');
const edgeObjectStore = tx.objectStore('edge');
const saved = yield Promise.all(envelopes.map(envelope => saveFact(factObjectStore, ancestorObjectStore, edgeObjectStore, ancestorMap, envelope.fact)));
return saved
.filter(fact => fact)
.map(fact => ({ signatures: [], fact }));
}));
});
}
read(start, specification) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['edge', 'fact', 'ancestor'], 'readonly', tx => {
const edgeObjectStore = tx.objectStore('edge');
const predecessorIndex = edgeObjectStore.index('predecessor');
const successorIndex = edgeObjectStore.index('successor');
const factObjectStore = tx.objectStore('fact');
const ancestorObjectStore = tx.objectStore('ancestor');
const runner = new specification_runner_1.SpecificationRunner({
getPredecessors(reference, name, predecessorType) {
return __awaiter(this, void 0, void 0, function* () {
const edges = yield (0, driver_1.execRequest)(successorIndex.getAll([(0, driver_1.factKey)(reference), name]));
return edges
.map(edge => (0, driver_1.keyToReference)(edge.predecessor))
.filter(reference => reference.type === predecessorType);
});
},
getSuccessors(reference, name, successorType) {
return __awaiter(this, void 0, void 0, function* () {
const edges = yield (0, driver_1.execRequest)(predecessorIndex.getAll([(0, driver_1.factKey)(reference), name]));
return edges
.map(edge => (0, driver_1.keyToReference)(edge.successor))
.filter(reference => reference.type === successorType);
});
},
findFact(reference) {
return (0, driver_1.execRequest)(factObjectStore.get((0, driver_1.factKey)(reference)));
},
hydrate(reference) {
return __awaiter(this, void 0, void 0, function* () {
const allAncestors = yield (0, driver_1.execRequest)(ancestorObjectStore.get((0, driver_1.factKey)(reference)));
const distinctAncestors = allAncestors.filter(fn_1.distinct);
const factRecords = yield Promise.all(distinctAncestors.map(key => (0, driver_1.execRequest)(factObjectStore.get(key))));
const facts = (0, hydrate_1.hydrateFromTree)([reference], factRecords);
if (facts.length === 0) {
return undefined;
}
if (facts.length > 1) {
throw new Error(`The fact ${reference} is defined more than once.`);
}
return facts[0];
});
}
});
return runner.read(start, specification);
});
});
}
feed(feed, start, bookmark) {
throw new Error('Method not implemented.');
}
whichExist(references) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['fact'], 'readonly', (tx) => __awaiter(this, void 0, void 0, function* () {
const factObjectStore = tx.objectStore('fact');
const factKeys = references.map(driver_1.factKey);
const factRecords = yield Promise.all(factKeys.map(key => (0, driver_1.execRequest)(factObjectStore.get(key))));
return factRecords
.filter(fact => !!fact)
.map(fact => (0, driver_1.keyToReference)((0, driver_1.factKey)(fact)));
}));
});
}
load(references) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['fact', 'ancestor'], 'readonly', (tx) => __awaiter(this, void 0, void 0, function* () {
const factObjectStore = tx.objectStore('fact');
const ancestorObjectStore = tx.objectStore('ancestor');
const allAncestors = yield (0, fn_1.flattenAsync)(references, reference => (0, driver_1.execRequest)(ancestorObjectStore.get((0, driver_1.factKey)(reference))));
const distinctAncestors = allAncestors
.filter(fn_1.distinct);
const factRecords = yield Promise.all(distinctAncestors.map((key) => __awaiter(this, void 0, void 0, function* () {
return ({
fact: yield (0, driver_1.execRequest)(factObjectStore.get(key)),
signatures: []
});
})));
return factRecords;
}));
});
}
purge(purgeConditions) {
// Not yet implemented
return Promise.resolve(0);
}
purgeDescendants(purgeRoot, triggers) {
// Not yet implemented
return Promise.resolve(0);
}
loadBookmark(feed) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['bookmark'], 'readonly', (tx) => __awaiter(this, void 0, void 0, function* () {
const bookmarkObjectStore = tx.objectStore('bookmark');
const bookmark = yield (0, driver_1.execRequest)(bookmarkObjectStore.get(feed));
return bookmark || '';
}));
});
}
saveBookmark(feed, bookmark) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['bookmark'], 'readwrite', (tx) => __awaiter(this, void 0, void 0, function* () {
const bookmarkObjectStore = tx.objectStore('bookmark');
yield (0, driver_1.execRequest)(bookmarkObjectStore.put(bookmark, feed));
}));
});
}
getMruDate(specificationHash) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['specification'], 'readonly', (tx) => __awaiter(this, void 0, void 0, function* () {
const specificationObjectStore = tx.objectStore('specification');
const mruDate = yield (0, driver_1.execRequest)(specificationObjectStore.get(specificationHash));
return mruDate || null;
}));
});
}
setMruDate(specificationHash, mruDate) {
return (0, driver_1.withDatabase)(this.indexName, db => {
return (0, driver_1.withTransaction)(db, ['specification'], 'readwrite', (tx) => __awaiter(this, void 0, void 0, function* () {
const specificationObjectStore = tx.objectStore('specification');
yield (0, driver_1.execRequest)(specificationObjectStore.put(mruDate, specificationHash));
// Remove specifications older than 30 days.
const oldMruDate = new Date(mruDate.getTime() - 1000 * 60 * 60 * 24 * 30);
const cursor = yield (0, driver_1.execRequest)(specificationObjectStore.openCursor(IDBKeyRange.upperBound(oldMruDate)));
while (cursor) {
yield (0, driver_1.execRequest)(cursor.delete());
yield cursor.continue();
}
}));
});
}
}
exports.IndexedDBStore = IndexedDBStore;
//# sourceMappingURL=indexeddb-store.js.map