UNPKG

jinaga

Version:

Data management for web and mobile applications.

216 lines 9.61 kB
"use strict"; 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.MemoryStore = exports.getPredecessors = void 0; const hydrate_1 = require("../fact/hydrate"); const specification_runner_1 = require("../specification/specification-runner"); const storage_1 = require("../storage"); 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 loadAll(references, source, target) { references.forEach(reference => { const predicate = (0, storage_1.factEnvelopeEquals)(reference); if (!target.some(predicate)) { const record = source.find(predicate); if (record) { target.push(record); for (const role in record.fact.predecessors) { const predecessors = getPredecessors(record.fact, role); loadAll(predecessors, source, target); } } } }); } class MemoryStore { constructor(timeProvider) { this.factEnvelopes = []; this.bookmarksByFeed = {}; this.mruDateBySpecificationHash = {}; this.timeProvider = timeProvider !== null && timeProvider !== void 0 ? timeProvider : (() => new Date()); this.runner = new specification_runner_1.SpecificationRunner({ getPredecessors: this.getPredecessors.bind(this), getSuccessors: this.getSuccessors.bind(this), findFact: this.findFact.bind(this), hydrate: this.hydrate.bind(this) }); } close() { return Promise.resolve(); } save(envelopes) { const added = []; const timestamp = this.timeProvider(); for (const envelope of envelopes) { const isFact = (0, storage_1.factReferenceEquals)(envelope.fact); const existing = this.factEnvelopes.find(e => isFact(e.fact)); if (!existing) { // Add timestamp to new facts const timestampedEnvelope = { fact: Object.assign(Object.assign({}, envelope.fact), { timestamp }), signatures: envelope.signatures }; this.factEnvelopes.push(timestampedEnvelope); added.push(envelope); } else { // Preserve original timestamp for duplicates const newSignatures = envelope.signatures.filter(s => !existing.signatures.some(s2 => s2.publicKey === s.publicKey)); if (newSignatures.length > 0) { existing.signatures = [...existing.signatures, ...newSignatures]; } } } return Promise.resolve(added); } read(start, specification) { return this.runner.read(start, specification); } feed(feed, start, _bookmark) { return __awaiter(this, void 0, void 0, function* () { // TODO: Implement monotonic bookmarks defined by the store. // Bookmarks must be monotonically increasing quantities with a store-defined // format and comparison function. Fact hashes are not monotonic, so prior // implementations that derived bookmarks from hashes have been removed. // For now, feeds ignore bookmarks and always return empty bookmark values. // Compute projected results using the same engine as application reads const results = yield this.runner.read(start, feed); // Map each projected result to a tuple of fact references const tuples = results.map(result => { const references = Object.values(result.tuple); const unique = (0, storage_1.uniqueFactReferences)(references); return { facts: unique, bookmark: '' }; }); return { tuples, bookmark: '' }; }); } whichExist(references) { const existing = references.filter(reference => { return this.factEnvelopes.some((0, storage_1.factEnvelopeEquals)(reference)); }); return Promise.resolve(existing); } load(references) { const target = []; loadAll(references, this.factEnvelopes, target); return Promise.resolve(target); } purge(purgeConditions) { // Not yet implemented return Promise.resolve(0); } purgeDescendants(purgeRoot, triggers) { // Remove all facts that are descendants of the purge root // and not a trigger or an ancestor of a trigger. const triggersAndTheirAncestors = [...triggers]; for (const trigger of triggers) { const triggerEnvelope = this.factEnvelopes.find((0, storage_1.factEnvelopeEquals)(trigger)); if (triggerEnvelope) { this.addAllAncestors(triggerEnvelope.fact, triggersAndTheirAncestors); } } const startingCount = this.factEnvelopes.length; this.factEnvelopes = this.factEnvelopes.filter(e => { const ancestors = this.ancestorsOf(e.fact); return !ancestors.some((0, storage_1.factReferenceEquals)(purgeRoot)) || triggersAndTheirAncestors.some((0, storage_1.factReferenceEquals)(e.fact)); }); const endingCount = this.factEnvelopes.length; return Promise.resolve(startingCount - endingCount); } loadBookmark(feed) { const bookmark = this.bookmarksByFeed.hasOwnProperty(feed) ? this.bookmarksByFeed[feed] : ''; return Promise.resolve(bookmark); } saveBookmark(feed, bookmark) { this.bookmarksByFeed[feed] = bookmark; return Promise.resolve(); } getMruDate(specificationHash) { var _a; const mruDate = (_a = this.mruDateBySpecificationHash[specificationHash]) !== null && _a !== void 0 ? _a : null; return Promise.resolve(mruDate); } setMruDate(specificationHash, mruDate) { this.mruDateBySpecificationHash[specificationHash] = mruDate; return Promise.resolve(); } findFact(reference) { var _a, _b; const envelope = (_a = this.factEnvelopes.find((0, storage_1.factEnvelopeEquals)(reference))) !== null && _a !== void 0 ? _a : null; return Promise.resolve((_b = envelope === null || envelope === void 0 ? void 0 : envelope.fact) !== null && _b !== void 0 ? _b : null); } getPredecessors(reference, name, predecessorType) { var _a; const record = (_a = this.factEnvelopes.find((0, storage_1.factEnvelopeEquals)(reference))) !== null && _a !== void 0 ? _a : null; if (record === null) { // Return empty array instead of throwing when fact is not found // This allows specifications to handle unpersisted given facts gracefully return Promise.resolve([]); } const predecessors = getPredecessors(record.fact, name); const matching = predecessors.filter(predecessor => predecessor.type === predecessorType); return Promise.resolve(matching); } getSuccessors(reference, name, successorType) { const successors = this.factEnvelopes.filter(record => record.fact.type === successorType && getPredecessors(record.fact, name).some((0, storage_1.factReferenceEquals)(reference))) .map(e => e.fact); return Promise.resolve(successors); } ancestorsOf(fact) { const ancestors = []; this.addAllAncestors(fact, ancestors); return ancestors; } addAllAncestors(fact, ancestors) { for (const role in fact.predecessors) { const predecessors = getPredecessors(fact, role); predecessors.forEach(predecessor => { if (!ancestors.some((0, storage_1.factReferenceEquals)(predecessor))) { ancestors.push(predecessor); const predecessorRecord = this.factEnvelopes.find((0, storage_1.factEnvelopeEquals)(predecessor)); if (predecessorRecord) { this.addAllAncestors(predecessorRecord.fact, ancestors); } } }); } } hydrate(reference) { const fact = (0, hydrate_1.hydrateFromTree)([reference], this.factEnvelopes.map(e => e.fact)); if (fact.length === 0) { return Promise.resolve(undefined); } if (fact.length > 1) { throw new Error(`The fact ${reference} is defined more than once.`); } return Promise.resolve(fact[0]); } } exports.MemoryStore = MemoryStore; //# sourceMappingURL=memory-store.js.map