forgemap 0.6.0 → 0.7.0-dev.115-dc2b700

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.
@@ -239,10 +239,17 @@ async function safeListDirs(path) {
239
239
  }
240
240
  }
241
241
  /**
242
- * Fingerprint of every directory mtime down to depth 3 (root, forge.dir,
243
- * owner). Catches new clones / removals at any of those levels. Stops
244
- * short of stat-ing each repo dir — that would mostly duplicate the
245
- * scan it's meant to avoid.
242
+ * Fingerprint of the layout: directory mtimes for root, forge.dir and
243
+ * every owner, plus the repo names each owner holds. Catches new clones
244
+ * / removals at any of those levels. Stops short of stat-ing each repo
245
+ * dir — that would mostly duplicate the scan it's meant to avoid.
246
+ *
247
+ * The repo names are what make the check reliable. An owner's mtime
248
+ * alone is not enough: a repo cloned beside an existing one moves only
249
+ * that mtime, and mtimes are compared at millisecond granularity, so
250
+ * two clones landing inside the same millisecond hash identically and
251
+ * a stale cache wins. Listing the names makes invalidation independent
252
+ * of clock and filesystem timestamp resolution.
246
253
  *
247
254
  * Stats are issued in parallel: one batch per forge for its forge.dir +
248
255
  * owner list, all forges in parallel. Beats the sequential version by
@@ -255,14 +262,15 @@ async function computeFingerprint(config, configDir) {
255
262
  const [forgeMtime, owners] = await Promise.all([safeStat(forgeRoot), safeListDirs(forgeRoot)]);
256
263
  const ownerEntries = await Promise.all(owners.map(async (owner) => {
257
264
  const ownerPath = join(forgeRoot, owner);
258
- return [ownerPath, await safeStat(ownerPath)];
265
+ const [mtime, repos] = await Promise.all([safeStat(ownerPath), safeListDirs(ownerPath)]);
266
+ return [ownerPath, `${mtime}:${JSON.stringify(repos.sort())}`];
259
267
  }));
260
- return [[forgeRoot, forgeMtime], ...ownerEntries];
268
+ return [[forgeRoot, String(forgeMtime)], ...ownerEntries];
261
269
  }));
262
- const entries = [[root, await safeStat(root)]];
270
+ const entries = [[root, String(await safeStat(root))]];
263
271
  for (const group of perForge) entries.push(...group);
264
272
  entries.sort((a, b) => a[0].localeCompare(b[0]));
265
- return createHash("sha1").update(entries.map(([p, m]) => `${p}:${m}`).join("\n")).digest("hex");
273
+ return createHash("sha1").update(entries.map(([p, marker]) => `${p}:${marker}`).join("\n")).digest("hex");
266
274
  }
267
275
  async function readCacheFile(file) {
268
276
  try {
@@ -2935,7 +2943,7 @@ var infoCommand = defineCommand({
2935
2943
  async run({ args }) {
2936
2944
  const binary = resolveBinary(process.argv[1]);
2937
2945
  const info = {
2938
- version: "0.6.0",
2946
+ version: "0.7.0-dev.115-dc2b700",
2939
2947
  build: detectBuild(binary.resolved),
2940
2948
  binary,
2941
2949
  node: process.version,
@@ -4073,7 +4081,7 @@ var completionCommand = defineCommand({
4073
4081
  runMain(defineCommand({
4074
4082
  meta: {
4075
4083
  name: "forgemap",
4076
- version: "0.6.0",
4084
+ version: "0.7.0-dev.115-dc2b700",
4077
4085
  description: "Manage a local repo layout of the form <root>/<forge.dir>/<owner>/<repo>"
4078
4086
  },
4079
4087
  subCommands: {