@arela/uploader 1.0.3 → 1.0.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.
Files changed (44) hide show
  1. package/.env.local +316 -0
  2. package/coverage/IdentifyCommand.js.html +1462 -0
  3. package/coverage/PropagateCommand.js.html +1507 -0
  4. package/coverage/PushCommand.js.html +1504 -0
  5. package/coverage/ScanCommand.js.html +1654 -0
  6. package/coverage/UploadCommand.js.html +1846 -0
  7. package/coverage/WatchCommand.js.html +4111 -0
  8. package/coverage/base.css +224 -0
  9. package/coverage/block-navigation.js +87 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +191 -0
  12. package/coverage/lcov-report/IdentifyCommand.js.html +1462 -0
  13. package/coverage/lcov-report/PropagateCommand.js.html +1507 -0
  14. package/coverage/lcov-report/PushCommand.js.html +1504 -0
  15. package/coverage/lcov-report/ScanCommand.js.html +1654 -0
  16. package/coverage/lcov-report/UploadCommand.js.html +1846 -0
  17. package/coverage/lcov-report/WatchCommand.js.html +4111 -0
  18. package/coverage/lcov-report/base.css +224 -0
  19. package/coverage/lcov-report/block-navigation.js +87 -0
  20. package/coverage/lcov-report/favicon.png +0 -0
  21. package/coverage/lcov-report/index.html +191 -0
  22. package/coverage/lcov-report/prettify.css +1 -0
  23. package/coverage/lcov-report/prettify.js +2 -0
  24. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  25. package/coverage/lcov-report/sorter.js +210 -0
  26. package/coverage/lcov.info +1937 -0
  27. package/coverage/prettify.css +1 -0
  28. package/coverage/prettify.js +2 -0
  29. package/coverage/sort-arrow-sprite.png +0 -0
  30. package/coverage/sorter.js +210 -0
  31. package/docs/CROSS_PLATFORM_PATH_HANDLING.md +597 -0
  32. package/package.json +28 -2
  33. package/src/commands/IdentifyCommand.js +1 -28
  34. package/src/commands/PropagateCommand.js +1 -1
  35. package/src/commands/PushCommand.js +1 -1
  36. package/src/commands/ScanCommand.js +27 -20
  37. package/src/config/config.js +27 -48
  38. package/src/services/ScanApiService.js +4 -5
  39. package/src/utils/PathNormalizer.js +272 -0
  40. package/tests/commands/IdentifyCommand.test.js +570 -0
  41. package/tests/commands/PropagateCommand.test.js +568 -0
  42. package/tests/commands/PushCommand.test.js +754 -0
  43. package/tests/commands/ScanCommand.test.js +382 -0
  44. package/tests/unit/PathAndTableNameGeneration.test.js +1211 -0
