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.
@@ -2890,15 +2890,30 @@ var FrontmatterRefusedError = class extends InvalidInputError {
2890
2890
  }
2891
2891
  };
2892
2892
  var FilesystemIdentityAliasError = class extends InvalidInputError {
2893
+ reason = "filesystem_alias";
2893
2894
  rel;
2894
2895
  segment;
2895
- constructor(rel, segment) {
2896
+ existing;
2897
+ suggestedPath;
2898
+ /**
2899
+ * `noun` names the existing entry ("folder" or "file"); `index` is the position of `segment`
2900
+ * in `rel`. Both default from the position of the first segment spelled `segment`.
2901
+ */
2902
+ constructor(rel, segment, existing, noun, index) {
2903
+ const segments = rel.split("/").filter((part) => part !== "" && part !== ".");
2904
+ const at = index ?? segments.indexOf(segment);
2905
+ const kind = noun ?? (at === segments.length - 1 ? "file" : "folder");
2906
+ const clash = existing === void 0 ? `but a ${kind} with that name in a different case or Unicode form already exists` : existing.normalize("NFC") === segment.normalize("NFC") ? `but ${kind} '${existing}' already exists, written in a different Unicode form` : `but ${kind} '${existing}' already exists`;
2907
+ 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";
2896
2908
  super(
2897
- `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.`
2909
+ `Path '${rel}' uses ${kind} '${segment}', ${clash}, and this disk treats names that differ only by ${difference} as the same name. Use the existing spelling.`
2898
2910
  );
2899
2911
  this.name = "FilesystemIdentityAliasError";
2900
2912
  this.rel = rel;
2901
2913
  this.segment = segment;
2914
+ this.existing = existing;
2915
+ const unusable = existing === void 0 || at < 0 || segments[at] !== segment || kind === "file" && at < segments.length - 1 || existing.normalize("NFC") === segment.normalize("NFC");
2916
+ this.suggestedPath = unusable ? void 0 : segments.map((part, position) => position === at ? existing : part).join("/");
2902
2917
  }
2903
2918
  };
2904
2919
  var ConcurrentReplacementError = class extends Error {
@@ -3103,6 +3118,9 @@ function delimiterLineEnd(input, offset) {
3103
3118
  if (input[suffix] === "\r" && input[suffix + 1] === "\n") return suffix + 2;
3104
3119
  return null;
3105
3120
  }
3121
+ function startsWithFrontmatterSyntax(text) {
3122
+ return text.startsWith("\uFEFF") || delimiterLineEnd(text, 0) !== null;
3123
+ }
3106
3124
  function splitLeadingFrontmatter(raw, context) {
3107
3125
  const input = raw.startsWith("\uFEFF") ? raw.slice(1) : raw;
3108
3126
  const openingEnd = delimiterLineEnd(input, 0);
@@ -3208,7 +3226,10 @@ function stringifyWithSerializedFrontmatter(dumped, body) {
3208
3226
  const content = body ?? "";
3209
3227
  const newline = (value) => value.endsWith("\n") ? value : `${value}
3210
3228
  `;
3211
- if (dumped === "{}") return normalizeDocumentBodyForStorage(content);
3229
+ if (dumped === "{}") {
3230
+ const stored = normalizeDocumentBodyForStorage(content);
3231
+ return `${startsWithFrontmatterSyntax(stored) ? "---\n---\n" : ""}${stored}`;
3232
+ }
3212
3233
  return `---
3213
3234
  ${newline(dumped)}---
3214
3235
  ${normalizeDocumentBodyForStorage(content)}`;
@@ -4019,36 +4040,57 @@ function hasSourceIdentity(sources, identity) {
4019
4040
  return Array.isArray(sources) && sources.some((entry) => matchesSourceIdentity(entry, identity));
4020
4041
  }
4021
4042
 
4043
+ // ../core/src/skip-reasons.ts
4044
+ var PERMISSION_DENIED_PREFIX = "permission denied (";
4045
+ function permissionDeniedReason(code) {
4046
+ return `${PERMISSION_DENIED_PREFIX}${code})`;
4047
+ }
4048
+ function isPermissionDeniedSkip(skip) {
4049
+ return skip.reason.startsWith(PERMISSION_DENIED_PREFIX);
4050
+ }
4051
+
4022
4052
  // ../core/src/document-scan.ts
4023
4053
  function isEnoent(err) {
4024
4054
  return typeof err === "object" && err !== null && err.code === "ENOENT";
4025
4055
  }
4026
- async function readManyExisting(backend, ids, onMalformed, onAliased) {
4027
- try {
4028
- return await backend.readMany(ids);
4029
- } catch (err) {
4030
- const malformed = err instanceof MalformedDocumentError && onMalformed !== void 0;
4031
- const aliased = err instanceof FilesystemIdentityAliasError && onAliased !== void 0;
4032
- if (!isEnoent(err) && !malformed && !aliased) throw err;
4033
- const out = [];
4034
- for (const id of ids) {
4035
- try {
4036
- out.push(await backend.read(id));
4037
- } catch (candidate) {
4038
- if (isEnoent(candidate)) continue;
4039
- if (candidate instanceof MalformedDocumentError && onMalformed) {
4040
- onMalformed({ id, reason: candidate.detail });
4041
- continue;
4042
- }
4043
- if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
4044
- onAliased({ id, reason: candidate.message });
4045
- continue;
4046
- }
4047
- throw candidate;
4056
+ function deniedReason(err) {
4057
+ const code = typeof err === "object" && err !== null ? err.code : void 0;
4058
+ return code === "EACCES" || code === "EPERM" ? permissionDeniedReason(code) : void 0;
4059
+ }
4060
+ async function readManyExisting(backend, ids, onMalformed, onAliased, onDenied) {
4061
+ const tolerated = (err) => isEnoent(err) || err instanceof MalformedDocumentError && onMalformed !== void 0 || err instanceof FilesystemIdentityAliasError && onAliased !== void 0 || deniedReason(err) !== void 0 && onDenied !== void 0;
4062
+ const readOne = async (id) => {
4063
+ try {
4064
+ return [await backend.read(id)];
4065
+ } catch (candidate) {
4066
+ if (isEnoent(candidate)) return [];
4067
+ if (candidate instanceof MalformedDocumentError && onMalformed) {
4068
+ onMalformed({ id, reason: candidate.detail });
4069
+ return [];
4070
+ }
4071
+ if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
4072
+ onAliased({ id, reason: candidate.message });
4073
+ return [];
4048
4074
  }
4075
+ const reason = deniedReason(candidate);
4076
+ if (reason !== void 0 && onDenied) {
4077
+ onDenied({ id, reason });
4078
+ return [];
4079
+ }
4080
+ throw candidate;
4049
4081
  }
4050
- return out;
4051
- }
4082
+ };
4083
+ const readBatch = async (batch) => {
4084
+ try {
4085
+ return await backend.readMany(batch);
4086
+ } catch (err) {
4087
+ if (!tolerated(err)) throw err;
4088
+ if (batch.length === 1) return readOne(batch[0]);
4089
+ const half = Math.ceil(batch.length / 2);
4090
+ return [...await readBatch(batch.slice(0, half)), ...await readBatch(batch.slice(half))];
4091
+ }
4092
+ };
4093
+ return readBatch(ids);
4052
4094
  }
4053
4095
 
4054
4096
  // ../core/src/engine.ts
@@ -4072,7 +4114,7 @@ async function readDocVersioned(backend, id) {
4072
4114
  async function scanMatching(backend, filter, onSkip) {
4073
4115
  const ids = await backend.list(filter.prefix);
4074
4116
  const results = [];
4075
- for (const result of await readManyExisting(backend, ids, onSkip)) {
4117
+ for (const result of await readManyExisting(backend, ids, onSkip, void 0, onSkip)) {
4076
4118
  if (matchesFilter(result.doc, filter)) results.push(result);
4077
4119
  }
4078
4120
  results.sort((a, b) => compareCodeUnits(a.doc.id, b.doc.id));
@@ -4197,7 +4239,7 @@ function assertStorableReservedWrite(dir, name, content) {
4197
4239
  // ../core/src/filesystem-identity.ts
4198
4240
  import { promises as fs2 } from "node:fs";
4199
4241
  import path2 from "node:path";
4200
- import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
4242
+ import { createHash as createHash2 } from "node:crypto";
4201
4243
 
4202
4244
  // ../core/src/filesystem-lock.ts
4203
4245
  import { promises as fs, realpathSync } from "node:fs";
@@ -4252,6 +4294,8 @@ import { createHash, randomUUID } from "node:crypto";
4252
4294
  var OWNER_FILE = "owner.json";
4253
4295
  var DEFAULT_WAIT_MS = 5e3;
4254
4296
  var DEFAULT_POLL_MS = 25;
4297
+ var UNREADABLE_OWNER_POLL_MS = 2e3;
4298
+ var ABANDONED_CLAIM_AGE_MS = 2e3;
4255
4299
  var FilesystemMutationLockError = class extends Error {
4256
4300
  lockPath;
4257
4301
  owner;
@@ -4339,9 +4383,13 @@ async function readOwnerRecord(lockPath) {
4339
4383
  return owner === null ? { state: "absent" } : { state: "record", owner };
4340
4384
  }
4341
4385
  async function resolveOwnerRecord(lockPath, started, waitMs, pollMs) {
4386
+ let firstUnreadable;
4342
4387
  while (true) {
4343
4388
  const record2 = await readOwnerRecord(lockPath);
4344
- if (record2.state !== "unreadable" || Date.now() - started >= waitMs) return record2;
4389
+ if (record2.state !== "unreadable") return record2;
4390
+ const now = Date.now();
4391
+ firstUnreadable ??= now;
4392
+ if (now - started >= waitMs || now - firstUnreadable >= UNREADABLE_OWNER_POLL_MS) return record2;
4345
4393
  await delay(pollMs);
4346
4394
  }
4347
4395
  }
@@ -4392,6 +4440,17 @@ async function quarantineStaleLock(lockPath, owner, policy) {
4392
4440
  throw err;
4393
4441
  }
4394
4442
  }
4443
+ async function removeAbandonedClaim(lockPath) {
4444
+ try {
4445
+ const info = await fs.lstat(lockPath);
4446
+ const age = Date.now() - info.mtimeMs;
4447
+ if (!info.isDirectory() || age < ABANDONED_CLAIM_AGE_MS) return false;
4448
+ await fs.rmdir(lockPath);
4449
+ return true;
4450
+ } catch {
4451
+ return false;
4452
+ }
4453
+ }
4395
4454
  function delay(ms) {
4396
4455
  return new Promise((resolve) => setTimeout(resolve, ms));
4397
4456
  }
@@ -4442,7 +4501,7 @@ async function timeoutError(lockPath, snapshot, guarded) {
4442
4501
  const sameHost = owner?.hostname === hostname();
4443
4502
  let message;
4444
4503
  if (malformed) {
4445
- 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.`;
4504
+ 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.`;
4446
4505
  } else if (stale) {
4447
4506
  message = `stale filesystem mutation lock '${lockPath}' belongs to absent PID ${owner.pid} on ${owner.hostname}. Inspect and remove the lock, then retry.`;
4448
4507
  } else if (!sameHost) {
@@ -4491,7 +4550,9 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
4491
4550
  } else {
4492
4551
  unwitnessedRetryUsed = false;
4493
4552
  }
4494
- let existingOwner = await readOwner(lockPath);
4553
+ const existing = await readOwnerRecord(lockPath);
4554
+ let existingOwner = existing.state === "record" ? existing.owner : null;
4555
+ if (existing.state === "absent" && await removeAbandonedClaim(lockPath)) continue;
4495
4556
  if (existingOwner !== null) {
4496
4557
  if (await quarantineStaleLock(lockPath, existingOwner, policy)) continue;
4497
4558
  existingOwner = await readOwner(lockPath);
@@ -4511,14 +4572,13 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
4511
4572
  });
4512
4573
  } catch (err) {
4513
4574
  const code = err.code;
4514
- if (code === "EEXIST") {
4575
+ if (code === "EEXIST" || code === "ENOENT" || code === "ENOTDIR" || await claimDirectoryLost(lockPath, claimed)) {
4515
4576
  heldTokens.delete(owner.token);
4516
4577
  continue;
4517
4578
  }
4518
4579
  await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => {
4519
4580
  });
4520
4581
  heldTokens.delete(owner.token);
4521
- if (code === "ENOENT" || code === "ENOTDIR") continue;
4522
4582
  throw err;
4523
4583
  }
4524
4584
  let inFlight;
@@ -4548,9 +4608,18 @@ async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy
4548
4608
  return;
4549
4609
  }
4550
4610
  if (record2.state === "unreadable" || !await isClaimedDirectory(lockPath, claimed)) return;
4551
- const remnant = releasedLockRemnantPath(lockPath, owner);
4552
- await fs.rename(lockPath, remnant);
4553
- await fs.rm(remnant, { recursive: true, force: true });
4611
+ await fs.rmdir(lockPath);
4612
+ }
4613
+ async function claimDirectoryLost(lockPath, claimed) {
4614
+ try {
4615
+ const current = await fs.lstat(lockPath, { bigint: true });
4616
+ if (claimed === null) return false;
4617
+ const reborn = claimed.birthtimeNs > 0n && current.birthtimeNs > 0n && current.birthtimeNs !== claimed.birthtimeNs;
4618
+ return current.dev !== claimed.dev || current.ino !== claimed.ino || reborn;
4619
+ } catch (err) {
4620
+ const code = err.code;
4621
+ return code === "ENOENT" || code === "ENOTDIR";
4622
+ }
4554
4623
  }
4555
4624
  async function isClaimedDirectory(lockPath, claimed) {
4556
4625
  if (claimed === null || claimed.birthtimeNs <= 0n) return false;
@@ -4652,6 +4721,12 @@ function foldSegment(segment) {
4652
4721
  return segment.normalize("NFKD").toLowerCase().toUpperCase().toLowerCase();
4653
4722
  }
4654
4723
 
4724
+ // ../core/src/filesystem-temp-name.ts
4725
+ import { randomUUID as randomUUID2 } from "node:crypto";
4726
+ function identityTempName(leaf) {
4727
+ return `.${leaf}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
4728
+ }
4729
+
4655
4730
  // ../core/src/filesystem-identity.ts
4656
4731
  async function replaceExistingLeaf(port, tmp, target, rel) {
4657
4732
  try {
@@ -4796,10 +4871,32 @@ async function probeSegment(port, candidate, rel, segment) {
4796
4871
  function hasExact(listing, segment) {
4797
4872
  return listing.some((entry) => entry.name === segment);
4798
4873
  }
4874
+ function existingSpelling(names, segment) {
4875
+ const folded = foldSegment(segment);
4876
+ const matches = [...names].filter((name) => name !== segment && foldSegment(name) === folded);
4877
+ return matches.length === 1 ? matches[0] : void 0;
4878
+ }
4879
+ function entryNoun(kind, leaf) {
4880
+ if (kind === void 0) return leaf ? "file" : "folder";
4881
+ return kind === "directory" ? "folder" : "file";
4882
+ }
4883
+ function aliasRefusal(rel, segments, index, names, kind) {
4884
+ const segment = segments[index];
4885
+ return new FilesystemIdentityAliasError(rel, segment, existingSpelling(names, segment), entryNoun(kind, index === segments.length - 1), index);
4886
+ }
4887
+ function aliasRefusalFromListing(rel, index, listing, leaf) {
4888
+ const segment = relSegments(rel)[index];
4889
+ const existing = existingSpelling(listing.map((entry) => entry.name), segment);
4890
+ const kind = existing === void 0 ? void 0 : listing.find((entry) => entry.name === existing)?.kind;
4891
+ return new FilesystemIdentityAliasError(rel, segment, existing, entryNoun(kind, leaf), index);
4892
+ }
4893
+ function listingView(names) {
4894
+ return { has: (name) => names.has(name), names: () => names };
4895
+ }
4799
4896
  function freshListings(port) {
4800
4897
  return async (dir) => {
4801
4898
  const listing = await port.entries(dir);
4802
- return listing === null ? null : { has: (name) => hasExact(listing, name) };
4899
+ return listing === null ? null : { has: (name) => hasExact(listing, name), names: () => listing.map((entry) => entry.name) };
4803
4900
  };
4804
4901
  }
4805
4902
  async function walkExact(port, rootResolved, segments, rel) {
@@ -4811,7 +4908,7 @@ async function walkExact(port, rootResolved, segments, rel) {
4811
4908
  if (probed === null) return { state: "absent", depth: index };
4812
4909
  const listing = await port.entries(parent);
4813
4910
  if (listing === null) return { state: "absent", depth: index };
4814
- if (!hasExact(listing, segment)) throw new FilesystemIdentityAliasError(rel, segment);
4911
+ if (!hasExact(listing, segment)) throw aliasRefusal(rel, segments, index, listing.map((entry) => entry.name), probed.kind);
4815
4912
  if (index === segments.length - 1) return { state: "exact", leaf: probed };
4816
4913
  if (probed.kind !== "directory") return { state: "shape-mismatch", segment };
4817
4914
  parent = candidate;
@@ -4826,35 +4923,53 @@ async function recordWalk(port, rootResolved, segments, rel) {
4826
4923
  const segment = segments[index];
4827
4924
  const candidate = path2.join(parent, segment);
4828
4925
  const probed = await probeSegment(port, candidate, rel, segment);
4829
- if (probed === null) return null;
4830
- if (index < segments.length - 1 && probed.kind !== "directory") return null;
4926
+ if (probed === null) return { recorded, complete: false };
4831
4927
  recorded.push(probed);
4928
+ if (index < segments.length - 1 && probed.kind !== "directory") return { recorded, complete: false };
4832
4929
  parent = candidate;
4833
4930
  }
4834
- return recorded;
4931
+ return { recorded, complete: true };
4835
4932
  }
4836
- async function confirmedWalk(port, rootResolved, segments, rel, recorded, handle, listings = freshListings(port)) {
4933
+ async function confirmedWalk(port, rootResolved, segments, rel, recorded, handle, listings = freshListings(port), stopAt = segments.length) {
4837
4934
  let parent = rootResolved;
4838
- for (let index = 0; index < segments.length; index++) {
4935
+ for (let index = 0; index < stopAt; index++) {
4839
4936
  const segment = segments[index];
4840
4937
  const candidate = path2.join(parent, segment);
4841
4938
  const listing = await listings(parent);
4842
4939
  if (listing === null) return "absent";
4843
4940
  const listed = listing.has(segment);
4844
4941
  if (handle !== null && index === segments.length - 1) {
4845
- const verdict = classifyLeaf({ listed, probe: await probeSegment(port, candidate, rel, segment), handle });
4846
- if (verdict === "aliased") throw new FilesystemIdentityAliasError(rel, segment);
4942
+ const probe = await probeSegment(port, candidate, rel, segment);
4943
+ const verdict = classifyLeaf({ listed, probe, handle });
4944
+ if (verdict === "aliased") throw aliasRefusal(rel, segments, index, listing.names(), probe?.kind);
4847
4945
  return verdict === "replaced" ? "restart" : verdict;
4848
4946
  }
4849
4947
  if (index < segments.length - 1 || !listed) {
4850
- const verdict = confirmAlias(listed, await probeSegment(port, candidate, rel, segment), recorded[index]);
4851
- if (verdict === "aliased") throw new FilesystemIdentityAliasError(rel, segment);
4948
+ const probe = await probeSegment(port, candidate, rel, segment);
4949
+ const verdict = confirmAlias(listed, probe, recorded[index]);
4950
+ if (verdict === "aliased") throw aliasRefusal(rel, segments, index, listing.names(), probe?.kind);
4852
4951
  if (verdict !== "continue") return verdict;
4853
4952
  }
4854
4953
  parent = candidate;
4855
4954
  }
4856
4955
  return "continue";
4857
4956
  }
4957
+ async function confirmPrefix(port, rootResolved, segments, rel, recorded) {
4958
+ if (recorded.length === 0) return "absent";
4959
+ let kept = prefixListings.get(port);
4960
+ if (kept === void 0) prefixListings.set(port, kept = /* @__PURE__ */ new Map());
4961
+ const cached = witnessedListings(port, kept, Date.now, PREFIX_LISTING_CAPACITY);
4962
+ let verdict;
4963
+ try {
4964
+ verdict = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, cached.source, recorded.length);
4965
+ } catch (error) {
4966
+ if (!cached.reused()) throw error;
4967
+ kept.clear();
4968
+ verdict = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, freshListings(port), recorded.length);
4969
+ }
4970
+ if (verdict === "restart" && cached.reused()) kept.clear();
4971
+ return verdict === "restart" ? "restart" : "absent";
4972
+ }
4858
4973
  function countRestart(restarts, rel) {
4859
4974
  if (restarts >= MAX_RESTARTS) throw new ConcurrentReplacementError(rel, restarts + 1);
4860
4975
  return restarts + 1;
@@ -4866,8 +4981,14 @@ async function observeWith(port, rootResolved, rel, read, listings, onExact) {
4866
4981
  const segments = relSegments(rel);
4867
4982
  const target = path2.join(rootResolved, ...segments);
4868
4983
  for (let restarts = 0; ; ) {
4869
- const recorded = await recordWalk(port, rootResolved, segments, rel);
4870
- if (recorded === null) return { state: "absent" };
4984
+ const walk = await recordWalk(port, rootResolved, segments, rel);
4985
+ if (!walk.complete) {
4986
+ const prefix = await confirmPrefix(port, rootResolved, segments, rel, walk.recorded);
4987
+ if (prefix === "absent") return { state: "absent" };
4988
+ restarts = countRestart(restarts, rel);
4989
+ continue;
4990
+ }
4991
+ const recorded = walk.recorded;
4871
4992
  const verified = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, listings);
4872
4993
  if (verified === "absent") return { state: "absent" };
4873
4994
  if (verified === "restart") {
@@ -4906,8 +5027,14 @@ async function probeExact(port, root, rel) {
4906
5027
  const segments = relSegments(rel);
4907
5028
  const rootResolved = path2.resolve(root);
4908
5029
  for (let restarts = 0; ; ) {
4909
- const recorded = await recordWalk(port, rootResolved, segments, rel);
4910
- if (recorded === null) return { state: "absent" };
5030
+ const walk = await recordWalk(port, rootResolved, segments, rel);
5031
+ if (!walk.complete) {
5032
+ const prefix = await confirmPrefix(port, rootResolved, segments, rel, walk.recorded);
5033
+ if (prefix === "absent") return { state: "absent" };
5034
+ restarts = countRestart(restarts, rel);
5035
+ continue;
5036
+ }
5037
+ const recorded = walk.recorded;
4911
5038
  const leaf = recorded[recorded.length - 1];
4912
5039
  const verified = await confirmedWalk(port, rootResolved, segments, rel, recorded, null);
4913
5040
  const verdict = verified === "continue" ? await confirmedWalk(port, rootResolved, segments, rel, recorded, leaf) : verified;
@@ -4917,45 +5044,56 @@ async function probeExact(port, root, rel) {
4917
5044
  }
4918
5045
  }
4919
5046
  var SETTLED_AFTER_NS = 3000000000n;
5047
+ function witnessCanDetectChange(witness) {
5048
+ return witness.mtimeNs > 0n && witness.ctimeNs > 0n;
5049
+ }
4920
5050
  function witnessKey(witness) {
4921
5051
  return `${witness.dev}:${witness.ino}:${witness.mtimeNs}:${witness.ctimeNs}`;
4922
5052
  }
4923
- function openObservationPass(port, root, now = Date.now) {
4924
- const rootResolved = path2.resolve(root);
4925
- const kept = /* @__PURE__ */ new Map();
4926
- const reusedVerdicts = [];
4927
- const fresh = freshListings(port);
4928
- const witnessOf = port.directoryWitness?.bind(port);
5053
+ function witnessedListings(port, kept, now, capacity = Infinity) {
5054
+ let reused = false;
5055
+ const portWitness = port.directoryWitness?.bind(port);
5056
+ if (portWitness === void 0) return { source: freshListings(port), reused: () => false };
5057
+ const witnessOf = async (dir) => {
5058
+ const witness = await portWitness(dir);
5059
+ return witness !== null && witnessCanDetectChange(witness) ? witness : null;
5060
+ };
4929
5061
  const settled = (witness) => {
4930
5062
  const nowNs = BigInt(Math.floor(now())) * 1000000n;
4931
5063
  const latest = witness.mtimeNs > witness.ctimeNs ? witness.mtimeNs : witness.ctimeNs;
4932
5064
  return nowNs - latest >= SETTLED_AFTER_NS;
4933
5065
  };
4934
- const listingsFor = () => {
4935
- let reused = false;
4936
- if (witnessOf === void 0) return { source: fresh, reused: () => false };
4937
- const source = async (dir) => {
4938
- const keep = kept.get(dir);
4939
- if (keep !== void 0) {
4940
- const current = await witnessOf(dir);
4941
- if (current !== null && witnessKey(current) === keep.witness) {
4942
- reused = true;
4943
- return keep.names;
4944
- }
4945
- kept.delete(dir);
4946
- }
4947
- const before = await witnessOf(dir);
4948
- const listing = await port.entries(dir);
4949
- if (listing === null) return null;
4950
- const names = new Set(listing.map((entry) => entry.name));
4951
- const after = await witnessOf(dir);
4952
- if (before !== null && after !== null && witnessKey(before) === witnessKey(after) && settled(after)) {
4953
- kept.set(dir, { witness: witnessKey(after), names });
4954
- }
4955
- return names;
4956
- };
4957
- return { source, reused: () => reused };
5066
+ const source = async (dir) => {
5067
+ const keep = kept.get(dir);
5068
+ if (keep !== void 0) {
5069
+ const current = await witnessOf(dir);
5070
+ if (current !== null && witnessKey(current) === keep.witness) {
5071
+ reused = true;
5072
+ return listingView(keep.names);
5073
+ }
5074
+ kept.delete(dir);
5075
+ }
5076
+ const before = await witnessOf(dir);
5077
+ const listing = await port.entries(dir);
5078
+ if (listing === null) return null;
5079
+ const names = new Set(listing.map((entry) => entry.name));
5080
+ const after = await witnessOf(dir);
5081
+ if (before !== null && after !== null && witnessKey(before) === witnessKey(after) && settled(after)) {
5082
+ kept.delete(dir);
5083
+ kept.set(dir, { witness: witnessKey(after), names });
5084
+ while (kept.size > capacity) kept.delete(kept.keys().next().value);
5085
+ }
5086
+ return listingView(names);
4958
5087
  };
5088
+ return { source, reused: () => reused };
5089
+ }
5090
+ var prefixListings = /* @__PURE__ */ new WeakMap();
5091
+ var PREFIX_LISTING_CAPACITY = 64;
5092
+ function openObservationPass(port, root, now = Date.now) {
5093
+ const rootResolved = path2.resolve(root);
5094
+ const kept = /* @__PURE__ */ new Map();
5095
+ const reusedVerdicts = [];
5096
+ const listingsFor = () => witnessedListings(port, kept, now);
4959
5097
  return {
4960
5098
  async observe(rel, read) {
4961
5099
  const listings = listingsFor();
@@ -5058,7 +5196,7 @@ async function readWhole(port, target) {
5058
5196
  await port.close(opened.handle);
5059
5197
  }
5060
5198
  }
5061
- async function mkdirExact(port, parent, segment, isTail, rel) {
5199
+ async function mkdirExact(port, parent, segment, isTail, rel, index = -1) {
5062
5200
  const outcome = await port.mkdir(path2.join(parent, segment));
5063
5201
  if (outcome === "created") return;
5064
5202
  const listing = await port.entries(parent);
@@ -5067,7 +5205,9 @@ async function mkdirExact(port, parent, segment, isTail, rel) {
5067
5205
  throw new FilesystemSymlinkEntryError(rel, segment);
5068
5206
  }
5069
5207
  const verdict = classifyMkdir(outcome, listing, segment, isTail);
5070
- if (verdict === "aliased") throw new FilesystemIdentityAliasError(rel, segment);
5208
+ if (verdict === "aliased") {
5209
+ throw aliasRefusalFromListing(rel, index, listing, false);
5210
+ }
5071
5211
  if (verdict === "shape-mismatch") throw new FilesystemShapeMismatchError(rel, segment);
5072
5212
  }
5073
5213
  async function ensureExactDirectories(port, rootResolved, segments, existingDepth, rel) {
@@ -5087,8 +5227,9 @@ async function ensureExactDirectories(port, rootResolved, segments, existingDept
5087
5227
  }
5088
5228
  }
5089
5229
  let parent = path2.join(rootResolved, ...segments.slice(0, existingDepth));
5090
- for (const segment of segments.slice(existingDepth, -1)) {
5091
- await mkdirExact(port, parent, segment, false, rel);
5230
+ for (let index = existingDepth; index < segments.length - 1; index++) {
5231
+ const segment = segments[index];
5232
+ await mkdirExact(port, parent, segment, false, rel, index);
5092
5233
  parent = path2.join(parent, segment);
5093
5234
  }
5094
5235
  }
@@ -5106,7 +5247,7 @@ async function createExactLeaf(port, tmp, target, rel, leaf, rootResolved) {
5106
5247
  }
5107
5248
  const listing = await port.entries(path2.dirname(target)) ?? [];
5108
5249
  if (hasExact(listing, leaf)) throw new ConcurrentReplacementError(rel, 1);
5109
- throw new FilesystemIdentityAliasError(rel, leaf);
5250
+ throw aliasRefusalFromListing(rel, relSegments(rel).length - 1, listing, true);
5110
5251
  }
5111
5252
  async function mutateExact(port, root, rel, body) {
5112
5253
  const segments = relSegments(rel);
@@ -5128,11 +5269,13 @@ async function mutateExact(port, root, rel, body) {
5128
5269
  async replace(bytes) {
5129
5270
  await ensureExactDirectories(port, rootResolved, segments, existingDepth, rel);
5130
5271
  const dir = path2.dirname(target);
5131
- const tmpName = `.${leaf}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
5272
+ const tmpName = identityTempName(leaf);
5132
5273
  const tmp = path2.join(dir, tmpName);
5133
5274
  await port.writeTemp(dir, tmpName, bytes);
5134
5275
  try {
5135
5276
  if (realized.state === "exact") {
5277
+ await port.copyMode?.(target, tmp).catch(() => {
5278
+ });
5136
5279
  await replaceExistingLeaf(port, tmp, target, rel);
5137
5280
  return;
5138
5281
  }
@@ -5292,6 +5435,9 @@ function createNodeFilesystemIdentityPort(hostPolicy) {
5292
5435
  async rename(from, to) {
5293
5436
  await fs2.rename(from, to);
5294
5437
  },
5438
+ async copyMode(from, to) {
5439
+ await fs2.chmod(to, (await fs2.lstat(from)).mode & 511);
5440
+ },
5295
5441
  async unlink(target) {
5296
5442
  await fs2.unlink(target);
5297
5443
  },
@@ -5314,6 +5460,7 @@ function blobVersion(bytes) {
5314
5460
 
5315
5461
  // ../core/src/backend.ts
5316
5462
  var publicationContexts = /* @__PURE__ */ new WeakMap();
5463
+ var fileMutations = /* @__PURE__ */ new WeakMap();
5317
5464
  function firstString(...vals) {
5318
5465
  for (const v of vals) {
5319
5466
  if (typeof v === "string" && v.trim() !== "") return v;
@@ -5430,6 +5577,7 @@ var FilesystemBackend = class _FilesystemBackend {
5430
5577
  this.#root = path3.resolve(root);
5431
5578
  this.#coordinator = createFilesystemMutationCoordinator(this.#port, this.#root, options.hostPolicy);
5432
5579
  publicationContexts.set(this, { root: this.#root, port: this.#port });
5580
+ fileMutations.set(this, (rel, body) => this.#mutateExact(rel, body));
5433
5581
  }
5434
5582
  async read(id) {
5435
5583
  return this.#readWithEdition(id, () => this.#readEdition());
@@ -5785,6 +5933,7 @@ var KIND_FIELD_TYPE_SET = new Set(KIND_FIELD_TYPES);
5785
5933
  function isKindFieldType(value) {
5786
5934
  return typeof value === "string" && KIND_FIELD_TYPE_SET.has(value);
5787
5935
  }
5936
+ var UNDECLARED_FIELDS_POLICIES = ["warn", "error"];
5788
5937
  function buildKindRegistry(docs, initialWarnings = [], options = {}) {
5789
5938
  const accepted = /* @__PURE__ */ new Map();
5790
5939
  const warnings = [...initialWarnings];
@@ -6379,6 +6528,19 @@ function parseConventionDoc(doc, options = {}) {
6379
6528
  });
6380
6529
  }
6381
6530
  }
6531
+ let undeclaredFields;
6532
+ if (fm.undeclared_fields !== void 0) {
6533
+ if (UNDECLARED_FIELDS_POLICIES.includes(fm.undeclared_fields)) {
6534
+ undeclaredFields = fm.undeclared_fields;
6535
+ } else {
6536
+ warnings.push({
6537
+ code: "KIND_CONVENTION_BAD_SHAPE",
6538
+ message: `kind convention '${doc.id}' has an invalid 'undeclared_fields' (${describeShape(fm.undeclared_fields)}; expected warn or error); using warn.`,
6539
+ field: "undeclared_fields",
6540
+ severity: "warning"
6541
+ });
6542
+ }
6543
+ }
6382
6544
  const kind = {
6383
6545
  id: doc.id,
6384
6546
  title,
@@ -6396,6 +6558,7 @@ function parseConventionDoc(doc, options = {}) {
6396
6558
  if (browseCollapsed !== void 0) kind.browseCollapsed = browseCollapsed;
6397
6559
  if (order !== void 0) kind.order = order;
6398
6560
  if (claim !== void 0) kind.claim = claim;
6561
+ if (undeclaredFields !== void 0) kind.undeclaredFields = undeclaredFields;
6399
6562
  if (Object.hasOwn(fm, "deprecated")) {
6400
6563
  kind.deprecated = parseKindDeprecation(fm.deprecated, kind, RESERVED_KIND_FIELD_NAMES, claimCoordinates(options.okfVersion, kind)?.fields ?? []);
6401
6564
  warnings.push(...kind.deprecated.problems ?? []);
@@ -6472,7 +6635,7 @@ async function loadKinds(bundle) {
6472
6635
  const docs = await query2(bundle, { prefix: CONVENTIONS_PREFIX, type: CONVENTION_TYPE }, {
6473
6636
  onSkip: ({ id, reason }) => warnings.push({
6474
6637
  code: "KIND_CONVENTION_MALFORMED",
6475
- message: `skipped kind convention '${id}' with unparseable frontmatter: ${reason}`,
6638
+ message: isPermissionDeniedSkip({ reason }) ? `skipped kind convention '${id}' that could not be read: ${reason}` : `skipped kind convention '${id}' with unparseable frontmatter: ${reason}`,
6476
6639
  field: id,
6477
6640
  severity: "warning"
6478
6641
  })