@arela/uploader 0.2.4 → 0.2.5

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,99 @@
1
+ import path from 'path';
2
+
3
+ /**
4
+ * File Sanitization Utility
5
+ * Handles filename sanitization with caching for performance
6
+ */
7
+ export class FileSanitizer {
8
+ constructor() {
9
+ this.cache = new Map();
10
+ this.patterns = [
11
+ [/[áàâäãåāăą]/gi, 'a'],
12
+ [/[éèêëēĕėę]/gi, 'e'],
13
+ [/[íìîïīĭį]/gi, 'i'],
14
+ [/[óòôöõōŏő]/gi, 'o'],
15
+ [/[úùûüūŭů]/gi, 'u'],
16
+ [/[ñň]/gi, 'n'],
17
+ [/[ç]/gi, 'c'],
18
+ [/[ý]/gi, 'y'],
19
+ [/[멕]/g, 'meok'],
20
+ [/[시]/g, 'si'],
21
+ [/[코]/g, 'ko'],
22
+ [/[용]/g, 'yong'],
23
+ [/[가-힣]/g, 'kr'],
24
+ [/[\u0300-\u036f]/g, ''],
25
+ [/[\\?%*:|"<>[\]~`^]/g, '-'],
26
+ [/[{}]/g, '-'],
27
+ [/[&]/g, 'and'],
28
+ [/[()]/g, ''],
29
+ [/\s+/g, '-'],
30
+ [/-+/g, '-'],
31
+ [/^-+|-+$/g, ''],
32
+ [/^\.+/, ''],
33
+ [/[^\w.-]/g, ''],
34
+ ];
35
+ }
36
+
37
+ /**
38
+ * Sanitize a filename for safe storage
39
+ * @param {string} fileName - Original filename
40
+ * @returns {string} Sanitized filename
41
+ */
42
+ sanitizeFileName(fileName) {
43
+ if (this.cache.has(fileName)) {
44
+ return this.cache.get(fileName);
45
+ }
46
+
47
+ const ext = path.extname(fileName);
48
+ const nameWithoutExt = path.basename(fileName, ext);
49
+
50
+ // If already clean, return as-is
51
+ if (/^[a-zA-Z0-9._-]+$/.test(nameWithoutExt)) {
52
+ const result = fileName;
53
+ this.cache.set(fileName, result);
54
+ return result;
55
+ }
56
+
57
+ let sanitized = nameWithoutExt.normalize('NFD');
58
+
59
+ // Apply all sanitization patterns
60
+ for (const [pattern, replacement] of this.patterns) {
61
+ sanitized = sanitized.replace(pattern, replacement);
62
+ }
63
+
64
+ // Additional cleanup
65
+ sanitized = sanitized
66
+ .replace(/~/g, '-')
67
+ .replace(/\s+/g, '-')
68
+ .replace(/\.+/g, '-')
69
+ .replace(/-+/g, '-')
70
+ .replace(/^-+|-+$/g, '');
71
+
72
+ if (!sanitized) {
73
+ sanitized = 'unnamed_file';
74
+ }
75
+
76
+ const result = sanitized + ext;
77
+ this.cache.set(fileName, result);
78
+ return result;
79
+ }
80
+
81
+ /**
82
+ * Clear the sanitization cache
83
+ */
84
+ clearCache() {
85
+ this.cache.clear();
86
+ }
87
+
88
+ /**
89
+ * Get cache size for monitoring
90
+ * @returns {number} Number of cached entries
91
+ */
92
+ getCacheSize() {
93
+ return this.cache.size;
94
+ }
95
+ }
96
+
97
+ // Export singleton instance
98
+ export const fileSanitizer = new FileSanitizer();
99
+ export default fileSanitizer;
@@ -0,0 +1,196 @@
1
+ import path from 'path';
2
+
3
+ /**
4
+ * Path Detection Utility
5
+ * Extracts year and pedimento information from file paths with caching
6
+ */
7
+ export class PathDetector {
8
+ constructor() {
9
+ this.cache = new Map();
10
+ }
11
+
12
+ /**
13
+ * Extract year and pedimento number from file path with caching
14
+ * @param {string} filePath - Full file path
15
+ * @param {string} basePath - Base path to make relative
16
+ * @returns {Object} Detection result with year, pedimento, and detected flag
17
+ */
18
+ extractYearAndPedimentoFromPath(filePath, basePath) {
19
+ const cacheKey = `${filePath}|${basePath}`;
20
+
21
+ if (this.cache.has(cacheKey)) {
22
+ return this.cache.get(cacheKey);
23
+ }
24
+
25
+ const detection = this.#performDetection(filePath, basePath);
26
+ this.cache.set(cacheKey, detection);
27
+
28
+ return detection;
29
+ }
30
+
31
+ /**
32
+ * Perform the actual path detection
33
+ * @private
34
+ * @param {string} filePath - Full file path
35
+ * @param {string} basePath - Base path to make relative
36
+ * @returns {Object} Detection result
37
+ */
38
+ #performDetection(filePath, basePath) {
39
+ try {
40
+ const relativePath = path.relative(basePath, filePath);
41
+ const pathParts = relativePath.split(path.sep);
42
+
43
+ let year = null;
44
+ let pedimento = null;
45
+
46
+ // Pattern 1: Direct year/pedimento structure (2024/4023260)
47
+ year = this.#detectDirectPattern(pathParts);
48
+ pedimento = year ? this.#findPedimentoForYear(pathParts, year) : null;
49
+
50
+ // Pattern 2: Named patterns (año2024, ped4023260)
51
+ if (!year || !pedimento) {
52
+ const namedDetection = this.#detectNamedPatterns(pathParts);
53
+ year = year || namedDetection.year;
54
+ pedimento = pedimento || namedDetection.pedimento;
55
+ }
56
+
57
+ // Pattern 3: Loose year detection
58
+ if (!year) {
59
+ year = this.#detectLooseYear(pathParts);
60
+ }
61
+
62
+ // Pattern 4: Loose pedimento detection
63
+ if (!pedimento) {
64
+ pedimento = this.#detectLoosePedimento(pathParts);
65
+ }
66
+
67
+ return {
68
+ year,
69
+ pedimento,
70
+ detected: !!(year && pedimento)
71
+ };
72
+ } catch (error) {
73
+ return {
74
+ year: null,
75
+ pedimento: null,
76
+ detected: false,
77
+ error: error.message,
78
+ };
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Detect direct year/pedimento pattern
84
+ * @private
85
+ * @param {string[]} pathParts - Path components
86
+ * @returns {string|null} Detected year
87
+ */
88
+ #detectDirectPattern(pathParts) {
89
+ for (let i = 0; i < pathParts.length - 1; i++) {
90
+ const yearMatch = pathParts[i].match(/^(202[0-9])$/);
91
+ if (yearMatch) {
92
+ return yearMatch[1];
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * Find pedimento for a detected year
100
+ * @private
101
+ * @param {string[]} pathParts - Path components
102
+ * @param {string} year - Detected year
103
+ * @returns {string|null} Detected pedimento
104
+ */
105
+ #findPedimentoForYear(pathParts, year) {
106
+ const yearIndex = pathParts.findIndex(part => part === year);
107
+ if (yearIndex >= 0 && yearIndex < pathParts.length - 1) {
108
+ const nextPart = pathParts[yearIndex + 1];
109
+ const pedimentoMatch = nextPart.match(/^(\d{4,8})$/);
110
+ if (pedimentoMatch) {
111
+ return pedimentoMatch[1];
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+
117
+ /**
118
+ * Detect named patterns (año2024, ped4023260)
119
+ * @private
120
+ * @param {string[]} pathParts - Path components
121
+ * @returns {Object} Detection result with year and pedimento
122
+ */
123
+ #detectNamedPatterns(pathParts) {
124
+ let year = null;
125
+ let pedimento = null;
126
+
127
+ for (const part of pathParts) {
128
+ if (!year) {
129
+ const namedYearMatch = part.match(/(?:año|year|anio)(\d{4})/i);
130
+ if (namedYearMatch) {
131
+ year = namedYearMatch[1];
132
+ }
133
+ }
134
+
135
+ if (!pedimento) {
136
+ const namedPedimentoMatch = part.match(/(?:ped|pedimento|pedi)(\d{4,8})/i);
137
+ if (namedPedimentoMatch) {
138
+ pedimento = namedPedimentoMatch[1];
139
+ }
140
+ }
141
+ }
142
+
143
+ return { year, pedimento };
144
+ }
145
+
146
+ /**
147
+ * Detect loose year pattern
148
+ * @private
149
+ * @param {string[]} pathParts - Path components
150
+ * @returns {string|null} Detected year
151
+ */
152
+ #detectLooseYear(pathParts) {
153
+ for (const part of pathParts) {
154
+ const yearMatch = part.match(/(202[0-9])/);
155
+ if (yearMatch) {
156
+ return yearMatch[1];
157
+ }
158
+ }
159
+ return null;
160
+ }
161
+
162
+ /**
163
+ * Detect loose pedimento pattern
164
+ * @private
165
+ * @param {string[]} pathParts - Path components
166
+ * @returns {string|null} Detected pedimento
167
+ */
168
+ #detectLoosePedimento(pathParts) {
169
+ for (const part of pathParts) {
170
+ const pedimentoMatch = part.match(/(\d{4,8})/);
171
+ if (pedimentoMatch && pedimentoMatch[1].length >= 4) {
172
+ return pedimentoMatch[1];
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+
178
+ /**
179
+ * Clear the detection cache
180
+ */
181
+ clearCache() {
182
+ this.cache.clear();
183
+ }
184
+
185
+ /**
186
+ * Get cache size for monitoring
187
+ * @returns {number} Number of cached entries
188
+ */
189
+ getCacheSize() {
190
+ return this.cache.size;
191
+ }
192
+ }
193
+
194
+ // Export singleton instance
195
+ export const pathDetector = new PathDetector();
196
+ export default pathDetector;