repo-cloak-cli 1.2.4 → 1.3.2

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.
@@ -1,235 +1,235 @@
1
- /**
2
- * Mapping File Manager
3
- * Tracks replacements and file mappings for push/pull operations
4
- * Now with encrypted sensitive data!
5
- */
6
-
7
- import { readFileSync, writeFileSync, existsSync } from 'fs';
8
- import { join } from 'path';
9
- import { getOrCreateSecret, encryptReplacements, decryptReplacements, encrypt, decrypt } from './crypto.js';
10
-
11
- const MAP_FILENAME = '.repo-cloak-map.json';
12
-
13
- /**
14
- * Create a mapping object with encrypted sensitive data
15
- */
16
- export function createMapping(options) {
17
- const {
18
- sourceDir,
19
- destDir,
20
- replacements,
21
- files,
22
- timestamp = new Date().toISOString()
23
- } = options;
24
-
25
- // Get user's secret for encryption
26
- const secret = getOrCreateSecret();
27
-
28
- // Encrypt sensitive paths
29
- const encryptedSource = encrypt(sourceDir, secret);
30
- const encryptedDest = encrypt(destDir, secret);
31
-
32
- // Encrypt replacements (only the "original" field)
33
- const encryptedReplacements = encryptReplacements(replacements, secret);
34
-
35
- // Encrypt original file paths
36
- const encryptedFiles = files.map(f => ({
37
- original: encrypt(f.original, secret),
38
- cloaked: f.cloaked // Keep cloaked version visible (it's anonymized)
39
- }));
40
-
41
- return {
42
- version: '1.1.0', // Updated version for encryption
43
- tool: 'repo-cloak',
44
- timestamp,
45
- encrypted: true, // Flag indicating encryption is used
46
- source: {
47
- path: encryptedSource,
48
- platform: process.platform
49
- },
50
- destination: {
51
- path: encryptedDest
52
- },
53
- replacements: encryptedReplacements,
54
- files: encryptedFiles,
55
- stats: {
56
- totalFiles: files.length,
57
- replacementsCount: replacements.length
58
- }
59
- };
60
- }
61
-
62
- /**
63
- * Save mapping to destination directory
64
- */
65
- export function saveMapping(destDir, mapping) {
66
- const mapPath = join(destDir, MAP_FILENAME);
67
- writeFileSync(mapPath, JSON.stringify(mapping, null, 2), 'utf-8');
68
- return mapPath;
69
- }
70
-
71
- /**
72
- * Load and decrypt mapping from a cloaked directory
73
- */
74
- export function loadMapping(cloakedDir, secret = null) {
75
- const mapPath = join(cloakedDir, MAP_FILENAME);
76
-
77
- if (!existsSync(mapPath)) {
78
- throw new Error(`No mapping file found in ${cloakedDir}. Is this a repo-cloak backup?`);
79
- }
80
-
81
- try {
82
- const content = readFileSync(mapPath, 'utf-8');
83
- const mapping = JSON.parse(content);
84
-
85
- // If encrypted, attempt decryption
86
- if (mapping.encrypted && secret) {
87
- return decryptMapping(mapping, secret);
88
- }
89
-
90
- return mapping;
91
- } catch (error) {
92
- throw new Error(`Failed to read mapping file: ${error.message}`);
93
- }
94
- }
95
-
96
- /**
97
- * Decrypt a mapping object
98
- */
99
- export function decryptMapping(mapping, secret) {
100
- if (!mapping.encrypted) {
101
- return mapping;
102
- }
103
-
104
- try {
105
- const decryptedSource = decrypt(mapping.source?.path, secret);
106
- const decryptedDest = decrypt(mapping.destination?.path, secret);
107
- const decryptedReplacements = decryptReplacements(mapping.replacements || [], secret);
108
-
109
- const decryptedFiles = (mapping.files || []).map(f => ({
110
- original: decrypt(f.original, secret),
111
- cloaked: f.cloaked
112
- }));
113
-
114
- return {
115
- ...mapping,
116
- source: {
117
- ...mapping.source,
118
- path: decryptedSource,
119
- decrypted: true
120
- },
121
- destination: {
122
- ...mapping.destination,
123
- path: decryptedDest,
124
- decrypted: true
125
- },
126
- replacements: decryptedReplacements,
127
- files: decryptedFiles
128
- };
129
- } catch (error) {
130
- throw new Error(`Failed to decrypt mapping: ${error.message}`);
131
- }
132
- }
133
-
134
- /**
135
- * Check if a directory has a mapping file
136
- */
137
- export function hasMapping(dir) {
138
- const mapPath = join(dir, MAP_FILENAME);
139
- return existsSync(mapPath);
140
- }
141
-
142
- /**
143
- * Get the original source path from mapping (requires secret)
144
- */
145
- export function getOriginalSource(mapping) {
146
- return mapping.source?.path || null;
147
- }
148
-
149
- /**
150
- * Get replacements from mapping
151
- */
152
- export function getReplacements(mapping) {
153
- return mapping.replacements || [];
154
- }
155
-
156
- /**
157
- * Get file list from mapping
158
- */
159
- export function getFiles(mapping) {
160
- return mapping.files || [];
161
- }
162
-
163
- /**
164
- * Update mapping with additional info
165
- */
166
- export function updateMapping(destDir, updates) {
167
- const mapPath = join(destDir, MAP_FILENAME);
168
-
169
- if (!existsSync(mapPath)) {
170
- throw new Error('No mapping file found');
171
- }
172
-
173
- const content = readFileSync(mapPath, 'utf-8');
174
- const mapping = JSON.parse(content);
175
- const updated = { ...mapping, ...updates, updatedAt: new Date().toISOString() };
176
-
177
- writeFileSync(mapPath, JSON.stringify(updated, null, 2), 'utf-8');
178
- return updated;
179
- }
180
-
181
- /**
182
- * Load existing raw mapping (without decryption) for merging
183
- */
184
- export function loadRawMapping(cloakedDir) {
185
- const mapPath = join(cloakedDir, MAP_FILENAME);
186
-
187
- if (!existsSync(mapPath)) {
188
- return null;
189
- }
190
-
191
- try {
192
- const content = readFileSync(mapPath, 'utf-8');
193
- return JSON.parse(content);
194
- } catch (error) {
195
- return null;
196
- }
197
- }
198
-
199
- /**
200
- * Merge new files into existing mapping (for incremental pulls)
201
- */
202
- export function mergeMapping(existingMapping, newFiles) {
203
- // Get existing file paths for deduplication
204
- const existingPaths = new Set(
205
- (existingMapping.files || []).map(f => f.cloaked)
206
- );
207
-
208
- // Filter out files that already exist (avoid duplicates)
209
- const uniqueNewFiles = newFiles.filter(f => !existingPaths.has(f.cloaked));
210
-
211
- // Merge files
212
- const mergedFiles = [
213
- ...(existingMapping.files || []),
214
- ...uniqueNewFiles
215
- ];
216
-
217
- // Track pull history
218
- const pullHistory = existingMapping.pullHistory || [];
219
- pullHistory.push({
220
- timestamp: new Date().toISOString(),
221
- filesAdded: uniqueNewFiles.length,
222
- totalFiles: mergedFiles.length
223
- });
224
-
225
- return {
226
- ...existingMapping,
227
- files: mergedFiles,
228
- pullHistory,
229
- stats: {
230
- ...existingMapping.stats,
231
- totalFiles: mergedFiles.length
232
- },
233
- updatedAt: new Date().toISOString()
234
- };
235
- }
1
+ /**
2
+ * Mapping File Manager
3
+ * Tracks replacements and file mappings for push/pull operations
4
+ * Now with encrypted sensitive data!
5
+ */
6
+
7
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { getOrCreateSecret, encryptReplacements, decryptReplacements, encrypt, decrypt } from './crypto.js';
10
+
11
+ const MAP_FILENAME = '.repo-cloak-map.json';
12
+
13
+ /**
14
+ * Create a mapping object with encrypted sensitive data
15
+ */
16
+ export function createMapping(options) {
17
+ const {
18
+ sourceDir,
19
+ destDir,
20
+ replacements,
21
+ files,
22
+ timestamp = new Date().toISOString()
23
+ } = options;
24
+
25
+ // Get user's secret for encryption
26
+ const secret = getOrCreateSecret();
27
+
28
+ // Encrypt sensitive paths
29
+ const encryptedSource = encrypt(sourceDir, secret);
30
+ const encryptedDest = encrypt(destDir, secret);
31
+
32
+ // Encrypt replacements (only the "original" field)
33
+ const encryptedReplacements = encryptReplacements(replacements, secret);
34
+
35
+ // Encrypt original file paths
36
+ const encryptedFiles = files.map(f => ({
37
+ original: encrypt(f.original, secret),
38
+ cloaked: f.cloaked // Keep cloaked version visible (it's anonymized)
39
+ }));
40
+
41
+ return {
42
+ version: '1.1.0', // Updated version for encryption
43
+ tool: 'repo-cloak',
44
+ timestamp,
45
+ encrypted: true, // Flag indicating encryption is used
46
+ source: {
47
+ path: encryptedSource,
48
+ platform: process.platform
49
+ },
50
+ destination: {
51
+ path: encryptedDest
52
+ },
53
+ replacements: encryptedReplacements,
54
+ files: encryptedFiles,
55
+ stats: {
56
+ totalFiles: files.length,
57
+ replacementsCount: replacements.length
58
+ }
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Save mapping to destination directory
64
+ */
65
+ export function saveMapping(destDir, mapping) {
66
+ const mapPath = join(destDir, MAP_FILENAME);
67
+ writeFileSync(mapPath, JSON.stringify(mapping, null, 2), 'utf-8');
68
+ return mapPath;
69
+ }
70
+
71
+ /**
72
+ * Load and decrypt mapping from a cloaked directory
73
+ */
74
+ export function loadMapping(cloakedDir, secret = null) {
75
+ const mapPath = join(cloakedDir, MAP_FILENAME);
76
+
77
+ if (!existsSync(mapPath)) {
78
+ throw new Error(`No mapping file found in ${cloakedDir}. Is this a repo-cloak backup?`);
79
+ }
80
+
81
+ try {
82
+ const content = readFileSync(mapPath, 'utf-8');
83
+ const mapping = JSON.parse(content);
84
+
85
+ // If encrypted, attempt decryption
86
+ if (mapping.encrypted && secret) {
87
+ return decryptMapping(mapping, secret);
88
+ }
89
+
90
+ return mapping;
91
+ } catch (error) {
92
+ throw new Error(`Failed to read mapping file: ${error.message}`);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Decrypt a mapping object
98
+ */
99
+ export function decryptMapping(mapping, secret) {
100
+ if (!mapping.encrypted) {
101
+ return mapping;
102
+ }
103
+
104
+ try {
105
+ const decryptedSource = decrypt(mapping.source?.path, secret);
106
+ const decryptedDest = decrypt(mapping.destination?.path, secret);
107
+ const decryptedReplacements = decryptReplacements(mapping.replacements || [], secret);
108
+
109
+ const decryptedFiles = (mapping.files || []).map(f => ({
110
+ original: decrypt(f.original, secret),
111
+ cloaked: f.cloaked
112
+ }));
113
+
114
+ return {
115
+ ...mapping,
116
+ source: {
117
+ ...mapping.source,
118
+ path: decryptedSource,
119
+ decrypted: true
120
+ },
121
+ destination: {
122
+ ...mapping.destination,
123
+ path: decryptedDest,
124
+ decrypted: true
125
+ },
126
+ replacements: decryptedReplacements,
127
+ files: decryptedFiles
128
+ };
129
+ } catch (error) {
130
+ throw new Error(`Failed to decrypt mapping: ${error.message}`);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Check if a directory has a mapping file
136
+ */
137
+ export function hasMapping(dir) {
138
+ const mapPath = join(dir, MAP_FILENAME);
139
+ return existsSync(mapPath);
140
+ }
141
+
142
+ /**
143
+ * Get the original source path from mapping (requires secret)
144
+ */
145
+ export function getOriginalSource(mapping) {
146
+ return mapping.source?.path || null;
147
+ }
148
+
149
+ /**
150
+ * Get replacements from mapping
151
+ */
152
+ export function getReplacements(mapping) {
153
+ return mapping.replacements || [];
154
+ }
155
+
156
+ /**
157
+ * Get file list from mapping
158
+ */
159
+ export function getFiles(mapping) {
160
+ return mapping.files || [];
161
+ }
162
+
163
+ /**
164
+ * Update mapping with additional info
165
+ */
166
+ export function updateMapping(destDir, updates) {
167
+ const mapPath = join(destDir, MAP_FILENAME);
168
+
169
+ if (!existsSync(mapPath)) {
170
+ throw new Error('No mapping file found');
171
+ }
172
+
173
+ const content = readFileSync(mapPath, 'utf-8');
174
+ const mapping = JSON.parse(content);
175
+ const updated = { ...mapping, ...updates, updatedAt: new Date().toISOString() };
176
+
177
+ writeFileSync(mapPath, JSON.stringify(updated, null, 2), 'utf-8');
178
+ return updated;
179
+ }
180
+
181
+ /**
182
+ * Load existing raw mapping (without decryption) for merging
183
+ */
184
+ export function loadRawMapping(cloakedDir) {
185
+ const mapPath = join(cloakedDir, MAP_FILENAME);
186
+
187
+ if (!existsSync(mapPath)) {
188
+ return null;
189
+ }
190
+
191
+ try {
192
+ const content = readFileSync(mapPath, 'utf-8');
193
+ return JSON.parse(content);
194
+ } catch (error) {
195
+ return null;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Merge new files into existing mapping (for incremental pulls)
201
+ */
202
+ export function mergeMapping(existingMapping, newFiles) {
203
+ // Get existing file paths for deduplication
204
+ const existingPaths = new Set(
205
+ (existingMapping.files || []).map(f => f.cloaked)
206
+ );
207
+
208
+ // Filter out files that already exist (avoid duplicates)
209
+ const uniqueNewFiles = newFiles.filter(f => !existingPaths.has(f.cloaked));
210
+
211
+ // Merge files
212
+ const mergedFiles = [
213
+ ...(existingMapping.files || []),
214
+ ...uniqueNewFiles
215
+ ];
216
+
217
+ // Track pull history
218
+ const pullHistory = existingMapping.pullHistory || [];
219
+ pullHistory.push({
220
+ timestamp: new Date().toISOString(),
221
+ filesAdded: uniqueNewFiles.length,
222
+ totalFiles: mergedFiles.length
223
+ });
224
+
225
+ return {
226
+ ...existingMapping,
227
+ files: mergedFiles,
228
+ pullHistory,
229
+ stats: {
230
+ ...existingMapping.stats,
231
+ totalFiles: mergedFiles.length
232
+ },
233
+ updatedAt: new Date().toISOString()
234
+ };
235
+ }