framer-code-link 0.5.0 → 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 +48 -54
  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" }, {
@@ -2138,9 +2137,8 @@ function transition(state, event) {
2138
2137
  break;
2139
2138
  case "delete":
2140
2139
  effects.push(log("debug", `Local delete detected: ${relativePath}`), {
2141
- type: "REQUEST_LOCAL_DELETE_DECISION",
2142
- fileName: relativePath,
2143
- requireConfirmation: true
2140
+ type: "LOCAL_INITIATED_FILE_DELETE",
2141
+ fileNames: [relativePath]
2144
2142
  });
2145
2143
  break;
2146
2144
  }
@@ -2160,18 +2158,17 @@ function transition(state, event) {
2160
2158
  const { autoResolvedLocal, autoResolvedRemote, remainingConflicts } = autoResolveConflicts(state.pendingConflicts, event.versions);
2161
2159
  if (autoResolvedLocal.length > 0) {
2162
2160
  effects.push(log("debug", `Auto-resolved ${autoResolvedLocal.length} local changes`));
2163
- for (const conflict of autoResolvedLocal) if (conflict.localContent === null) effects.push({
2164
- type: "SEND_MESSAGE",
2165
- payload: {
2166
- type: "file-delete",
2167
- fileNames: [conflict.fileName]
2168
- }
2169
- });
2161
+ const localDeletes = [];
2162
+ for (const conflict of autoResolvedLocal) if (conflict.localContent === null) localDeletes.push(conflict.fileName);
2170
2163
  else effects.push({
2171
2164
  type: "SEND_LOCAL_CHANGE",
2172
2165
  fileName: conflict.fileName,
2173
2166
  content: conflict.localContent
2174
2167
  });
2168
+ if (localDeletes.length > 0) effects.push({
2169
+ type: "LOCAL_INITIATED_FILE_DELETE",
2170
+ fileNames: localDeletes
2171
+ });
2175
2172
  }
2176
2173
  if (autoResolvedRemote.length > 0) {
2177
2174
  effects.push(log("debug", `Auto-resolved ${autoResolvedRemote.length} remote changes`));
@@ -2317,13 +2314,6 @@ async function executeEffect(effect, context) {
2317
2314
  });
2318
2315
  return [];
2319
2316
  }
2320
- case "REQUEST_DELETE_CONFIRMATION":
2321
- if (syncState.socket) await sendMessage(syncState.socket, {
2322
- type: "file-delete",
2323
- fileNames: [effect.fileName],
2324
- requireConfirmation: effect.requireConfirmation
2325
- });
2326
- return [];
2327
2317
  case "UPDATE_FILE_METADATA": {
2328
2318
  if (!config.filesDir || !config.projectDir) return [];
2329
2319
  const currentContent = await readFileSafe(effect.fileName, config.filesDir);
@@ -2357,27 +2347,31 @@ async function executeEffect(effect, context) {
2357
2347
  }
2358
2348
  return [];
2359
2349
  }
2360
- case "REQUEST_LOCAL_DELETE_DECISION":
2361
- if (hashTracker.shouldSkipDelete(effect.fileName)) {
2362
- hashTracker.clearDelete(effect.fileName);
2363
- return [];
2364
- }
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 [];
2365
2357
  try {
2366
- if (await userActions.requestDeleteDecision(syncState.socket, {
2367
- fileName: effect.fileName,
2358
+ const confirmedFiles = await userActions.requestDeleteDecision(syncState.socket, {
2359
+ fileNames: filesToDelete,
2368
2360
  requireConfirmation: !config.dangerouslyAutoDelete
2369
- })) {
2370
- hashTracker.forget(effect.fileName);
2371
- fileMetadataCache.recordDelete(effect.fileName);
2372
- if (syncState.socket) await sendMessage(syncState.socket, {
2373
- type: "file-delete",
2374
- fileNames: [effect.fileName]
2375
- });
2361
+ });
2362
+ for (const fileName of confirmedFiles) {
2363
+ hashTracker.forget(fileName);
2364
+ fileMetadataCache.recordDelete(fileName);
2376
2365
  }
2366
+ if (confirmedFiles.length > 0 && syncState.socket) await sendMessage(syncState.socket, {
2367
+ type: "file-delete",
2368
+ fileNames: confirmedFiles
2369
+ });
2377
2370
  } catch (err) {
2378
- console.warn(`Failed to handle deletion for ${effect.fileName}:`, err);
2371
+ console.warn(`Failed to handle deletion for ${filesToDelete.join(", ")}:`, err);
2379
2372
  }
2380
2373
  return [];
2374
+ }
2381
2375
  case "PERSIST_STATE":
2382
2376
  await fileMetadataCache.flush();
2383
2377
  return [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framer-code-link",
3
- "version": "0.5.0",
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",