@teambit/objects 0.0.487 → 0.0.489

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.
@@ -10,6 +10,10 @@ export type HistoryItem = {
10
10
  log: Log;
11
11
  components: string[];
12
12
  deleted?: string[];
13
+ // hidden lane.updateDependents at the time of the snapshot. Recorded in their own field so
14
+ // `historyItem.components` keeps its workspace-checkout/revert contract intact — those flows
15
+ // must not materialize hidden entries into the bitmap.
16
+ updateDependents?: string[];
13
17
  };
14
18
 
15
19
  type History = { [uuid: string]: HistoryItem };
@@ -83,7 +87,16 @@ export class LaneHistory extends BitObject {
83
87
  const deleted = laneObj.components
84
88
  .filter((c) => c.isDeleted)
85
89
  .map((c) => c.id.changeVersion(c.head.toString()).toString());
86
- this.history[historyKey || v4()] = { log, components, ...(deleted.length && { deleted }) };
90
+ // Always write `updateDependents` (even when empty) so checkout/revert can distinguish a
91
+ // post-PR entry that legitimately had no hidden entries (drop current hidden) from a legacy
92
+ // pre-PR entry that never recorded the field at all (leave current hidden alone).
93
+ const updateDependents = (laneObj.updateDependents || []).map((id) => id.toString());
94
+ this.history[historyKey || v4()] = {
95
+ log,
96
+ components,
97
+ ...(deleted.length && { deleted }),
98
+ updateDependents,
99
+ };
87
100
  }
88
101
 
