@antongolub/lockfile 0.0.0-snapshot.70 → 0.0.0-snapshot.72

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.
package/dist/index.js CHANGED
@@ -10609,6 +10609,12 @@ async function replaceVersion(graph, selector, toRange, context, options = {}) {
10609
10609
  currentGraph = currentGraph.mutate((m) => {
10610
10610
  for (const e of staleOut) m.removeEdge(targetId, e.dst, e.kind);
10611
10611
  }).graph;
10612
+ for (const e of staleOut) {
10613
+ const dst = currentGraph.getNode(e.dst);
10614
+ if (dst !== void 0 && dst.workspacePath === void 0 && currentGraph.in(e.dst).length === 0) {
10615
+ recentlyOrphaned.add(e.dst);
10616
+ }
10617
+ }
10612
10618
  }
10613
10619
  replaced.push({ from: node.id, to: targetId });
10614
10620
  recentlyAdded.add(targetId);
@@ -10939,6 +10945,12 @@ function optimize(graph, options = {}) {
10939
10945
  }
10940
10946
  }
10941
10947
  }
10948
+ for (const node of graph.nodes()) {
10949
+ if (!live.has(node.id)) continue;
10950
+ if (node.patch === void 0 && node.source === void 0) continue;
10951
+ const baseId = serializeNodeId(node.name, node.version, node.peerContext, void 0, void 0);
10952
+ if (baseId !== node.id && graph.getNode(baseId) !== void 0) live.add(baseId);
10953
+ }
10942
10954
  const removed = [];
10943
10955
  const unresolved = [];
10944
10956
  let next = graph;
package/dist/modify.js CHANGED
@@ -628,6 +628,12 @@ async function replaceVersion(graph, selector, toRange, context, options = {}) {
628
628
  currentGraph = currentGraph.mutate((m) => {
629
629
  for (const e of staleOut) m.removeEdge(targetId, e.dst, e.kind);
630
630
  }).graph;
631
+ for (const e of staleOut) {
632
+ const dst = currentGraph.getNode(e.dst);
633
+ if (dst !== void 0 && dst.workspacePath === void 0 && currentGraph.in(e.dst).length === 0) {
634
+ recentlyOrphaned.add(e.dst);
635
+ }
636
+ }
631
637
  }
632
638
  replaced.push({ from: node.id, to: targetId });
633
639
  recentlyAdded.add(targetId);
@@ -9,9 +9,17 @@ interface PruneOrphansOptions {
9
9
  /**
10
10
  * Bound the sweep to a candidate set. When provided, ONLY these NodeIds (and
11
11
  * the closure they transitively strand) are removal candidates — any OTHER
12
- * pre-existing in-degree-0 node is left untouched. Pass the mutation's removed
13
- * / orphaned NodeIds (e.g. `replaceVersion`'s `recentlyOrphaned`) for a purely
14
- * differential GC. Omit for a whole-graph sweep.
12
+ * pre-existing in-degree-0 node is left untouched.
13
+ *
14
+ * **Seeded is the SAFE / recommended mode for post-mutation GC.** Pass the
15
+ * mutation's orphaned NodeIds (`replaceVersion`'s `recentlyOrphaned` — which
16
+ * now includes the targets whose last incoming edge a rebind's edge-refresh
17
+ * dropped). The sweep then retires exactly that delta and CANNOT touch a node
18
+ * the mutation never affected — including ones that only LOOK orphaned because
19
+ * an incoming edge is unresolved in the parse (e.g. berry `@patch:…!builtin`
20
+ * fsevents). Omit only for a whole-graph sweep on a graph whose edges you trust
21
+ * to be complete; the unseeded path over-prunes such unresolved-edge nodes and
22
+ * is guarded against wiping a rootless lock (PRUNE_NO_ROOTS).
15
23
  */
16
24
  seed?: ReadonlySet<NodeId>;
17
25
  }
