superbee 0.4.6-pre.1 → 0.4.6-pre.2

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.
@@ -30844,15 +30844,30 @@ var FrontmatterRefusedError = class extends InvalidInputError {
30844
30844
  }
30845
30845
  };
30846
30846
  var FilesystemIdentityAliasError = class extends InvalidInputError {
30847
+ reason = "filesystem_alias";
30847
30848
  rel;
30848
30849
  segment;
30849
- constructor(rel, segment) {
30850
+ existing;
30851
+ suggestedPath;
30852
+ /**
30853
+ * `noun` names the existing entry ("folder" or "file"); `index` is the position of `segment`
30854
+ * in `rel`. Both default from the position of the first segment spelled `segment`.
30855
+ */
30856
+ constructor(rel, segment, existing, noun, index2) {
30857
+ const segments = rel.split("/").filter((part) => part !== "" && part !== ".");
30858
+ const at = index2 ?? segments.indexOf(segment);
30859
+ const kind2 = noun ?? (at === segments.length - 1 ? "file" : "folder");
30860
+ const clash = existing === void 0 ? `but a ${kind2} with that name in a different case or Unicode form already exists` : existing.normalize("NFC") === segment.normalize("NFC") ? `but ${kind2} '${existing}' already exists, written in a different Unicode form` : `but ${kind2} '${existing}' already exists`;
30861
+ const difference = existing === void 0 ? "case or Unicode form" : existing.normalize("NFC") === segment.normalize("NFC") ? "Unicode form" : existing.toLowerCase() === segment.toLowerCase() ? "case" : "case and Unicode form";
30850
30862
  super(
30851
- `Path '${rel}' does not match the exact spelling of an existing filesystem entry at segment '${segment}'; ids that differ only by case or Unicode normalization cannot share one file.`
30863
+ `Path '${rel}' uses ${kind2} '${segment}', ${clash}, and this disk treats names that differ only by ${difference} as the same name. Use the existing spelling.`
30852
30864
  );
30853
30865
  this.name = "FilesystemIdentityAliasError";
30854
30866
  this.rel = rel;
30855
30867
  this.segment = segment;
30868
+ this.existing = existing;
30869
+ const unusable = existing === void 0 || at < 0 || segments[at] !== segment || kind2 === "file" && at < segments.length - 1 || existing.normalize("NFC") === segment.normalize("NFC");
30870
+ this.suggestedPath = unusable ? void 0 : segments.map((part, position2) => position2 === at ? existing : part).join("/");
30856
30871
  }
30857
30872
  };
30858
30873
  var ConcurrentReplacementError = class extends Error {
@@ -31057,6 +31072,9 @@ function delimiterLineEnd(input, offset) {
31057
31072
  if (input[suffix] === "\r" && input[suffix + 1] === "\n") return suffix + 2;
31058
31073
  return null;
31059
31074
  }
31075
+ function startsWithFrontmatterSyntax(text4) {
31076
+ return text4.startsWith("\uFEFF") || delimiterLineEnd(text4, 0) !== null;
31077
+ }
31060
31078
  function splitLeadingFrontmatter(raw, context) {
31061
31079
  const input = raw.startsWith("\uFEFF") ? raw.slice(1) : raw;
31062
31080
  const openingEnd = delimiterLineEnd(input, 0);
@@ -31162,7 +31180,10 @@ function stringifyWithSerializedFrontmatter(dumped, body) {
31162
31180
  const content3 = body ?? "";
31163
31181
  const newline = (value) => value.endsWith("\n") ? value : `${value}
31164
31182
  `;
31165
- if (dumped === "{}") return normalizeDocumentBodyForStorage(content3);
31183
+ if (dumped === "{}") {
31184
+ const stored = normalizeDocumentBodyForStorage(content3);
31185
+ return `${startsWithFrontmatterSyntax(stored) ? "---\n---\n" : ""}${stored}`;
31186
+ }
31166
31187
  return `---
31167
31188
  ${newline(dumped)}---
31168
31189
  ${normalizeDocumentBodyForStorage(content3)}`;
@@ -31980,36 +32001,57 @@ function hasSourceIdentity(sources, identity) {
31980
32001
  return Array.isArray(sources) && sources.some((entry) => matchesSourceIdentity(entry, identity));
31981
32002
  }
31982
32003
 
32004
+ // ../core/src/skip-reasons.ts
32005
+ var PERMISSION_DENIED_PREFIX = "permission denied (";
32006
+ function permissionDeniedReason(code2) {
32007
+ return `${PERMISSION_DENIED_PREFIX}${code2})`;
32008
+ }
32009
+ function isPermissionDeniedSkip(skip) {
32010
+ return skip.reason.startsWith(PERMISSION_DENIED_PREFIX);
32011
+ }
32012
+
31983
32013
  // ../core/src/document-scan.ts
31984
32014
  function isEnoent(err) {
31985
32015
  return typeof err === "object" && err !== null && err.code === "ENOENT";
31986
32016
  }
31987
- async function readManyExisting(backend, ids, onMalformed, onAliased) {
31988
- try {
31989
- return await backend.readMany(ids);
31990
- } catch (err) {
31991
- const malformed = err instanceof MalformedDocumentError && onMalformed !== void 0;
31992
- const aliased = err instanceof FilesystemIdentityAliasError && onAliased !== void 0;
31993
- if (!isEnoent(err) && !malformed && !aliased) throw err;
31994
- const out = [];
31995
- for (const id of ids) {
31996
- try {
31997
- out.push(await backend.read(id));
31998
- } catch (candidate) {
31999
- if (isEnoent(candidate)) continue;
32000
- if (candidate instanceof MalformedDocumentError && onMalformed) {
32001
- onMalformed({ id, reason: candidate.detail });
32002
- continue;
32003
- }
32004
- if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
32005
- onAliased({ id, reason: candidate.message });
32006
- continue;
32007
- }
32008
- throw candidate;
32017
+ function deniedReason(err) {
32018
+ const code2 = typeof err === "object" && err !== null ? err.code : void 0;
32019
+ return code2 === "EACCES" || code2 === "EPERM" ? permissionDeniedReason(code2) : void 0;
32020
+ }
32021
+ async function readManyExisting(backend, ids, onMalformed, onAliased, onDenied) {
32022
+ const tolerated = (err) => isEnoent(err) || err instanceof MalformedDocumentError && onMalformed !== void 0 || err instanceof FilesystemIdentityAliasError && onAliased !== void 0 || deniedReason(err) !== void 0 && onDenied !== void 0;
32023
+ const readOne = async (id) => {
32024
+ try {
32025
+ return [await backend.read(id)];
32026
+ } catch (candidate) {
32027
+ if (isEnoent(candidate)) return [];
32028
+ if (candidate instanceof MalformedDocumentError && onMalformed) {
32029
+ onMalformed({ id, reason: candidate.detail });
32030
+ return [];
32031
+ }
32032
+ if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
32033
+ onAliased({ id, reason: candidate.message });
32034
+ return [];
32035
+ }
32036
+ const reason = deniedReason(candidate);
32037
+ if (reason !== void 0 && onDenied) {
32038
+ onDenied({ id, reason });
32039
+ return [];
32009
32040
  }
32041
+ throw candidate;
32010
32042
  }
32011
- return out;
32012
- }
32043
+ };
32044
+ const readBatch = async (batch) => {
32045
+ try {
32046
+ return await backend.readMany(batch);
32047
+ } catch (err) {
32048
+ if (!tolerated(err)) throw err;
32049
+ if (batch.length === 1) return readOne(batch[0]);
32050
+ const half = Math.ceil(batch.length / 2);
32051
+ return [...await readBatch(batch.slice(0, half)), ...await readBatch(batch.slice(half))];
32052
+ }
32053
+ };
32054
+ return readBatch(ids);
32013
32055
  }
32014
32056
 
32015
32057
  // ../core/src/engine.ts
@@ -32033,7 +32075,7 @@ async function readDocVersioned(backend, id) {
32033
32075
  async function scanMatching(backend, filter, onSkip) {
32034
32076
  const ids = await backend.list(filter.prefix);
32035
32077
  const results = [];
32036
- for (const result of await readManyExisting(backend, ids, onSkip)) {
32078
+ for (const result of await readManyExisting(backend, ids, onSkip, void 0, onSkip)) {
32037
32079
  if (matchesFilter(result.doc, filter)) results.push(result);
32038
32080
  }
32039
32081
  results.sort((a, b) => compareCodeUnits(a.doc.id, b.doc.id));
@@ -32158,7 +32200,7 @@ function assertStorableReservedWrite(dir, name, content3) {
32158
32200
  // ../core/src/filesystem-identity.ts
32159
32201
  import { promises as fs2 } from "node:fs";
32160
32202
  import path2 from "node:path";
32161
- import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
32203
+ import { createHash as createHash2 } from "node:crypto";
32162
32204
 
32163
32205
  // ../core/src/filesystem-lock.ts
32164
32206
  import { promises as fs, realpathSync } from "node:fs";
@@ -32213,6 +32255,8 @@ import { createHash, randomUUID } from "node:crypto";
32213
32255
  var OWNER_FILE = "owner.json";
32214
32256
  var DEFAULT_WAIT_MS = 5e3;
32215
32257
  var DEFAULT_POLL_MS = 25;
32258
+ var UNREADABLE_OWNER_POLL_MS = 2e3;
32259
+ var ABANDONED_CLAIM_AGE_MS = 2e3;
32216
32260
  var FilesystemMutationLockError = class extends Error {
32217
32261
  lockPath;
32218
32262
  owner;
@@ -32300,9 +32344,13 @@ async function readOwnerRecord(lockPath) {
32300
32344
  return owner === null ? { state: "absent" } : { state: "record", owner };
32301
32345
  }
32302
32346
  async function resolveOwnerRecord(lockPath, started, waitMs, pollMs) {
32347
+ let firstUnreadable;
32303
32348
  while (true) {
32304
32349
  const record3 = await readOwnerRecord(lockPath);
32305
- if (record3.state !== "unreadable" || Date.now() - started >= waitMs) return record3;
32350
+ if (record3.state !== "unreadable") return record3;
32351
+ const now = Date.now();
32352
+ firstUnreadable ??= now;
32353
+ if (now - started >= waitMs || now - firstUnreadable >= UNREADABLE_OWNER_POLL_MS) return record3;
32306
32354
  await delay(pollMs);
32307
32355
  }
32308
32356
  }
@@ -32353,6 +32401,17 @@ async function quarantineStaleLock(lockPath, owner, policy) {
32353
32401
  throw err;
32354
32402
  }
32355
32403
  }
32404
+ async function removeAbandonedClaim(lockPath) {
32405
+ try {
32406
+ const info = await fs.lstat(lockPath);
32407
+ const age = Date.now() - info.mtimeMs;
32408
+ if (!info.isDirectory() || age < ABANDONED_CLAIM_AGE_MS) return false;
32409
+ await fs.rmdir(lockPath);
32410
+ return true;
32411
+ } catch {
32412
+ return false;
32413
+ }
32414
+ }
32356
32415
  function delay(ms) {
32357
32416
  return new Promise((resolve) => setTimeout(resolve, ms));
32358
32417
  }
@@ -32403,7 +32462,7 @@ async function timeoutError(lockPath, snapshot, guarded) {
32403
32462
  const sameHost = owner?.hostname === hostname();
32404
32463
  let message;
32405
32464
  if (malformed) {
32406
- message = `timed out waiting for filesystem mutation lock '${lockPath}'; its owner metadata is missing or malformed. Inspect and remove the lock only after confirming no process is mutating the target, then retry.`;
32465
+ message = `timed out waiting for filesystem mutation lock '${lockPath}'; its owner metadata is missing or malformed. Remove the lock directory once no Superbee process is running, then retry.`;
32407
32466
  } else if (stale) {
32408
32467
  message = `stale filesystem mutation lock '${lockPath}' belongs to absent PID ${owner.pid} on ${owner.hostname}. Inspect and remove the lock, then retry.`;
32409
32468
  } else if (!sameHost) {
@@ -32452,7 +32511,9 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
32452
32511
  } else {
32453
32512
  unwitnessedRetryUsed = false;
32454
32513
  }
32455
- let existingOwner = await readOwner(lockPath);
32514
+ const existing = await readOwnerRecord(lockPath);
32515
+ let existingOwner = existing.state === "record" ? existing.owner : null;
32516
+ if (existing.state === "absent" && await removeAbandonedClaim(lockPath)) continue;
32456
32517
  if (existingOwner !== null) {
32457
32518
  if (await quarantineStaleLock(lockPath, existingOwner, policy)) continue;
32458
32519
  existingOwner = await readOwner(lockPath);
@@ -32472,14 +32533,13 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
32472
32533
  });
32473
32534
  } catch (err) {
32474
32535
  const code2 = err.code;
32475
- if (code2 === "EEXIST") {
32536
+ if (code2 === "EEXIST" || code2 === "ENOENT" || code2 === "ENOTDIR" || await claimDirectoryLost(lockPath, claimed)) {
32476
32537
  heldTokens.delete(owner.token);
32477
32538
  continue;
32478
32539
  }
32479
32540
  await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => {
32480
32541
  });
32481
32542
  heldTokens.delete(owner.token);
32482
- if (code2 === "ENOENT" || code2 === "ENOTDIR") continue;
32483
32543
  throw err;
32484
32544
  }
32485
32545
  let inFlight;
@@ -32509,9 +32569,18 @@ async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy
32509
32569
  return;
32510
32570
  }
32511
32571
  if (record3.state === "unreadable" || !await isClaimedDirectory(lockPath, claimed)) return;
32512
- const remnant = releasedLockRemnantPath(lockPath, owner);
32513
- await fs.rename(lockPath, remnant);
32514
- await fs.rm(remnant, { recursive: true, force: true });
32572
+ await fs.rmdir(lockPath);
32573
+ }
32574
+ async function claimDirectoryLost(lockPath, claimed) {
32575
+ try {
32576
+ const current = await fs.lstat(lockPath, { bigint: true });
32577
+ if (claimed === null) return false;
32578
+ const reborn = claimed.birthtimeNs > 0n && current.birthtimeNs > 0n && current.birthtimeNs !== claimed.birthtimeNs;
32579
+ return current.dev !== claimed.dev || current.ino !== claimed.ino || reborn;
32580
+ } catch (err) {
32581
+ const code2 = err.code;
32582
+ return code2 === "ENOENT" || code2 === "ENOTDIR";
32583
+ }
32515
32584
  }
32516
32585
  async function isClaimedDirectory(lockPath, claimed) {
32517
32586
  if (claimed === null || claimed.birthtimeNs <= 0n) return false;
@@ -32613,6 +32682,12 @@ function foldSegment(segment) {
32613
32682
  return segment.normalize("NFKD").toLowerCase().toUpperCase().toLowerCase();
32614
32683
  }
32615
32684
 
32685
+ // ../core/src/filesystem-temp-name.ts
32686
+ import { randomUUID as randomUUID2 } from "node:crypto";
32687
+ function identityTempName(leaf) {
32688
+ return `.${leaf}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
32689
+ }
32690
+
32616
32691
  // ../core/src/filesystem-identity.ts
32617
32692
  async function replaceExistingLeaf(port, tmp, target, rel) {
32618
32693
  try {
@@ -32757,10 +32832,32 @@ async function probeSegment(port, candidate, rel, segment) {
32757
32832
  function hasExact(listing, segment) {
32758
32833
  return listing.some((entry) => entry.name === segment);
32759
32834
  }
32835
+ function existingSpelling(names, segment) {
32836
+ const folded = foldSegment(segment);
32837
+ const matches = [...names].filter((name) => name !== segment && foldSegment(name) === folded);
32838
+ return matches.length === 1 ? matches[0] : void 0;
32839
+ }
32840
+ function entryNoun(kind2, leaf) {
32841
+ if (kind2 === void 0) return leaf ? "file" : "folder";
32842
+ return kind2 === "directory" ? "folder" : "file";
32843
+ }
32844
+ function aliasRefusal(rel, segments, index2, names, kind2) {
32845
+ const segment = segments[index2];
32846
+ return new FilesystemIdentityAliasError(rel, segment, existingSpelling(names, segment), entryNoun(kind2, index2 === segments.length - 1), index2);
32847
+ }
32848
+ function aliasRefusalFromListing(rel, index2, listing, leaf) {
32849
+ const segment = relSegments(rel)[index2];
32850
+ const existing = existingSpelling(listing.map((entry) => entry.name), segment);
32851
+ const kind2 = existing === void 0 ? void 0 : listing.find((entry) => entry.name === existing)?.kind;
32852
+ return new FilesystemIdentityAliasError(rel, segment, existing, entryNoun(kind2, leaf), index2);
32853
+ }
32854
+ function listingView(names) {
32855
+ return { has: (name) => names.has(name), names: () => names };
32856
+ }
32760
32857
  function freshListings(port) {
32761
32858
  return async (dir) => {
32762
32859
  const listing = await port.entries(dir);
32763
- return listing === null ? null : { has: (name) => hasExact(listing, name) };
32860
+ return listing === null ? null : { has: (name) => hasExact(listing, name), names: () => listing.map((entry) => entry.name) };
32764
32861
  };
32765
32862
  }
32766
32863
  async function walkExact(port, rootResolved, segments, rel) {
@@ -32772,7 +32869,7 @@ async function walkExact(port, rootResolved, segments, rel) {
32772
32869
  if (probed === null) return { state: "absent", depth: index2 };
32773
32870
  const listing = await port.entries(parent);
32774
32871
  if (listing === null) return { state: "absent", depth: index2 };
32775
- if (!hasExact(listing, segment)) throw new FilesystemIdentityAliasError(rel, segment);
32872
+ if (!hasExact(listing, segment)) throw aliasRefusal(rel, segments, index2, listing.map((entry) => entry.name), probed.kind);
32776
32873
  if (index2 === segments.length - 1) return { state: "exact", leaf: probed };
32777
32874
  if (probed.kind !== "directory") return { state: "shape-mismatch", segment };
32778
32875
  parent = candidate;
@@ -32787,35 +32884,53 @@ async function recordWalk(port, rootResolved, segments, rel) {
32787
32884
  const segment = segments[index2];
32788
32885
  const candidate = path2.join(parent, segment);
32789
32886
  const probed = await probeSegment(port, candidate, rel, segment);
32790
- if (probed === null) return null;
32791
- if (index2 < segments.length - 1 && probed.kind !== "directory") return null;
32887
+ if (probed === null) return { recorded, complete: false };
32792
32888
  recorded.push(probed);
32889
+ if (index2 < segments.length - 1 && probed.kind !== "directory") return { recorded, complete: false };
32793
32890
  parent = candidate;
32794
32891
  }
32795
- return recorded;
32892
+ return { recorded, complete: true };
32796
32893
  }
32797
- async function confirmedWalk(port, rootResolved, segments, rel, recorded, handle, listings = freshListings(port)) {
32894
+ async function confirmedWalk(port, rootResolved, segments, rel, recorded, handle, listings = freshListings(port), stopAt = segments.length) {
32798
32895
  let parent = rootResolved;
32799
- for (let index2 = 0; index2 < segments.length; index2++) {
32896
+ for (let index2 = 0; index2 < stopAt; index2++) {
32800
32897
  const segment = segments[index2];
32801
32898
  const candidate = path2.join(parent, segment);
32802
32899
  const listing = await listings(parent);
32803
32900
  if (listing === null) return "absent";
32804
32901
  const listed = listing.has(segment);
32805
32902
  if (handle !== null && index2 === segments.length - 1) {
32806
- const verdict = classifyLeaf({ listed, probe: await probeSegment(port, candidate, rel, segment), handle });
32807
- if (verdict === "aliased") throw new FilesystemIdentityAliasError(rel, segment);
32903
+ const probe = await probeSegment(port, candidate, rel, segment);
32904
+ const verdict = classifyLeaf({ listed, probe, handle });
32905
+ if (verdict === "aliased") throw aliasRefusal(rel, segments, index2, listing.names(), probe?.kind);
32808
32906
  return verdict === "replaced" ? "restart" : verdict;
32809
32907
  }
32810
32908
  if (index2 < segments.length - 1 || !listed) {
32811
- const verdict = confirmAlias(listed, await probeSegment(port, candidate, rel, segment), recorded[index2]);
32812
- if (verdict === "aliased") throw new FilesystemIdentityAliasError(rel, segment);
32909
+ const probe = await probeSegment(port, candidate, rel, segment);
32910
+ const verdict = confirmAlias(listed, probe, recorded[index2]);
32911
+ if (verdict === "aliased") throw aliasRefusal(rel, segments, index2, listing.names(), probe?.kind);
32813
32912
  if (verdict !== "continue") return verdict;
32814
32913
  }
32815
32914
  parent = candidate;
32816
32915
  }
32817
32916
  return "continue";
32818
32917
  }
32918
+ async function confirmPrefix(port, rootResolved, segments, rel, recorded) {
32919
+ if (recorded.length === 0) return "absent";
32920
+ let kept = prefixListings.get(port);
32921
+ if (kept === void 0) prefixListings.set(port, kept = /* @__PURE__ */ new Map());
32922
+ const cached = witnessedListings(port, kept, Date.now, PREFIX_LISTING_CAPACITY);
32923
+ let verdict;
32924
+ try {
32925
+ verdict = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, cached.source, recorded.length);
32926
+ } catch (error) {
32927
+ if (!cached.reused()) throw error;
32928
+ kept.clear();
32929
+ verdict = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, freshListings(port), recorded.length);
32930
+ }
32931
+ if (verdict === "restart" && cached.reused()) kept.clear();
32932
+ return verdict === "restart" ? "restart" : "absent";
32933
+ }
32819
32934
  function countRestart(restarts, rel) {
32820
32935
  if (restarts >= MAX_RESTARTS) throw new ConcurrentReplacementError(rel, restarts + 1);
32821
32936
  return restarts + 1;
@@ -32827,8 +32942,14 @@ async function observeWith(port, rootResolved, rel, read, listings, onExact) {
32827
32942
  const segments = relSegments(rel);
32828
32943
  const target = path2.join(rootResolved, ...segments);
32829
32944
  for (let restarts = 0; ; ) {
32830
- const recorded = await recordWalk(port, rootResolved, segments, rel);
32831
- if (recorded === null) return { state: "absent" };
32945
+ const walk = await recordWalk(port, rootResolved, segments, rel);
32946
+ if (!walk.complete) {
32947
+ const prefix = await confirmPrefix(port, rootResolved, segments, rel, walk.recorded);
32948
+ if (prefix === "absent") return { state: "absent" };
32949
+ restarts = countRestart(restarts, rel);
32950
+ continue;
32951
+ }
32952
+ const recorded = walk.recorded;
32832
32953
  const verified = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, listings);
32833
32954
  if (verified === "absent") return { state: "absent" };
32834
32955
  if (verified === "restart") {
@@ -32867,8 +32988,14 @@ async function probeExact(port, root, rel) {
32867
32988
  const segments = relSegments(rel);
32868
32989
  const rootResolved = path2.resolve(root);
32869
32990
  for (let restarts = 0; ; ) {
32870
- const recorded = await recordWalk(port, rootResolved, segments, rel);
32871
- if (recorded === null) return { state: "absent" };
32991
+ const walk = await recordWalk(port, rootResolved, segments, rel);
32992
+ if (!walk.complete) {
32993
+ const prefix = await confirmPrefix(port, rootResolved, segments, rel, walk.recorded);
32994
+ if (prefix === "absent") return { state: "absent" };
32995
+ restarts = countRestart(restarts, rel);
32996
+ continue;
32997
+ }
32998
+ const recorded = walk.recorded;
32872
32999
  const leaf = recorded[recorded.length - 1];
32873
33000
  const verified = await confirmedWalk(port, rootResolved, segments, rel, recorded, null);
32874
33001
  const verdict = verified === "continue" ? await confirmedWalk(port, rootResolved, segments, rel, recorded, leaf) : verified;
@@ -32878,45 +33005,56 @@ async function probeExact(port, root, rel) {
32878
33005
  }
32879
33006
  }
32880
33007
  var SETTLED_AFTER_NS = 3000000000n;
33008
+ function witnessCanDetectChange(witness) {
33009
+ return witness.mtimeNs > 0n && witness.ctimeNs > 0n;
33010
+ }
32881
33011
  function witnessKey(witness) {
32882
33012
  return `${witness.dev}:${witness.ino}:${witness.mtimeNs}:${witness.ctimeNs}`;
32883
33013
  }
32884
- function openObservationPass(port, root, now = Date.now) {
32885
- const rootResolved = path2.resolve(root);
32886
- const kept = /* @__PURE__ */ new Map();
32887
- const reusedVerdicts = [];
32888
- const fresh = freshListings(port);
32889
- const witnessOf = port.directoryWitness?.bind(port);
33014
+ function witnessedListings(port, kept, now, capacity = Infinity) {
33015
+ let reused = false;
33016
+ const portWitness = port.directoryWitness?.bind(port);
33017
+ if (portWitness === void 0) return { source: freshListings(port), reused: () => false };
33018
+ const witnessOf = async (dir) => {
33019
+ const witness = await portWitness(dir);
33020
+ return witness !== null && witnessCanDetectChange(witness) ? witness : null;
33021
+ };
32890
33022
  const settled = (witness) => {
32891
33023
  const nowNs = BigInt(Math.floor(now())) * 1000000n;
32892
33024
  const latest = witness.mtimeNs > witness.ctimeNs ? witness.mtimeNs : witness.ctimeNs;
32893
33025
  return nowNs - latest >= SETTLED_AFTER_NS;
32894
33026
  };
32895
- const listingsFor = () => {
32896
- let reused = false;
32897
- if (witnessOf === void 0) return { source: fresh, reused: () => false };
32898
- const source = async (dir) => {
32899
- const keep = kept.get(dir);
32900
- if (keep !== void 0) {
32901
- const current = await witnessOf(dir);
32902
- if (current !== null && witnessKey(current) === keep.witness) {
32903
- reused = true;
32904
- return keep.names;
32905
- }
32906
- kept.delete(dir);
32907
- }
32908
- const before = await witnessOf(dir);
32909
- const listing = await port.entries(dir);
32910
- if (listing === null) return null;
32911
- const names = new Set(listing.map((entry) => entry.name));
32912
- const after = await witnessOf(dir);
32913
- if (before !== null && after !== null && witnessKey(before) === witnessKey(after) && settled(after)) {
32914
- kept.set(dir, { witness: witnessKey(after), names });
32915
- }
32916
- return names;
32917
- };
32918
- return { source, reused: () => reused };
33027
+ const source = async (dir) => {
33028
+ const keep = kept.get(dir);
33029
+ if (keep !== void 0) {
33030
+ const current = await witnessOf(dir);
33031
+ if (current !== null && witnessKey(current) === keep.witness) {
33032
+ reused = true;
33033
+ return listingView(keep.names);
33034
+ }
33035
+ kept.delete(dir);
33036
+ }
33037
+ const before = await witnessOf(dir);
33038
+ const listing = await port.entries(dir);
33039
+ if (listing === null) return null;
33040
+ const names = new Set(listing.map((entry) => entry.name));
33041
+ const after = await witnessOf(dir);
33042
+ if (before !== null && after !== null && witnessKey(before) === witnessKey(after) && settled(after)) {
33043
+ kept.delete(dir);
33044
+ kept.set(dir, { witness: witnessKey(after), names });
33045
+ while (kept.size > capacity) kept.delete(kept.keys().next().value);
33046
+ }
33047
+ return listingView(names);
32919
33048
  };
33049
+ return { source, reused: () => reused };
33050
+ }
33051
+ var prefixListings = /* @__PURE__ */ new WeakMap();
33052
+ var PREFIX_LISTING_CAPACITY = 64;
33053
+ function openObservationPass(port, root, now = Date.now) {
33054
+ const rootResolved = path2.resolve(root);
33055
+ const kept = /* @__PURE__ */ new Map();
33056
+ const reusedVerdicts = [];
33057
+ const listingsFor = () => witnessedListings(port, kept, now);
32920
33058
  return {
32921
33059
  async observe(rel, read) {
32922
33060
  const listings = listingsFor();
@@ -33019,7 +33157,7 @@ async function readWhole(port, target) {
33019
33157
  await port.close(opened.handle);
33020
33158
  }
33021
33159
  }
33022
- async function mkdirExact(port, parent, segment, isTail, rel) {
33160
+ async function mkdirExact(port, parent, segment, isTail, rel, index2 = -1) {
33023
33161
  const outcome = await port.mkdir(path2.join(parent, segment));
33024
33162
  if (outcome === "created") return;
33025
33163
  const listing = await port.entries(parent);
@@ -33028,7 +33166,9 @@ async function mkdirExact(port, parent, segment, isTail, rel) {
33028
33166
  throw new FilesystemSymlinkEntryError(rel, segment);
33029
33167
  }
33030
33168
  const verdict = classifyMkdir(outcome, listing, segment, isTail);
33031
- if (verdict === "aliased") throw new FilesystemIdentityAliasError(rel, segment);
33169
+ if (verdict === "aliased") {
33170
+ throw aliasRefusalFromListing(rel, index2, listing, false);
33171
+ }
33032
33172
  if (verdict === "shape-mismatch") throw new FilesystemShapeMismatchError(rel, segment);
33033
33173
  }
33034
33174
  async function ensureExactDirectories(port, rootResolved, segments, existingDepth, rel) {
@@ -33048,8 +33188,9 @@ async function ensureExactDirectories(port, rootResolved, segments, existingDept
33048
33188
  }
33049
33189
  }
33050
33190
  let parent = path2.join(rootResolved, ...segments.slice(0, existingDepth));
33051
- for (const segment of segments.slice(existingDepth, -1)) {
33052
- await mkdirExact(port, parent, segment, false, rel);
33191
+ for (let index2 = existingDepth; index2 < segments.length - 1; index2++) {
33192
+ const segment = segments[index2];
33193
+ await mkdirExact(port, parent, segment, false, rel, index2);
33053
33194
  parent = path2.join(parent, segment);
33054
33195
  }
33055
33196
  }
@@ -33067,7 +33208,7 @@ async function createExactLeaf(port, tmp, target, rel, leaf, rootResolved) {
33067
33208
  }
33068
33209
  const listing = await port.entries(path2.dirname(target)) ?? [];
33069
33210
  if (hasExact(listing, leaf)) throw new ConcurrentReplacementError(rel, 1);
33070
- throw new FilesystemIdentityAliasError(rel, leaf);
33211
+ throw aliasRefusalFromListing(rel, relSegments(rel).length - 1, listing, true);
33071
33212
  }
33072
33213
  async function mutateExact(port, root, rel, body) {
33073
33214
  const segments = relSegments(rel);
@@ -33089,11 +33230,13 @@ async function mutateExact(port, root, rel, body) {
33089
33230
  async replace(bytes) {
33090
33231
  await ensureExactDirectories(port, rootResolved, segments, existingDepth, rel);
33091
33232
  const dir = path2.dirname(target);
33092
- const tmpName = `.${leaf}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
33233
+ const tmpName = identityTempName(leaf);
33093
33234
  const tmp = path2.join(dir, tmpName);
33094
33235
  await port.writeTemp(dir, tmpName, bytes);
33095
33236
  try {
33096
33237
  if (realized.state === "exact") {
33238
+ await port.copyMode?.(target, tmp).catch(() => {
33239
+ });
33097
33240
  await replaceExistingLeaf(port, tmp, target, rel);
33098
33241
  return;
33099
33242
  }
@@ -33253,6 +33396,9 @@ function createNodeFilesystemIdentityPort(hostPolicy) {
33253
33396
  async rename(from, to) {
33254
33397
  await fs2.rename(from, to);
33255
33398
  },
33399
+ async copyMode(from, to) {
33400
+ await fs2.chmod(to, (await fs2.lstat(from)).mode & 511);
33401
+ },
33256
33402
  async unlink(target) {
33257
33403
  await fs2.unlink(target);
33258
33404
  },
@@ -33275,6 +33421,7 @@ function blobVersion(bytes) {
33275
33421
 
33276
33422
  // ../core/src/backend.ts
33277
33423
  var publicationContexts = /* @__PURE__ */ new WeakMap();
33424
+ var fileMutations = /* @__PURE__ */ new WeakMap();
33278
33425
  function firstString(...vals) {
33279
33426
  for (const v of vals) {
33280
33427
  if (typeof v === "string" && v.trim() !== "") return v;
@@ -33391,6 +33538,7 @@ var FilesystemBackend = class _FilesystemBackend {
33391
33538
  this.#root = path3.resolve(root);
33392
33539
  this.#coordinator = createFilesystemMutationCoordinator(this.#port, this.#root, options.hostPolicy);
33393
33540
  publicationContexts.set(this, { root: this.#root, port: this.#port });
33541
+ fileMutations.set(this, (rel, body) => this.#mutateExact(rel, body));
33394
33542
  }
33395
33543
  async read(id) {
33396
33544
  return this.#readWithEdition(id, () => this.#readEdition());
@@ -33776,6 +33924,7 @@ var KIND_FIELD_TYPE_SET = new Set(KIND_FIELD_TYPES);
33776
33924
  function isKindFieldType(value) {
33777
33925
  return typeof value === "string" && KIND_FIELD_TYPE_SET.has(value);
33778
33926
  }
33927
+ var UNDECLARED_FIELDS_POLICIES = ["warn", "error"];
33779
33928
  function buildKindRegistry(docs, initialWarnings = [], options = {}) {
33780
33929
  const accepted = /* @__PURE__ */ new Map();
33781
33930
  const warnings = [...initialWarnings];
@@ -34370,6 +34519,19 @@ function parseConventionDoc(doc, options = {}) {
34370
34519
  });
34371
34520
  }
34372
34521
  }
34522
+ let undeclaredFields;
34523
+ if (fm.undeclared_fields !== void 0) {
34524
+ if (UNDECLARED_FIELDS_POLICIES.includes(fm.undeclared_fields)) {
34525
+ undeclaredFields = fm.undeclared_fields;
34526
+ } else {
34527
+ warnings.push({
34528
+ code: "KIND_CONVENTION_BAD_SHAPE",
34529
+ message: `kind convention '${doc.id}' has an invalid 'undeclared_fields' (${describeShape(fm.undeclared_fields)}; expected warn or error); using warn.`,
34530
+ field: "undeclared_fields",
34531
+ severity: "warning"
34532
+ });
34533
+ }
34534
+ }
34373
34535
  const kind2 = {
34374
34536
  id: doc.id,
34375
34537
  title,
@@ -34387,6 +34549,7 @@ function parseConventionDoc(doc, options = {}) {
34387
34549
  if (browseCollapsed !== void 0) kind2.browseCollapsed = browseCollapsed;
34388
34550
  if (order !== void 0) kind2.order = order;
34389
34551
  if (claim !== void 0) kind2.claim = claim;
34552
+ if (undeclaredFields !== void 0) kind2.undeclaredFields = undeclaredFields;
34390
34553
  if (Object.hasOwn(fm, "deprecated")) {
34391
34554
  kind2.deprecated = parseKindDeprecation(fm.deprecated, kind2, RESERVED_KIND_FIELD_NAMES, claimCoordinates(options.okfVersion, kind2)?.fields ?? []);
34392
34555
  warnings.push(...kind2.deprecated.problems ?? []);
@@ -34463,7 +34626,7 @@ async function loadKinds(bundle) {
34463
34626
  const docs = await query2(bundle, { prefix: CONVENTIONS_PREFIX, type: CONVENTION_TYPE }, {
34464
34627
  onSkip: ({ id, reason }) => warnings.push({
34465
34628
  code: "KIND_CONVENTION_MALFORMED",
34466
- message: `skipped kind convention '${id}' with unparseable frontmatter: ${reason}`,
34629
+ message: isPermissionDeniedSkip({ reason }) ? `skipped kind convention '${id}' that could not be read: ${reason}` : `skipped kind convention '${id}' with unparseable frontmatter: ${reason}`,
34467
34630
  field: id,
34468
34631
  severity: "warning"
34469
34632
  })
@@ -44601,12 +44764,27 @@ var CAPABILITIES = [
44601
44764
  ];
44602
44765
  async function authorizePublicationRoot(requested) {
44603
44766
  const entry = await lstat(requested);
44604
- if (entry.isSymbolicLink() || !entry.isDirectory()) {
44767
+ if (entry.isSymbolicLink()) {
44768
+ const target = await realpath(requested).catch(() => void 0);
44769
+ if (target === void 0) {
44770
+ throw new PublicationError("INVALID_OBJECT_IDENTITY", `the filesystem publication root ${JSON.stringify(requested)} is a symlink that does not resolve to a directory`, { actual: requested });
44771
+ }
44772
+ throw new PublicationError(
44773
+ "INVALID_OBJECT_IDENTITY",
44774
+ `the filesystem publication root ${JSON.stringify(requested)} is a symlink to ${JSON.stringify(target)}; capture does not follow symlinks, so pass the resolved path (await fs.realpath(root))`,
44775
+ { expected: target, actual: requested }
44776
+ );
44777
+ }
44778
+ if (!entry.isDirectory()) {
44605
44779
  throw new PublicationError("INVALID_OBJECT_IDENTITY", "the filesystem publication root must be a real directory");
44606
44780
  }
44607
44781
  const canonical2 = await realpath(requested);
44608
44782
  if (canonical2 !== requested) {
44609
- throw new PublicationError("INVALID_OBJECT_IDENTITY", "the filesystem publication root must not be a symlink or aliased path");
44783
+ throw new PublicationError(
44784
+ "INVALID_OBJECT_IDENTITY",
44785
+ `the filesystem publication root ${JSON.stringify(requested)} is not in canonical form (it resolves to ${JSON.stringify(canonical2)}); capture does not follow symlinks or normalize paths, so pass the resolved path (await fs.realpath(root))`,
44786
+ { expected: canonical2, actual: requested }
44787
+ );
44610
44788
  }
44611
44789
  const identity = await stat(requested);
44612
44790
  return { requested, canonical: canonical2, dev: identity.dev, ino: identity.ino };