framer-code-link 0.5.1 → 0.6.0

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 (2) hide show
  1. package/dist/index.mjs +47 -45
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1440,33 +1440,33 @@ var PluginUserPromptCoordinator = class {
1440
1440
  });
1441
1441
  }
1442
1442
  /**
1443
- * Sends the delete request to the plugin and awaits the user's decision
1443
+ * Sends the delete request to the plugin and awaits the user's decision.
1444
+ * Returns the list of fileNames that were confirmed for deletion.
1444
1445
  */
1445
- async requestDeleteDecision(socket, { fileName, requireConfirmation }) {
1446
+ async requestDeleteDecision(socket, { fileNames, requireConfirmation }) {
1446
1447
  if (!socket) throw new Error("Cannot request delete decision: plugin not connected");
1448
+ if (fileNames.length === 0) return [];
1447
1449
  if (requireConfirmation) {
1448
- const confirmationPromise = this.awaitAction(`delete:${fileName}`, "delete confirmation");
1449
- await sendMessage(socket, {
1450
- type: "file-delete",
1451
- fileNames: [fileName],
1452
- requireConfirmation: true
1453
- });
1454
- try {
1455
- return await confirmationPromise;
1456
- } catch (err) {
1450
+ const confirmationPromises = fileNames.map((fileName) => this.awaitAction(`delete:${fileName}`, "delete confirmation").then((confirmed) => confirmed ? fileName : null).catch((err) => {
1457
1451
  if (err instanceof PluginDisconnectedError) {
1458
1452
  debug(`Plugin disconnected while waiting for delete confirmation: ${fileName}`);
1459
- return false;
1453
+ return null;
1460
1454
  }
1461
1455
  throw err;
1462
- }
1456
+ }));
1457
+ await sendMessage(socket, {
1458
+ type: "file-delete",
1459
+ fileNames,
1460
+ requireConfirmation: true
1461
+ });
1462
+ return (await Promise.all(confirmationPromises)).filter((name) => name !== null);
1463
1463
  }
1464
1464
  await sendMessage(socket, {
1465
1465
  type: "file-delete",
1466
- fileNames: [fileName],
1466
+ fileNames,
1467
1467
  requireConfirmation: false
1468
1468
  });
1469
- return true;
1469
+ return fileNames;
1470
1470
  }
1471
1471
  /**
1472
1472
  * Sends conflicts to the plugin and awaits user resolutions
@@ -2079,13 +2079,8 @@ function transition(state, event) {
2079
2079
  });
2080
2080
  effects.push(log("success", "Keeping Framer changes"));
2081
2081
  } else {
2082
- for (const conflict of state.pendingConflicts) if (conflict.localContent === null) effects.push({
2083
- type: "SEND_MESSAGE",
2084
- payload: {
2085
- type: "file-delete",
2086
- fileNames: [conflict.fileName]
2087
- }
2088
- });
2082
+ const localDeletes = [];
2083
+ for (const conflict of state.pendingConflicts) if (conflict.localContent === null) localDeletes.push(conflict.fileName);
2089
2084
  else effects.push({
2090
2085
  type: "SEND_MESSAGE",
2091
2086
  payload: {
@@ -2094,6 +2089,10 @@ function transition(state, event) {
2094
2089
  content: conflict.localContent
2095
2090
  }
2096
2091
  });
2092
+ if (localDeletes.length > 0) effects.push({
2093
+ type: "LOCAL_INITIATED_FILE_DELETE",
2094
+ fileNames: localDeletes
2095
+ });
2097
2096
  effects.push(log("success", "Keeping local changes"));
2098
2097
  }
2099
2098
  effects.push({ type: "PERSIST_STATE" }, {
@@ -2139,7 +2138,7 @@ function transition(state, event) {
2139
2138
  case "delete":
2140
2139
  effects.push(log("debug", `Local delete detected: ${relativePath}`), {
2141
2140
  type: "LOCAL_INITIATED_FILE_DELETE",
2142
- fileName: relativePath
2141
+ fileNames: [relativePath]
2143
2142
  });
2144
2143
  break;
2145
2144
  }
@@ -2159,18 +2158,17 @@ function transition(state, event) {
2159
2158
  const { autoResolvedLocal, autoResolvedRemote, remainingConflicts } = autoResolveConflicts(state.pendingConflicts, event.versions);
2160
2159
  if (autoResolvedLocal.length > 0) {
2161
2160
  effects.push(log("debug", `Auto-resolved ${autoResolvedLocal.length} local changes`));
2162
- for (const conflict of autoResolvedLocal) if (conflict.localContent === null) effects.push({
2163
- type: "SEND_MESSAGE",
2164
- payload: {
2165
- type: "file-delete",
2166
- fileNames: [conflict.fileName]
2167
- }
2168
- });
2161
+ const localDeletes = [];
2162
+ for (const conflict of autoResolvedLocal) if (conflict.localContent === null) localDeletes.push(conflict.fileName);
2169
2163
  else effects.push({
2170
2164
  type: "SEND_LOCAL_CHANGE",
2171
2165
  fileName: conflict.fileName,
2172
2166
  content: conflict.localContent
2173
2167
  });
2168
+ if (localDeletes.length > 0) effects.push({
2169
+ type: "LOCAL_INITIATED_FILE_DELETE",
2170
+ fileNames: localDeletes
2171
+ });
2174
2172
  }
2175
2173
  if (autoResolvedRemote.length > 0) {
2176
2174
  effects.push(log("debug", `Auto-resolved ${autoResolvedRemote.length} remote changes`));
@@ -2349,27 +2347,31 @@ async function executeEffect(effect, context) {
2349
2347
  }
2350
2348
  return [];
2351
2349
  }
2352
- case "LOCAL_INITIATED_FILE_DELETE":
2353
- if (hashTracker.shouldSkipDelete(effect.fileName)) {
2354
- hashTracker.clearDelete(effect.fileName);
2355
- return [];
2356
- }
2350
+ case "LOCAL_INITIATED_FILE_DELETE": {
2351
+ const filesToDelete = effect.fileNames.filter((fileName) => {
2352
+ const shouldSkip = hashTracker.shouldSkipDelete(fileName);
2353
+ if (shouldSkip) hashTracker.clearDelete(fileName);
2354
+ return !shouldSkip;
2355
+ });
2356
+ if (filesToDelete.length === 0) return [];
2357
2357
  try {
2358
- if (await userActions.requestDeleteDecision(syncState.socket, {
2359
- fileName: effect.fileName,
2358
+ const confirmedFiles = await userActions.requestDeleteDecision(syncState.socket, {
2359
+ fileNames: filesToDelete,
2360
2360
  requireConfirmation: !config.dangerouslyAutoDelete
2361
- })) {
2362
- hashTracker.forget(effect.fileName);
2363
- fileMetadataCache.recordDelete(effect.fileName);
2364
- if (syncState.socket) await sendMessage(syncState.socket, {
2365
- type: "file-delete",
2366
- fileNames: [effect.fileName]
2367
- });
2361
+ });
2362
+ for (const fileName of confirmedFiles) {
2363
+ hashTracker.forget(fileName);
2364
+ fileMetadataCache.recordDelete(fileName);
2368
2365
  }
2366
+ if (confirmedFiles.length > 0 && syncState.socket) await sendMessage(syncState.socket, {
2367
+ type: "file-delete",
2368
+ fileNames: confirmedFiles
2369
+ });
2369
2370
  } catch (err) {
2370
- console.warn(`Failed to handle deletion for ${effect.fileName}:`, err);
2371
+ console.warn(`Failed to handle deletion for ${filesToDelete.join(", ")}:`, err);
2371
2372
  }
2372
2373
  return [];
2374
+ }
2373
2375
  case "PERSIST_STATE":
2374
2376
  await fileMetadataCache.flush();
2375
2377
  return [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framer-code-link",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "CLI tool for syncing Framer code components - controller-centric architecture",
5
5
  "main": "dist/index.mjs",
6
6
  "type": "module",