UNPKG

jinaga

Version:

Data management for web and mobile applications.

136 lines 7.14 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.IndexedDBQueue = void 0; const driver_1 = require("./driver"); const fn_1 = require("../util/fn"); const sorter_1 = require("../fact/sorter"); class IndexedDBQueue { constructor(indexName) { this.indexName = indexName; } peek() { return (0, driver_1.withDatabase)(this.indexName, db => (0, driver_1.withTransaction)(db, ['queue', 'fact', 'ancestor'], 'readonly', (tx) => __awaiter(this, void 0, void 0, function* () { const queueObjectStore = tx.objectStore('queue'); const factObjectStore = tx.objectStore('fact'); const ancestorObjectStore = tx.objectStore('ancestor'); // Get all envelopes from the queue const queuedEnvelopes = yield (0, driver_1.execRequest)(queueObjectStore.getAll()); // If queue is empty, return empty array if (queuedEnvelopes.length === 0) { return []; } // Get references to all queued facts const queuedReferences = queuedEnvelopes.map(envelope => ({ type: envelope.fact.type, hash: envelope.fact.hash })); // Get all ancestors of queued facts const allAncestors = yield (0, fn_1.flattenAsync)(queuedReferences, reference => (0, driver_1.execRequest)(ancestorObjectStore.get((0, driver_1.factKey)(reference)))); // Remove duplicates and queued facts from ancestors const queuedKeys = new Set(queuedReferences.map(driver_1.factKey)); const distinctAncestorKeys = allAncestors .filter(fn_1.distinct) .filter(key => !queuedKeys.has(key)); // Get fact records for all ancestors const ancestorEnvelopes = yield Promise.all(distinctAncestorKeys.map((key) => __awaiter(this, void 0, void 0, function* () { return ({ fact: yield (0, driver_1.execRequest)(factObjectStore.get(key)), signatures: [] }); }))); // Combine queued envelopes and ancestor envelopes const allEnvelopes = [...queuedEnvelopes, ...ancestorEnvelopes]; // Sort envelopes in topological order return this.sortAndValidateEnvelopes(allEnvelopes); }))); } sortAndValidateEnvelopes(envelopes) { // Extract facts from envelopes const facts = envelopes.map(envelope => envelope.fact); // Create a map from fact key to envelope for quick lookup const envelopeMap = new Map(); envelopes.forEach(envelope => { envelopeMap.set((0, driver_1.factKey)(envelope.fact), envelope); }); // Use TopologicalSorter to sort facts const sorter = new sorter_1.TopologicalSorter(); const sortedKeys = sorter.sort(facts, (_, fact) => (0, driver_1.factKey)(fact)); // Check if sorting was successful (no circular dependencies) if (!sorter.finished()) { throw new Error(`Circular dependencies detected in the fact queue. Some facts have predecessors that depend on them, creating a cycle.`); } // Validate the topological ordering this.validateTopologicalOrdering(sortedKeys, facts); // Convert sorted keys back to envelopes return sortedKeys.map(key => envelopeMap.get(key)); } validateTopologicalOrdering(sortedKeys, facts) { // Create a map of fact keys to their positions in the sorted list const positionMap = new Map(); sortedKeys.forEach((key, index) => { positionMap.set(key, index); }); // Create a map of fact keys to facts for quick lookup const factMap = new Map(); facts.forEach(fact => { factMap.set((0, driver_1.factKey)(fact), fact); }); // Validate that for each fact, all its predecessors appear earlier in the list for (let i = 0; i < sortedKeys.length; i++) { const key = sortedKeys[i]; const fact = factMap.get(key); if (!fact) { throw new Error(`Internal error: Fact with key ${key} not found in fact map.`); } const predecessors = this.getAllPredecessors(fact); for (const predecessor of predecessors) { const predKey = (0, driver_1.factKey)(predecessor); const predPosition = positionMap.get(predKey); // If predecessor is not in the list, it's an error if (predPosition === undefined) { throw new Error(`Missing predecessor: ${predKey} for fact ${key}`); } // If predecessor appears after the current fact, it's a topological ordering violation if (predPosition >= i) { throw new Error(`Topological ordering violation: ${predKey} (position ${predPosition}) should appear before ${key} (position ${i})`); } } } } getAllPredecessors(fact) { const predecessors = []; for (const role in fact.predecessors) { const references = fact.predecessors[role]; if (Array.isArray(references)) { predecessors.push(...references); } else { predecessors.push(references); } } return predecessors; } enqueue(envelopes) { return (0, driver_1.withDatabase)(this.indexName, db => (0, driver_1.withTransaction)(db, ['queue'], 'readwrite', (tx) => __awaiter(this, void 0, void 0, function* () { const queueObjectStore = tx.objectStore('queue'); yield Promise.all(envelopes.map(envelope => (0, driver_1.execRequest)(queueObjectStore.put(envelope, (0, driver_1.factKey)(envelope.fact))))); }))); } dequeue(envelopes) { return (0, driver_1.withDatabase)(this.indexName, db => (0, driver_1.withTransaction)(db, ['queue'], 'readwrite', (tx) => __awaiter(this, void 0, void 0, function* () { const queueObjectStore = tx.objectStore('queue'); yield Promise.all(envelopes.map(envelope => (0, driver_1.execRequest)(queueObjectStore.delete((0, driver_1.factKey)(envelope.fact))))); }))); } } exports.IndexedDBQueue = IndexedDBQueue; //# sourceMappingURL=indexeddb-queue.js.map