@kadi.build/file-manager 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -0
- package/package.json +48 -0
- package/src/ConfigManager.js +301 -0
- package/src/FileManager.js +526 -0
- package/src/index.js +48 -0
- package/src/providers/CompressionProvider.js +968 -0
- package/src/providers/LocalProvider.js +824 -0
- package/src/providers/RemoteProvider.js +514 -0
- package/src/providers/WatchProvider.js +611 -0
- package/src/utils/FileStreamingUtils.js +757 -0
- package/src/utils/PathUtils.js +144 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileManager - Main orchestrator for file operations
|
|
3
|
+
*
|
|
4
|
+
* Provides unified interface for local and remote file management.
|
|
5
|
+
* API-compatible with the original LocalRemoteManager for test migration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EventEmitter } from 'events';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
import { LocalProvider } from './providers/LocalProvider.js';
|
|
12
|
+
import { WatchProvider } from './providers/WatchProvider.js';
|
|
13
|
+
import { CompressionProvider } from './providers/CompressionProvider.js';
|
|
14
|
+
import { RemoteProvider } from './providers/RemoteProvider.js';
|
|
15
|
+
|
|
16
|
+
class FileManager extends EventEmitter {
|
|
17
|
+
constructor(config) {
|
|
18
|
+
super();
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.providers = {};
|
|
21
|
+
this._initializeProviders();
|
|
22
|
+
|
|
23
|
+
// Set up event handling after providers are initialized
|
|
24
|
+
process.nextTick(() => {
|
|
25
|
+
this._setupEventHandling();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_initializeProviders() {
|
|
30
|
+
// Always initialize local provider
|
|
31
|
+
this.providers.local = new LocalProvider(this.config.getLocalConfig());
|
|
32
|
+
|
|
33
|
+
// Initialize watch provider
|
|
34
|
+
this.providers.watch = new WatchProvider(this.config.getWatchConfig());
|
|
35
|
+
|
|
36
|
+
// Initialize compression provider
|
|
37
|
+
this.providers.compression = new CompressionProvider(this.config.getCompressionConfig());
|
|
38
|
+
|
|
39
|
+
// Initialize remote provider if configured
|
|
40
|
+
if (this.config.hasRemoteConfig()) {
|
|
41
|
+
this.providers.remote = new RemoteProvider(this.config.getRemoteConfig());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get a specific provider
|
|
47
|
+
* @param {string} name - Provider name: 'local', 'remote', 'watch', 'compression'
|
|
48
|
+
*/
|
|
49
|
+
getProvider(name = 'local') {
|
|
50
|
+
const provider = this.providers[name.toLowerCase()];
|
|
51
|
+
if (!provider) {
|
|
52
|
+
throw new Error(`Provider '${name}' not available. Available providers: ${Object.keys(this.providers).join(', ')}`);
|
|
53
|
+
}
|
|
54
|
+
return provider;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Convenience method to get compression provider
|
|
59
|
+
*/
|
|
60
|
+
getCompressionProvider() {
|
|
61
|
+
return this.getProvider('compression');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get list of available provider names
|
|
66
|
+
*/
|
|
67
|
+
getAvailableProviders() {
|
|
68
|
+
return Object.keys(this.providers);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// FILE OPERATIONS (delegates to local or remote provider)
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
async uploadFile(sourcePath, targetPath, providerName = 'local') {
|
|
76
|
+
const provider = this.getProvider(providerName);
|
|
77
|
+
return await provider.uploadFile(sourcePath, targetPath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async downloadFile(sourcePath, targetPath, providerName = 'local') {
|
|
81
|
+
const provider = this.getProvider(providerName);
|
|
82
|
+
return await provider.downloadFile(sourcePath, targetPath);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getFileInfo(filePath, providerName = 'local') {
|
|
86
|
+
const provider = this.getProvider(providerName);
|
|
87
|
+
return await provider.getFile(filePath);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async listFiles(directoryPath = '/', providerName = 'local', options = {}) {
|
|
91
|
+
const provider = this.getProvider(providerName);
|
|
92
|
+
return await provider.listFiles(directoryPath, options);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async deleteFile(filePath, providerName = 'local') {
|
|
96
|
+
const provider = this.getProvider(providerName);
|
|
97
|
+
return await provider.deleteFile(filePath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async renameFile(oldPath, newName, providerName = 'local') {
|
|
101
|
+
const provider = this.getProvider(providerName);
|
|
102
|
+
return await provider.renameFile(oldPath, newName);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async copyFile(sourcePath, targetPath, providerName = 'local') {
|
|
106
|
+
const provider = this.getProvider(providerName);
|
|
107
|
+
return await provider.copyFile(sourcePath, targetPath);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async moveFile(sourcePath, targetPath, providerName = 'local') {
|
|
111
|
+
const provider = this.getProvider(providerName);
|
|
112
|
+
return await provider.moveFile(sourcePath, targetPath);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// FOLDER OPERATIONS
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
async createFolder(folderPath, providerName = 'local') {
|
|
120
|
+
const provider = this.getProvider(providerName);
|
|
121
|
+
return await provider.createFolder(folderPath);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async listFolders(directoryPath = '/', providerName = 'local') {
|
|
125
|
+
const provider = this.getProvider(providerName);
|
|
126
|
+
return await provider.listFolders(directoryPath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async deleteFolder(folderPath, recursive = false, providerName = 'local') {
|
|
130
|
+
const provider = this.getProvider(providerName);
|
|
131
|
+
return await provider.deleteFolder(folderPath, recursive);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async renameFolder(oldPath, newName, providerName = 'local') {
|
|
135
|
+
const provider = this.getProvider(providerName);
|
|
136
|
+
return await provider.renameFolder(oldPath, newName);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async getFolderInfo(folderPath, providerName = 'local') {
|
|
140
|
+
const provider = this.getProvider(providerName);
|
|
141
|
+
return await provider.getFolder(folderPath);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async copyFolder(sourcePath, targetPath, providerName = 'local') {
|
|
145
|
+
const provider = this.getProvider(providerName);
|
|
146
|
+
return await provider.copyFolder(sourcePath, targetPath);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async moveFolder(sourcePath, targetPath, providerName = 'local') {
|
|
150
|
+
const provider = this.getProvider(providerName);
|
|
151
|
+
return await provider.moveFolder(sourcePath, targetPath);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// SEARCH OPERATIONS
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
async searchFiles(query, providerName = 'local', options = {}) {
|
|
159
|
+
const provider = this.getProvider(providerName);
|
|
160
|
+
return await provider.searchFiles(query, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// UTILITY OPERATIONS
|
|
165
|
+
// ============================================================================
|
|
166
|
+
|
|
167
|
+
async testConnection(providerName = 'local') {
|
|
168
|
+
const provider = this.getProvider(providerName);
|
|
169
|
+
return await provider.testConnection();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async validateProvider(providerName = 'local') {
|
|
173
|
+
const provider = this.getProvider(providerName);
|
|
174
|
+
if (provider.validateConfig) {
|
|
175
|
+
return await provider.validateConfig();
|
|
176
|
+
}
|
|
177
|
+
return { isValid: true, errors: [], warnings: [] };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// FILE WATCHING METHODS
|
|
182
|
+
// ============================================================================
|
|
183
|
+
|
|
184
|
+
async startWatching(directoryPath, options = {}) {
|
|
185
|
+
const watchProvider = this.getProvider('watch');
|
|
186
|
+
return await watchProvider.startWatching(directoryPath, options);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async stopWatching(watchIdOrPath) {
|
|
190
|
+
const watchProvider = this.getProvider('watch');
|
|
191
|
+
return await watchProvider.stopWatching(watchIdOrPath);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async stopAllWatching() {
|
|
195
|
+
const watchProvider = this.getProvider('watch');
|
|
196
|
+
return await watchProvider.stopAllWatching();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
listActiveWatchers() {
|
|
200
|
+
const watchProvider = this.getProvider('watch');
|
|
201
|
+
return watchProvider.listActiveWatchers();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
getWatcherInfo(watchIdOrPath) {
|
|
205
|
+
const watchProvider = this.getProvider('watch');
|
|
206
|
+
return watchProvider.getWatcherInfo(watchIdOrPath);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
getWatchingStatus() {
|
|
210
|
+
const watchProvider = this.getProvider('watch');
|
|
211
|
+
return watchProvider.getWatchingStatus();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Alias for tests that use watch()
|
|
215
|
+
async watch(directoryPath, options = {}) {
|
|
216
|
+
const watchProvider = this.getProvider('watch');
|
|
217
|
+
const watchId = await watchProvider.startWatching(directoryPath, options);
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
id: watchId,
|
|
221
|
+
stop: () => watchProvider.stopWatching(watchId),
|
|
222
|
+
getInfo: () => watchProvider.getWatcherInfo(watchId)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async stopWatch(watchId) {
|
|
227
|
+
return this.stopWatching(watchId);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async listWatches() {
|
|
231
|
+
return this.listActiveWatchers();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ============================================================================
|
|
235
|
+
// COMPRESSION METHODS
|
|
236
|
+
// ============================================================================
|
|
237
|
+
|
|
238
|
+
async compressFile(inputPath, outputPath, options = {}) {
|
|
239
|
+
const compressionProvider = this.getProvider('compression');
|
|
240
|
+
return await compressionProvider.compressFile(inputPath, outputPath, options);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async decompressFile(archivePath, outputDirectory, options = {}) {
|
|
244
|
+
const compressionProvider = this.getProvider('compression');
|
|
245
|
+
return await compressionProvider.decompressFile(archivePath, outputDirectory, options);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async compress(source, output, options = {}) {
|
|
249
|
+
const compressionProvider = this.getProvider('compression');
|
|
250
|
+
return await compressionProvider.compress(source, output, options);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async decompress(archive, outputDir, options = {}) {
|
|
254
|
+
const compressionProvider = this.getProvider('compression');
|
|
255
|
+
return await compressionProvider.decompress(archive, outputDir, options);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async compressMultipleFiles(fileList, outputDirectory, options = {}) {
|
|
259
|
+
const compressionProvider = this.getProvider('compression');
|
|
260
|
+
return await compressionProvider.compressMultipleFiles(fileList, outputDirectory, options);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async decompressMultipleFiles(archiveList, outputDirectory, options = {}) {
|
|
264
|
+
const compressionProvider = this.getProvider('compression');
|
|
265
|
+
return await compressionProvider.decompressMultipleFiles(archiveList, outputDirectory, options);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
listActiveCompressionOperations() {
|
|
269
|
+
const compressionProvider = this.getProvider('compression');
|
|
270
|
+
return compressionProvider.listActiveOperations();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getCompressionOperationInfo(operationId) {
|
|
274
|
+
const compressionProvider = this.getProvider('compression');
|
|
275
|
+
return compressionProvider.getOperationInfo(operationId);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
getCompressionStatus() {
|
|
279
|
+
const compressionProvider = this.getProvider('compression');
|
|
280
|
+
return compressionProvider.getCompressionStatus();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// REMOTE-SPECIFIC OPERATIONS
|
|
285
|
+
// ============================================================================
|
|
286
|
+
|
|
287
|
+
async connectRemote(options = {}) {
|
|
288
|
+
if (!this.providers.remote) {
|
|
289
|
+
this.providers.remote = new RemoteProvider({
|
|
290
|
+
...this.config.getRemoteConfig(),
|
|
291
|
+
...options
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return this.providers.remote.connect();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async disconnectRemote() {
|
|
298
|
+
if (this.providers.remote) {
|
|
299
|
+
return this.providers.remote.disconnect();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ============================================================================
|
|
304
|
+
// CROSS-PROVIDER OPERATIONS
|
|
305
|
+
// ============================================================================
|
|
306
|
+
|
|
307
|
+
async syncToRemote(localPath, remotePath, options = {}) {
|
|
308
|
+
const local = this.getProvider('local');
|
|
309
|
+
const remote = this.getProvider('remote');
|
|
310
|
+
|
|
311
|
+
const localFiles = await local.listFiles(localPath, { recursive: true });
|
|
312
|
+
const results = [];
|
|
313
|
+
|
|
314
|
+
for (const file of localFiles) {
|
|
315
|
+
const targetPath = remotePath + '/' + (file.relativePath || file.name);
|
|
316
|
+
try {
|
|
317
|
+
await remote.uploadFile(file.path, targetPath);
|
|
318
|
+
results.push({ file: file.path, status: 'success' });
|
|
319
|
+
} catch (error) {
|
|
320
|
+
results.push({ file: file.path, status: 'error', error: error.message });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return results;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async syncFromRemote(remotePath, localPath, options = {}) {
|
|
328
|
+
const remote = this.getProvider('remote');
|
|
329
|
+
|
|
330
|
+
const remoteFiles = await remote.listFiles(remotePath, { recursive: true });
|
|
331
|
+
const results = [];
|
|
332
|
+
|
|
333
|
+
for (const file of remoteFiles) {
|
|
334
|
+
const targetPath = localPath + '/' + (file.relativePath || file.name);
|
|
335
|
+
try {
|
|
336
|
+
await remote.downloadFile(file.path, targetPath);
|
|
337
|
+
results.push({ file: file.path, status: 'success' });
|
|
338
|
+
} catch (error) {
|
|
339
|
+
results.push({ file: file.path, status: 'error', error: error.message });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return results;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ============================================================================
|
|
347
|
+
// BATCH OPERATIONS
|
|
348
|
+
// ============================================================================
|
|
349
|
+
|
|
350
|
+
async uploadMultipleFiles(fileList, targetDirectory = '/', providerName = 'local') {
|
|
351
|
+
const results = [];
|
|
352
|
+
const errors = [];
|
|
353
|
+
|
|
354
|
+
for (const filePath of fileList) {
|
|
355
|
+
try {
|
|
356
|
+
const fileName = path.basename(filePath);
|
|
357
|
+
const targetPath = path.join(targetDirectory, fileName);
|
|
358
|
+
const result = await this.uploadFile(filePath, targetPath, providerName);
|
|
359
|
+
results.push({ sourcePath: filePath, targetPath, result });
|
|
360
|
+
} catch (error) {
|
|
361
|
+
errors.push({ filePath, error: error.message });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return { results, errors };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async downloadMultipleFiles(fileList, targetDirectory = './', providerName = 'local') {
|
|
369
|
+
const results = [];
|
|
370
|
+
const errors = [];
|
|
371
|
+
|
|
372
|
+
for (const filePath of fileList) {
|
|
373
|
+
try {
|
|
374
|
+
const fileName = path.basename(filePath);
|
|
375
|
+
const targetPath = path.join(targetDirectory, fileName);
|
|
376
|
+
const result = await this.downloadFile(filePath, targetPath, providerName);
|
|
377
|
+
results.push({ sourcePath: filePath, targetPath, result });
|
|
378
|
+
} catch (error) {
|
|
379
|
+
errors.push({ filePath, error: error.message });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return { results, errors };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// EVENT HANDLING SETUP
|
|
388
|
+
// ============================================================================
|
|
389
|
+
|
|
390
|
+
_setupEventHandling() {
|
|
391
|
+
try {
|
|
392
|
+
// Forward file watching events
|
|
393
|
+
const watchProvider = this.getProvider('watch');
|
|
394
|
+
watchProvider.on('fileEvent', (eventData) => {
|
|
395
|
+
this.emit('fileEvent', eventData);
|
|
396
|
+
});
|
|
397
|
+
watchProvider.on('watcherError', (errorData) => {
|
|
398
|
+
this.emit('watcherError', errorData);
|
|
399
|
+
});
|
|
400
|
+
} catch (error) {
|
|
401
|
+
// Watch provider might not be available
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
// Forward compression events
|
|
406
|
+
const compressionProvider = this.getProvider('compression');
|
|
407
|
+
compressionProvider.on('compressionProgress', (progressData) => {
|
|
408
|
+
this.emit('compressionProgress', progressData);
|
|
409
|
+
});
|
|
410
|
+
compressionProvider.on('decompressionProgress', (progressData) => {
|
|
411
|
+
this.emit('decompressionProgress', progressData);
|
|
412
|
+
});
|
|
413
|
+
} catch (error) {
|
|
414
|
+
// Compression provider might not be available
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ============================================================================
|
|
419
|
+
// UTILITY METHODS
|
|
420
|
+
// ============================================================================
|
|
421
|
+
|
|
422
|
+
formatBytes(bytes) {
|
|
423
|
+
if (bytes === 0) return '0 Bytes';
|
|
424
|
+
const k = 1024;
|
|
425
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
426
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
427
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
getSystemInfo() {
|
|
431
|
+
return {
|
|
432
|
+
platform: os.platform(),
|
|
433
|
+
arch: os.arch(),
|
|
434
|
+
nodeVersion: process.version,
|
|
435
|
+
availableProviders: this.getAvailableProviders(),
|
|
436
|
+
config: {
|
|
437
|
+
local: this.config.getLocalConfig(),
|
|
438
|
+
performance: this.config.getPerformanceConfig ? this.config.getPerformanceConfig() : {},
|
|
439
|
+
watch: this.config.getWatchConfig(),
|
|
440
|
+
compression: this.config.getCompressionConfig()
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async getUsageStats() {
|
|
446
|
+
const provider = this.getProvider('local');
|
|
447
|
+
const localConfig = this.config.getLocalConfig();
|
|
448
|
+
const fsPromises = (await import('fs')).promises;
|
|
449
|
+
|
|
450
|
+
const stats = {
|
|
451
|
+
uploadDirectory: {
|
|
452
|
+
path: localConfig.uploadDirectory,
|
|
453
|
+
exists: false,
|
|
454
|
+
fileCount: 0,
|
|
455
|
+
totalSize: 0
|
|
456
|
+
},
|
|
457
|
+
downloadDirectory: {
|
|
458
|
+
path: localConfig.downloadDirectory,
|
|
459
|
+
exists: false,
|
|
460
|
+
fileCount: 0,
|
|
461
|
+
totalSize: 0
|
|
462
|
+
},
|
|
463
|
+
tempDirectory: {
|
|
464
|
+
path: localConfig.tempDirectory,
|
|
465
|
+
exists: false,
|
|
466
|
+
fileCount: 0,
|
|
467
|
+
totalSize: 0
|
|
468
|
+
},
|
|
469
|
+
watchStatus: this.getWatchingStatus(),
|
|
470
|
+
compressionStatus: this.getCompressionStatus()
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
for (const [key, dirInfo] of Object.entries(stats)) {
|
|
474
|
+
if (key === 'watchStatus' || key === 'compressionStatus') continue;
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const dirStat = await fsPromises.stat(dirInfo.path);
|
|
478
|
+
if (dirStat.isDirectory()) {
|
|
479
|
+
dirInfo.exists = true;
|
|
480
|
+
const files = await provider.listFiles(dirInfo.path, { recursive: true });
|
|
481
|
+
dirInfo.fileCount = files.length;
|
|
482
|
+
dirInfo.totalSize = files.reduce((sum, file) => sum + (file.size || 0), 0);
|
|
483
|
+
}
|
|
484
|
+
} catch (error) {
|
|
485
|
+
dirInfo.exists = false;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return stats;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ============================================================================
|
|
493
|
+
// CLEANUP AND SHUTDOWN
|
|
494
|
+
// ============================================================================
|
|
495
|
+
|
|
496
|
+
async shutdown() {
|
|
497
|
+
try {
|
|
498
|
+
// Stop all watchers first
|
|
499
|
+
const watchProvider = this.getProvider('watch');
|
|
500
|
+
await watchProvider.shutdown();
|
|
501
|
+
} catch (error) {
|
|
502
|
+
// Watch provider might not be available
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
// Shutdown compression provider
|
|
507
|
+
const compressionProvider = this.getProvider('compression');
|
|
508
|
+
if (compressionProvider.shutdown) {
|
|
509
|
+
await compressionProvider.shutdown();
|
|
510
|
+
}
|
|
511
|
+
} catch (error) {
|
|
512
|
+
// Compression provider might not be available
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
try {
|
|
516
|
+
// Disconnect remote if connected
|
|
517
|
+
if (this.providers.remote) {
|
|
518
|
+
await this.providers.remote.disconnect();
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
// Remote provider might not be available
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export { FileManager };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kadi.build/file-manager
|
|
3
|
+
*
|
|
4
|
+
* Complete local and remote file management library.
|
|
5
|
+
* Provides file operations, watching, compression, and remote SSH/SFTP support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Providers
|
|
9
|
+
export { LocalProvider } from './providers/LocalProvider.js';
|
|
10
|
+
export { WatchProvider } from './providers/WatchProvider.js';
|
|
11
|
+
export { CompressionProvider } from './providers/CompressionProvider.js';
|
|
12
|
+
export { RemoteProvider } from './providers/RemoteProvider.js';
|
|
13
|
+
|
|
14
|
+
// Utilities
|
|
15
|
+
export { FileStreamingUtils, FileStreamer, DownloadTracker } from './utils/FileStreamingUtils.js';
|
|
16
|
+
export { PathUtils } from './utils/PathUtils.js';
|
|
17
|
+
|
|
18
|
+
// Main classes
|
|
19
|
+
export { FileManager } from './FileManager.js';
|
|
20
|
+
export { ConfigManager } from './ConfigManager.js';
|
|
21
|
+
|
|
22
|
+
// Factory function
|
|
23
|
+
export async function createFileManager(options = {}) {
|
|
24
|
+
const { ConfigManager: CM } = await import('./ConfigManager.js');
|
|
25
|
+
const { FileManager: FM } = await import('./FileManager.js');
|
|
26
|
+
const config = new CM();
|
|
27
|
+
await config.load(options);
|
|
28
|
+
return new FM(config);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Convenience functions
|
|
32
|
+
export async function compressFile(sourcePath, outputPath, options = {}) {
|
|
33
|
+
const manager = await createFileManager();
|
|
34
|
+
return manager.compress(sourcePath, outputPath, options);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function decompressFile(archivePath, outputDir, options = {}) {
|
|
38
|
+
const manager = await createFileManager();
|
|
39
|
+
return manager.decompress(archivePath, outputDir, options);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function watchDirectory(dirPath, options = {}) {
|
|
43
|
+
const manager = await createFileManager();
|
|
44
|
+
return manager.watch(dirPath, options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Default export
|
|
48
|
+
export { FileManager as default } from './FileManager.js';
|