@@ -0,0 +1,597 @@
1
+ # Absolute Path Strategy & SCAN_DIRECTORY_LEVEL Implementation
2
+
3
+ **Updated**: January 19, 2026
4
+
5
+ ## Overview
6
+
7
+ The system now uses **absolute paths throughout** for maximum clarity and correctness. This ensures that different relative paths like `../sample` and `./sample` create different tables as expected.
8
+
9
+ ## ✅ Simplified Strategy: Use Absolute Paths Everywhere
10
+
11
+ ### Key Principle
12
+ **Always resolve paths to absolute paths first, then sanitize for table names.**
13
+
14
+ ### Why This Works Better
15
+
16
+ 1. **Unambiguous**: `../sample` and `./sample` resolve to different absolute paths
17
+ 2. **Simpler**: Same absolute path used for both `base_path_label` and `base_path_full`
18
+ 3. **Cross-platform**: Forward slashes in stored paths for consistency
19
+ 4. **PostgreSQL-friendly**: Special characters sanitized only in table names
20
+
21
+ ### Path Examples
22
+
23
+ ```bash
24
+ # From /data/project:
25
+ ../sample → /data/sample → scan_palco_local_data_sample
26
+ ./sample → /data/project/sample → scan_palco_local_data_project_sample
27
+
28
+ # Windows:
29
+ C:\Users\Docs → C:/Users/Docs (stored) → scan_palco_local_c_users_docs
30
+
31
+ # Linux:
32
+ /home/user → /home/user (stored) → scan_palco_local_home_user
33
+ ```
34
+
35
+ ## Storage Details
36
+
37
+ ### cli_registry Table
38
+ - `company_slug`: Company identifier
39
+ - `server_id`: Server identifier
40
+ - `base_path_full`: Absolute path (preserves original format for Windows paths like `O:\expediente\archivos`)
41
+ - `table_name`: Sanitized version (`scan_palco_local_c_users_documents_2023`)
42
+ - **Removed**: `base_path_label` (was redundant - same as base_path_full)
43
+
44
+ ### File Records (scan_* tables)
45
+ - `directory_path`: Absolute path with forward slashes
46
+ - `relative_path`: Relative to base with forward slashes
47
+ - `absolute_path`: Absolute path with forward slashes
48
+
49
+ **Note**: Paths are stored consistently. Windows absolute paths (with drive letters) are preserved when the CLI runs on the same Windows machine or a different OS (macOS/Linux).
50
+
51
+ ## Key Features
52
+
53
+ ### 1. Cross-Platform Path Detection
54
+
55
+ The CLI correctly identifies Windows absolute paths even when running on macOS/Linux:
56
+
57
+ - **Windows paths on any OS**: `O:\expediente\archivos` → recognized as absolute, preserved as-is
58
+ - **Unix paths**: `/home/user/docs` → recognized as absolute, normalized
59
+ - **Relative paths**: `./sample` → resolved against cwd
60
+
61
+ This allows:
62
+ - Running `arela scan` on Windows server → creates table with Windows path in `base_path_full`
63
+ - Running `arela identify` on macOS (for testing) → correctly uses the same Windows path
64
+ - Both resolve to the same table lookup
65
+
66
+ ### 2. Full Path in Table Names
67
+
68
+ Table names now include the complete normalized path:
69
+
70
+ ```
71
+ Format: scan_<company>_<server>_<normalized_path>
72
+
73
+ Examples:
74
+ - scan_palco_local_data_2023
75
+ - scan_palco_local_users_documents_projects
76
+ - scan_company_server_c_users_admin_files
77
+ ```
78
+
79
+ ### 3. SCAN_DIRECTORY_LEVEL Support
80
+
81
+ The `SCAN_DIRECTORY_LEVEL` environment variable controls table granularity:
82
+
83
+ - **Level 0** (default): Single table for entire base path
84
+ - **Level 1**: One table per first-level subdirectory
85
+ - **Level 2**: One table per second-level subdirectory
86
+ - **Level N**: One table per N-th level subdirectory
87
+
88
+ Example with `SCAN_DIRECTORY_LEVEL=1` and base path `/data`:
89
+ ```
90
+ /data/2023 → scan_palco_local_data_2023
91
+ /data/2024 → scan_palco_local_data_2024
92
+ /data/archive → scan_palco_local_data_archive
93
+ ```
94
+
95
+ ## Implementation Details
96
+
97
+ ### CLI Components
98
+
99
+ #### 1. PathNormalizer Utility (`src/utils/PathNormalizer.js`)
100
+
101
+ Centralized utility for all path operations:
102
+
103
+ ```javascript
104
+ // Normalize any path to POSIX format
105
+ const normalized = PathNormalizer.normalizePath('C:\\Users\\Documents');
106
+ // Result: /Users/Documents
107
+
108
+ // Generate table name
109
+ const tableName = PathNormalizer.generateTableName({
110
+ companySlug: 'palco',
111
+ serverId: 'local',
112
+ basePathLabel: '/data/2023'
113
+ });
114
+ // Result: scan_palco_local_data_2023
115
+
116
+ // Build full path label
117
+ const fullPath = PathNormalizer.buildBasePathLabel('/data', '2023/subfolder');
118
+ // Result: /data/2023/subfolder
119
+ ```
120
+
121
+ #### 2. ScanCommand Updates
122
+
123
+ **Directory Discovery** ([ScanCommand.js](../src/commands/ScanCommand.js)):
124
+
125
+ ```javascript
126
+ async #discoverDirectories(basePath, level) {
127
+ // Level 0: Single entry
128
+ if (level === 0) {
129
+ return sources.map((source) => {
130
+ const sourcePath = source === '.' ? basePath : path.resolve(basePath, source);
131
+ const relativePath = source === '.' ? '' : PathNormalizer.getRelativePath(sourcePath, basePath);
132
+ return { path: sourcePath, label: relativePath };
133
+ });
134
+ }
135
+
136
+ // Level > 0: Discover directories at specified depth
137
+ const levelDirs = await this.#getDirectoriesAtLevel(basePath, level, '');
138
+ // ... combine with sources
139
+ }
140
+ ```
141
+
142
+ **Table Registration**:
143
+
144
+ ```javascript
145
+ for (const dir of directories) {
146
+ // Build full normalized path label
147
+ const fullPathLabel = PathNormalizer.buildBasePathLabel(
148
+ basePath,
149
+ dir.label || '',
150
+ );
151
+
152
+ const registration = await this.scanApiService.registerInstance({
153
+ companySlug: scanConfig.companySlug,
154
+ serverId: scanConfig.serverId,
155
+ basePathLabel: fullPathLabel, // Full normalized path
156
+ basePathFull: dir.path, // Absolute system path
157
+ });
158
+ }
159
+ ```
160
+
161
+ **File Record Normalization**:
162
+
163
+ ```javascript
164
+ #normalizeFileRecord(filePath, fileStats, basePath, scanTimestamp) {
165
+ return {
166
+ fileName: path.basename(filePath),
167
+ fileExtension: path.extname(filePath).toLowerCase().replace('.', ''),
168
+ directoryPath: PathNormalizer.normalizePath(path.dirname(filePath)),
169
+ relativePath: PathNormalizer.getRelativePath(filePath, basePath),
170
+ absolutePath: PathNormalizer.normalizePath(filePath),
171
+ sizeBytes: Number(fileStats.size),
172
+ modifiedAt: fileStats.mtime.toISOString(),
173
+ scanTimestamp,
174
+ };
175
+ }
176
+ ```
177
+
178
+ #### 3. Config Updates
179
+
180
+ Configuration now uses normalized paths ([config.js](../src/config/config.js)):
181
+
182
+ ```javascript
183
+ #loadScanConfig() {
184
+ // Auto-derive basePathLabel from UPLOAD_BASE_PATH
185
+ if (!basePathLabel && process.env.UPLOAD_BASE_PATH) {
186
+ const basePath = process.env.UPLOAD_BASE_PATH;
187
+ // Normalize for consistent table naming across platforms
188
+ basePathLabel = PathNormalizer.normalizePath(basePath);
189
+ }
190
+
191
+ // Generate table name using PathNormalizer
192
+ if (companySlug && serverId && basePathLabel) {
193
+ tableName = PathNormalizer.generateTableName({
194
+ companySlug,
195
+ serverId,
196
+ basePathLabel,
197
+ });
198
+ }
199
+ }
200
+ ```
201
+
202
+ ### Backend Components
203
+
204
+ #### 1. PathNormalizer Utility (`src/uploader/utils/path-normalizer.util.ts`)
205
+
206
+ TypeScript version matching CLI logic exactly:
207
+
208
+ ```typescript
209
+ export class PathNormalizer {
210
+ static normalizePath(inputPath: string): string { /* ... */ }
211
+ static generateTableName(config: {...}): string { /* ... */ }
212
+ static buildBasePathLabel(basePath: string, relativePath: string): string { /* ... */ }
213
+ // ... other utilities
214
+ }
215
+ ```
216
+
217
+ #### 2. FileStatsTableManagerService Updates
218
+
219
+ **Table Name Generation** ([file-stats-table-manager.service.ts](../../arela-api/src/uploader/services/file-stats-table-manager.service.ts)):
220
+
221
+ ```typescript
222
+ private generateTableName(config: ScanInstanceConfig): string {
223
+ // Delegate to PathNormalizer for consistency with CLI
224
+ return PathNormalizer.generateTableName(config);
225
+ }
226
+ ```
227
+
228
+ **Instance Table Lookup**:
229
+
230
+ ```typescript
231
+ async getInstanceTables(
232
+ companySlug: string,
233
+ serverId: string,
234
+ basePathLabel: string,
235
+ ): Promise<CliRegistry[]> {
236
+ // Normalize the base path
237
+ const normalizedBasePath = PathNormalizer.normalizePath(basePathLabel);
238
+
239
+ // Match all tables for this company/server with base_path_full starting with normalized path
240
+ return this.cliRegistryRepository
241
+ .createQueryBuilder('registry')
242
+ .where('registry.table_name LIKE :pattern', {
243
+ pattern: `${basePattern}%`,
244
+ })
245
+ .andWhere('registry.company_slug = :companySlug', { companySlug })
246
+ .andWhere('registry.server_id = :serverId', { serverId })
247
+ .andWhere(
248
+ '(registry.base_path_full = :basePath OR registry.base_path_full LIKE :basePathPrefix)',
249
+ {
250
+ basePath: normalizedBasePath,
251
+ basePathPrefix: `${normalizedBasePath}/%`,
252
+ },
253
+ )
254
+ .orderBy('registry.table_name', 'ASC')
255
+ .getMany();
256
+ }
257
+ ```
258
+
259
+ ## Command Flow
260
+
261
+ ### 1. Scan Command
262
+
263
+ ```bash
264
+ arela scan
265
+ ```
266
+
267
+ **Flow**:
268
+ 1. Load configuration (company, server, base path)
269
+ 2. Normalize base path using PathNormalizer
270
+ 3. Discover directories at specified `SCAN_DIRECTORY_LEVEL`
271
+ 4. For each directory:
272
+ - Build full normalized path label
273
+ - Register instance (creates/finds table)
274
+ - Scan files with normalized paths
275
+ - Upload batch to API
276
+
277
+ ### 2. Identify Command
278
+
279
+ ```bash
280
+ arela identify
281
+ ```
282
+
283
+ **Flow**:
284
+ 1. Load configuration
285
+ 2. Fetch all tables using `getInstanceTables()`
286
+ 3. For each table:
287
+ - Fetch pending PDFs
288
+ - Detect locally
289
+ - Update with normalized paths
290
+
291
+ ### 3. Propagate Command
292
+
293
+ ```bash
294
+ arela propagate
295
+ ```
296
+
297
+ **Flow**:
298
+ 1. Load configuration
299
+ 2. Fetch all tables using `getInstanceTables()`
300
+ 3. For each table:
301
+ - Mark files needing propagation
302
+ - Process pedimentos by directory
303
+ - Propagate arela_path to same-directory files
304
+
305
+ ### 4. Push Command
306
+
307
+ ```bash
308
+ arela push
309
+ ```
310
+
311
+ **Flow**:
312
+ 1. Load configuration
313
+ 2. Fetch all tables using `getInstanceTables()`
314
+ 3. For each table:
315
+ - Fetch files with arela_path
316
+ - Upload to storage API
317
+ - Update upload status
318
+
319
+ ## Configuration Examples
320
+
321
+ ### Single-Level Scan
322
+
323
+ ```bash
324
+ # .env
325
+ ARELA_COMPANY_SLUG=palco
326
+ ARELA_SERVER_ID=local
327
+ UPLOAD_BASE_PATH=/data
328
+ SCAN_DIRECTORY_LEVEL=0
329
+
330
+ # Result: Single table scan_palco_local_data
331
+ ```
332
+
333
+ ### Multi-Level Scan (Year-based)
334
+
335
+ ```bash
336
+ # .env
337
+ ARELA_COMPANY_SLUG=palco
338
+ ARELA_SERVER_ID=local
339
+ UPLOAD_BASE_PATH=/data
340
+ SCAN_DIRECTORY_LEVEL=1
341
+
342
+ # Directory structure:
343
+ # /data/2023/...
344
+ # /data/2024/...
345
+ # /data/archive/...
346
+
347
+ # Result: Three tables
348
+ # - scan_palco_local_data_2023
349
+ # - scan_palco_local_data_2024
350
+ # - scan_palco_local_data_archive
351
+ ```
352
+
353
+ ### Multi-Level Scan with Sources
354
+
355
+ ```bash
356
+ # .env
357
+ ARELA_COMPANY_SLUG=palco
358
+ ARELA_SERVER_ID=local
359
+ UPLOAD_BASE_PATH=/data
360
+ UPLOAD_SOURCES=documents,images
361
+ SCAN_DIRECTORY_LEVEL=1
362
+
363
+ # Directory structure:
364
+ # /data/2023/documents/...
365
+ # /data/2023/images/...
366
+ # /data/2024/documents/...
367
+ # /data/2024/images/...
368
+
369
+ # Result: Four tables
370
+ # - scan_palco_local_data_2023_documents
371
+ # - scan_palco_local_data_2023_images
372
+ # - scan_palco_local_data_2024_documents
373
+ # - scan_palco_local_data_2024_images
374
+ ```
375
+
376
+ ## Platform-Specific Handling
377
+
378
+ ### Windows
379
+
380
+ **Input Paths**:
381
+ ```
382
+ C:\Users\Documents\2023
383
+ D:\Archive\Files
384
+ O:\Network\Share
385
+ ```
386
+
387
+ **Normalized Paths**:
388
+ ```
389
+ /Users/Documents/2023
390
+ /Archive/Files
391
+ /Network/Share
392
+ ```
393
+
394
+ **Table Names**:
395
+ ```
396
+ scan_company_server_users_documents_2023
397
+ scan_company_server_archive_files
398
+ scan_company_server_network_share
399
+ ```
400
+
401
+ ### Linux/macOS
402
+
403
+ **Input Paths**:
404
+ ```
405
+ /home/user/documents/2023
406
+ /mnt/storage/archive
407
+ /Volumes/backup/files
408
+ ```
409
+
410
+ **Normalized Paths** (unchanged):
411
+ ```
412
+ /home/user/documents/2023
413
+ /mnt/storage/archive
414
+ /Volumes/backup/files
415
+ ```
416
+
417
+ **Table Names**:
418
+ ```
419
+ scan_company_server_home_user_documents_2023
420
+ scan_company_server_mnt_storage_archive
421
+ scan_company_server_volumes_backup_files
422
+ ```
423
+
424
+ ## Database Schema
425
+
426
+ ### cli_registry Table
427
+
428
+ Stores metadata for each scan instance:
429
+
430
+ ```sql
431
+ CREATE TABLE cli.cli_registry (
432
+ id UUID PRIMARY KEY,
433
+ table_name VARCHAR(63) UNIQUE NOT NULL,
434
+ company_slug VARCHAR(255) NOT NULL,
435
+ server_id VARCHAR(255) NOT NULL,
436
+ base_path_label TEXT NOT NULL, -- Normalized path (e.g., /data/2023)
437
+ base_path_full TEXT NOT NULL, -- Original absolute path (e.g., C:\data\2023)
438
+ status VARCHAR(50) DEFAULT 'active',
439
+ created_at TIMESTAMP DEFAULT NOW(),
440
+ last_scan_at TIMESTAMP,
441
+ total_files INTEGER DEFAULT 0,
442
+ total_size_bytes BIGINT DEFAULT 0
443
+ );
444
+
445
+ -- Index for efficient lookup
446
+ CREATE INDEX idx_cli_registry_lookup
447
+ ON cli.cli_registry(company_slug, server_id, base_path_label);
448
+ ```
449
+
450
+ ### scan_* Tables
451
+
452
+ Dynamic tables created per instance:
453
+
454
+ ```sql
455
+ CREATE TABLE cli.scan_palco_local_data_2023 (
456
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
457
+ file_name VARCHAR(255) NOT NULL,
458
+ file_extension VARCHAR(50),
459
+ directory_path TEXT NOT NULL, -- Normalized directory path
460
+ relative_path TEXT NOT NULL, -- Normalized relative path
461
+ absolute_path TEXT NOT NULL, -- Normalized absolute path
462
+ size_bytes BIGINT NOT NULL,
463
+ modified_at TIMESTAMP NOT NULL,
464
+ scan_timestamp TIMESTAMP NOT NULL,
465
+
466
+ -- Detection fields
467
+ detected_type VARCHAR(100),
468
+ detected_pedimento VARCHAR(50),
469
+ detected_pedimento_year INTEGER,
470
+ rfc VARCHAR(20),
471
+ arela_path TEXT,
472
+ detection_error TEXT,
473
+ detection_attempts INTEGER DEFAULT 0,
474
+ is_not_pedimento BOOLEAN DEFAULT FALSE,
475
+
476
+ -- Propagation fields
477
+ propagated_from_id UUID,
478
+ propagation_error TEXT,
479
+ propagation_attempts INTEGER DEFAULT 0,
480
+ needs_propagation BOOLEAN DEFAULT FALSE,
481
+
482
+ -- Upload fields
483
+ uploaded BOOLEAN DEFAULT FALSE,
484
+ upload_path TEXT,
485
+ uploaded_to_storage_id TEXT,
486
+ upload_error TEXT,
487
+ upload_attempts INTEGER DEFAULT 0
488
+ );
489
+ ```
490
+
491
+ ## Benefits
492
+
493
+ ### 1. Cross-Platform Consistency
494
+
495
+ - Same table names regardless of OS
496
+ - Paths stored consistently in database
497
+ - No special handling needed per platform
498
+
499
+ ### 2. Full Path Visibility
500
+
501
+ - Table names clearly show what directory they represent
502
+ - Easy to identify and manage specific scans
503
+ - Better organization for multi-directory setups
504
+
505
+ ### 3. Scalability
506
+
507
+ - Support for any directory structure
508
+ - Configurable granularity via `SCAN_DIRECTORY_LEVEL`
509
+ - Efficient batch processing per directory
510
+
511
+ ### 4. Maintainability
512
+
513
+ - Centralized path logic in PathNormalizer
514
+ - Consistent behavior between CLI and backend
515
+ - Easy to test and debug
516
+
517
+ ## Migration Guide
518
+
519
+ ### For Existing Installations
520
+
521
+ If you have existing scan tables with old naming:
522
+
523
+ 1. **New scans will create new tables** with the updated naming scheme
524
+ 2. **Old tables remain functional** but won't be automatically migrated
525
+ 3. **To migrate**:
526
+ - Re-run `arela scan` to create new tables
527
+ - Use `identify`, `propagate`, `push` on new tables
528
+ - Optionally drop old tables when confident
529
+
530
+ ### Backward Compatibility
531
+
532
+ The system is designed to be backward compatible:
533
+
534
+ - `getInstanceTables()` matches by `base_path_full` pattern
535
+ - Old tables can coexist with new tables
536
+ - Commands process all matching tables
537
+
538
+ ## Troubleshooting
539
+
540
+ ### Issue: Tables not found
541
+
542
+ **Cause**: Base path mismatch between scan and other commands
543
+
544
+ **Solution**: Ensure `UPLOAD_BASE_PATH` is consistent across commands
545
+
546
+ ### Issue: Duplicate tables
547
+
548
+ **Cause**: Different path formats creating separate tables
549
+
550
+ **Solution**: System now prevents this with PathNormalizer
551
+
552
+ ### Issue: Table name too long
553
+
554
+ **Cause**: Very deep directory structures
555
+
556
+ **Solution**: System automatically truncates and adds hash for uniqueness
557
+
558
+ ## Testing
559
+
560
+ ### Manual Testing
561
+
562
+ ```bash
563
+ # Test different path formats
564
+ UPLOAD_BASE_PATH="C:\Users\Documents" arela scan # Windows
565
+ UPLOAD_BASE_PATH="/Users/Documents" arela scan # Unix
566
+ UPLOAD_BASE_PATH="O:/Data/Files" arela scan # Network
567
+
568
+ # All should create the same table name (normalized)
569
+ ```
570
+
571
+ ### Unit Testing
572
+
573
+ Tests are provided in [tests/PathNormalizer.test.js](../tests/PathNormalizer.test.js):
574
+
575
+ ```bash
576
+ npm test -- PathNormalizer.test.js
577
+ ```
578
+
579
+ ## Future Enhancements
580
+
581
+ 1. **Table Consolidation**: Utility to merge data from old tables to new naming scheme
582
+ 2. **Path Aliases**: Support for custom path labels independent of physical path
583
+ 3. **Virtual Directories**: Scan multiple non-contiguous directories into one table
584
+ 4. **Path Filters**: Regex-based filtering at path level
585
+
586
+ ## Summary
587
+
588
+ The cross-platform path handling system provides:
589
+
590
+ ✅ Consistent table naming across Windows, Linux, and macOS
591
+ ✅ Full path information in table names
592
+ ✅ Proper support for `SCAN_DIRECTORY_LEVEL`
593
+ ✅ All commands (scan, identify, propagate, push) working seamlessly
594
+ ✅ Normalized paths in database for consistency
595
+ ✅ Centralized path logic for maintainability
596
+
597
+ All path operations are now platform-independent and maintain consistency between CLI and backend implementations.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arela/uploader",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "CLI to upload files/directories to Arela",
5
5
  "bin": {
6
6
  "arela": "./src/index.js"
@@ -8,7 +8,9 @@
8
8
  "type": "module",
9
9
  "scripts": {
10
10
  "start": "node ./src/index.js",
11
- "test": "echo \"Error: no test specified\" && exit 1",
11
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
12
+ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
13
+ "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
12
14
  "format": "prettier --write \"src/**/*.js\""
13
15
  },
14
16
  "repository": {
@@ -43,7 +45,31 @@
43
45
  "pdf-parse": "^2.4.5"
44
46
  },
45
47
  "devDependencies": {
48
+ "@jest/globals": "^30.2.0",
46
49
  "@trivago/prettier-plugin-sort-imports": "5.2.2",
50
+ "jest": "^30.2.0",
47
51
  "prettier": "3.5.3"
52
+ },
53
+ "jest": {
54
+ "testEnvironment": "node",
55
+ "transform": {},
56
+ "testMatch": [
57
+ "**/tests/**/*.test.js"
58
+ ],
59
+ "testPathIgnorePatterns": [
60
+ "/node_modules/",
61
+ "/scripts/"
62
+ ],
63
+ "collectCoverageFrom": [
64
+ "src/commands/**/*.js",
65
+ "!src/commands/**/index.js"
66
+ ],
67
+ "coverageDirectory": "coverage",
68
+ "coverageReporters": [
69
+ "text",
70
+ "text-summary",
71
+ "html",
72
+ "lcov"
73
+ ]
48
74
  }
49
75
  }
