@patchworkdev/pdk
Version:
Patchwork Development Kit
218 lines (217 loc) • 8.57 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_1 = __importDefault(require("crypto"));
const fs_1 = __importDefault(require("fs"));
const glob_1 = require("glob");
const path_1 = __importDefault(require("path"));
class LockFileManager {
lockFilePath;
rootDir;
lockData;
excludePatterns;
constructor(configPath, excludePatterns = []) {
this.excludePatterns = excludePatterns;
this.rootDir = path_1.default.dirname(configPath);
this.lockFilePath = path_1.default.join(this.rootDir, '.deploy.lock');
this.lockData = this.readLockFile();
}
readLockFile() {
try {
const data = fs_1.default.readFileSync(this.lockFilePath, 'utf8');
return JSON.parse(data);
}
catch {
return {
currentNetwork: 'local',
lastDeployment: null,
fileHashes: {},
directoryHashes: {},
deploymentHistory: [],
projectHash: '',
};
}
}
saveLockFile() {
fs_1.default.writeFileSync(this.lockFilePath, JSON.stringify(this.lockData, null, 2));
}
shouldExclude(itemPath) {
const relativePath = path_1.default.relative(this.rootDir, itemPath);
return this.excludePatterns.some((pattern) => {
if (pattern.startsWith('*')) {
return relativePath.endsWith(pattern.slice(1));
}
return relativePath.includes(pattern);
});
}
getRelativePath(absolutePath) {
return path_1.default.relative(this.rootDir, absolutePath);
}
getAbsolutePath(relativePath) {
return path_1.default.join(this.rootDir, relativePath);
}
updateNetwork(network) {
this.lockData.currentNetwork = network;
this.saveLockFile();
}
logDeployment(contract, hash, address, network, timestamp, block) {
const deploymentInfo = {
contract,
hash,
address,
network,
timestamp,
block,
};
this.lockData.lastDeployment = deploymentInfo;
this.lockData.deploymentHistory.push(deploymentInfo);
this.saveLockFile();
}
getLatestDeploymentForContract(contract, network) {
const networkDeployments = this.lockData.deploymentHistory
.filter((deployment) => deployment.network === network && deployment.contract === contract)
.sort((a, b) => b.block - a.block);
return networkDeployments[0] || null;
}
calculateFileHash(filepath) {
const absolutePath = path_1.default.isAbsolute(filepath) ? filepath : this.getAbsolutePath(filepath);
// Handle virtual files (like generator states) that don't exist on disk
if (!fs_1.default.existsSync(absolutePath)) {
return this.lockData.fileHashes[this.getRelativePath(filepath)] || '';
}
const content = fs_1.default.readFileSync(absolutePath);
const hash = crypto_1.default.createHash('sha256');
hash.update(content);
return hash.digest('hex');
}
updateFileHash(filepath, hash) {
const relativePath = this.getRelativePath(filepath);
const finalHash = hash ?? this.calculateFileHash(filepath);
this.lockData.fileHashes[relativePath] = finalHash;
this.saveLockFile();
}
hasFileChanged(filepath) {
const relativePath = this.getRelativePath(filepath);
const currentHash = this.calculateFileHash(filepath);
return this.lockData.fileHashes[relativePath] !== currentHash;
}
async getMatchingFiles(pattern) {
const absolutePattern = path_1.default.isAbsolute(pattern) ? pattern : path_1.default.join(this.rootDir, pattern);
try {
const files = await (0, glob_1.glob)(absolutePattern, {
nodir: true,
ignore: this.excludePatterns,
});
return files.filter((file) => !this.shouldExclude(file));
}
catch (error) {
console.error(`Error matching files for pattern ${pattern}:`, error);
return [];
}
}
calculateDirectoryHash(dirpath) {
const absolutePath = path_1.default.isAbsolute(dirpath) ? dirpath : this.getAbsolutePath(dirpath);
const hash = crypto_1.default.createHash('sha256');
hash.update(path_1.default.basename(absolutePath));
try {
const items = fs_1.default.readdirSync(absolutePath);
const sortedItems = items.sort();
for (const item of sortedItems) {
const fullPath = path_1.default.join(absolutePath, item);
if (this.shouldExclude(fullPath)) {
continue;
}
const stats = fs_1.default.statSync(fullPath);
const relativePath = this.getRelativePath(fullPath);
if (stats.isFile()) {
const fileHash = this.calculateFileHash(fullPath);
hash.update(fileHash);
this.lockData.fileHashes[relativePath] = fileHash;
}
else if (stats.isDirectory()) {
const dirHash = this.calculateDirectoryHash(fullPath);
hash.update(dirHash);
this.lockData.directoryHashes[relativePath] = dirHash;
}
}
return hash.digest('hex');
}
catch (error) {
console.error(`Error processing directory ${dirpath}:`, error);
return '';
}
}
getChangedFiles() {
return Object.keys(this.lockData.fileHashes).filter((filepath) => {
const absolutePath = this.getAbsolutePath(filepath);
return !this.shouldExclude(absolutePath) && this.hasFileChanged(absolutePath);
});
}
watchDirectory(callback) {
const watcher = fs_1.default.watch(this.rootDir, { recursive: true }, async (eventType, filename) => {
if (!filename)
return;
const absolutePath = this.getAbsolutePath(filename);
if (this.shouldExclude(absolutePath)) {
return;
}
// Give the filesystem a moment to settle
await new Promise((resolve) => setTimeout(resolve, 100));
const changes = this.getAllChangedItems();
callback(changes);
});
return watcher;
}
updateProjectHash() {
const projectHash = this.calculateDirectoryHash(this.rootDir);
this.lockData.projectHash = projectHash;
this.saveLockFile();
}
hasProjectChanged() {
const currentHash = this.calculateDirectoryHash(this.rootDir);
return this.lockData.projectHash !== currentHash;
}
getAllChangedItems() {
const changedFiles = Object.keys(this.lockData.fileHashes).filter((filepath) => {
// Skip virtual files (like generator states)
if (filepath.startsWith('generator:'))
return false;
const absolutePath = this.getAbsolutePath(filepath);
return !this.shouldExclude(absolutePath) && this.hasFileChanged(absolutePath);
});
const changedDirs = Object.keys(this.lockData.directoryHashes).filter((dirpath) => {
const absolutePath = this.getAbsolutePath(dirpath);
return !this.shouldExclude(absolutePath) && this.lockData.directoryHashes[dirpath] !== this.calculateDirectoryHash(absolutePath);
});
return {
files: changedFiles,
directories: changedDirs,
};
}
getDeploymentHistory() {
return this.lockData.deploymentHistory;
}
getCurrentNetwork() {
return this.lockData.currentNetwork;
}
getLastDeployment() {
return this.lockData.lastDeployment;
}
getFileHash(filepath) {
const relativePath = this.getRelativePath(filepath);
return this.lockData.fileHashes[relativePath];
}
getDirectoryHash(dirpath) {
const relativePath = this.getRelativePath(dirpath);
return this.lockData.directoryHashes[relativePath];
}
getProjectHash() {
return this.lockData.projectHash;
}
getRootDir() {
return this.rootDir;
}
}
exports.default = LockFileManager;