@@ -42,7 +50,7 @@ interface OptimizeDiagnostic extends Diagnostic {
42
50
  * rationale — the NodeId alone may carry a long peerContext suffix.
43
51
  */
44
52
  declare function optimizeNodeRemoved(nodeId: NodeId): OptimizeDiagnostic;
45
- type PruneDiagnosticCode = 'PRUNE_NODE_REMOVED' | 'PRUNE_NOOP';
53
+ type PruneDiagnosticCode = 'PRUNE_NODE_REMOVED' | 'PRUNE_NOOP' | 'PRUNE_NO_ROOTS';
46
54
  interface PruneDiagnostic extends Diagnostic {
47
55
  code: PruneDiagnosticCode;
48
56
  }
@@ -50,6 +58,15 @@ interface PruneDiagnostic extends Diagnostic {
50
58
  declare function pruneNodeRemoved(nodeId: NodeId): PruneDiagnostic;
51
59
  /** Fires once per `pruneOrphans(graph)` call when nothing was removed. */
52
60
  declare function pruneNoop(): PruneDiagnostic;
61
+ /**
62
+ * Fires when an UNSEEDED pruneOrphans runs on a non-empty graph with no
63
+ * workspace anchor (e.g. a rootless yarn-classic lock). Without a workspace
64
+ * every top-level dependency is itself in-degree 0, so a whole-graph sweep would
65
+ * cascade and wipe the lock. We no-op instead — the caller bounds the sweep with
66
+ * an explicit `seed` (or `preserve`) to GC a rootless graph. Mirrors
67
+ * OPTIMIZE_NO_ROOTS.
68
+ */
69
+ declare function pruneNoRoots(): PruneDiagnostic;
53
70
  declare function optimizeWorkspaceUnreachable(nodeId: NodeId): OptimizeDiagnostic;
54
71
  /**
55
72
  * Fires once per `optimize(graph)` call when `removed.length === 0`. The
@@ -60,4 +77,4 @@ declare function optimizeWorkspaceUnreachable(nodeId: NodeId): OptimizeDiagnosti
60
77
  */
61
78
  declare function optimizeNoop(): OptimizeDiagnostic;
62
79
 
63
- export { type OptimizeDiagnostic, type OptimizeDiagnosticCode, type PruneDiagnostic, type PruneDiagnosticCode, type PruneOrphansOptions, type PruneOrphansResult, optimizeNodeRemoved, optimizeNoop, optimizeWorkspaceUnreachable, pruneNodeRemoved, pruneNoop, pruneOrphans };
80
+ export { type OptimizeDiagnostic, type OptimizeDiagnosticCode, type PruneDiagnostic, type PruneDiagnosticCode, type PruneOrphansOptions, type PruneOrphansResult, optimizeNodeRemoved, optimizeNoop, optimizeWorkspaceUnreachable, pruneNoRoots, pruneNodeRemoved, pruneNoop, pruneOrphans };
package/dist/optimize.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'crypto';
2
2
  import 'buffer';
3
3
 
4
- // src/main/ts/recipe/patch.ts
4
+ // src/main/ts/errors.ts
5
5
 
6
6
  // src/main/ts/graph.ts
7
7
  function nameOf(id) {
@@ -15,6 +15,16 @@ function nameOf(id) {
15
15
  }
16
16
  return lastAt < 0 ? id : id.slice(0, lastAt);
17
17
  }
18
+ function serializeNodeId(name, version, peerContext, patch, source) {
19
+ const base = toTarballKey({ name, version});
20
+ if (peerContext.length === 0) return base;
21
+ return base + peerContext.map((p) => `(${p})`).join("");
22
+ }
23
+ function toTarballKey(inputs) {
24
+ const slots = [];
25
+ return slots.length === 0 ? `${inputs.name}@${inputs.version}` : `${inputs.name}@${inputs.version}+${slots.sort(cmpStr).join("+")}`;
26
+ }
27
+ var cmpStr = (a, b) => a < b ? -1 : a > b ? 1 : 0;
18
28
 
19
29
  // src/main/ts/optimize/diagnostics.ts
20
30
  function optimizeNodeRemoved(nodeId) {
@@ -58,6 +68,14 @@ function pruneNoop() {
58
68
  message: "pruneOrphans: no unreferenced nodes to collect"
59
69
  };
60
70
  }
71
+ function pruneNoRoots() {
72
+ return {
73
+ code: "PRUNE_NO_ROOTS",
74
+ severity: "warning",
75
+ subject: "graph",
76
+ message: "pruneOrphans: no workspace anchor and no seed on a non-empty graph \u2014 kept all nodes to avoid wiping a rootless lock"
77
+ };
78
+ }
61
79
  function optimizeWorkspaceUnreachable(nodeId) {
62
80
  return {
63
81
  code: "OPTIMIZE_WORKSPACE_UNREACHABLE",
@@ -128,6 +146,12 @@ function optimize(graph, options = {}) {
128
146
  }
129
147
  }
130
148
  }
149
+ for (const node of graph.nodes()) {
150
+ if (!live.has(node.id)) continue;
151
+ if (node.patch === void 0 && node.source === void 0) continue;
152
+ const baseId = serializeNodeId(node.name, node.version, node.peerContext);
153
+ if (baseId !== node.id && graph.getNode(baseId) !== void 0) live.add(baseId);
154
+ }
131
155
  const removed = [];
132
156
  const unresolved = [];
133
157
  let next = graph;
@@ -177,12 +201,33 @@ function pruneOrphans(graph, options = {}) {
177
201
  if (onDiagnostic !== void 0) onDiagnostic(d);
178
202
  };
179
203
  let current = graph;
204
+ if (options.seed === void 0) {
205
+ let hasNodes = false;
206
+ let hasWorkspace = false;
207
+ for (const node of current.nodes()) {
208
+ hasNodes = true;
209
+ if (node.workspacePath !== void 0) {
210
+ hasWorkspace = true;
211
+ break;
212
+ }
213
+ }
214
+ if (hasNodes && !hasWorkspace) {
215
+ const diag = pruneNoRoots();
216
+ const guarded = current.mutate((m) => {
217
+ m.diagnostic(diag);
218
+ }).graph;
219
+ emit(diag);
220
+ return { graph: guarded, removed, unresolved };
221
+ }
222
+ }
180
223
  const collectable = (id) => {
181
224
  const node = current.getNode(id);
182
225
  if (node === void 0) return false;
183
226
  if (node.workspacePath !== void 0) return false;
184
227
  if (preserve.has(id)) return false;
185
- return current.in(id).length === 0;
228
+ if (current.in(id).length > 0) return false;
229
+ if (hasLivePatchedVariant(current, node)) return false;
230
+ return true;
186
231
  };
187
232
  const queue = [];
188
233
  if (options.seed !== void 0) {
@@ -217,6 +262,18 @@ function pruneOrphans(graph, options = {}) {
217
262
  }
218
263
  return { graph: current, removed, unresolved };
219
264
  }
265
+ function hasLivePatchedVariant(graph, node) {
266
+ if (node.patch !== void 0 || node.source !== void 0) return false;
267
+ for (const id of graph.byName(node.name)) {
268
+ if (id === node.id) continue;
269
+ const other = graph.getNode(id);
270
+ if (other === void 0) continue;
271
+ if (other.patch === void 0 && other.source === void 0) continue;
272
+ if (other.version !== node.version) continue;
273
+ if (serializeNodeId(other.name, other.version, other.peerContext) === node.id) return true;
274
+ }
275
+ return false;
276
+ }
220
277
  function tarballSharedByOther(graph, removingId, key) {
221
278
  for (const id of graph.byName(key.name)) {
222
279
  if (id === removingId) continue;
@@ -226,4 +283,4 @@ function tarballSharedByOther(graph, removingId, key) {
226
283
  return false;
227
284
  }
228
285
 
229
- export { optimize, optimizeNodeRemoved, optimizeNoop, optimizeWorkspaceUnreachable, pruneNodeRemoved, pruneNoop, pruneOrphans };
286
+ export { optimize, optimizeNodeRemoved, optimizeNoop, optimizeWorkspaceUnreachable, pruneNoRoots, pruneNodeRemoved, pruneNoop, pruneOrphans };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antongolub/lockfile",
3
- "version": "0.0.0-snapshot.70",
3
+ "version": "0.0.0-snapshot.72",
4
4
  "private": false,
5
5
  "description": "Universal lockfile model and converter for npm, yarn, pnpm, bun",
6
6
  "type": "module",