UNPKG

@dataql/node

Version:

DataQL core SDK for unified data management with MongoDB and GraphQL - Production Multi-Cloud Ready

148 lines (147 loc) 4.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateChecksum = generateChecksum; exports.verifyChecksum = verifyChecksum; exports.deepClone = deepClone; exports.validateWALEntry = validateWALEntry; exports.validateWALTransaction = validateWALTransaction; exports.generateWALId = generateWALId; exports.sanitizeOperationData = sanitizeOperationData; exports.calculateEntrySize = calculateEntrySize; exports.isEntryExpired = isEntryExpired; exports.groupEntriesBySession = groupEntriesBySession; exports.sortEntriesByTimestamp = sortEntriesByTimestamp; const crypto_1 = require("crypto"); /** * Generate checksum for WAL entry data integrity */ function generateChecksum(data) { const serialized = JSON.stringify(data, Object.keys(data).sort()); return (0, crypto_1.createHash)("sha256").update(serialized).digest("hex"); } /** * Verify WAL entry checksum */ function verifyChecksum(entry) { if (!entry.checksum) return true; // No checksum to verify const expectedChecksum = generateChecksum({ sessionId: entry.sessionId, collection: entry.collection, operation: entry.operation, data: entry.data, previousData: entry.previousData, transactionId: entry.transactionId, }); return entry.checksum === expectedChecksum; } /** * Create a deep copy of data to prevent mutations */ function deepClone(obj) { if (obj === null || typeof obj !== "object") return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (Array.isArray(obj)) return obj.map(deepClone); const cloned = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key]); } } return cloned; } /** * Validate WAL entry structure */ function validateWALEntry(entry) { const errors = []; if (!entry.sessionId) errors.push("sessionId is required"); if (!entry.collection) errors.push("collection is required"); if (!entry.operation) errors.push("operation is required"); if (!["create", "update", "delete", "transaction"].includes(entry.operation)) { errors.push("operation must be create, update, delete, or transaction"); } if (entry.data === undefined || entry.data === null) { errors.push("data is required"); } return errors; } /** * Validate WAL transaction structure */ function validateWALTransaction(transaction) { const errors = []; if (!transaction.sessionId) errors.push("sessionId is required"); if (!transaction.operations || !Array.isArray(transaction.operations)) { errors.push("operations must be an array"); } else if (transaction.operations.length === 0) { errors.push("operations array cannot be empty"); } return errors; } /** * Generate unique WAL entry ID */ function generateWALId(prefix = "wal") { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 15); return `${prefix}_${timestamp}_${random}`; } /** * Convert operation data to WAL-safe format */ function sanitizeOperationData(operation, data, previousData) { const sanitized = { operation, data: deepClone(data), }; if (previousData !== undefined) { sanitized.previousData = deepClone(previousData); } // Remove circular references and functions return JSON.parse(JSON.stringify(sanitized)); } /** * Calculate WAL entry size in bytes */ function calculateEntrySize(entry) { return Buffer.byteLength(JSON.stringify(entry), "utf8"); } /** * Check if WAL entry is expired based on retention policy */ function isEntryExpired(entry, maxRetentionDays) { const maxAge = maxRetentionDays * 24 * 60 * 60 * 1000; // days to milliseconds const entryAge = Date.now() - entry.timestamp.getTime(); return entryAge > maxAge; } /** * Group WAL entries by session for batch processing */ function groupEntriesBySession(entries) { const grouped = new Map(); for (const entry of entries) { if (!grouped.has(entry.sessionId)) { grouped.set(entry.sessionId, []); } grouped.get(entry.sessionId).push(entry); } return grouped; } /** * Sort WAL entries by timestamp */ function sortEntriesByTimestamp(entries, ascending = true) { return entries.sort((a, b) => { const diff = a.timestamp.getTime() - b.timestamp.getTime(); return ascending ? diff : -diff; }); }