@@ -66,7 +66,7 @@ export class IdentifyCommand {
66
66
  const tables = await this.scanApiService.getInstanceTables(
67
67
  scanConfig.companySlug,
68
68
  scanConfig.serverId,
69
- scanConfig.basePathLabel,
69
+ scanConfig.basePathFull,
70
70
  );
71
71
 
72
72
  if (tables.length === 0) {
@@ -421,33 +421,6 @@ export class IdentifyCommand {
421
421
  return missing;
422
422
  }
423
423
 
424
- /**
425
- * Generate table name from scan config (same logic as scan command)
426
- * @private
427
- * @param {Object} scanConfig - Scan configuration
428
- * @returns {string} Table name
429
- */
430
- #generateTableName(scanConfig) {
431
- const { companySlug, serverId, basePathLabel } = scanConfig;
432
-
433
- // Combine components
434
- const rawName = `${companySlug}_${serverId}_${basePathLabel}`;
435
-
436
- // Sanitize: lowercase, replace special chars with underscore
437
- let sanitized = rawName
438
- .toLowerCase()
439
- .replace(/[^a-z0-9_]/g, '_')
440
- .replace(/_+/g, '_')
441
- .replace(/^_|_$/g, '');
442
-
443
- // Add prefix
444
- const tableName = 'scan_' + sanitized;
445
-
446
- // Note: Hash truncation logic should match backend
447
- // For simplicity, we rely on backend validation
448
- return tableName;
449
- }
450
-
451
424
  /**
452
425
  * Show detailed performance statistics
453
426
  * @private
@@ -47,7 +47,7 @@ export class PropagateCommand {
47
47
  const tables = await this.scanApiService.getInstanceTables(
48
48
  scanConfig.companySlug,
49
49
  scanConfig.serverId,
50
- scanConfig.basePathLabel,
50
+ scanConfig.basePathFull,
51
51
  );
52
52
 
53
53
  if (tables.length === 0) {
@@ -76,7 +76,7 @@ export class PushCommand {
76
76
  const tables = await this.scanApiService.getInstanceTables(
77
77
  scanConfig.companySlug,
78
78
  scanConfig.serverId,
79
- scanConfig.basePathLabel,
79
+ scanConfig.basePathFull,
80
80
  );
81
81
 
82
82
  if (tables.length === 0) {