89
102
  removeHistoryEntries(keys: string[]) {
package/models/lane.ts CHANGED
@@ -50,10 +50,11 @@ export default class Lane extends BitObject {
50
50
  isNew = false; // doesn't get saved in the object. only needed for in-memory instance
51
51
  hasChanged = false; // doesn't get saved in the object. only needed for in-memory instance
52
52
  /**
53
- * populated when a user clicks on "update" in the UI. it's a list of components that are dependents on the
54
- * components in the lane. their dependencies are updated according to the lane.
55
- * from the CLI perspective, it's added by "bit _snap" and merged by "bit _merge-lane".
56
- * otherwise, the user is not aware of it. it's not imported to the workspace and the objects are not fetched.
53
+ * components that are dependents of components in the lane and need to be re-snapped against the
54
+ * lane's heads. populated by the bare-scope cascade producer (UI "snap updates" / `bit _snap`)
55
+ * and by `bit _merge-lane`. these entries are part of the lane's graph (Ripple CI builds them,
56
+ * merge engine refreshes them) but are intentionally hidden from workspace-facing flows
57
+ * (`bit status`, `bit compile`, `bit install`, the bitmap).
57
58
  */
58
59
  updateDependents?: ComponentID[];
59
60
  private overrideUpdateDependents?: boolean;
@@ -179,7 +180,12 @@ export default class Lane extends BitObject {
179
180
  addComponent(component: LaneComponent) {
180
181
  const existsComponent = this.getComponent(component.id);
181
182
  if (existsComponent) {
182
- if (!existsComponent.head.isEqual(component.head)) this.hasChanged = true;
183
+ if (
184
+ !existsComponent.head.isEqual(component.head) ||
185
+ Boolean(existsComponent.isDeleted) !== Boolean(component.isDeleted)
186
+ ) {
187
+ this.hasChanged = true;
188
+ }
183
189
  existsComponent.id = component.id;
184
190
  existsComponent.head = component.head;
185
191
  existsComponent.isDeleted = component.isDeleted;
@@ -189,39 +195,49 @@ export default class Lane extends BitObject {
189
195
  this.hasChanged = true;
190
196
  }
191
197
  }
198
+ findUpdateDependent(componentId: ComponentID): ComponentID | undefined {
199
+ return this.updateDependents?.find((c) => c.isEqualWithoutVersion(componentId));
200
+ }
201
+ getUpdateDependentAsLaneComponent(componentId: ComponentID): LaneComponent | undefined {
202
+ const found = this.findUpdateDependent(componentId);
203
+ if (!found?.version) return undefined;
204
+ return { id: found.changeVersion(undefined), head: Ref.from(found.version) };
205
+ }
192
206
  removeComponentFromUpdateDependentsIfExist(componentId: ComponentID) {
193
- const updateDependentsList = ComponentIdList.fromArray(this.updateDependents || []);
194
- const exist = updateDependentsList.searchWithoutVersion(componentId);
195
- if (!exist) return;
196
- this.updateDependents = updateDependentsList.removeIfExist(exist);
197
- if (!this.updateDependents.length) this.updateDependents = undefined;
207
+ if (!this.findUpdateDependent(componentId)) return;
208
+ this.updateDependents = this.updateDependents?.filter((c) => !c.isEqualWithoutVersion(componentId));
209
+ if (!this.updateDependents?.length) this.updateDependents = undefined;
198
210
  this.hasChanged = true;
199
211
  }
200
212
  addComponentToUpdateDependents(componentId: ComponentID) {
213
+ if (!componentId.hasVersion()) {
214
+ throw new ValidationError(`Lane.addComponentToUpdateDependents: ${componentId.toString()} is missing a version`);
215
+ }
201
216
  this.removeComponentFromUpdateDependentsIfExist(componentId);
202
217
  (this.updateDependents ||= []).push(componentId);
203
218
  this.hasChanged = true;
204
219
  }
205
220
  removeAllUpdateDependents() {
206
- if (this.updateDependents?.length) return;
221
+ if (!this.updateDependents?.length) return;
207
222
  this.updateDependents = undefined;
208
223
  this.hasChanged = true;
209
224
  }
210
- shouldOverrideUpdateDependents() {
211
- return this.overrideUpdateDependents;
212
- }
213
225
  /**
214
- * !!! important !!!
215
- * this should get called only on a "temp lane", such as running "bit _snap", which the scope gets destroys after the
216
- * command is done. when _scope exports the lane, this "overrideUpdateDependents" is not saved to the remote-scope.
217
- *
218
- * on a user local lane object, this prop should never be true. otherwise, it'll override the remote-scope data.
226
+ * Signals an explicit `_snap --update-dependents` insertion. On export, the receiving scope
227
+ * gates its "add this new hidden entry" branch on this flag so a stale workspace lane can't
228
+ * resurrect entries the Cloud UI dropped. Workspace cascades which only update existing
229
+ * entries — deliberately do NOT set the flag. The field is also serialized by `Lane.toObject`
230
+ * for older remotes that gate their pre-diverge-check override branch on it.
219
231
  */
220
232
  setOverrideUpdateDependents(overrideUpdateDependents: boolean) {
221
233
  this.overrideUpdateDependents = overrideUpdateDependents;
222
234
  this.hasChanged = true;
223
235
  }
224
236
 
237
+ shouldOverrideUpdateDependents(): boolean {
238
+ return Boolean(this.overrideUpdateDependents);
239
+ }
240
+
225
241
  removeComponent(id: ComponentID): boolean {
226
242
  const existsComponent = this.getComponent(id);
227
243
  if (!existsComponent) return false;
@@ -240,7 +256,11 @@ export default class Lane extends BitObject {
240
256
  setLaneComponents(laneComponents: LaneComponent[]) {
241
257
  // this gets called when adding lane-components from other lanes/remotes, so it's better to
242
258
  // clone the objects to not change the original data.
243
- this.components = laneComponents.map((c) => ({ id: c.id.clone(), head: c.head.clone() }));
259
+ this.components = laneComponents.map((c) => ({
260
+ id: c.id.clone(),
261
+ head: c.head.clone(),
262
+ ...(c.isDeleted && { isDeleted: c.isDeleted }),
263
+ }));
244
264
  this.hasChanged = true;
245
265
  }
246
266
  setReadmeComponent(id?: ComponentID) {
@@ -325,9 +345,7 @@ export default class Lane extends BitObject {
325
345
  getCompHeadIncludeUpdateDependents(componentId: ComponentID): Ref | undefined {
326
346
  const comp = this.getComponent(componentId);
327
347
  if (comp) return comp.head;
328
- const fromUpdateDependents = this.updateDependents?.find((c) => c.isEqualWithoutVersion(componentId));
329
- if (fromUpdateDependents) return Ref.from(fromUpdateDependents.version);
330
- return undefined;
348
+ return this.getUpdateDependentAsLaneComponent(componentId)?.head;
331
349
  }
332
350
  validate() {
333
351
  const message = `unable to save Lane object "${this.id()}"`;
@@ -353,9 +371,22 @@ export default class Lane extends BitObject {
353
371
  }
354
372
  isEqual(lane: Lane): boolean {
355
373
  if (this.id() !== lane.id()) return false;
356
- const thisComponents = this.toComponentIds().toStringArray().sort();
357
- const otherComponents = lane.toComponentIds().toStringArray().sort();
358
- return isEqual(thisComponents, otherComponents);
374
+ // include isDeleted in equality so a soft-delete flip with the same head still counts as a
375
+ // state change — `Lane.isEqual` is used by importers to decide whether to write a
376
+ // LaneHistory entry.
377
+ const normalize = (l: Lane) =>
378
+ l.components
379
+ .map((c) => ({
380
+ id: c.id.toStringWithoutVersion(),
381
+ head: c.head.toString(),
382
+ isDeleted: Boolean(c.isDeleted),
383
+ }))
384
+ .sort((a, b) =>
385
+ `${a.id}@${a.head}:${a.isDeleted ? 1 : 0}`.localeCompare(`${b.id}@${b.head}:${b.isDeleted ? 1 : 0}`)
386
+ );
387
+ const thisUpdDeps = (this.updateDependents || []).map((c) => c.toString()).sort();
388
+ const otherUpdDeps = (lane.updateDependents || []).map((c) => c.toString()).sort();
389
+ return isEqual(normalize(this), normalize(lane)) && isEqual(thisUpdDeps, otherUpdDeps);
359
390
  }
360
391
  clone() {
361
392
  return new Lane({
@@ -363,6 +394,7 @@ export default class Lane extends BitObject {
363
394
  hash: this._hash,
364
395
  overrideUpdateDependents: this.overrideUpdateDependents,
365
396
  components: cloneDeep(this.components),
397
+ updateDependents: this.updateDependents ? cloneDeep(this.updateDependents) : undefined,
366
398
  });
367
399
  }
368
400
  }
@@ -382,7 +382,9 @@ export default class Component extends BitObject {
382
382
 
383
383
  setLaneHeadLocal(lane?: Lane) {
384
384
  if (lane) {
385
- this.laneHeadLocal = lane.getComponentHead(this.toComponentId());
385
+ // include lane.updateDependents hidden cascade entries need a lane-local head so the
386
+ // export-pending detector picks them up after a workspace cascade snap.
387
+ this.laneHeadLocal = lane.getCompHeadIncludeUpdateDependents(this.toComponentId()) || null;
386
388
  }
387
389
  }
388
390
 
@@ -704,11 +706,27 @@ export default class Component extends BitObject {
704
706
  if (parent && !parent.isEqual(versionToAddRef)) {
705
707
  version.addAsOnlyParent(parent);
706
708
  }
707
- if (addToUpdateDependentsInLane) {
709
+ // When the caller didn't explicitly opt in or out, preserve the existing entry's bucket so
710
+ // a merge-from-main producing a new snap for a hidden updateDependent keeps it hidden
711
+ // rather than promoting it into workspace-tracked state.
712
+ const existingHidden = lane.findUpdateDependent(currentBitId);
713
+ const existingVisible = lane.getComponent(currentBitId);
714
+ const shouldBeHidden = addToUpdateDependentsInLane ?? (Boolean(existingHidden) && !existingVisible);
715
+ if (shouldBeHidden) {
716
+ if (existingVisible) lane.removeComponent(currentBitId);
708
717
  lane.addComponentToUpdateDependents(currentBitId.changeVersion(versionToAddRef.toString()));
709
- lane.setOverrideUpdateDependents(true);
718
+ // Only brand-new hidden insertions raise the wire flag — that's the `_snap --update-dependents`
719
+ // path adding a component that wasn't already on the lane. Cascades update an existing
720
+ // hidden entry (wasAlreadyHidden=true) and must NOT raise the flag, otherwise origin would
721
+ // re-add entries the Cloud UI explicitly dropped.
722
+ if (!existingHidden) lane.setOverrideUpdateDependents(true);
710
723
  } else {
711
- lane.addComponent({ id: currentBitId, head: versionToAddRef, isDeleted: version.isRemoved() });
724
+ if (existingHidden) lane.removeComponentFromUpdateDependentsIfExist(currentBitId);
725
+ lane.addComponent({
726
+ id: currentBitId,
727
+ head: versionToAddRef,
728
+ isDeleted: version.isRemoved(),
729
+ });
712
730
  }
713
731
 
714
732
  if (lane.readmeComponent && lane.readmeComponent.id.fullName === currentBitId.fullName) {
package/package.json CHANGED
@@ -1,62 +1,62 @@
1
1
  {
2
2
  "name": "@teambit/objects",
3
- "version": "0.0.487",
3
+ "version": "0.0.489",
4
4
  "homepage": "https://bit.cloud/teambit/scope/objects",
5
5
  "main": "dist/index.js",
6
6
  "componentId": {
7
7
  "scope": "teambit.scope",
8
8
  "name": "objects",
9
- "version": "0.0.487"
9
+ "version": "0.0.489"
10
10
  },
11
11
  "dependencies": {
12
- "@pnpm/dependency-path": "1001.1.10",
13
- "@pnpm/lockfile.types": "^1002.1.0",
14
- "semver": "7.7.1",
15
12
  "lodash": "4.17.21",
16
- "uuid": "8.3.2",
17
- "async-mutex": "0.3.1",
18
- "p-map-series": "2.1.0",
19
13
  "tar-stream": "2.2.0",
14
+ "p-map-series": "2.1.0",
15
+ "async-mutex": "0.3.1",
20
16
  "fs-extra": "10.0.0",
21
17
  "glob": "13.0.0",
22
18
  "uid-number": "0.0.6",
19
+ "@pnpm/dependency-path": "1001.1.10",
20
+ "@pnpm/lockfile.types": "^1002.1.0",
21
+ "semver": "7.7.1",
22
+ "uuid": "8.3.2",
23
23
  "@teambit/harmony": "0.4.7",
24
- "@teambit/cli": "0.0.1321",
25
24
  "@teambit/component-id": "1.2.4",
26
- "@teambit/legacy.utils": "0.0.35",
27
- "@teambit/harmony.modules.get-basic-log": "0.0.113",
25
+ "@teambit/graph.cleargraph": "0.0.11",
26
+ "@teambit/harmony.modules.concurrency": "0.0.27",
27
+ "@teambit/legacy.logger": "0.0.38",
28
+ "@teambit/toolbox.promise.map-pool": "0.0.14",
29
+ "@teambit/legacy.constants": "0.0.26",
30
+ "@teambit/legacy.utils": "0.0.36",
31
+ "@teambit/toolbox.crypto.sha1": "0.0.15",
28
32
  "@teambit/bit-error": "0.0.404",
29
33
  "@teambit/component-version": "1.0.4",
30
- "@teambit/component.snap-distance": "0.0.113",
31
- "@teambit/config-store": "0.0.202",
34
+ "@teambit/harmony.modules.in-memory-cache": "0.0.30",
35
+ "@teambit/toolbox.fs.remove-empty-dir": "0.0.13",
32
36
  "@teambit/lane-id": "0.0.312",
33
37
  "@teambit/legacy.cli.error": "0.0.38",
34
- "@teambit/legacy.constants": "0.0.26",
35
- "@teambit/legacy.logger": "0.0.38",
36
- "@teambit/legacy.scope": "0.0.112",
37
- "@teambit/toolbox.crypto.sha1": "0.0.15",
38
- "@teambit/component.sources": "0.0.164",
39
38
  "@teambit/legacy-bit-id": "1.1.3",
40
39
  "@teambit/legacy-component-log": "0.0.417",
41
- "@teambit/legacy.consumer-component": "0.0.113",
42
- "@teambit/legacy.consumer-config": "0.0.112",
43
- "@teambit/legacy.extension-data": "0.0.114",
44
40
  "@teambit/pkg.modules.semver-helper": "0.0.22",
45
41
  "@teambit/toolbox.array.duplications-finder": "0.0.3",
46
- "@teambit/graph.cleargraph": "0.0.11",
47
42
  "@teambit/bit.get-bit-version": "0.0.15",
48
- "@teambit/semantics.doc-parser": "0.0.120",
49
- "@teambit/harmony.modules.concurrency": "0.0.27",
50
- "@teambit/toolbox.promise.map-pool": "0.0.14",
51
- "@teambit/harmony.modules.in-memory-cache": "0.0.30",
52
- "@teambit/toolbox.fs.remove-empty-dir": "0.0.13",
53
- "@teambit/graph": "1.0.980"
43
+ "@teambit/cli": "0.0.1323",
44
+ "@teambit/component.snap-distance": "0.0.115",
45
+ "@teambit/graph": "1.0.982",
46
+ "@teambit/legacy.scope": "0.0.114",
47
+ "@teambit/harmony.modules.get-basic-log": "0.0.115",
48
+ "@teambit/config-store": "0.0.204",
49
+ "@teambit/component.sources": "0.0.166",
50
+ "@teambit/legacy.consumer-component": "0.0.115",
51
+ "@teambit/legacy.consumer-config": "0.0.114",
52
+ "@teambit/legacy.extension-data": "0.0.116",
53
+ "@teambit/semantics.doc-parser": "0.0.122"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/semver": "7.5.8",
57
56
  "@types/lodash": "4.14.165",
58
- "@types/uuid": "8.3.4",
59
57
  "@types/fs-extra": "9.0.7",
58
+ "@types/semver": "7.5.8",
59
+ "@types/uuid": "8.3.4",
60
60
  "@teambit/harmony.envs.core-aspect-env": "0.1.4"
61
61
  },
62
62
  "peerDependencies": {