@magentrix-corp/magentrix-cli 1.1.1 → 1.1.3

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.
@@ -165,27 +165,98 @@ const handleCreateStaticAssetAction = async (instanceUrl, apiKey, action) => {
165
165
  * Handles DELETE_STATIC_ASSET action.
166
166
  */
167
167
  const handleDeleteStaticAssetAction = async (instanceUrl, apiKey, action) => {
168
- const response = await deleteAsset(instanceUrl, apiKey, `/${action.folder}`, action.names);
169
- if (response?.error) throw new Error(response.error);
170
- return response;
168
+ try {
169
+ const response = await deleteAsset(instanceUrl, apiKey, `/${action.folder}`, action.names);
170
+ return response;
171
+ } catch (error) {
172
+ // Check if this is a "not found" error
173
+ const errorMessage = error?.message || String(error);
174
+ const errorLower = errorMessage.toLowerCase();
175
+ const isNotFound = errorLower.includes('404') ||
176
+ errorLower.includes('not found') ||
177
+ errorLower.includes('item not found');
178
+
179
+ if (isNotFound) {
180
+ // Clean up base.json since file doesn't exist on server
181
+ for (const name of action.names) {
182
+ const filePath = action.filePath || `${EXPORT_ROOT}/${action.folder}/${name}`;
183
+ removeFromBase(filePath);
184
+ }
185
+ return { cleanedFromCache: true };
186
+ }
187
+
188
+ // Other errors should still fail
189
+ throw error;
190
+ }
171
191
  };
172
192
 
173
193
  /**
174
194
  * Handles CREATE_FOLDER action.
175
195
  */
176
196
  const handleCreateFolderAction = async (instanceUrl, apiKey, action) => {
177
- const response = await createFolder(instanceUrl, apiKey, action.parentPath, action.folderName);
178
- if (response?.error) throw new Error(response.error);
179
- return response;
197
+ try {
198
+ const response = await createFolder(instanceUrl, apiKey, action.parentPath, action.folderName);
199
+ return response;
200
+ } catch (error) {
201
+ // Check if folder already exists (likely created by file upload)
202
+ const errorMessage = error?.message || String(error);
203
+ const errorLower = errorMessage.toLowerCase();
204
+ const alreadyExists = errorLower.includes('already exists') ||
205
+ errorLower.includes('folder exists') ||
206
+ errorLower.includes('duplicate');
207
+
208
+ if (alreadyExists) {
209
+ // Folder already exists, update cache and treat as success
210
+ return { alreadyExisted: true };
211
+ }
212
+
213
+ // Other errors should still fail
214
+ throw error;
215
+ }
180
216
  };
181
217
 
182
218
  /**
183
219
  * Handles DELETE_FOLDER action.
184
220
  */
185
221
  const handleDeleteFolderAction = async (instanceUrl, apiKey, action) => {
186
- const response = await deleteAsset(instanceUrl, apiKey, action.parentPath, [action.folderName]);
187
- if (response?.error) throw new Error(response.error);
188
- return response;
222
+ try {
223
+ const response = await deleteAsset(instanceUrl, apiKey, action.parentPath, [action.folderName]);
224
+ return response;
225
+ } catch (error) {
226
+ // Check if this is a "not found" error
227
+ const errorMessage = error?.message || String(error);
228
+ const errorLower = errorMessage.toLowerCase();
229
+ const isNotFound = errorLower.includes('404') ||
230
+ errorLower.includes('not found') ||
231
+ errorLower.includes('item not found');
232
+
233
+ if (isNotFound) {
234
+ // Clean up base.json since folder doesn't exist on server
235
+ removeFromBase(action.folderPath);
236
+
237
+ // Also remove all files and subfolders inside this folder from base
238
+ const hits = await config.searchObject({}, { filename: "base.json", global: false });
239
+ const cachedResults = hits?.[0]?.value || {};
240
+
241
+ for (const [recordId, cachedEntry] of Object.entries(cachedResults)) {
242
+ const entryPath = cachedEntry.filePath || cachedEntry.lastKnownPath;
243
+ if (entryPath && typeof entryPath === 'string') {
244
+ const normalizedEntryPath = path.normalize(entryPath);
245
+ const normalizedFolderPath = path.normalize(action.folderPath);
246
+
247
+ // Check if this entry is inside the deleted folder
248
+ if (normalizedEntryPath.startsWith(normalizedFolderPath + path.sep)) {
249
+ removeFromBase(recordId);
250
+ }
251
+ }
252
+ }
253
+
254
+ return { cleanedFromCache: true };
255
+ }
256
+
257
+ // Other errors should still fail
258
+ throw error;
259
+ }
189
260
  };
190
261
 
191
262
  /**
@@ -227,8 +298,12 @@ const updateCacheAfterSuccess = async (action, operationResult) => {
227
298
  break;
228
299
 
229
300
  case "delete_static_asset":
230
- for (const name of action.names) {
231
- removeFromBase(`${EXPORT_ROOT}/${action.folder}/${name}`);
301
+ // Skip if already cleaned from cache during 404 handling
302
+ if (!operationResult?.cleanedFromCache) {
303
+ for (const name of action.names) {
304
+ const filePath = action.filePath || `${EXPORT_ROOT}/${action.folder}/${name}`;
305
+ removeFromBase(filePath);
306
+ }
232
307
  }
233
308
  break;
234
309
 
@@ -240,9 +315,31 @@ const updateCacheAfterSuccess = async (action, operationResult) => {
240
315
  updateBase(action.folderPath, { Id: action.folderPath, Type: "Folder" });
241
316
  break;
242
317
 
243
- case "delete_folder":
244
- removeFromBase(action.folderPath);
318
+ case "delete_folder": {
319
+ // Skip if already cleaned from cache during 404 handling
320
+ if (!operationResult?.cleanedFromCache) {
321
+ // Remove the folder itself from base
322
+ removeFromBase(action.folderPath);
323
+
324
+ // Also remove all files and subfolders inside this folder from base
325
+ const hits = await config.searchObject({}, { filename: "base.json", global: false });
326
+ const cachedResults = hits?.[0]?.value || {};
327
+
328
+ for (const [recordId, cachedEntry] of Object.entries(cachedResults)) {
329
+ const entryPath = cachedEntry.filePath || cachedEntry.lastKnownPath;
330
+ if (entryPath && typeof entryPath === 'string') {
331
+ const normalizedEntryPath = path.normalize(entryPath);
332
+ const normalizedFolderPath = path.normalize(action.folderPath);
333
+
334
+ // Check if this entry is inside the deleted folder
335
+ if (normalizedEntryPath.startsWith(normalizedFolderPath + path.sep)) {
336
+ removeFromBase(recordId);
337
+ }
338
+ }
339
+ }
340
+ }
245
341
  break;
342
+ }
246
343
  }
247
344
  } catch (error) {
248
345
  console.warn(chalk.yellow(`Warning: Failed to update cache for ${action.action}: ${error.message}`));
@@ -472,7 +569,8 @@ export const runPublish = async (options = {}) => {
472
569
  actionQueue.push({
473
570
  action: 'delete_static_asset',
474
571
  folder: toApiPath(cacheFile.filePath),
475
- names: [path.basename(cacheFile.filePath)]
572
+ names: [path.basename(cacheFile.filePath)],
573
+ filePath: cacheFile.filePath // Store actual file path for filtering
476
574
  });
477
575
  continue;
478
576
  }
@@ -578,10 +676,17 @@ export const runPublish = async (options = {}) => {
578
676
  .map(a => a.folderPath);
579
677
 
580
678
  const filteredActionQueue = actionQueue.filter(action => {
581
- if (action.action === 'delete_static_asset') {
582
- const fileFolder = path.join(EXPORT_ROOT, action.folder);
679
+ if (action.action === 'delete_static_asset' && action.filePath) {
680
+ // Check if this file's directory is inside any folder being deleted
681
+ const fileDir = path.dirname(action.filePath);
583
682
  for (const deletedFolder of foldersBeingDeleted) {
584
- if (fileFolder.startsWith(deletedFolder)) {
683
+ // Use path.normalize to handle trailing slashes and ensure proper comparison
684
+ const normalizedFileDir = path.normalize(fileDir);
685
+ const normalizedDeletedFolder = path.normalize(deletedFolder);
686
+
687
+ // Check if file is inside the deleted folder (or is the folder itself)
688
+ if (normalizedFileDir === normalizedDeletedFolder ||
689
+ normalizedFileDir.startsWith(normalizedDeletedFolder + path.sep)) {
585
690
  return false; // Skip - covered by folder deletion
586
691
  }
587
692
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magentrix-corp/magentrix-cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "CLI tool for synchronizing local files with Magentrix cloud platform",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -80,7 +80,7 @@ export const deleteAsset = async (instanceUrl, token, path = '/contents/assets',
80
80
  if (!path) throw new Error("Path is required when deleting assets.");
81
81
  if (!Array.isArray(names) || names?.length === 0) throw new Error("At least one file name is required when deleting static assets.");
82
82
 
83
- let reqPath = `/api/3.0/staticassets?path=${encodeURIComponent(path)}&names=${names.join(",")}`;
83
+ let reqPath = `/api/3.0/staticassets?path=${encodeURIComponent(path)}&names=${names.map(name => encodeURIComponent(name)).join(",")}`;
84
84
 
85
85
  const response = await fetchMagentrix({
86
86
  instanceUrl,