@arela/uploader 0.2.5 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arela/uploader",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "CLI to upload files/directories to Arela",
5
5
  "bin": {
6
6
  "arela": "./src/index.js"
@@ -43,4 +43,4 @@
43
43
  "@trivago/prettier-plugin-sort-imports": "5.2.2",
44
44
  "prettier": "3.5.3"
45
45
  }
46
- }
46
+ }
@@ -3,12 +3,16 @@ import { globby } from 'globby';
3
3
  import mime from 'mime-types';
4
4
  import path from 'path';
5
5
 
6
- import appConfig from '../config/config.js';
7
- import ErrorHandler from '../errors/ErrorHandler.js';
8
- import { ConfigurationError, FileOperationError } from '../errors/ErrorTypes.js';
9
6
  import databaseService from '../services/DatabaseService.js';
10
7
  import logger from '../services/LoggingService.js';
11
8
  import uploadServiceFactory from '../services/upload/UploadServiceFactory.js';
9
+
10
+ import appConfig from '../config/config.js';
11
+ import ErrorHandler from '../errors/ErrorHandler.js';
12
+ import {
13
+ ConfigurationError,
14
+ FileOperationError,
15
+ } from '../errors/ErrorTypes.js';
12
16
  import FileOperations from '../utils/FileOperations.js';
13
17
  import fileSanitizer from '../utils/FileSanitizer.js';
14
18
  import pathDetector from '../utils/PathDetector.js';
