@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.
@@ -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 };