UNPKG

signing-enclave

Version:

A code signing and notarization tool for macOS and Windows applications. Uses Azure Key Vault for secret and certificate management.

329 lines 12 kB
// src/receiptWatcher.ts import fs from "fs-extra"; import path from "path"; import { EventEmitter } from "events"; export const receiptWatcher = (someFolderPath) => { const receiptFolder = someFolderPath; const emitter = new EventEmitter(); let watcher = null; // Helper function to parse receipt const parseReceipt = async (receiptPath) => { try { const receipt = await fs.readJson(receiptPath); return receipt; } catch (error) { console.error(`Error parsing receipt at ${receiptPath}:`, error); return null; } }; // Helper function to emit events based on receipt changes const handleReceiptChange = async (receiptPath) => { const receipt = await parseReceipt(receiptPath); if (!receipt) return; // Determine the event based on receipt status changes switch (receipt.status) { case "In Progress": emitter.emit("file-added", receipt); emitter.emit("*", { type: "file-added", receipt }); break; case "Completed": emitter.emit("file-completed", receipt); emitter.emit("*", { type: "file-completed", receipt }); break; case "Failed": emitter.emit("file-completed", receipt); // Assuming failure is part of completion emitter.emit("*", { type: "file-completed", receipt }); break; case "Notarizing": emitter.emit("file-signed", receipt); emitter.emit("*", { type: "file-signed", receipt }); break; // Add more cases as needed default: // Emit wildcard for any other status emitter.emit("*", { type: "unknown-status", receipt }); break; } // Check if all files are signed const receipts = await getReceipts(); const allSigned = receipts.every((r) => r.signingStatus === "Completed"); if (allSigned) { emitter.emit("all-files-signed", receipts); emitter.emit("*", { type: "all-files-signed", receipts }); } // Check if all files are completed const allCompleted = receipts.every((r) => r.status === "Completed" || (os.platform() === "darwin" ? r.status === "Completed" : r.status === "Completed")); if (allCompleted) { emitter.emit("all-files-completed", receipts); emitter.emit("*", { type: "all-files-completed", receipts }); } }; // Function to add a file and wait for receipt const addFile = async (someFilePath) => { const fileName = path.basename(someFilePath); const destinationPath = path.join(receiptFolder, fileName); // Copy the file to the watcher directory await fs.copy(someFilePath, destinationPath); // Wait for the receipt to be created const receiptFilePath = `${destinationPath}.receipt.json`; const timeout = 5000; // 5 seconds const receipt = await new Promise((resolve, reject) => { const timer = setTimeout(() => { reject(new Error(`Receipt not found for file: ${fileName} after ${timeout} ms`)); }, timeout); // Listen for 'file-added' event const onFileAdded = (addedReceipt) => { if (addedReceipt.originalFile === fileName) { clearTimeout(timer); emitter.removeListener("file-added", onFileAdded); resolve(addedReceipt); } }; emitter.on("file-added", onFileAdded); }); return receipt; }; // Function to start watching receipts const start = async () => { if (watcher) { console.warn("Receipt watcher is already running."); return; } watcher = fs.watch(receiptFolder, { persistent: true }, async (eventType, filename) => { if (filename && filename.endsWith(".receipt.json") && eventType === "change") { const receiptPath = path.join(receiptFolder, filename); await handleReceiptChange(receiptPath); } }); watcher.on("error", (error) => { console.error("Watcher error:", error); }); }; // Function to stop watching receipts const stop = async () => { if (watcher) { watcher.close(); watcher = null; console.log("Receipt watcher stopped."); } else { console.warn("Receipt watcher is not running."); } }; // Function to get all receipts const getReceipts = async () => { const files = await fs.readdir(receiptFolder); const receiptFiles = files.filter((file) => file.endsWith(".receipt.json")); const receipts = []; for (const file of receiptFiles) { const receiptPath = path.join(receiptFolder, file); const receipt = await parseReceipt(receiptPath); if (receipt) { receipts.push(receipt); } } return receipts; }; // Function to get a specific receipt by filename or id const getReceipt = async (identifier) => { const receipts = await getReceipts(); const receipt = receipts.find((r) => r.id === identifier || r.originalFile === identifier); if (!receipt) { throw new Error(`Receipt not found for identifier: ${identifier}`); } return receipt; }; // Function to check if a file is signed const isFileSigned = async (identifier) => { const receipt = await getReceipt(identifier); if (receipt.signingStatus === "Completed") { return true; } else if (receipt.signingStatus === "Failed") { throw new Error(receipt.signingError || "Signing failed."); } else { return false; } }; // Function to check if a file is completed const isFileCompleted = async (identifier) => { const receipt = await getReceipt(identifier); if (receipt.status === "Completed") { return true; } else if (receipt.status === "Failed") { throw new Error(receipt.error || "Processing failed."); } else { return false; } }; // Function to check if all files are signed const allFilesSigned = async () => { const receipts = await getReceipts(); const notSigned = receipts.filter((r) => r.signingStatus !== "Completed"); if (notSigned.length === 0) { return true; } else { const errors = notSigned .filter((r) => r.signingError) .map((r) => `${r.originalFile}: ${r.signingError}`) .join("; "); if (errors) { throw new Error(errors); } return false; } }; // Function to check if all files are completed const allFilesCompleted = async () => { const receipts = await getReceipts(); const notCompleted = receipts.filter((r) => r.status !== "Completed"); if (notCompleted.length === 0) { return true; } else { const errors = notCompleted .filter((r) => r.error) .map((r) => `${r.originalFile}: ${r.error}`) .join("; "); if (errors) { throw new Error(errors); } return false; } }; // Function to wait until all files are signed const whenAllFilesSigned = async () => { return new Promise((resolve, reject) => { const checkSigned = async () => { try { const signed = await allFilesSigned(); if (signed) { resolve(); } } catch (error) { reject(error); } }; // Initial check checkSigned(); // Listen for 'all-files-signed' event const onAllSigned = () => { resolve(); }; emitter.once("all-files-signed", onAllSigned); // Timeout after a certain period (e.g., 60 seconds) const timeout = setTimeout(() => { emitter.removeListener("all-files-signed", onAllSigned); reject(new Error("Timeout waiting for all files to be signed.")); }, 60000); emitter.once("all-files-signed", () => { clearTimeout(timeout); }); }); }; // Function to wait until all files are completed const whenAllFilesCompleted = async () => { return new Promise((resolve, reject) => { const checkCompleted = async () => { try { const completed = await allFilesCompleted(); if (completed) { resolve(); } } catch (error) { reject(error); } }; // Initial check checkCompleted(); // Listen for 'all-files-completed' event const onAllCompleted = () => { resolve(); }; emitter.once("all-files-completed", onAllCompleted); // Timeout after a certain period (e.g., 120 seconds) const timeout = setTimeout(() => { emitter.removeListener("all-files-completed", onAllCompleted); reject(new Error("Timeout waiting for all files to be completed.")); }, 120000); emitter.once("all-files-completed", () => { clearTimeout(timeout); }); }); }; // Function to cleanup receipts and files const cleanup = async () => { try { await fs.emptyDir(receiptFolder); console.log("All receipts and files have been deleted."); } catch (error) { console.error("Error during cleanup:", error); } }; // Function to handle events const on = (event, callback) => { emitter.on(event, callback); }; // Function to start the watcher const start = async () => { if (watcher) { console.warn("Receipt watcher is already running."); return; } watcher = fs.watch(receiptFolder, { persistent: true }, async (eventType, filename) => { if (filename && filename.endsWith(".receipt.json") && eventType === "change") { const receiptPath = path.join(receiptFolder, filename); await handleReceiptChange(receiptPath); } }); watcher.on("error", (error) => { console.error("Watcher error:", error); }); console.log("Receipt watcher started."); }; // Function to stop the watcher const stop = async () => { if (watcher) { watcher.close(); watcher = null; console.log("Receipt watcher stopped."); } else { console.warn("Receipt watcher is not running."); } }; // Initialize watcher variable let watcher = null; return { addFile, start, getReceipts, getReceipt, isFileSigned, isFileCompleted, allFilesSigned, allFilesCompleted, whenAllFilesSigned, whenAllFilesCompleted, stop, cleanup, on, }; }; //# sourceMappingURL=receiptWatcher.js.map