@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,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PathUtils - Path validation and normalization utilities
|
|
3
|
+
*
|
|
4
|
+
* Extracted from LocalProvider for reuse across providers
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { promises as fs } from 'fs';
|
|
9
|
+
|
|
10
|
+
class PathUtils {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.basePath = options.basePath || process.cwd();
|
|
13
|
+
this.maxPathLength = options.maxPathLength || 255;
|
|
14
|
+
this.allowSymlinks = options.allowSymlinks || false;
|
|
15
|
+
this.restrictToBasePath = options.restrictToBasePath !== false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a file path, resolving relative segments and ensuring consistency
|
|
20
|
+
*/
|
|
21
|
+
normalizePath(inputPath) {
|
|
22
|
+
if (!inputPath) return this.basePath;
|
|
23
|
+
|
|
24
|
+
// Handle home directory
|
|
25
|
+
if (inputPath.startsWith('~')) {
|
|
26
|
+
inputPath = inputPath.replace('~', process.env.HOME || '');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Resolve relative paths against basePath
|
|
30
|
+
if (!path.isAbsolute(inputPath)) {
|
|
31
|
+
inputPath = path.resolve(this.basePath, inputPath);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Normalize to remove double slashes, . and ..
|
|
35
|
+
return path.normalize(inputPath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Validate a path for safety and constraints
|
|
40
|
+
*/
|
|
41
|
+
validatePath(inputPath) {
|
|
42
|
+
const errors = [];
|
|
43
|
+
const warnings = [];
|
|
44
|
+
|
|
45
|
+
if (!inputPath) {
|
|
46
|
+
errors.push('Path is required');
|
|
47
|
+
return { isValid: false, errors, warnings };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const normalized = this.normalizePath(inputPath);
|
|
51
|
+
|
|
52
|
+
// Check path length
|
|
53
|
+
if (normalized.length > this.maxPathLength) {
|
|
54
|
+
errors.push(`Path exceeds maximum length of ${this.maxPathLength} characters`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for null bytes
|
|
58
|
+
if (normalized.includes('\0')) {
|
|
59
|
+
errors.push('Path contains null bytes');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check for path traversal if restricted
|
|
63
|
+
if (this.restrictToBasePath) {
|
|
64
|
+
const resolvedBase = path.resolve(this.basePath);
|
|
65
|
+
const resolvedPath = path.resolve(normalized);
|
|
66
|
+
if (!resolvedPath.startsWith(resolvedBase)) {
|
|
67
|
+
errors.push('Path traversal detected - path is outside base directory');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Warn about special characters
|
|
72
|
+
const specialChars = /[<>:"|?*]/;
|
|
73
|
+
if (specialChars.test(path.basename(normalized))) {
|
|
74
|
+
warnings.push('Path contains special characters that may not be portable');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
isValid: errors.length === 0,
|
|
79
|
+
normalizedPath: normalized,
|
|
80
|
+
errors,
|
|
81
|
+
warnings
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if a path exists
|
|
87
|
+
*/
|
|
88
|
+
async exists(inputPath) {
|
|
89
|
+
try {
|
|
90
|
+
const normalized = this.normalizePath(inputPath);
|
|
91
|
+
await fs.access(normalized);
|
|
92
|
+
return true;
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Ensure a directory exists, creating it if necessary
|
|
100
|
+
*/
|
|
101
|
+
async ensureDirectory(dirPath) {
|
|
102
|
+
const normalized = this.normalizePath(dirPath);
|
|
103
|
+
await fs.mkdir(normalized, { recursive: true });
|
|
104
|
+
return normalized;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get the relative path from base
|
|
109
|
+
*/
|
|
110
|
+
relativePath(inputPath) {
|
|
111
|
+
const normalized = this.normalizePath(inputPath);
|
|
112
|
+
return path.relative(this.basePath, normalized);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Join paths safely
|
|
117
|
+
*/
|
|
118
|
+
join(...segments) {
|
|
119
|
+
return path.join(...segments);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get file extension
|
|
124
|
+
*/
|
|
125
|
+
getExtension(filePath) {
|
|
126
|
+
return path.extname(filePath).toLowerCase();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get filename without extension
|
|
131
|
+
*/
|
|
132
|
+
getBaseName(filePath, ext) {
|
|
133
|
+
return path.basename(filePath, ext);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get directory name
|
|
138
|
+
*/
|
|
139
|
+
getDirName(filePath) {
|
|
140
|
+
return path.dirname(filePath);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { PathUtils };
|