@teambit/lanes 1.0.1191 → 1.0.1192

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.
@@ -87,6 +87,13 @@ function _laneId() {
87
87
  };
88
88
  return data;
89
89
  }
90
+ function _legacy2() {
91
+ const data = require("@teambit/legacy.scope-api");
92
+ _legacy2 = function () {
93
+ return data;
94
+ };
95
+ return data;
96
+ }
90
97
  function _bitError() {
91
98
  const data = require("@teambit/bit-error");
92
99
  _bitError = function () {
@@ -317,6 +324,11 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
317
324
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
318
325
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
319
326
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable max-lines */
327
+ /**
328
+ * where the entries were read from. the remote lane is the source of truth, so anything else means the
329
+ * local lane object was used instead, and why.
330
+ */
331
+
320
332
  /** where the base was resolved from: `workspace` (already local) or `scope` (fetched from remote). */
321
333
 
322
334
  class LanesMain {
@@ -1429,24 +1441,123 @@ please create a new lane instead, which will include all components of this lane
1429
1441
  }
1430
1442
 
1431
1443
  /**
1432
- * default to remove all of them.
1433
- * returns true if the lane has changed
1444
+ * the dependents Ripple CI cascaded onto a lane (hidden `lane.updateDependents` entries).
1445
+ * the cascade producer runs on the remote, so the remote lane is the source of truth. the local lane
1446
+ * object is used only when the remote lane can't be fetched (e.g. the lane was never exported).
1434
1447
  */
1435
- async removeUpdateDependents(laneId, ids) {
1436
- const lane = await this.loadLane(laneId);
1437
- if (!lane) throw new (_bitError().BitError)(`unable to find a lane ${laneId.toString()}`);
1438
- if (ids?.length) {
1439
- ids.forEach(id => lane.removeComponentFromUpdateDependentsIfExist(id));
1440
- } else {
1441
- lane.removeAllUpdateDependents();
1448
+ async getLaneUpdateDependents(laneId) {
1449
+ let remoteError;
1450
+ const remoteLane = this.isLaneLocalOnly(laneId) ? undefined : await this.importLaneObject(laneId, false).catch(err => {
1451
+ remoteError = err;
1452
+ return undefined;
1453
+ });
1454
+ if (remoteLane) {
1455
+ return {
1456
+ laneId,
1457
+ ids: remoteLane.updateDependents || [],
1458
+ source: 'remote'
1459
+ };
1442
1460
  }
1443
- if (lane.hasChanged) {
1444
- await this.scope.legacyScope.lanes.saveLane(lane, {
1445
- laneHistoryMsg: 'remove update-dependents'
1446
- });
1447
- return true;
1461
+ const localLane = await this.loadLane(laneId);
1462
+ if (!localLane) {
1463
+ const reason = remoteError ? `, and fetching it from the remote failed: ${remoteError.message}` : '';
1464
+ throw new (_bitError().BitError)(`lane "${laneId.toString()}" was not found locally${reason}`);
1465
+ }
1466
+ // no error means no fetch was attempted, i.e. the lane is local-only. LaneNotFound means the same
1467
+ // thing from the remote's side: either way the remote has no lane to be the source of truth.
1468
+ const source = !remoteError || remoteError instanceof _legacy2().LaneNotFound ? 'no-remote-lane' : 'remote-unavailable';
1469
+ return {
1470
+ laneId,
1471
+ ids: localLane.updateDependents || [],
1472
+ source,
1473
+ remoteError: remoteError?.message
1474
+ };
1475
+ }
1476
+
1477
+ /**
1478
+ * remove the dependents Ripple CI cascaded onto a lane.
1479
+ * exporting a lane never removes hidden entries from the remote (the remote-side merge walks the
1480
+ * incoming entries only), so the removal is done directly on the remote, the same way the cloud UI
1481
+ * does. the local lane object is updated as well so it doesn't keep stale entries until the next
1482
+ * import. a lane that was never exported is changed locally only.
1483
+ */
1484
+ async undoLaneUpdateDependents(laneId, ids) {
1485
+ const current = await this.getLaneUpdateDependents(laneId);
1486
+ if (current.source === 'remote-unavailable') {
1487
+ // the remote is the source of truth for these entries. removing them locally while the remote keeps
1488
+ // them would only be reverted by the next import, so fail instead of pretending it worked.
1489
+ throw new (_bitError().BitError)(`unable to undo the cascaded updates of lane "${laneId.toString()}": the remote lane could not be fetched (${current.remoteError}). the remote is the source of truth for these entries, retry when it's reachable`);
1448
1490
  }
1449
- return false;
1491
+ // the remote has no such lane, so there's nothing to remove there: change the local lane only
1492
+ const remoteSkipped = current.source === 'no-remote-lane';
1493
+ const removed = ids?.length ? current.ids.filter(id => ids.some(toRemove => toRemove.isEqualWithoutVersion(id))) : current.ids;
1494
+ if (!removed.length) {
1495
+ // nothing left to remove on the remote, but the local lane can still hold entries the remote already
1496
+ // dropped - e.g. an earlier undo whose local save failed after the remote removal succeeded. reconcile
1497
+ // them here so retrying the command fixes it, instead of the stale entries lingering until the next import.
1498
+ const localChanged = remoteSkipped ? false : await this.pruneStaleUpdateDependents(laneId, current.ids);
1499
+ return {
1500
+ laneId,
1501
+ removed,
1502
+ remoteChanged: false,
1503
+ localChanged,
1504
+ remoteSkipped
1505
+ };
1506
+ }
1507
+ // remove exactly the resolved entries, on the remote and locally. this keeps local-only entries (e.g. a
1508
+ // workspace cascade that wasn't exported yet) intact instead of wiping them with a remove-all.
1509
+ const removedIdsStr = removed.map(id => id.toString());
1510
+ let remoteChanged = false;
1511
+ if (!remoteSkipped) {
1512
+ const remote = await (0, _scope2().getRemoteByName)(laneId.scope, this.workspace?.consumer);
1513
+ remoteChanged = await remote.removeLaneUpdateDependents(laneId.toString(), removedIdsStr);
1514
+ }
1515
+ const localLane = await this.loadLane(laneId);
1516
+ // the local removal matches by component-id and not by the full versioned id on purpose. when a local
1517
+ // cascade moved ahead of the remote, that entry is still for a component the user just undid: it can't
1518
+ // be exported (the remote refuses new hidden entries without the override flag) and the next import
1519
+ // prunes it anyway. keeping it would leave a hidden entry that "bit lane updates" no longer reports.
1520
+ const localChanged = localLane ? await this.removeUpdateDependents(laneId, removed) : false;
1521
+ return {
1522
+ laneId,
1523
+ removed,
1524
+ remoteChanged,
1525
+ localChanged,
1526
+ remoteSkipped
1527
+ };
1528
+ }
1529
+
1530
+ /**
1531
+ * remove local hidden entries whose component-id is absent from `remoteIds`, i.e. entries the remote has
1532
+ * already dropped. matches by component-id and not by version, the same rule the import-side reconciliation
1533
+ * in the lane merge uses, so a local cascade that only moved ahead of the remote survives.
1534
+ * returns true if the local lane has changed.
1535
+ */
1536
+ async pruneStaleUpdateDependents(laneId, remoteIds) {
1537
+ const localLane = await this.loadLane(laneId);
1538
+ const localIds = localLane?.updateDependents;
1539
+ if (!localIds?.length) return false;
1540
+ const stale = localIds.filter(id => !remoteIds.some(remoteId => remoteId.isEqualWithoutVersion(id)));
1541
+ if (!stale.length) return false;
1542
+ return this.removeUpdateDependents(laneId, stale);
1543
+ }
1544
+
1545
+ /**
1546
+ * true when `laneId` is the current lane and it was never exported, i.e. it doesn't exist on the remote yet.
1547
+ */
1548
+ isLaneLocalOnly(laneId) {
1549
+ if (!this.workspace) return false;
1550
+ const currentLaneId = this.getCurrentLaneId();
1551
+ if (!currentLaneId?.isEqual(laneId)) return false;
1552
+ return !this.workspace.consumer.bitMap.isLaneExported;
1553
+ }
1554
+
1555
+ /**
1556
+ * default to remove all of them.
1557
+ * returns true if the lane has changed
1558
+ */
1559
+ removeUpdateDependents(laneId, ids) {
1560
+ return this.scope.legacyScope.lanes.removeUpdateDependents(laneId, ids);
1450
1561
  }
1451
1562
  async getLaneDataOfDefaultLane() {
1452
1563
  const consumer = this.workspace?.consumer;
@@ -1490,6 +1601,7 @@ please create a new lane instead, which will include all components of this lane
1490
1601
  laneCmd.commands.push(new (_lanesModules().LaneHistoryDiffCmd)(lanesMain, workspace, scope, componentCompare));
1491
1602
  laneCmd.commands.push(new (_lane().LaneCheckoutCmd)(lanesMain));
1492
1603
  laneCmd.commands.push(new (_lane().LaneRevertCmd)(lanesMain));
1604
+ laneCmd.commands.push(new (_lane().LaneUpdatesCmd)(lanesMain));
1493
1605
  cli.register(laneCmd, switchCmd, new (_lane().CatLaneHistoryCmd)(lanesMain));
1494
1606
  cli.registerOnStart(async () => {
1495
1607
  await lanesMain.recreateNewLaneIfDeleted();