@@ -30,15 +34,17 @@ export class UploadCommand {
30
34
  try {
31
35
  // Validate configuration
32
36
  this.#validateOptions(options);
33
-
37
+
34
38
  // Initialize services
35
- const uploadService = await uploadServiceFactory.getUploadService(options.forceSupabase);
39
+ const uploadService = await uploadServiceFactory.getUploadService(
40
+ options.forceSupabase,
41
+ );
36
42
  const sources = appConfig.getUploadSources();
37
43
  const basePath = appConfig.getBasePath();
38
44
 
39
45
  // Log command start
40
46
  logger.info(`Starting upload with ${uploadService.getServiceName()}`);
41
-
47
+
42
48
  if (options.clearLog) {
43
49
  logger.clearLogFile();
44
50
  logger.info('Log file cleared');
@@ -67,7 +73,7 @@ export class UploadCommand {
67
73
  uploadService,
68
74
  basePath,
69
75
  source,
70
- sourcePath
76
+ sourcePath,
71
77
  );
72
78
 
73
79
  this.#updateGlobalResults(globalResults, result);
@@ -79,12 +85,11 @@ export class UploadCommand {
79
85
  }
80
86
 
81
87
  this.#logFinalSummary(globalResults, options, uploadService);
82
-
88
+
83
89
  // Handle additional phases if requested
84
90
  if (options.runAllPhases && options.statsOnly) {
85
91
  await this.#runAdditionalPhases(options);
86
92
  }
87
-
88
93
  } catch (error) {
89
94
  this.errorHandler.handleFatalError(error, { command: 'upload', options });
90
95
  }
@@ -102,7 +107,10 @@ export class UploadCommand {
102
107
  throw new ConfigurationError(error.message);
103
108
  }
104
109
 
105
- if (options.batchSize && (options.batchSize < 1 || options.batchSize > 100)) {
110
+ if (
111
+ options.batchSize &&
112
+ (options.batchSize < 1 || options.batchSize > 100)
113
+ ) {
106
114
  throw new ConfigurationError('Batch size must be between 1 and 100');
107
115
  }
108
116
  }
@@ -116,18 +124,24 @@ export class UploadCommand {
116
124
  async #discoverFiles(sourcePath) {
117
125
  try {
118
126
  if (!FileOperations.fileExists(sourcePath)) {
119
- throw new FileOperationError(`Source path does not exist: ${sourcePath}`);
127
+ throw new FileOperationError(
128
+ `Source path does not exist: ${sourcePath}`,
129
+ );
120
130
  }
121
131
 
122
132
  const stats = FileOperations.getFileStats(sourcePath);
123
-
133
+
124
134
  if (stats?.isDirectory()) {
125
135
  return await globby([`${sourcePath}/**/*`], { onlyFiles: true });
126
136
  } else {
127
137
  return [sourcePath];
128
138
  }
129
139
  } catch (error) {
130
- throw new FileOperationError(`Failed to discover files in ${sourcePath}`, sourcePath, { originalError: error.message });
140
+ throw new FileOperationError(
141
+ `Failed to discover files in ${sourcePath}`,
142
+ sourcePath,
143
+ { originalError: error.message },
144
+ );
131
145
  }
132
146
  }
133
147
 
@@ -142,7 +156,14 @@ export class UploadCommand {
142
156
  * @param {string} sourcePath - Source path
143
157
  * @returns {Promise<Object>} Processing results
144
158
  */
145
- async #processFilesInBatches(files, options, uploadService, basePath, source, sourcePath) {
159
+ async #processFilesInBatches(
160
+ files,
161
+ options,
162
+ uploadService,
163
+ basePath,
164
+ source,
165
+ sourcePath,
166
+ ) {
146
167
  const batchSize = parseInt(options.batchSize) || 10;
147
168
  const results = {
148
169
  successCount: 0,
@@ -153,7 +174,9 @@ export class UploadCommand {
153
174
  };
154
175
 
155
176
  // Get processed paths if available
156
- const processedPaths = options.skipProcessed ? databaseService.getProcessedPaths() : new Set();
177
+ const processedPaths = options.skipProcessed
178
+ ? databaseService.getProcessedPaths()
179
+ : new Set();
157
180
 
158
181
  // Create progress bar
159
182
  const progressBar = new cliProgress.SingleBar({
@@ -168,29 +191,34 @@ export class UploadCommand {
168
191
  // Process files in batches
169
192
  for (let i = 0; i < files.length; i += batchSize) {
170
193
  const batch = files.slice(i, i + batchSize);
171
-
194
+
172
195
  try {
173
196
  const batchResult = await this.#processBatch(
174
- batch,
175
- options,
176
- uploadService,
177
- basePath,
178
- processedPaths
197
+ batch,
198
+ options,
199
+ uploadService,
200
+ basePath,
201
+ processedPaths,
179
202
  );
180
-
203
+
181
204
  this.#updateResults(results, batchResult);
182
-
183
- progressBar.update(Math.min(i + batchSize, files.length), {
184
- success: results.successCount,
185
- errors: results.failureCount
205
+
206
+ progressBar.update(Math.min(i + batchSize, files.length), {
207
+ success: results.successCount,
208
+ errors: results.failureCount,
186
209
  });
187
210
 
188
211
  // Delay between batches if configured
189
212
  if (appConfig.performance.batchDelay > 0) {
190
- await new Promise(resolve => setTimeout(resolve, appConfig.performance.batchDelay));
213
+ await new Promise((resolve) =>
214
+ setTimeout(resolve, appConfig.performance.batchDelay),
215
+ );
191
216
  }
192
217
  } catch (error) {
193
- this.errorHandler.handleError(error, { batch: i / batchSize + 1, batchSize });
218
+ this.errorHandler.handleError(error, {
219
+ batch: i / batchSize + 1,
220
+ batchSize,
221
+ });
194
222
  results.failureCount += batch.length;
195
223
  }
196
224
  }
@@ -220,14 +248,17 @@ export class UploadCommand {
220
248
 
221
249
  if (options.statsOnly) {
222
250
  // Stats-only mode: just record file information
223
- const fileObjects = batch.map(filePath => ({
251
+ const fileObjects = batch.map((filePath) => ({
224
252
  path: filePath,
225
253
  originalName: path.basename(filePath),
226
254
  stats: FileOperations.getFileStats(filePath),
227
255
  }));
228
256
 
229
257
  try {
230
- const result = await databaseService.insertStatsOnlyToUploaderTable(fileObjects, options);
258
+ const result = await databaseService.insertStatsOnlyToUploaderTable(
259
+ fileObjects,
260
+ options,
261
+ );
231
262
  batchResults.successCount = result.totalInserted;
232
263
  batchResults.skippedCount = result.totalSkipped;
233
264
  } catch (error) {
@@ -237,7 +268,14 @@ export class UploadCommand {
237
268
  // Upload mode: process files for upload
238
269
  for (const filePath of batch) {
239
270
  try {
240
- await this.#processFile(filePath, options, uploadService, basePath, processedPaths, batchResults);
271
+ await this.#processFile(
272
+ filePath,
273
+ options,
274
+ uploadService,
275
+ basePath,
276
+ processedPaths,
277
+ batchResults,
278
+ );
241
279
  } catch (error) {
242
280
  this.errorHandler.handleError(error, { filePath });
243
281
  batchResults.failureCount++;
@@ -252,7 +290,14 @@ export class UploadCommand {
252
290
  * Process a single file
253
291
  * @private
254
292
  */
255
- async #processFile(filePath, options, uploadService, basePath, processedPaths, batchResults) {
293
+ async #processFile(
294
+ filePath,
295
+ options,
296
+ uploadService,
297
+ basePath,
298
+ processedPaths,
299
+ batchResults,
300
+ ) {
256
301
  // Skip if already processed
257
302
  if (processedPaths.has(filePath)) {
258
303
  batchResults.skippedCount++;
@@ -260,9 +305,14 @@ export class UploadCommand {
260
305
  }
261
306
 
262
307
  // Prepare file for upload
263
- const sanitizedName = fileSanitizer.sanitizeFileName(path.basename(filePath));
264
- const pathInfo = pathDetector.extractYearAndPedimentoFromPath(filePath, basePath);
265
-
308
+ const sanitizedName = fileSanitizer.sanitizeFileName(
309
+ path.basename(filePath),
310
+ );
311
+ const pathInfo = pathDetector.extractYearAndPedimentoFromPath(
312
+ filePath,
313
+ basePath,
314
+ );
315
+
266
316
  let uploadPath = sanitizedName;
267
317
  if (pathInfo.detected && options.autoDetectStructure) {
268
318
  uploadPath = `${pathInfo.year}/${pathInfo.pedimento}/${sanitizedName}`;
@@ -280,10 +330,12 @@ export class UploadCommand {
280
330
  ...options,
281
331
  uploadPath,
282
332
  });
283
-
333
+
284
334
  batchResults.successCount++;
285
- if (result.detectedCount) batchResults.detectedCount += result.detectedCount;
286
- if (result.organizedCount) batchResults.organizedCount += result.organizedCount;
335
+ if (result.detectedCount)
336
+ batchResults.detectedCount += result.detectedCount;
337
+ if (result.organizedCount)
338
+ batchResults.organizedCount += result.organizedCount;
287
339
  } else {
288
340
  // Supabase direct upload
289
341
  await uploadService.upload([fileObject], { uploadPath });
@@ -332,8 +384,10 @@ export class UploadCommand {
332
384
  console.log(` ⏭️ Duplicates: ${result.skippedCount}`);
333
385
  } else {
334
386
  console.log(` ✅ Uploaded: ${result.successCount}`);
335
- if (result.detectedCount) console.log(` 🔍 Detected: ${result.detectedCount}`);
336
- if (result.organizedCount) console.log(` 📁 Organized: ${result.organizedCount}`);
387
+ if (result.detectedCount)
388
+ console.log(` 🔍 Detected: ${result.detectedCount}`);
389
+ if (result.organizedCount)
390
+ console.log(` 📁 Organized: ${result.organizedCount}`);
337
391
  console.log(` ⏭️ Skipped: ${result.skippedCount}`);
338
392
  }
339
393
  console.log(` ❌ Errors: ${result.failureCount}`);
@@ -350,10 +404,14 @@ export class UploadCommand {
350
404
  console.log(` 📊 Total stats recorded: ${results.successCount}`);
351
405
  console.log(` ⏭️ Total duplicates: ${results.skippedCount}`);
352
406
  } else {
353
- console.log(`🎯 ${uploadService.getServiceName().toUpperCase()} UPLOAD COMPLETED`);
407
+ console.log(
408
+ `🎯 ${uploadService.getServiceName().toUpperCase()} UPLOAD COMPLETED`,
409
+ );
354
410
  console.log(` ✅ Total uploaded: ${results.successCount}`);
355
- if (results.detectedCount) console.log(` 🔍 Total detected: ${results.detectedCount}`);
356
- if (results.organizedCount) console.log(` 📁 Total organized: ${results.organizedCount}`);
411
+ if (results.detectedCount)
412
+ console.log(` 🔍 Total detected: ${results.detectedCount}`);
413
+ if (results.organizedCount)
414
+ console.log(` 📁 Total organized: ${results.organizedCount}`);
357
415
  console.log(` ⏭️ Total skipped: ${results.skippedCount}`);
358
416
  }
359
417
  console.log(` ❌ Total errors: ${results.failureCount}`);
@@ -385,4 +443,4 @@ export class UploadCommand {
385
443
  }
386
444
  }
387
445
 
388
- export default UploadCommand;
446
+ export default UploadCommand;
@@ -28,10 +28,10 @@ class Config {
28
28
  const __dirname = path.dirname(__filename);
29
29
  const packageJsonPath = path.resolve(__dirname, '../../package.json');
30
30
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
31
- return packageJson.version || '1.0.0';
31
+ return packageJson.version || '0.2.7';
32
32
  } catch (error) {
33
33
  console.warn('⚠️ Could not read package.json version, using fallback');
34
- return '0.2.4';
34
+ return '0.2.7';
35
35
  }
36
36
  }
37
37
 
@@ -67,7 +67,7 @@ class Config {
67
67
  const sources = process.env.UPLOAD_SOURCES?.split('|')
68
68
  .map((s) => s.trim())
69
69
  .filter(Boolean);
70
-
70
+
71
71
  const uploadRfcs = process.env.UPLOAD_RFCS?.split('|')
72
72
  .map((s) => s.trim())
73
73
  .filter(Boolean);
@@ -86,7 +86,8 @@ class Config {
86
86
  #loadPerformanceConfig() {
87
87
  return {
88
88
  batchDelay: parseInt(process.env.BATCH_DELAY) || 100,
89
- progressUpdateInterval: parseInt(process.env.PROGRESS_UPDATE_INTERVAL) || 10,
89
+ progressUpdateInterval:
90
+ parseInt(process.env.PROGRESS_UPDATE_INTERVAL) || 10,
90
91
  logBufferSize: 100,
91
92
  logFlushInterval: 5000,
92
93
  };
@@ -128,7 +129,7 @@ class Config {
128
129
  if (forceSupabase) {
129
130
  if (!this.isSupabaseModeAvailable()) {
130
131
  throw new Error(
131
- '⚠️ Missing Supabase credentials. Please set SUPABASE_URL, SUPABASE_KEY, and SUPABASE_BUCKET'
132
+ '⚠️ Missing Supabase credentials. Please set SUPABASE_URL, SUPABASE_KEY, and SUPABASE_BUCKET',
132
133
  );
133
134
  }
134
135
  return;
@@ -137,8 +138,8 @@ class Config {
137
138
  if (!this.isApiModeAvailable() && !this.isSupabaseModeAvailable()) {
138
139
  throw new Error(
139
140
  '⚠️ Missing credentials. Please set either:\n' +
140
- ' - ARELA_API_URL and ARELA_API_TOKEN for API mode, or\n' +
141
- ' - SUPABASE_URL, SUPABASE_KEY, and SUPABASE_BUCKET for direct mode'
141
+ ' - ARELA_API_URL and ARELA_API_TOKEN for API mode, or\n' +
142
+ ' - SUPABASE_URL, SUPABASE_KEY, and SUPABASE_BUCKET for direct mode',
142
143
  );
143
144
  }
144
145
  }
@@ -150,7 +151,9 @@ class Config {
150
151
  */
151
152
  getUploadSources() {
152
153
  if (!this.upload.sources || this.upload.sources.length === 0) {
153
- throw new Error('⚠️ No upload sources configured. Please set UPLOAD_SOURCES environment variable.');
154
+ throw new Error(
155
+ '⚠️ No upload sources configured. Please set UPLOAD_SOURCES environment variable.',
156
+ );
154
157
  }
155
158
  return this.upload.sources;
156
159
  }
@@ -162,7 +165,9 @@ class Config {
162
165
  */
163
166
  getBasePath() {
164
167
  if (!this.upload.basePath) {
165
- throw new Error('⚠️ No base path configured. Please set UPLOAD_BASE_PATH environment variable.');
168
+ throw new Error(
169
+ '⚠️ No base path configured. Please set UPLOAD_BASE_PATH environment variable.',
170
+ );
166
171
  }
167
172
  return this.upload.basePath;
168
173
  }
@@ -170,4 +175,4 @@ class Config {
170
175
 
171
176
  // Export singleton instance
172
177
  export const appConfig = new Config();
173
- export default appConfig;
178
+ export default appConfig;
@@ -27,10 +27,10 @@ export class ErrorHandler {
27
27
  */
28
28
  handleError(error, context = {}) {
29
29
  const errorInfo = this.#analyzeError(error, context);
30
-
30
+
31
31
  // Log the error with appropriate level
32
32
  this.#logError(errorInfo);
33
-
33
+
34
34
  return errorInfo;
35
35
  }
36
36
 
@@ -41,14 +41,14 @@ export class ErrorHandler {
41
41
  */
42
42
  handleFatalError(error, context = {}) {
43
43
  const errorInfo = this.handleError(error, context);
44
-
44
+
45
45
  console.error('\n❌ Fatal Error - Application will exit');
46
46
  console.error(` ${errorInfo.userMessage}`);
47
-
47
+
48
48
  if (this.logger.isVerboseEnabled() && errorInfo.technicalDetails) {
49
49
  console.error(` Technical Details: ${errorInfo.technicalDetails}`);
50
50
  }
51
-
51
+
52
52
  this.logger.flush();
53
53
  process.exit(1);
54
54
  }
@@ -65,7 +65,7 @@ export class ErrorHandler {
65
65
  originalError: originalError.message,
66
66
  originalStack: originalError.stack,
67
67
  });
68
-
68
+
69
69
  return error;
70
70
  }
71
71
 
@@ -103,45 +103,45 @@ export class ErrorHandler {
103
103
  if (error instanceof ConfigurationError) {
104
104
  return `Configuration issue: ${error.message}`;
105
105
  }
106
-
106
+
107
107
  if (error instanceof FileOperationError) {
108
108
  const filePath = error.details?.filePath;
109
109
  return `File operation failed${filePath ? ` for ${filePath}` : ''}: ${error.message}`;
110
110
  }
111
-
111
+
112
112
  if (error instanceof UploadServiceError) {
113
113
  const serviceName = error.details?.serviceName;
114
114
  return `Upload failed${serviceName ? ` (${serviceName})` : ''}: ${error.message}`;
115
115
  }
116
-
116
+
117
117
  if (error instanceof ApiError) {
118
118
  const statusCode = error.details?.statusCode;
119
119
  return `API request failed${statusCode ? ` (HTTP ${statusCode})` : ''}: ${error.message}`;
120
120
  }
121
-
121
+
122
122
  if (error instanceof DatabaseError) {
123
123
  const operation = error.details?.operation;
124
124
  return `Database operation failed${operation ? ` (${operation})` : ''}: ${error.message}`;
125
125
  }
126
-
126
+
127
127
  if (error instanceof DetectionError) {
128
128
  const filePath = error.details?.filePath;
129
129
  return `File detection failed${filePath ? ` for ${filePath}` : ''}: ${error.message}`;
130
130
  }
131
-
131
+
132
132
  if (error instanceof ValidationError) {
133
133
  const field = error.details?.field;
134
134
  return `Validation failed${field ? ` for ${field}` : ''}: ${error.message}`;
135
135
  }
136
-
136
+
137
137
  if (error instanceof NetworkError) {
138
138
  return `Network error: ${error.message}`;
139
139
  }
140
-
140
+
141
141
  if (error instanceof ArelaError) {
142
142
  return error.message;
143
143
  }
144
-
144
+
145
145
  // Generic error handling
146
146
  return `An unexpected error occurred: ${error.message}`;
147
147
  }
@@ -156,23 +156,26 @@ export class ErrorHandler {
156
156
  if (error instanceof ConfigurationError) {
157
157
  return 'fatal';
158
158
  }
159
-
159
+
160
160
  if (error instanceof NetworkError || error instanceof ApiError) {
161
161
  return 'high';
162
162
  }
163
-
163
+
164
164
  if (error instanceof DatabaseError || error instanceof UploadServiceError) {
165
165
  return 'high';
166
166
  }
167
-
168
- if (error instanceof FileOperationError || error instanceof DetectionError) {
167
+
168
+ if (
169
+ error instanceof FileOperationError ||
170
+ error instanceof DetectionError
171
+ ) {
169
172
  return 'medium';
170
173
  }
171
-
174
+
172
175
  if (error instanceof ValidationError) {
173
176
  return 'low';
174
177
  }
175
-
178
+
176
179
  return 'medium';
177
180
  }
178
181
 
@@ -186,21 +189,21 @@ export class ErrorHandler {
186
189
  if (error instanceof ConfigurationError) {
187
190
  return false;
188
191
  }
189
-
192
+
190
193
  if (error instanceof NetworkError || error instanceof ApiError) {
191
194
  // Network errors might be temporary
192
195
  return true;
193
196
  }
194
-
197
+
195
198
  if (error instanceof FileOperationError) {
196
199
  // File errors might be recoverable depending on the specific error
197
200
  return true;
198
201
  }
199
-
202
+
200
203
  if (error instanceof DetectionError || error instanceof ValidationError) {
201
204
  return true;
202
205
  }
203
-
206
+
204
207
  return false;
205
208
  }
206
209
 
@@ -211,11 +214,15 @@ export class ErrorHandler {
211
214
  */
212
215
  #logError(errorInfo) {
213
216
  const logMessage = `${errorInfo.type}: ${errorInfo.message}`;
214
- const contextInfo = errorInfo.context ? ` | Context: ${JSON.stringify(errorInfo.context)}` : '';
215
- const detailsInfo = errorInfo.technicalDetails ? ` | Details: ${JSON.stringify(errorInfo.technicalDetails)}` : '';
216
-
217
+ const contextInfo = errorInfo.context
218
+ ? ` | Context: ${JSON.stringify(errorInfo.context)}`
219
+ : '';
220
+ const detailsInfo = errorInfo.technicalDetails
221
+ ? ` | Details: ${JSON.stringify(errorInfo.technicalDetails)}`
222
+ : '';
223
+
217
224
  const fullLogMessage = `${logMessage}${contextInfo}${detailsInfo}`;
218
-
225
+
219
226
  switch (errorInfo.severity) {
220
227
  case 'fatal':
221
228
  this.logger.error(`FATAL: ${fullLogMessage}`);
@@ -232,7 +239,7 @@ export class ErrorHandler {
232
239
  default:
233
240
  this.logger.error(fullLogMessage);
234
241
  }
235
-
242
+
236
243
  // Log stack trace in verbose mode
237
244
  if (this.logger.isVerboseEnabled() && errorInfo.stack) {
238
245
  this.logger.debug(`Stack trace: ${errorInfo.stack}`);
@@ -268,4 +275,4 @@ export class ErrorHandler {
268
275
  }
269
276
  }
270
277
 
271
- export default ErrorHandler;
278
+ export default ErrorHandler;
@@ -12,7 +12,7 @@ export class ArelaError extends Error {
12
12
  this.code = code;
13
13
  this.details = details;
14
14
  this.timestamp = new Date().toISOString();
15
-
15
+
16
16
  // Ensure proper stack trace
17
17
  if (Error.captureStackTrace) {
18
18
  Error.captureStackTrace(this, this.constructor);
@@ -101,4 +101,4 @@ export class NetworkError extends ArelaError {
101
101
  constructor(message, url = null, details = null) {
102
102
  super(message, 'NETWORK_ERROR', { url, ...details });
103
103
  }
104
- }
104
+ }
package/src/index-old.js CHANGED
@@ -158,8 +158,8 @@ const checkCredentials = async (forceSupabase = false) => {
158
158
  if (!supabaseUrl || !supabaseKey || !bucket) {
159
159
  console.error(
160
160
  '⚠️ Missing credentials. Please set either:\n' +
161
- ' - ARELA_API_URL and ARELA_API_TOKEN for API mode, or\n' +
162
- ' - SUPABASE_URL, SUPABASE_KEY, and SUPABASE_BUCKET for direct mode',
161
+ ' - ARELA_API_URL and ARELA_API_TOKEN for API mode, or\n' +
162
+ ' - SUPABASE_URL, SUPABASE_KEY, and SUPABASE_BUCKET for direct mode',
163
163
  );
164
164
  process.exit(1);
165
165
  }
@@ -709,20 +709,29 @@ const insertStatsOnlyToUploaderTable = async (files, options) => {
709
709
 
710
710
  try {
711
711
  // First, check which records already exist
712
- const originalPaths = batch.map(r => r.original_path);
712
+ const originalPaths = batch.map((r) => r.original_path);
713
713
  const { data: existingRecords, error: checkError } = await supabase
714
714
  .from('uploader')
715
715
  .select('original_path')
716
716
  .in('original_path', originalPaths);
717
717
 
718
718
  if (checkError) {
719
- console.error(`❌ Error checking existing records:`, checkError.message);
719
+ console.error(
720
+ `❌ Error checking existing records:`,
721
+ checkError.message,
722
+ );
720
723
  continue;
721
724
  }
722
725
 
723
- const existingPaths = new Set(existingRecords?.map(r => r.original_path) || []);
724
- const newRecords = batch.filter(r => !existingPaths.has(r.original_path));
725
- const updateRecords = batch.filter(r => existingPaths.has(r.original_path));
726
+ const existingPaths = new Set(
727
+ existingRecords?.map((r) => r.original_path) || [],
728
+ );
729
+ const newRecords = batch.filter(
730
+ (r) => !existingPaths.has(r.original_path),
731
+ );
732
+ const updateRecords = batch.filter((r) =>
733
+ existingPaths.has(r.original_path),
734
+ );
726
735
 
727
736
  console.log(
728
737
  `📦 Batch ${Math.floor(i / batchSize) + 1}: ${newRecords.length} new, ${updateRecords.length} updates`,
@@ -763,7 +772,6 @@ const insertStatsOnlyToUploaderTable = async (files, options) => {
763
772
  totalUpdated += batchUpdated;
764
773
  console.log(`🔄 Updated ${batchUpdated} existing records`);
765
774
  }
766
-
767
775
  } catch (error) {
768
776
  console.error(
769
777
  `❌ Unexpected error in batch ${Math.floor(i / batchSize) + 1}:`,
@@ -1785,8 +1793,8 @@ const uploadFilesByRfc = async (options = {}) => {
1785
1793
  // Set folder structure for this group - concatenate custom prefix with arela_path
1786
1794
  const folderStructure = options.folderStructure
1787
1795
  ? `${options.folderStructure}/${arelaPath}`
1788
- .replace(/\/+/g, '/')
1789
- .replace(/\/$/, '')
1796
+ .replace(/\/+/g, '/')
1797
+ .replace(/\/$/, '')
1790
1798
  : arelaPath;
1791
1799
  pathFormData.append('folderStructure', folderStructure);
1792
1800
  pathFormData.append('autoDetect', 'true');
@@ -2300,13 +2308,13 @@ program
2300
2308
  .name('arela-uploader')
2301
2309
  .description(
2302
2310
  'CLI to upload folders to Arela API or Supabase Storage with automatic processing\n\n' +
2303
- 'Status workflow:\n' +
2304
- ' fs-stats → detected → file-uploaded\n' +
2305
- ' ├─ Phase 1: --stats-only (collects filesystem stats, status: fs-stats)\n' +
2306
- ' ├─ Phase 2: --detect-pdfs (detects document types, status: detected)\n' +
2307
- ' ├─ Phase 3: --propagate-arela-path (organizes files by pedimento)\n' +
2308
- ' └─ Phase 4: --upload-by-rfc (uploads files, status: file-uploaded)\n\n' +
2309
- 'Use --query-ready-files to see files ready for upload (status: detected with arela_path)',
2311
+ 'Status workflow:\n' +
2312
+ ' fs-stats → detected → file-uploaded\n' +
2313
+ ' ├─ Phase 1: --stats-only (collects filesystem stats, status: fs-stats)\n' +
2314
+ ' ├─ Phase 2: --detect-pdfs (detects document types, status: detected)\n' +
2315
+ ' ├─ Phase 3: --propagate-arela-path (organizes files by pedimento)\n' +
2316
+ ' └─ Phase 4: --upload-by-rfc (uploads files, status: file-uploaded)\n\n' +
2317
+ 'Use --query-ready-files to see files ready for upload (status: detected with arela_path)',
2310
2318
  )
2311
2319
  .option('-v, --version', 'output the version number')
2312
2320
  .option('-p, --prefix <prefix>', 'Prefix path in bucket', '')