framer-code-link 0.5.1 → 0.7.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 +51 -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
@@ -1990,12 +1990,9 @@ function transition(state, event) {
1990
1990
  case "REMOTE_FILE_CHANGE": {
1991
1991
  const validation = validateIncomingChange(event.fileMeta, state.mode);
1992
1992
  if (validation.action === "queue") {
1993
- effects.push(log("debug", `Queueing file change: ${event.file.name} (${validation.reason})`));
1993
+ effects.push(log("debug", `Ignoring file change during sync: ${event.file.name}`));
1994
1994
  return {
1995
- state: {
1996
- ...state,
1997
- pendingRemoteChanges: [...state.pendingRemoteChanges, event.file]
1998
- },
1995
+ state,
1999
1996
  effects
2000
1997
  };
2001
1998
  }
@@ -2079,13 +2076,8 @@ function transition(state, event) {
2079
2076
  });
2080
2077
  effects.push(log("success", "Keeping Framer changes"));
2081
2078
  } 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
- });
2079
+ const localDeletes = [];
2080
+ for (const conflict of state.pendingConflicts) if (conflict.localContent === null) localDeletes.push(conflict.fileName);
2089
2081
  else effects.push({
2090
2082
  type: "SEND_MESSAGE",
2091
2083
  payload: {
@@ -2094,6 +2086,10 @@ function transition(state, event) {
2094
2086
  content: conflict.localContent
2095
2087
  }
2096
2088
  });
2089
+ if (localDeletes.length > 0) effects.push({
2090
+ type: "LOCAL_INITIATED_FILE_DELETE",
2091
+ fileNames: localDeletes
2092
+ });
2097
2093
  effects.push(log("success", "Keeping local changes"));
2098
2094
  }
2099
2095
  effects.push({ type: "PERSIST_STATE" }, {
@@ -2139,7 +2135,7 @@ function transition(state, event) {
2139
2135
  case "delete":
2140
2136
  effects.push(log("debug", `Local delete detected: ${relativePath}`), {
2141
2137
  type: "LOCAL_INITIATED_FILE_DELETE",
2142
- fileName: relativePath
2138
+ fileNames: [relativePath]
2143
2139
  });
2144
2140
  break;
2145
2141
  }
@@ -2159,18 +2155,17 @@ function transition(state, event) {
2159
2155
  const { autoResolvedLocal, autoResolvedRemote, remainingConflicts } = autoResolveConflicts(state.pendingConflicts, event.versions);
2160
2156
  if (autoResolvedLocal.length > 0) {
2161
2157
  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
- });
2158
+ const localDeletes = [];
2159
+ for (const conflict of autoResolvedLocal) if (conflict.localContent === null) localDeletes.push(conflict.fileName);
2169
2160
  else effects.push({
2170
2161
  type: "SEND_LOCAL_CHANGE",
2171
2162
  fileName: conflict.fileName,
2172
2163
  content: conflict.localContent
2173
2164
  });
2165
+ if (localDeletes.length > 0) effects.push({
2166
+ type: "LOCAL_INITIATED_FILE_DELETE",
2167
+ fileNames: localDeletes
2168
+ });
2174
2169
  }
2175
2170
  if (autoResolvedRemote.length > 0) {
2176
2171
  effects.push(log("debug", `Auto-resolved ${autoResolvedRemote.length} remote changes`));
@@ -2349,27 +2344,31 @@ async function executeEffect(effect, context) {
2349
2344
  }
2350
2345
  return [];
2351
2346
  }
2352
- case "LOCAL_INITIATED_FILE_DELETE":
2353
- if (hashTracker.shouldSkipDelete(effect.fileName)) {
2354
- hashTracker.clearDelete(effect.fileName);
2355
- return [];
2356
- }
2347
+ case "LOCAL_INITIATED_FILE_DELETE": {
2348
+ const filesToDelete = effect.fileNames.filter((fileName) => {
2349
+ const shouldSkip = hashTracker.shouldSkipDelete(fileName);
2350
+ if (shouldSkip) hashTracker.clearDelete(fileName);
2351
+ return !shouldSkip;
2352
+ });
2353
+ if (filesToDelete.length === 0) return [];
2357
2354
  try {
2358
- if (await userActions.requestDeleteDecision(syncState.socket, {
2359
- fileName: effect.fileName,
2355
+ const confirmedFiles = await userActions.requestDeleteDecision(syncState.socket, {
2356
+ fileNames: filesToDelete,
2360
2357
  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
- });
2358
+ });
2359
+ for (const fileName of confirmedFiles) {
2360
+ hashTracker.forget(fileName);
2361
+ fileMetadataCache.recordDelete(fileName);
2368
2362
  }
2363
+ if (confirmedFiles.length > 0 && syncState.socket) await sendMessage(syncState.socket, {
2364
+ type: "file-delete",
2365
+ fileNames: confirmedFiles
2366
+ });
2369
2367
  } catch (err) {
2370
- console.warn(`Failed to handle deletion for ${effect.fileName}:`, err);
2368
+ console.warn(`Failed to handle deletion for ${filesToDelete.join(", ")}:`, err);
2371
2369
  }
2372
2370
  return [];
2371
+ }
2373
2372
  case "PERSIST_STATE":
2374
2373
  await fileMetadataCache.flush();
2375
2374
  return [];
@@ -2411,9 +2410,7 @@ async function start(config) {
2411
2410
  let syncState = {
2412
2411
  mode: "disconnected",
2413
2412
  socket: null,
2414
- pendingRemoteChanges: [],
2415
- pendingOperations: /* @__PURE__ */ new Map(),
2416
- nextOperationId: 1
2413
+ pendingRemoteChanges: []
2417
2414
  };
2418
2415
  const userActions = new PluginUserPromptCoordinator();
2419
2416
  async function processEvent(event) {
@@ -2516,7 +2513,7 @@ async function start(config) {
2516
2513
  await processEvent({
2517
2514
  type: "LOCAL_DELETE_REJECTED",
2518
2515
  fileName: file.fileName,
2519
- content: file.content ?? ""
2516
+ content: file.content
2520
2517
  });
2521
2518
  }
2522
2519
  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.7.0",
4
4
  "description": "CLI tool for syncing Framer code components - controller-centric architecture",
5
5
  "main": "dist/index.mjs",
6
6
  "type": "module",