"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IncrementalTracker = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const logger_1 = __importDefault(require("./logger"));
class IncrementalTracker {
constructor(orgId, baseDir = './.cache') {
this.state = null;
this.stateFilePath = path.join(baseDir, `${orgId}-metadata-state.json`);
}
async loadState() {
try {
await fs.mkdir(path.dirname(this.stateFilePath), { recursive: true });
const data = await fs.readFile(this.stateFilePath, 'utf-8');
this.state = JSON.parse(data);
logger_1.default.debug('Loaded incremental state', {
snapshots: this.state.snapshots.length,
lastSync: this.state.lastSyncTime
});
return this.state;
}
catch (error) {
if (error.code === 'ENOENT') {
// First run - create initial state
this.state = {
lastSyncTime: new Date(0).toISOString(),
snapshots: [],
orgId: ''
};
logger_1.default.info('Creating initial incremental state');
return this.state;
}
throw error;
}
}
async saveState() {
if (!this.state) {
throw new Error('No state to save. Call loadState() first.');
}
try {
await fs.mkdir(path.dirname(this.stateFilePath), { recursive: true });
await fs.writeFile(this.stateFilePath, JSON.stringify(this.state, null, 2));
logger_1.default.debug('Saved incremental state', {
snapshots: this.state.snapshots.length
});
}
catch (error) {
logger_1.default.error('Failed to save incremental state', { error });
throw error;
}
}
detectChanges(currentMetadata) {
if (!this.state) {
throw new Error('State not loaded. Call loadState() first.');
}
const previousMap = new Map();
this.state.snapshots.forEach(snapshot => {
const key = `${snapshot.type}:${snapshot.fullName}`;
previousMap.set(key, snapshot);
});
const currentMap = new Map();
currentMetadata.forEach(item => {
const key = `${item.type}:${item.fullName}`;
currentMap.set(key, item);
});
const added = [];
const modified = [];
const unchanged = [];
// Check for additions and modifications
currentMetadata.forEach(current => {
const key = `${current.type}:${current.fullName}`;
const previous = previousMap.get(key);
if (!previous) {
added.push(current);
}
else if (previous.lastModifiedDate !== current.lastModifiedDate) {
modified.push(current);
}
else {
unchanged.push(current);
}
});
// Check for deletions
const deleted = [];
this.state.snapshots.forEach(previous => {
const key = `${previous.type}:${previous.fullName}`;
if (!currentMap.has(key)) {
deleted.push(previous);
}
});
logger_1.default.info('Change detection completed', {
added: added.length,
modified: modified.length,
deleted: deleted.length,
unchanged: unchanged.length
});
return { added, modified, deleted, unchanged };
}
updateSnapshots(metadata) {
if (!this.state) {
throw new Error('State not loaded. Call loadState() first.');
}
// Create new snapshots from current metadata
const newSnapshots = metadata.map(item => ({
type: item.type,
fullName: item.fullName,
lastModifiedDate: item.lastModifiedDate
}));
this.state.snapshots = newSnapshots;
this.state.lastSyncTime = new Date().toISOString();
logger_1.default.debug('Updated metadata snapshots', {
count: newSnapshots.length,
lastSync: this.state.lastSyncTime
});
}
getChangedSince(sinceDate) {
if (!this.state) {
throw new Error('State not loaded. Call loadState() first.');
}
return this.state.snapshots.filter(snapshot => new Date(snapshot.lastModifiedDate) > sinceDate);
}
async cleanup(retentionDays = 30) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
// Clean up old state files
try {
const stateDir = path.dirname(this.stateFilePath);
const files = await fs.readdir(stateDir);
for (const file of files) {
if (file.endsWith('-metadata-state.json')) {
const filePath = path.join(stateDir, file);
const stats = await fs.stat(filePath);
if (stats.mtime < cutoffDate) {
await fs.unlink(filePath);
logger_1.default.info(`Cleaned up old state file: ${file}`);
}
}
}
}
catch (error) {
logger_1.default.warn('Failed to cleanup old state files', { error });
}
}
}
exports.IncrementalTracker = IncrementalTracker;