const nodePath = require('path'); function createInternalLogPathResolver(options) { const source = options && typeof options === 'object' ? options : {}; const fs = source.fs; const path = source.path || nodePath; const userDataPath = typeof source.userDataPath === 'string' ? source.userDataPath.trim() : ''; if (!fs || typeof fs.mkdirSync !== 'function' || !userDataPath) { throw new TypeError('createInternalLogPathResolver requires fs and userDataPath'); } const directories = [path.join(userDataPath, 'logs'), path.join(userDataPath, 'internal-logs')]; return function resolveInternalLogPath(fileName, excludedPaths = new Set()) { if (typeof fileName !== 'string' || !fileName || path.basename(fileName) !== fileName) return null; const excluded = excludedPaths instanceof Set ? excludedPaths : new Set(Array.isArray(excludedPaths) ? excludedPaths : [excludedPaths]); for (const directory of directories) { const targetPath = path.join(directory, fileName); if (excluded.has(targetPath)) continue; try { fs.mkdirSync(directory, { recursive: true }); return targetPath; } catch {} } return null; }; } function createInternalLogWriter(options) { const source = options && typeof options === 'object' ? options : {}; const fs = source.fs; const path = source.path || nodePath; const fileName = source.fileName; const resolveInternalLogPath = source.resolveInternalLogPath; const rotateLogFile = typeof source.rotateLogFile === 'function' ? source.rotateLogFile : () => {}; const reportError = typeof source.reportError === 'function' ? source.reportError : () => {}; const retryDelays = Array.isArray(source.retryDelays) && source.retryDelays.length > 0 ? source.retryDelays : [0, 100, 250]; const maxBytes = Number.isFinite(source.maxBytes) ? source.maxBytes : 10 * 1024 * 1024; const maxBackups = Number.isFinite(source.maxBackups) ? source.maxBackups : 2; let activePath = null; if (!fs || !fs.promises || typeof fs.promises.appendFile !== 'function' || typeof fs.appendFileSync !== 'function' || typeof resolveInternalLogPath !== 'function' || typeof fileName !== 'string') { throw new TypeError('createInternalLogWriter requires fs, fileName and resolveInternalLogPath'); } async function append(line, label) { const excludedPaths = new Set(); for (const delay of retryDelays) { if (delay) await new Promise(resolve => setTimeout(resolve, delay)); const targetPath = resolveInternalLogPath(fileName, excludedPaths); if (!targetPath) break; excludedPaths.add(targetPath); try { fs.mkdirSync(path.dirname(targetPath), { recursive: true }); rotateLogFile(targetPath, maxBytes, maxBackups); await fs.promises.appendFile(targetPath, line, 'utf-8'); activePath = targetPath; return true; } catch (error) { reportError(label, error); } } return false; } function appendSync(line, label) { const excludedPaths = new Set(); for (let attempt = 0; attempt < retryDelays.length; attempt++) { const targetPath = resolveInternalLogPath(fileName, excludedPaths); if (!targetPath) break; excludedPaths.add(targetPath); try { fs.mkdirSync(path.dirname(targetPath), { recursive: true }); rotateLogFile(targetPath, maxBytes, maxBackups); fs.appendFileSync(targetPath, line, 'utf-8'); activePath = targetPath; return true; } catch (error) { reportError(label, error); } } return false; } function flushSync(buffer, label) { if (!Array.isArray(buffer) || buffer.length === 0) return true; if (!appendSync(buffer.join(''), label)) return false; buffer.length = 0; return true; } return { append, appendSync, flushSync, getActivePath: () => activePath, getPath: () => activePath || resolveInternalLogPath(fileName) }; } function createUploadAuditWriter(options) { return createInternalLogWriter({ ...options, fileName: 'upload-audit.log' }); } function createBufferedInternalLogFlusher(options) { const source = options && typeof options === 'object' ? options : {}; const buffer = source.buffer; const writer = source.writer; const schedule = typeof source.schedule === 'function' ? source.schedule : setImmediate; const reportError = typeof source.reportError === 'function' ? source.reportError : () => {}; let writing = false; if (!Array.isArray(buffer) || !writer || typeof writer.append !== 'function' || typeof writer.flushSync !== 'function') { throw new TypeError('createBufferedInternalLogFlusher requires buffer and writer'); } function restoreChunk(chunk) { for (let end = chunk.length; end > 0;) { const start = Math.max(0, end - 1024); buffer.splice(0, 0, ...chunk.slice(start, end)); end = start; } } async function flush(label) { if (writing || buffer.length === 0) return null; const chunk = buffer.splice(0); writing = true; let written = false; try { written = await writer.append(chunk.join(''), label); } catch (error) { reportError(label, error); } finally { writing = false; } if (!written) { restoreChunk(chunk); return false; } if (buffer.length > 0) { try { schedule(() => { void flush(label); }); } catch (error) { reportError(label, error); } } return true; } return { flush, flushSync: label => writer.flushSync(buffer, label), isWriting: () => writing }; } function getLogOpenDirectory(targetPath, fallbackDirectory, pathApi = nodePath) { return typeof targetPath === 'string' && targetPath ? pathApi.dirname(targetPath) : fallbackDirectory; } module.exports = { createInternalLogPathResolver, createInternalLogWriter, createUploadAuditWriter, createBufferedInternalLogFlusher, getLogOpenDirectory };