-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
338 lines (287 loc) · 9.21 KB
/
index.js
File metadata and controls
338 lines (287 loc) · 9.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
const fs = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');
// Global registry to track temporary files for cleanup
const tempFiles = new Set();
let cleanupRegistered = false;
/**
* Write content to a temporary file synchronously
* @param {string|Buffer} content - Content to write to the file
* @param {string} extension - File extension (e.g., '.txt', '.json')
* @param {Object} options - Configuration options
* @param {string} options.dir - Custom temporary directory
* @param {string} options.prefix - Filename prefix (default: 'temp-')
* @param {boolean} options.cleanup - Enable automatic cleanup (default: true)
* @param {number} options.mode - File permissions (default: 0o600)
* @returns {string} Path to the created temporary file
*/
function tempWriteSync(content, extension = '', options = {}) {
const {
dir = os.tmpdir(),
prefix = 'temp-',
cleanup = true,
mode = 0o600
} = options;
// Validate inputs
if (content === null || content === undefined) {
throw new Error('Content cannot be null or undefined');
}
if (typeof extension !== 'string') {
throw new Error('Extension must be a string');
}
// Ensure extension starts with dot if provided
if (extension && !extension.startsWith('.')) {
extension = '.' + extension;
}
// Generate unique filename
const randomId = crypto.randomBytes(6).toString('hex');
const timestamp = Date.now().toString(36);
const filename = `${prefix}${timestamp}-${randomId}${extension}`;
const filePath = path.join(dir, filename);
try {
// Ensure temp directory exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
// Write content to file
fs.writeFileSync(filePath, content, { mode });
// Register for cleanup if enabled
if (cleanup) {
registerCleanup();
tempFiles.add(filePath);
}
return filePath;
} catch (error) {
throw new Error(`Failed to write temporary file: ${error.message}`);
}
}
/**
* Write JSON content to a temporary file
* @param {Object} obj - Object to serialize as JSON
* @param {Object} options - Configuration options
* @returns {string} Path to the created temporary JSON file
*/
function tempWriteJsonSync(obj, options = {}) {
if (typeof obj !== 'object' || obj === null) {
throw new Error('Input must be a valid object');
}
const jsonContent = JSON.stringify(obj, null, 2);
return tempWriteSync(jsonContent, '.json', options);
}
/**
* Write CSV content to a temporary file
* @param {Array<Array>|Array<Object>} data - CSV data as array of arrays or objects
* @param {Object} options - Configuration options
* @param {string} options.delimiter - CSV delimiter (default: ',')
* @returns {string} Path to the created temporary CSV file
*/
function tempWriteCsvSync(data, options = {}) {
const { delimiter = ',' } = options;
if (!Array.isArray(data)) {
throw new Error('CSV data must be an array');
}
let csvContent = '';
if (data.length > 0) {
if (Array.isArray(data[0])) {
// Array of arrays
csvContent = data.map(row =>
row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(delimiter)
).join('\n');
} else if (typeof data[0] === 'object') {
// Array of objects
const headers = Object.keys(data[0]);
const headerRow = headers.map(h => `"${h}"`).join(delimiter);
const dataRows = data.map(row =>
headers.map(header => `"${String(row[header] || '').replace(/"/g, '""')}"`).join(delimiter)
);
csvContent = [headerRow, ...dataRows].join('\n');
} else {
throw new Error('Invalid CSV data format');
}
}
return tempWriteSync(csvContent, '.csv', options);
}
/**
* Create a temporary directory
* @param {Object} options - Configuration options
* @param {string} options.dir - Parent directory for temp dir
* @param {string} options.prefix - Directory name prefix
* @param {boolean} options.cleanup - Enable automatic cleanup (default: true)
* @returns {string} Path to the created temporary directory
*/
function tempDirSync(options = {}) {
const {
dir = os.tmpdir(),
prefix = 'temp-dir-',
cleanup = true
} = options;
const randomId = crypto.randomBytes(6).toString('hex');
const timestamp = Date.now().toString(36);
const dirName = `${prefix}${timestamp}-${randomId}`;
const dirPath = path.join(dir, dirName);
try {
fs.mkdirSync(dirPath, { recursive: true });
if (cleanup) {
registerCleanup();
tempFiles.add(dirPath);
}
return dirPath;
} catch (error) {
throw new Error(`Failed to create temporary directory: ${error.message}`);
}
}
/**
* Manually clean up a specific temporary file or directory
* @param {string} filePath - Path to the file or directory to clean up
* @returns {boolean} True if cleanup was successful
*/
function cleanupSync(filePath) {
try {
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
fs.rmSync(filePath, { recursive: true, force: true });
} else {
fs.unlinkSync(filePath);
}
}
tempFiles.delete(filePath);
return true;
} catch (error) {
console.warn(`Failed to cleanup ${filePath}: ${error.message}`);
return false;
}
}
/**
* Clean up all registered temporary files and directories
* @returns {number} Number of items successfully cleaned up
*/
function cleanupAllSync() {
let cleanedCount = 0;
for (const filePath of tempFiles) {
if (cleanupSync(filePath)) {
cleanedCount++;
}
}
tempFiles.clear();
return cleanedCount;
}
/**
* Get list of all registered temporary files
* @returns {string[]} Array of temporary file paths
*/
function getTempFiles() {
return Array.from(tempFiles);
}
/**
* Copy an existing file to a temporary location
* @param {string} sourcePath - Path to the source file
* @param {string} extension - Extension for temp file
* @param {Object} options - Configuration options
* @returns {string} Path to the temporary copy
*/
function tempCopySync(sourcePath, extension = '', options = {}) {
if (!fs.existsSync(sourcePath)) {
throw new Error(`Source file does not exist: ${sourcePath}`);
}
const content = fs.readFileSync(sourcePath);
// Use original extension if none provided
if (!extension) {
extension = path.extname(sourcePath);
}
return tempWriteSync(content, extension, options);
}
/**
* Register cleanup handlers for process exit
*/
function registerCleanup() {
if (cleanupRegistered) {
return;
}
cleanupRegistered = true;
// Clean up on normal exit
process.on('exit', () => {
cleanupAllSync();
});
// Clean up on SIGINT (Ctrl+C)
process.on('SIGINT', () => {
cleanupAllSync();
process.exit(0);
});
// Clean up on SIGTERM
process.on('SIGTERM', () => {
cleanupAllSync();
process.exit(0);
});
// Clean up on uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
cleanupAllSync();
process.exit(1);
});
// Clean up on unhandled promise rejections
process.on('unhandledRejection', (reason) => {
console.error('Unhandled Rejection:', reason);
cleanupAllSync();
process.exit(1);
});
}
/**
* Disable automatic cleanup for specific file
* @param {string} filePath - Path to exclude from cleanup
*/
function excludeFromCleanup(filePath) {
tempFiles.delete(filePath);
}
/**
* Check if a path is managed by temp-write-sync
* @param {string} filePath - Path to check
* @returns {boolean} True if path is managed
*/
function isTempFile(filePath) {
return tempFiles.has(filePath);
}
/**
* Write content to a temporary file with a specific name pattern
* @param {string|Buffer} content - Content to write
* @param {string} pattern - Filename pattern with {random} placeholder
* @param {Object} options - Configuration options
* @returns {string} Path to the created file
*/
function tempWritePatternSync(content, pattern, options = {}) {
const { dir = os.tmpdir(), cleanup = true } = options;
const randomId = crypto.randomBytes(6).toString('hex');
const timestamp = Date.now().toString(36);
const filename = pattern
.replace('{random}', randomId)
.replace('{timestamp}', timestamp)
.replace('{time}', timestamp);
const filePath = path.join(dir, filename);
try {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, content);
if (cleanup) {
registerCleanup();
tempFiles.add(filePath);
}
return filePath;
} catch (error) {
throw new Error(`Failed to write temporary file: ${error.message}`);
}
}
// Export main function and utilities
module.exports = tempWriteSync;
module.exports.tempWriteSync = tempWriteSync;
module.exports.tempWriteJsonSync = tempWriteJsonSync;
module.exports.tempWriteCsvSync = tempWriteCsvSync;
module.exports.tempDirSync = tempDirSync;
module.exports.tempCopySync = tempCopySync;
module.exports.tempWritePatternSync = tempWritePatternSync;
module.exports.cleanupSync = cleanupSync;
module.exports.cleanupAllSync = cleanupAllSync;
module.exports.getTempFiles = getTempFiles;
module.exports.excludeFromCleanup = excludeFromCleanup;
module.exports.isTempFile = isTempFile;