superbee 0.2.1-pre.2 → 0.3.0-pre.1

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.
@@ -30917,21 +30917,28 @@ function normalizeFrontmatter(data, okfVersion) {
30917
30917
  function parseMarkdown(raw, context, options = {}) {
30918
30918
  const split = splitLeadingFrontmatter(raw, context);
30919
30919
  if (!("yamlSource" in split)) return { frontmatter: {}, body: split.body };
30920
+ return { frontmatter: parseFrontmatter(split.yamlSource, context, options), body: split.body };
30921
+ }
30922
+ function parseFrontmatter(source, context, options = {}) {
30920
30923
  let parsed;
30921
30924
  try {
30922
- parsed = yamlEngine.parse(split.yamlSource);
30925
+ parsed = yamlEngine.parse(source);
30923
30926
  } catch (err) {
30924
30927
  throw new MalformedDocumentError(context, err);
30925
30928
  }
30926
- const frontmatter2 = normalizeFrontmatter(parsed, options.okfVersion);
30927
- return { frontmatter: frontmatter2, body: split.body };
30929
+ return normalizeFrontmatter(parsed, options.okfVersion);
30930
+ }
30931
+ function stringifyFrontmatter(data) {
30932
+ return import_js_yaml.default.safeDump(data).trim();
30928
30933
  }
30929
30934
  function normalizeDocumentBodyForStorage(body) {
30930
30935
  return body.endsWith("\n") ? body : `${body}
30931
30936
  `;
30932
30937
  }
30933
30938
  function stringifyWithData(data, body) {
30934
- const dumped = import_js_yaml.default.safeDump(data).trim();
30939
+ return stringifyWithSerializedFrontmatter(stringifyFrontmatter(data), body);
30940
+ }
30941
+ function stringifyWithSerializedFrontmatter(dumped, body) {
30935
30942
  const content3 = body ?? "";
30936
30943
  const newline = (value) => value.endsWith("\n") ? value : `${value}
30937
30944
  `;
@@ -30992,6 +30999,29 @@ function mutationActorFromFrontmatter(frontmatter2) {
30992
30999
 
30993
31000
  // ../core/src/paths.ts
30994
31001
  var RESERVED_FILENAMES = ["index.md", "log.md"];
31002
+ function compareStorageKeys(a, b) {
31003
+ const primary = a.localeCompare(b);
31004
+ if (primary !== 0 || a === b) return primary;
31005
+ const left = Array.from(a, (character) => character.codePointAt(0));
31006
+ const right = Array.from(b, (character) => character.codePointAt(0));
31007
+ for (let i = 0; i < Math.min(left.length, right.length); i++) {
31008
+ if (left[i] !== right[i]) return left[i] - right[i];
31009
+ }
31010
+ return left.length - right.length;
31011
+ }
31012
+ function assertPortablePath(value, label) {
31013
+ if (/[\u0000-\u001f\u007f]/u.test(value) || /^[a-z]:/i.test(value)) {
31014
+ throw new InvalidInputError(`${label} must not contain controls or start with a drive prefix.`);
31015
+ }
31016
+ if (value.includes("\\") || value.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
31017
+ throw new InvalidInputError(`${label} must be canonical and bundle-relative.`);
31018
+ }
31019
+ }
31020
+ function assertSafeReservedFilename(name) {
31021
+ if (!RESERVED_FILENAMES.includes(name)) {
31022
+ throw new InvalidInputError("Reserved filename must be index.md or log.md.");
31023
+ }
31024
+ }
30995
31025
  function toPosix(p) {
30996
31026
  return p.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
30997
31027
  }
@@ -31011,21 +31041,8 @@ function assertSafeConceptId(id) {
31011
31041
  if (typeof id !== "string" || id.trim() === "") {
31012
31042
  throw new InvalidInputError("Concept id must be a non-empty string.");
31013
31043
  }
31014
- if (id.startsWith("/")) {
31015
- throw new InvalidInputError(`Concept id must be bundle-relative, got absolute '${id}'.`);
31016
- }
31017
- if (id.includes("\\")) {
31018
- throw new InvalidInputError(`Concept id must use forward slashes: '${id}'.`);
31019
- }
31044
+ assertPortablePath(id, "Concept id");
31020
31045
  const segments = id.split("/");
31021
- if (segments.some((seg) => seg === "..")) {
31022
- throw new InvalidInputError(`Concept id must not contain '..' segments: '${id}'.`);
31023
- }
31024
- if (segments.some((seg) => seg === "." || seg === "")) {
31025
- throw new InvalidInputError(
31026
- `Concept id must be canonical (no '.', duplicate-slash, leading './', or trailing-slash segments): '${id}'.`
31027
- );
31028
- }
31029
31046
  if (segments.slice(0, -1).some((seg) => seg.toLowerCase().endsWith(".md"))) {
31030
31047
  throw new InvalidInputError(
31031
31048
  `Concept id must not contain a non-final segment ending in '.md': '${id}' (it collides with a concept file at that path).`
@@ -31036,21 +31053,11 @@ function assertSafeBlobKey(key) {
31036
31053
  if (typeof key !== "string" || key.trim() === "") {
31037
31054
  throw new InvalidInputError("Blob key must be a non-empty string.");
31038
31055
  }
31039
- const norm = toPosix(key);
31040
- if (norm.startsWith("/")) {
31041
- throw new InvalidInputError(`Blob key must be bundle-relative, got absolute '${key}'.`);
31042
- }
31043
- const segments = norm.split("/");
31044
- if (segments.some((seg) => seg === "..")) {
31045
- throw new InvalidInputError(`Blob key must not contain '..' segments: '${key}'.`);
31046
- }
31056
+ assertPortablePath(key, "Blob key");
31057
+ const segments = key.split("/");
31047
31058
  if (segments.some((seg) => seg.startsWith("."))) {
31048
31059
  throw new InvalidInputError(`Blob key must not contain dot-prefixed segments: '${key}'.`);
31049
31060
  }
31050
- const last = segments[segments.length - 1] ?? "";
31051
- if (last === "") {
31052
- throw new InvalidInputError(`Blob key must name a file, not end with '/': '${key}'.`);
31053
- }
31054
31061
  if (segments.some((seg) => seg.toLowerCase().endsWith(".md"))) {
31055
31062
  throw new InvalidInputError(
31056
31063
  `Blob key '${key}' has a path segment ending in '.md' (checked case-insensitively, at any depth), which collides with the concept-document namespace \u2014 write it as a doc instead.`
@@ -31061,13 +31068,7 @@ function assertSafeReservedDir(dir) {
31061
31068
  if (typeof dir !== "string") {
31062
31069
  throw new InvalidInputError("Reserved-file directory must be a string.");
31063
31070
  }
31064
- const norm = toPosix(dir);
31065
- if (norm.startsWith("/")) {
31066
- throw new InvalidInputError(`Reserved-file directory must be bundle-relative, got absolute '${dir}'.`);
31067
- }
31068
- if (norm.split("/").some((seg) => seg === "..")) {
31069
- throw new InvalidInputError(`Reserved-file directory must not contain '..' segments: '${dir}'.`);
31070
- }
31071
+ if (dir !== "") assertPortablePath(dir, "Reserved-file directory");
31071
31072
  }
31072
31073
 
31073
31074
  // ../core/src/links.ts
@@ -31372,7 +31373,52 @@ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypt
31372
31373
 
31373
31374
  // ../core/src/filesystem-lock.ts
31374
31375
  import { promises as fs, realpathSync } from "node:fs";
31375
- import { homedir, hostname, userInfo } from "node:os";
31376
+ import { homedir, hostname } from "node:os";
31377
+
31378
+ // ../core/src/host-policy-snapshot.ts
31379
+ function snapshotHostPolicy(input, seen = /* @__PURE__ */ new WeakMap()) {
31380
+ const existing = seen.get(input);
31381
+ if (existing) return existing;
31382
+ const result = Array.isArray(input) ? [] : {};
31383
+ seen.set(input, result);
31384
+ const keys = /* @__PURE__ */ new Set();
31385
+ for (let owner = input; owner && owner !== Object.prototype && owner !== Array.prototype; owner = Object.getPrototypeOf(owner)) {
31386
+ for (const key of Reflect.ownKeys(owner)) {
31387
+ if (key !== "constructor" && !(Array.isArray(input) && key === "length")) keys.add(key);
31388
+ }
31389
+ }
31390
+ for (const key of keys) {
31391
+ const value = input[key];
31392
+ Object.defineProperty(result, key, {
31393
+ value: typeof value === "function" ? value.bind(result) : value && typeof value === "object" ? snapshotHostPolicy(value, seen) : value,
31394
+ enumerable: true
31395
+ });
31396
+ }
31397
+ if (Array.isArray(input)) result.length = input.length;
31398
+ return Object.freeze(result);
31399
+ }
31400
+
31401
+ // ../core/src/filesystem-host.ts
31402
+ function defaultFilesystemHostPolicy() {
31403
+ if (process.platform !== "darwin" && process.platform !== "linux") {
31404
+ throw new InvalidInputError(
31405
+ "This host requires an explicit filesystem host policy; the default filesystem supports macOS and Linux."
31406
+ );
31407
+ }
31408
+ return {
31409
+ runtimeLockParent: () => "/tmp",
31410
+ runtimeOwnerKey: () => `uid-${process.getuid()}`,
31411
+ enforcePrivateMode: true,
31412
+ isTransientOpenError: () => false,
31413
+ isReplacementConflict: () => false,
31414
+ isDirectoryContentionError: () => false
31415
+ };
31416
+ }
31417
+ function captureFilesystemHostPolicy(policy) {
31418
+ return snapshotHostPolicy(policy ?? defaultFilesystemHostPolicy());
31419
+ }
31420
+
31421
+ // ../core/src/filesystem-lock.ts
31376
31422
  import path from "node:path";
31377
31423
  import { createHash, randomUUID } from "node:crypto";
31378
31424
  var OWNER_FILE = "owner.json";
@@ -31392,16 +31438,6 @@ var FilesystemMutationLockError = class extends Error {
31392
31438
  this.malformed = details.malformed;
31393
31439
  }
31394
31440
  };
31395
- function runtimeOwnerKey() {
31396
- const uid = process.getuid?.();
31397
- if (uid !== void 0) return `uid-${uid}`;
31398
- let username = "unknown";
31399
- try {
31400
- username = userInfo().username;
31401
- } catch {
31402
- }
31403
- return `user-${createHash("sha256").update(username).digest("hex").slice(0, 16)}`;
31404
- }
31405
31441
  function canonicalExistingPath(value) {
31406
31442
  try {
31407
31443
  return realpathSync(value);
@@ -31413,11 +31449,12 @@ function pathContains(root, candidate) {
31413
31449
  const rel = path.relative(root, candidate);
31414
31450
  return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
31415
31451
  }
31416
- function filesystemMutationLockRoot(portableRoot) {
31417
- const runtimeParent = process.platform === "win32" ? path.join(homedir(), "AppData", "Local") : "/tmp";
31452
+ function filesystemMutationLockRoot(portableRoot, hostPolicy) {
31453
+ const policy = captureFilesystemHostPolicy(hostPolicy);
31454
+ const runtimeParent = policy.runtimeLockParent();
31418
31455
  const tempParent = canonicalExistingPath(runtimeParent);
31419
31456
  const homeParent = canonicalExistingPath(homedir());
31420
- const ownerKey = runtimeOwnerKey();
31457
+ const ownerKey = policy.runtimeOwnerKey();
31421
31458
  const candidates = [
31422
31459
  path.join(tempParent, `agentstate-lite-mutation-locks-${ownerKey}`),
31423
31460
  path.join(homeParent, ".agentstate", `mutation-locks-${ownerKey}`)
@@ -31457,15 +31494,33 @@ function parseFilesystemMutationLockOwner(value) {
31457
31494
  target: value.target
31458
31495
  };
31459
31496
  }
31460
- async function readOwner(lockPath) {
31497
+ async function readOwnerRecord(lockPath) {
31498
+ let raw;
31461
31499
  try {
31462
- return parseFilesystemMutationLockOwner(
31463
- JSON.parse(await fs.readFile(path.join(lockPath, OWNER_FILE), "utf8"))
31464
- );
31500
+ raw = await fs.readFile(path.join(lockPath, OWNER_FILE), "utf8");
31501
+ } catch (err) {
31502
+ const code2 = err.code;
31503
+ return code2 === "ENOENT" || code2 === "ENOTDIR" ? { state: "absent" } : { state: "unreadable", error: err };
31504
+ }
31505
+ let owner = null;
31506
+ try {
31507
+ owner = parseFilesystemMutationLockOwner(JSON.parse(raw));
31465
31508
  } catch {
31466
- return null;
31509
+ owner = null;
31510
+ }
31511
+ return owner === null ? { state: "absent" } : { state: "record", owner };
31512
+ }
31513
+ async function resolveOwnerRecord(lockPath, started, waitMs, pollMs) {
31514
+ while (true) {
31515
+ const record = await readOwnerRecord(lockPath);
31516
+ if (record.state !== "unreadable" || Date.now() - started >= waitMs) return record;
31517
+ await delay(pollMs);
31467
31518
  }
31468
31519
  }
31520
+ async function readOwner(lockPath) {
31521
+ const record = await readOwnerRecord(lockPath);
31522
+ return record.state === "record" ? record.owner : null;
31523
+ }
31469
31524
  function processExists(pid) {
31470
31525
  try {
31471
31526
  process.kill(pid, 0);
@@ -31487,7 +31542,7 @@ async function pathExists(candidate) {
31487
31542
  throw err;
31488
31543
  }
31489
31544
  }
31490
- async function quarantineStaleLock(lockPath, owner) {
31545
+ async function quarantineStaleLock(lockPath, owner, policy) {
31491
31546
  if (owner.hostname !== hostname() || processExists(owner.pid)) return false;
31492
31547
  const quarantinePath = staleLockQuarantinePath(lockPath, owner);
31493
31548
  try {
@@ -31497,7 +31552,7 @@ async function quarantineStaleLock(lockPath, owner) {
31497
31552
  const code2 = err.code;
31498
31553
  if (code2 === "ENOENT") return false;
31499
31554
  if (await pathExists(quarantinePath)) return false;
31500
- if (process.platform === "win32" && WINDOWS_DIRECTORY_CONTENTION_CODES.has(code2 ?? "")) return false;
31555
+ if (policy.isDirectoryContentionError(err)) return false;
31501
31556
  throw err;
31502
31557
  }
31503
31558
  }
@@ -31516,7 +31571,7 @@ function isPrivateFilesystemMutationLockRoot(facts) {
31516
31571
  const unsafeMode = facts.enforcePrivateMode && (facts.mode & 511) !== 448;
31517
31572
  return facts.directory && !facts.symbolicLink && !wrongOwner && !unsafeMode;
31518
31573
  }
31519
- async function ensurePrivateLockRoot(root) {
31574
+ async function ensurePrivateLockRoot(root, policy) {
31520
31575
  try {
31521
31576
  await fs.mkdir(root, { recursive: true, mode: 448 });
31522
31577
  } catch (err) {
@@ -31530,7 +31585,7 @@ async function ensurePrivateLockRoot(root) {
31530
31585
  ownerUid: stat2.uid,
31531
31586
  expectedUid: uid,
31532
31587
  mode: stat2.mode,
31533
- enforcePrivateMode: process.platform !== "win32"
31588
+ enforcePrivateMode: policy.enforcePrivateMode
31534
31589
  })) {
31535
31590
  throw new FilesystemMutationLockError(
31536
31591
  `refusing unsafe filesystem mutation lock root '${root}'; it must be a private directory owned by this user`,
@@ -31557,44 +31612,43 @@ async function resolvedPortableRoot(portableRoot) {
31557
31612
  if (portableRoot === void 0) return void 0;
31558
31613
  return fs.realpath(portableRoot).catch(() => path.resolve(portableRoot));
31559
31614
  }
31560
- async function selectLockRoot(options) {
31615
+ async function selectLockRoot(options, policy) {
31561
31616
  const portableRoot = await resolvedPortableRoot(options.portableRoot);
31562
- const lockRoot = options.lockRoot !== void 0 ? explicitFilesystemMutationLockRoot(options.lockRoot, portableRoot) : filesystemMutationLockRoot(portableRoot);
31563
- await ensurePrivateLockRoot(lockRoot);
31617
+ const lockRoot = options.lockRoot !== void 0 ? explicitFilesystemMutationLockRoot(options.lockRoot, portableRoot) : filesystemMutationLockRoot(portableRoot, policy);
31618
+ await ensurePrivateLockRoot(lockRoot, policy);
31564
31619
  return lockRoot;
31565
31620
  }
31566
- var WINDOWS_DIRECTORY_CONTENTION_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
31567
- async function classifyLockClaimFailure(error, lockPath) {
31621
+ async function classifyLockClaimFailure(error, lockPath, policy) {
31568
31622
  const code2 = error.code;
31569
31623
  if (code2 === "EEXIST") return "contention";
31570
- if (process.platform !== "win32" || !WINDOWS_DIRECTORY_CONTENTION_CODES.has(code2 ?? "")) return "terminal";
31624
+ if (!policy.isDirectoryContentionError(error)) return "terminal";
31571
31625
  try {
31572
31626
  await fs.lstat(lockPath);
31573
31627
  return "contention";
31574
31628
  } catch (probeError) {
31575
31629
  const probeCode = probeError.code;
31576
- return probeCode === "ENOENT" || probeCode === "ENOTDIR" ? "unwitnessed-windows-sharing-error" : "terminal";
31630
+ return probeCode === "ENOENT" || probeCode === "ENOTDIR" ? "unwitnessed-contention-error" : "terminal";
31577
31631
  }
31578
31632
  }
31579
- async function claimLockPath(lockPath, owner, waitMs, pollMs) {
31633
+ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
31580
31634
  const started = owner.created_at_ms;
31581
- let unwitnessedWindowsRetryUsed = false;
31635
+ let unwitnessedRetryUsed = false;
31582
31636
  while (true) {
31583
31637
  try {
31584
31638
  await fs.mkdir(lockPath, { mode: 448 });
31585
31639
  } catch (err) {
31586
- const failure = await classifyLockClaimFailure(err, lockPath);
31640
+ const failure = await classifyLockClaimFailure(err, lockPath, policy);
31587
31641
  if (failure === "terminal") throw err;
31588
- if (failure === "unwitnessed-windows-sharing-error") {
31589
- if (unwitnessedWindowsRetryUsed) throw err;
31590
- unwitnessedWindowsRetryUsed = true;
31642
+ if (failure === "unwitnessed-contention-error") {
31643
+ if (unwitnessedRetryUsed) throw err;
31644
+ unwitnessedRetryUsed = true;
31591
31645
  continue;
31592
31646
  } else {
31593
- unwitnessedWindowsRetryUsed = false;
31647
+ unwitnessedRetryUsed = false;
31594
31648
  }
31595
31649
  let existingOwner = await readOwner(lockPath);
31596
31650
  if (existingOwner !== null) {
31597
- if (await quarantineStaleLock(lockPath, existingOwner)) continue;
31651
+ if (await quarantineStaleLock(lockPath, existingOwner, policy)) continue;
31598
31652
  existingOwner = await readOwner(lockPath);
31599
31653
  }
31600
31654
  if (Date.now() - started >= waitMs) throw timeoutError(lockPath, existingOwner, owner.target);
@@ -31613,24 +31667,89 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs) {
31613
31667
  });
31614
31668
  throw err;
31615
31669
  }
31616
- return async () => {
31617
- const current = await readOwner(lockPath);
31618
- if (current?.token !== owner.token) {
31619
- throw new FilesystemMutationLockError(
31620
- `refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
31621
- { lockPath, owner: current, stale: false, malformed: current === null }
31622
- );
31670
+ let inFlight;
31671
+ return () => {
31672
+ if (inFlight) return inFlight;
31673
+ inFlight = (async () => {
31674
+ const started2 = Date.now();
31675
+ const current = await resolveOwnerRecord(lockPath, started2, waitMs, pollMs);
31676
+ if (current.state === "unreadable") throw unreadableOwnerRefusal(lockPath, current.error);
31677
+ if (current.state !== "record" || current.owner.token !== owner.token) {
31678
+ throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
31679
+ }
31680
+ await removeReleasedLock(lockPath, owner, started2, waitMs, pollMs, policy);
31681
+ })();
31682
+ return inFlight.finally(() => {
31683
+ inFlight = void 0;
31684
+ });
31685
+ };
31686
+ }
31687
+ }
31688
+ function changedOwnerRefusal(lockPath, current) {
31689
+ return new FilesystemMutationLockError(
31690
+ `refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
31691
+ { lockPath, owner: current, stale: false, malformed: current === null }
31692
+ );
31693
+ }
31694
+ function unreadableOwnerRefusal(lockPath, error) {
31695
+ const message = error instanceof Error ? error.message : String(error);
31696
+ return new FilesystemMutationLockError(
31697
+ `refusing to release filesystem mutation lock '${lockPath}' because its owner record could not be read within the wait budget (${message}); the mutation may have completed, inspect the lock before retrying.`,
31698
+ { lockPath, owner: null, stale: false, malformed: true }
31699
+ );
31700
+ }
31701
+ function releasedLockRemnantPath(lockPath, owner) {
31702
+ const tokenHash = createHash("sha256").update(owner.token).digest("hex");
31703
+ return `${lockPath}.released-${tokenHash}`;
31704
+ }
31705
+ function removalFailure(inspectPath, owner, err, attempts, detail) {
31706
+ const message = err instanceof Error ? err.message : String(err);
31707
+ const suffix = attempts > 1 ? ` after ${attempts} bounded attempts` : "";
31708
+ return new FilesystemMutationLockError(
31709
+ `mutation completed but ${detail}${suffix} (${message}); inspect the lock before retrying.`,
31710
+ { lockPath: inspectPath, owner, stale: false, malformed: false }
31711
+ );
31712
+ }
31713
+ async function removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy) {
31714
+ const remnant = releasedLockRemnantPath(lockPath, owner);
31715
+ let attempts = 0;
31716
+ while (true) {
31717
+ try {
31718
+ await fs.rename(lockPath, remnant);
31719
+ break;
31720
+ } catch (err) {
31721
+ attempts += 1;
31722
+ if (attempts > 1 && err.code === "ENOENT") return;
31723
+ if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
31724
+ throw removalFailure(lockPath, owner, err, attempts, `filesystem lock '${lockPath}' could not be removed`);
31623
31725
  }
31624
- try {
31625
- await fs.rm(lockPath, { recursive: true, force: false });
31626
- } catch (err) {
31627
- const message = err instanceof Error ? err.message : String(err);
31628
- throw new FilesystemMutationLockError(
31629
- `mutation completed but filesystem lock '${lockPath}' could not be removed (${message}); inspect the lock before retrying.`,
31630
- { lockPath, owner: current, stale: false, malformed: false }
31726
+ }
31727
+ await delay(pollMs);
31728
+ const current = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
31729
+ if (current.state === "record" && current.owner.token === owner.token) continue;
31730
+ if (current.state === "unreadable") throw unreadableOwnerRefusal(lockPath, current.error);
31731
+ if (current.state === "absent" && !await pathExists(lockPath)) return;
31732
+ throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
31733
+ }
31734
+ attempts = 0;
31735
+ while (true) {
31736
+ try {
31737
+ await fs.rm(remnant, { recursive: true, force: false });
31738
+ return;
31739
+ } catch (err) {
31740
+ attempts += 1;
31741
+ if (err.code === "ENOENT") return;
31742
+ if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
31743
+ throw removalFailure(
31744
+ remnant,
31745
+ owner,
31746
+ err,
31747
+ attempts,
31748
+ `filesystem lock '${lockPath}' was released yet its remnant '${remnant}' could not be removed`
31631
31749
  );
31632
31750
  }
31633
- };
31751
+ }
31752
+ await delay(pollMs);
31634
31753
  }
31635
31754
  }
31636
31755
  function newOwner(target) {
@@ -31648,21 +31767,20 @@ function assertIdentityKey(key) {
31648
31767
  }
31649
31768
  async function acquireFilesystemIdentityLock(key, identity, options = {}) {
31650
31769
  assertIdentityKey(key);
31770
+ const policy = captureFilesystemHostPolicy(options.hostPolicy);
31651
31771
  const waitMs = positiveOption(options.waitMs, DEFAULT_WAIT_MS, "waitMs");
31652
31772
  const pollMs = positiveOption(options.pollMs, DEFAULT_POLL_MS, "pollMs");
31653
31773
  const owner = newOwner(identity);
31654
- const lockRoot = await selectLockRoot(options);
31655
- return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs);
31774
+ const lockRoot = await selectLockRoot(options, policy);
31775
+ return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs, policy);
31656
31776
  }
31657
31777
 
31658
31778
  // ../core/src/filesystem-identity.ts
31659
- var WINDOWS_REPLACE_CONFLICT_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
31660
31779
  async function replaceExistingLeaf(port, tmp, target, rel) {
31661
31780
  try {
31662
31781
  await port.rename(tmp, target);
31663
31782
  } catch (err) {
31664
- const code2 = err.code ?? "";
31665
- if ((port.platform ?? process.platform) === "win32" && WINDOWS_REPLACE_CONFLICT_CODES.has(code2)) {
31783
+ if (port.isReplacementConflict?.(err)) {
31666
31784
  throw new ConcurrentReplacementError(rel, 1, 10);
31667
31785
  }
31668
31786
  throw err;
@@ -31829,7 +31947,7 @@ async function observeExact(port, root, rel, read) {
31829
31947
  opened = await port.open(target);
31830
31948
  } catch (err) {
31831
31949
  if (isAbsentPathError(err)) return { state: "absent" };
31832
- if (process.platform === "win32" && err.code === "EPERM") {
31950
+ if (port.isTransientOpenError?.(err)) {
31833
31951
  restarts = countRestart(restarts, rel);
31834
31952
  continue;
31835
31953
  }
@@ -31863,7 +31981,15 @@ async function probeExact(port, root, rel) {
31863
31981
  restarts = countRestart(restarts, rel);
31864
31982
  }
31865
31983
  }
31866
- var linkUnsupportedRoots = /* @__PURE__ */ new Set();
31984
+ var linkUnsupportedRoots = /* @__PURE__ */ new WeakMap();
31985
+ function unsupportedLinkRoots(port) {
31986
+ let roots = linkUnsupportedRoots.get(port);
31987
+ if (!roots) {
31988
+ roots = /* @__PURE__ */ new Set();
31989
+ linkUnsupportedRoots.set(port, roots);
31990
+ }
31991
+ return roots;
31992
+ }
31867
31993
  var queues = /* @__PURE__ */ new Map();
31868
31994
  function enqueue(key, fn) {
31869
31995
  const tail = queues.get(key) ?? Promise.resolve();
@@ -31945,7 +32071,7 @@ async function createExactLeaf(port, tmp, target, rel, leaf, rootResolved) {
31945
32071
  return;
31946
32072
  }
31947
32073
  if (outcome === "unsupported") {
31948
- linkUnsupportedRoots.add(rootResolved);
32074
+ unsupportedLinkRoots(port).add(rootResolved);
31949
32075
  await port.rename(tmp, target);
31950
32076
  return;
31951
32077
  }
@@ -31981,7 +32107,7 @@ async function mutateExact(port, root, rel, body) {
31981
32107
  await replaceExistingLeaf(port, tmp, target, rel);
31982
32108
  return;
31983
32109
  }
31984
- if (linkUnsupportedRoots.has(rootResolved)) {
32110
+ if (unsupportedLinkRoots(port).has(rootResolved)) {
31985
32111
  await port.rename(tmp, target);
31986
32112
  return;
31987
32113
  }
@@ -32008,80 +32134,84 @@ function kindOf(stats) {
32008
32134
  if (stats.isFile()) return "file";
32009
32135
  return "other";
32010
32136
  }
32011
- var nodeFilesystemIdentityPort = Object.freeze({
32012
- platform: process.platform,
32013
- async probe(target) {
32014
- let lstats;
32015
- try {
32016
- lstats = await fs2.lstat(target);
32017
- } catch (err) {
32018
- if (isAbsentPathError(err)) return null;
32019
- throw err;
32020
- }
32021
- return { kind: kindOf(lstats), dev: lstats.dev, ino: lstats.ino };
32022
- },
32023
- async entries(dir) {
32024
- try {
32025
- const dirents = await fs2.readdir(dir, { withFileTypes: true });
32026
- return dirents.map((dirent) => ({ name: dirent.name, kind: kindOf(dirent) }));
32027
- } catch (err) {
32028
- if (isAbsentPathError(err)) return null;
32029
- throw err;
32030
- }
32031
- },
32032
- async open(target) {
32033
- const handle = await fs2.open(target, "r");
32034
- try {
32035
- const stats = await handle.stat();
32036
- return { handle, dev: stats.dev, ino: stats.ino };
32037
- } catch (err) {
32038
- await handle.close().catch(() => {
32039
- });
32040
- throw err;
32041
- }
32042
- },
32043
- readAll(handle) {
32044
- return handle.readFile();
32045
- },
32046
- close(handle) {
32047
- return handle.close();
32048
- },
32049
- async stat(target) {
32050
- return { mtime: (await fs2.stat(target)).mtime };
32051
- },
32052
- async mkdir(dir) {
32053
- try {
32054
- await fs2.mkdir(dir);
32055
- return "created";
32056
- } catch (err) {
32057
- if (err.code === "EEXIST") return "exists";
32058
- throw err;
32059
- }
32060
- },
32061
- async writeTemp(dir, name, bytes) {
32062
- await fs2.writeFile(path2.join(dir, name), bytes);
32063
- },
32064
- async link(from, to) {
32065
- try {
32066
- await fs2.link(from, to);
32067
- return "linked";
32068
- } catch (err) {
32069
- const code2 = err.code;
32070
- if (code2 === "EEXIST") return "exists";
32071
- if (code2 === "EPERM" || code2 === "ENOTSUP" || code2 === "EOPNOTSUPP" || code2 === "EXDEV") return "unsupported";
32072
- throw err;
32137
+ function createNodeFilesystemIdentityPort(hostPolicy) {
32138
+ const policy = captureFilesystemHostPolicy(hostPolicy);
32139
+ return Object.freeze({
32140
+ isTransientOpenError: policy.isTransientOpenError,
32141
+ isReplacementConflict: policy.isReplacementConflict,
32142
+ async probe(target) {
32143
+ let lstats;
32144
+ try {
32145
+ lstats = await fs2.lstat(target);
32146
+ } catch (err) {
32147
+ if (isAbsentPathError(err)) return null;
32148
+ throw err;
32149
+ }
32150
+ return { kind: kindOf(lstats), dev: lstats.dev, ino: lstats.ino };
32151
+ },
32152
+ async entries(dir) {
32153
+ try {
32154
+ const dirents = await fs2.readdir(dir, { withFileTypes: true });
32155
+ return dirents.map((dirent) => ({ name: dirent.name, kind: kindOf(dirent) }));
32156
+ } catch (err) {
32157
+ if (isAbsentPathError(err)) return null;
32158
+ throw err;
32159
+ }
32160
+ },
32161
+ async open(target) {
32162
+ const handle = await fs2.open(target, "r");
32163
+ try {
32164
+ const stats = await handle.stat();
32165
+ return { handle, dev: stats.dev, ino: stats.ino };
32166
+ } catch (err) {
32167
+ await handle.close().catch(() => {
32168
+ });
32169
+ throw err;
32170
+ }
32171
+ },
32172
+ readAll(handle) {
32173
+ return handle.readFile();
32174
+ },
32175
+ close(handle) {
32176
+ return handle.close();
32177
+ },
32178
+ async stat(target) {
32179
+ return { mtime: (await fs2.stat(target)).mtime };
32180
+ },
32181
+ async mkdir(dir) {
32182
+ try {
32183
+ await fs2.mkdir(dir);
32184
+ return "created";
32185
+ } catch (err) {
32186
+ if (err.code === "EEXIST") return "exists";
32187
+ throw err;
32188
+ }
32189
+ },
32190
+ async writeTemp(dir, name, bytes) {
32191
+ await fs2.writeFile(path2.join(dir, name), bytes);
32192
+ },
32193
+ async link(from, to) {
32194
+ try {
32195
+ await fs2.link(from, to);
32196
+ return "linked";
32197
+ } catch (err) {
32198
+ const code2 = err.code;
32199
+ if (code2 === "EEXIST") return "exists";
32200
+ if (code2 === "EPERM" || code2 === "ENOTSUP" || code2 === "EOPNOTSUPP" || code2 === "EXDEV") return "unsupported";
32201
+ throw err;
32202
+ }
32203
+ },
32204
+ async rename(from, to) {
32205
+ await fs2.rename(from, to);
32206
+ },
32207
+ async unlink(target) {
32208
+ await fs2.unlink(target);
32209
+ },
32210
+ claim(key, identity) {
32211
+ return acquireFilesystemIdentityLock(key, `${identity.root}:${identity.rel}`, { portableRoot: identity.root, hostPolicy: policy });
32073
32212
  }
32074
- },
32075
- async rename(from, to) {
32076
- await fs2.rename(from, to);
32077
- },
32078
- async unlink(target) {
32079
- await fs2.unlink(target);
32080
- },
32081
- claim(key, identity) {
32082
- return acquireFilesystemIdentityLock(key, `${identity.root}:${identity.rel}`, { portableRoot: identity.root });
32083
- }
32084
- });
32213
+ });
32214
+ }
32085
32215
 
32086
32216
  // ../core/src/sha256.ts
32087
32217
  var K = new Uint32Array([
@@ -32240,6 +32370,9 @@ function sha256HexOfUtf8(input) {
32240
32370
  }
32241
32371
 
32242
32372
  // ../core/src/version-transport.ts
32373
+ function isContentVersion(value) {
32374
+ return typeof value === "string" && /^sha256:[0-9a-f]{64}$/.test(value);
32375
+ }
32243
32376
  var VersionConflict = class extends Error {
32244
32377
  name = "VersionConflict";
32245
32378
  id;
@@ -32271,7 +32404,7 @@ function defaultActor() {
32271
32404
  }
32272
32405
 
32273
32406
  // ../core/src/backend.ts
32274
- var publicationRoots = /* @__PURE__ */ new WeakMap();
32407
+ var publicationContexts = /* @__PURE__ */ new WeakMap();
32275
32408
  function firstString(...vals) {
32276
32409
  for (const v of vals) {
32277
32410
  if (typeof v === "string" && v.trim() !== "") return v;
@@ -32290,22 +32423,22 @@ function notFound(root, rel) {
32290
32423
  err.path = target;
32291
32424
  return err;
32292
32425
  }
32293
- async function readBytes(handle) {
32426
+ async function readBytes(port, handle) {
32294
32427
  try {
32295
- return await nodeFilesystemIdentityPort.readAll(handle);
32428
+ return await port.readAll(handle);
32296
32429
  } catch (err) {
32297
32430
  if (isAbsentFileError(err)) return null;
32298
32431
  throw err;
32299
32432
  }
32300
32433
  }
32301
- async function walkFiles(root, keep, sub = "") {
32302
- const entries = await nodeFilesystemIdentityPort.entries(path3.join(root, sub)) ?? [];
32434
+ async function walkFiles(port, root, keep, sub = "") {
32435
+ const entries = await port.entries(path3.join(root, sub)) ?? [];
32303
32436
  const out = [];
32304
32437
  for (const entry of entries) {
32305
32438
  if (entry.name.startsWith(".")) continue;
32306
32439
  const rel = sub === "" ? entry.name : `${sub}/${entry.name}`;
32307
32440
  if (entry.kind === "directory") {
32308
- out.push(...await walkFiles(root, keep, rel));
32441
+ out.push(...await walkFiles(port, root, keep, rel));
32309
32442
  } else if (entry.kind === "file" && keep(entry.name)) {
32310
32443
  out.push(rel);
32311
32444
  }
@@ -32313,19 +32446,20 @@ async function walkFiles(root, keep, sub = "") {
32313
32446
  return out;
32314
32447
  }
32315
32448
  function reservedPath(dir, name) {
32316
- const d = toPosix(dir).replace(/^\.?\//, "").replace(/\/$/, "");
32317
- return d === "" ? name : `${d}/${name}`;
32449
+ return dir === "" ? name : `${dir}/${name}`;
32318
32450
  }
32319
32451
  var FilesystemBackend = class _FilesystemBackend {
32320
32452
  #root;
32453
+ #port;
32321
32454
  /**
32322
32455
  * The root is resolved once here: a relative root would otherwise re-resolve against the
32323
32456
  * process's current directory on every operation, so a later `chdir` would silently move the
32324
32457
  * bundle and derive a different identity key.
32325
32458
  */
32326
- constructor(root) {
32459
+ constructor(root, options = {}) {
32460
+ this.#port = createNodeFilesystemIdentityPort(options.hostPolicy);
32327
32461
  this.#root = path3.resolve(root);
32328
- publicationRoots.set(this, this.#root);
32462
+ publicationContexts.set(this, { root: this.#root, port: this.#port });
32329
32463
  }
32330
32464
  async read(id) {
32331
32465
  return this.#readWithEdition(id, () => this.#readEdition());
@@ -32341,7 +32475,7 @@ var FilesystemBackend = class _FilesystemBackend {
32341
32475
  async #readWithEdition(id, edition) {
32342
32476
  assertSafeConceptId(id);
32343
32477
  const rel = pathFromConceptId(id);
32344
- const observed = await observeExact(nodeFilesystemIdentityPort, this.#root, rel, readBytes);
32478
+ const observed = await observeExact(this.#port, this.#root, rel, (handle) => readBytes(this.#port, handle));
32345
32479
  if (observed.state === "absent") throw notFound(this.#root, rel);
32346
32480
  const raw = observed.value.toString("utf8");
32347
32481
  const { frontmatter: frontmatter2, body } = parseMarkdown(raw, rel, { okfVersion: await edition() });
@@ -32363,7 +32497,7 @@ var FilesystemBackend = class _FilesystemBackend {
32363
32497
  assertSafeConceptId(id);
32364
32498
  const raw = stringifyDoc(doc.frontmatter, doc.body ?? "");
32365
32499
  const rel = pathFromConceptId(id);
32366
- return mutateExact(nodeFilesystemIdentityPort, this.#root, rel, async (target) => {
32500
+ return mutateExact(this.#port, this.#root, rel, async (target) => {
32367
32501
  if (options.expectedVersion !== void 0) {
32368
32502
  const bytes = await target.current();
32369
32503
  const current = bytes === null ? null : versionOfBytes(bytes.toString("utf8"));
@@ -32377,7 +32511,7 @@ var FilesystemBackend = class _FilesystemBackend {
32377
32511
  }
32378
32512
  async delete(id, options = {}) {
32379
32513
  assertSafeConceptId(id);
32380
- return mutateExact(nodeFilesystemIdentityPort, this.#root, pathFromConceptId(id), async (target) => {
32514
+ return mutateExact(this.#port, this.#root, pathFromConceptId(id), async (target) => {
32381
32515
  const bytes = await target.current();
32382
32516
  if (bytes === null) return false;
32383
32517
  const current = versionOfBytes(bytes.toString("utf8"));
@@ -32391,10 +32525,10 @@ var FilesystemBackend = class _FilesystemBackend {
32391
32525
  async versions(id) {
32392
32526
  assertSafeConceptId(id);
32393
32527
  const rel = pathFromConceptId(id);
32394
- const observed = await observeExact(nodeFilesystemIdentityPort, this.#root, rel, async (handle, target) => {
32528
+ const observed = await observeExact(this.#port, this.#root, rel, async (handle, target) => {
32395
32529
  try {
32396
- const bytes = await nodeFilesystemIdentityPort.readAll(handle);
32397
- const { mtime: mtime2 } = await nodeFilesystemIdentityPort.stat(target);
32530
+ const bytes = await this.#port.readAll(handle);
32531
+ const { mtime: mtime2 } = await this.#port.stat(target);
32398
32532
  return { raw: bytes.toString("utf8"), mtime: mtime2 };
32399
32533
  } catch {
32400
32534
  return null;
@@ -32409,10 +32543,10 @@ var FilesystemBackend = class _FilesystemBackend {
32409
32543
  }
32410
32544
  async exists(id) {
32411
32545
  assertSafeConceptId(id);
32412
- return (await probeExact(nodeFilesystemIdentityPort, this.#root, pathFromConceptId(id))).state === "exact";
32546
+ return (await probeExact(this.#port, this.#root, pathFromConceptId(id))).state === "exact";
32413
32547
  }
32414
32548
  async list(prefix) {
32415
- const files = await walkFiles(this.#root, (name) => name.endsWith(".md"));
32549
+ const files = await walkFiles(this.#port, this.#root, (name) => name.endsWith(".md"));
32416
32550
  const ids = [];
32417
32551
  for (const rel of files) {
32418
32552
  if (isReservedFile(rel)) continue;
@@ -32426,20 +32560,22 @@ var FilesystemBackend = class _FilesystemBackend {
32426
32560
  if (prefix && !id.startsWith(prefix)) continue;
32427
32561
  ids.push(id);
32428
32562
  }
32429
- ids.sort((a, b) => a.localeCompare(b));
32563
+ ids.sort(compareStorageKeys);
32430
32564
  return ids;
32431
32565
  }
32432
32566
  async readReserved(dir, name) {
32433
32567
  assertSafeReservedDir(dir);
32434
- const observed = await observeExact(nodeFilesystemIdentityPort, this.#root, reservedPath(dir, name), readBytes);
32568
+ assertSafeReservedFilename(name);
32569
+ const observed = await observeExact(this.#port, this.#root, reservedPath(dir, name), (handle) => readBytes(this.#port, handle));
32435
32570
  if (observed.state === "absent") return null;
32436
32571
  const content3 = observed.value.toString("utf8");
32437
32572
  return { content: content3, version: versionOfBytes(content3) };
32438
32573
  }
32439
32574
  async writeReserved(dir, name, content3, options = {}) {
32440
32575
  assertSafeReservedDir(dir);
32576
+ assertSafeReservedFilename(name);
32441
32577
  const rel = reservedPath(dir, name);
32442
- return mutateExact(nodeFilesystemIdentityPort, this.#root, rel, async (target) => {
32578
+ return mutateExact(this.#port, this.#root, rel, async (target) => {
32443
32579
  if (options.expectedVersion !== void 0) {
32444
32580
  const bytes = await target.current();
32445
32581
  const current = bytes === null ? null : versionOfBytes(bytes.toString("utf8"));
@@ -32454,14 +32590,14 @@ var FilesystemBackend = class _FilesystemBackend {
32454
32590
  // ── blobs: opaque bytes + a content-type ──────────────────────────────────
32455
32591
  async readBlob(key) {
32456
32592
  assertSafeBlobKey(key);
32457
- const observed = await observeExact(nodeFilesystemIdentityPort, this.#root, key, readBytes);
32593
+ const observed = await observeExact(this.#port, this.#root, key, (handle) => readBytes(this.#port, handle));
32458
32594
  if (observed.state === "absent") return null;
32459
32595
  const bytes = observed.value;
32460
32596
  return { bytes, contentType: resolveContentType(key), version: blobVersion(bytes) };
32461
32597
  }
32462
32598
  async writeBlob(key, bytes, _contentType, options = {}) {
32463
32599
  assertSafeBlobKey(key);
32464
- return mutateExact(nodeFilesystemIdentityPort, this.#root, key, async (target) => {
32600
+ return mutateExact(this.#port, this.#root, key, async (target) => {
32465
32601
  if (options.expectedVersion !== void 0) {
32466
32602
  const existing = await target.current();
32467
32603
  const current = existing === null ? null : blobVersion(existing);
@@ -32475,7 +32611,7 @@ var FilesystemBackend = class _FilesystemBackend {
32475
32611
  }
32476
32612
  async deleteBlob(key, options = {}) {
32477
32613
  assertSafeBlobKey(key);
32478
- return mutateExact(nodeFilesystemIdentityPort, this.#root, key, async (target) => {
32614
+ return mutateExact(this.#port, this.#root, key, async (target) => {
32479
32615
  const existing = await target.current();
32480
32616
  if (existing === null) return false;
32481
32617
  const current = blobVersion(existing);
@@ -32488,14 +32624,14 @@ var FilesystemBackend = class _FilesystemBackend {
32488
32624
  }
32489
32625
  async existsBlob(key) {
32490
32626
  assertSafeBlobKey(key);
32491
- const observed = await probeExact(nodeFilesystemIdentityPort, this.#root, key);
32627
+ const observed = await probeExact(this.#port, this.#root, key);
32492
32628
  return observed.state === "exact" && observed.value.kind === "file";
32493
32629
  }
32494
32630
  async listBlobs(prefix) {
32495
- const files = await walkFiles(this.#root, (name) => !name.toLowerCase().endsWith(".md"));
32631
+ const files = await walkFiles(this.#port, this.#root, (name) => !name.toLowerCase().endsWith(".md"));
32496
32632
  const keys = files.map(toPosix);
32497
32633
  const filtered = prefix ? keys.filter((k) => k.startsWith(prefix)) : keys;
32498
- filtered.sort((a, b) => a.localeCompare(b));
32634
+ filtered.sort(compareStorageKeys);
32499
32635
  return filtered;
32500
32636
  }
32501
32637
  capabilities() {
@@ -32508,24 +32644,25 @@ var FilesystemBackend = class _FilesystemBackend {
32508
32644
  };
32509
32645
  }
32510
32646
  };
32511
- function publicationRoot(backend) {
32512
- const root = publicationRoots.get(backend);
32513
- if (!root) throw new InvalidInputError("Unknown filesystem backend publication source.");
32514
- return root;
32647
+ function publicationContext(backend) {
32648
+ const context = publicationContexts.get(backend);
32649
+ if (!context) throw new InvalidInputError("Unknown filesystem backend publication source.");
32650
+ return context;
32515
32651
  }
32516
32652
  async function readRawFilesystemDocument(backend, id) {
32517
32653
  assertSafeConceptId(id);
32518
- const root = publicationRoot(backend);
32654
+ const { root, port } = publicationContext(backend);
32519
32655
  const rel = pathFromConceptId(id);
32520
- const observed = await observeExact(nodeFilesystemIdentityPort, root, rel, readBytes);
32656
+ const observed = await observeExact(port, root, rel, (handle) => readBytes(port, handle));
32521
32657
  if (observed.state === "absent") throw notFound(root, rel);
32522
32658
  const raw = new TextDecoder("utf-8", { fatal: true }).decode(observed.value);
32523
32659
  return { bytes: observed.value.slice(), version: versionOfBytes(raw) };
32524
32660
  }
32525
32661
  async function readRawFilesystemReserved(backend, dir, name) {
32526
32662
  assertSafeReservedDir(dir);
32527
- const root = publicationRoot(backend);
32528
- const observed = await observeExact(nodeFilesystemIdentityPort, root, reservedPath(dir, name), readBytes);
32663
+ assertSafeReservedFilename(name);
32664
+ const { root, port } = publicationContext(backend);
32665
+ const observed = await observeExact(port, root, reservedPath(dir, name), (handle) => readBytes(port, handle));
32529
32666
  if (observed.state === "absent") return null;
32530
32667
  return {
32531
32668
  bytes: observed.value.slice(),
@@ -32533,15 +32670,16 @@ async function readRawFilesystemReserved(backend, dir, name) {
32533
32670
  };
32534
32671
  }
32535
32672
  async function listFilesystemReservedObjects(backend) {
32536
- const root = publicationRoot(backend);
32537
- const files = await walkFiles(root, (name) => name === "index.md" || name === "log.md");
32673
+ const { root, port } = publicationContext(backend);
32674
+ const files = await walkFiles(port, root, (name) => name === "index.md" || name === "log.md");
32538
32675
  return files.map((relative) => {
32539
32676
  const slash = relative.lastIndexOf("/");
32540
32677
  const dir = slash === -1 ? "" : relative.slice(0, slash);
32541
32678
  const name = relative.slice(slash + 1);
32542
32679
  assertSafeReservedDir(dir);
32680
+ assertSafeReservedFilename(name);
32543
32681
  return { dir, name };
32544
- }).sort((a, b) => reservedPath(a.dir, a.name).localeCompare(reservedPath(b.dir, b.name)));
32682
+ }).sort((a, b) => compareStorageKeys(reservedPath(a.dir, a.name), reservedPath(b.dir, b.name)));
32545
32683
  }
32546
32684
 
32547
32685
  // ../core/src/bundle-ops.ts
@@ -32581,7 +32719,7 @@ setDefaultBackendFactory((root) => new FilesystemBackend(root));
32581
32719
  var CONVENTIONS_PREFIX = "conventions/";
32582
32720
  var CONVENTION_TYPE = "Convention";
32583
32721
  function buildKindRegistry(docs, initialWarnings = [], options = {}) {
32584
- const kinds = /* @__PURE__ */ new Map();
32722
+ const accepted = /* @__PURE__ */ new Map();
32585
32723
  const warnings = [...initialWarnings];
32586
32724
  for (const doc of [...docs].sort((a, b) => a.id.localeCompare(b.id))) {
32587
32725
  const parsed = parseConventionDoc(doc, options);
@@ -32604,10 +32742,10 @@ function buildKindRegistry(docs, initialWarnings = [], options = {}) {
32604
32742
  severity: "warning"
32605
32743
  });
32606
32744
  }
32607
- if (kinds.has(kind.governs)) {
32745
+ if (accepted.has(kind.governs)) {
32608
32746
  warnings.push({
32609
32747
  code: "KIND_DUPLICATE_GOVERNS",
32610
- message: `duplicate kind convention for '${kind.governs}': '${doc.id}' ignored, keeping the first-declared '${kinds.get(kind.governs).id}'.`,
32748
+ message: `duplicate kind convention for '${kind.governs}': '${doc.id}' ignored, keeping the first-declared '${accepted.get(kind.governs).id}'.`,
32611
32749
  field: kind.governs,
32612
32750
  severity: "warning"
32613
32751
  });
@@ -32621,10 +32759,18 @@ function buildKindRegistry(docs, initialWarnings = [], options = {}) {
32621
32759
  severity: "warning"
32622
32760
  });
32623
32761
  }
32624
- kinds.set(kind.governs, kind);
32762
+ accepted.set(kind.governs, kind);
32625
32763
  }
32764
+ const kinds = /* @__PURE__ */ new Map();
32765
+ for (const kind of [...accepted.values()].sort(compareReadingOrder)) kinds.set(kind.governs, kind);
32626
32766
  return { kinds, warnings };
32627
32767
  }
32768
+ function compareReadingOrder(a, b) {
32769
+ if (a.order !== void 0 && b.order !== void 0 && a.order !== b.order) return a.order - b.order;
32770
+ if (a.order !== void 0 && b.order === void 0) return -1;
32771
+ if (a.order === void 0 && b.order !== void 0) return 1;
32772
+ return a.id.localeCompare(b.id);
32773
+ }
32628
32774
  var PROGRESS_STATUS_FIELD = "progress_status";
32629
32775
  var SUPERBEE_PROGRESS_STATUS_FIELD = "superbee_progress_status";
32630
32776
  function progressStatusStorageField(okfVersion) {
@@ -33093,6 +33239,19 @@ function parseConventionDoc(doc, options = {}) {
33093
33239
  const path6 = typeof fm.path === "string" && fm.path.trim() !== "" ? fm.path.trim() : void 0;
33094
33240
  const freshnessHorizon = typeof fm.freshness_horizon === "string" && fm.freshness_horizon.trim() !== "" ? fm.freshness_horizon.trim() : void 0;
33095
33241
  const browseCollapsed = fm.browse_collapsed === true ? true : void 0;
33242
+ let order;
33243
+ if (fm.order !== void 0) {
33244
+ if (typeof fm.order === "number" && Number.isFinite(fm.order)) {
33245
+ order = fm.order;
33246
+ } else {
33247
+ warnings.push({
33248
+ code: "KIND_CONVENTION_BAD_SHAPE",
33249
+ message: `kind convention '${doc.id}' has an invalid 'order' (${describeShape(fm.order)}; expected a finite number such as 10); ignoring it.`,
33250
+ field: "order",
33251
+ severity: "warning"
33252
+ });
33253
+ }
33254
+ }
33096
33255
  const kind = {
33097
33256
  id: doc.id,
33098
33257
  title,
@@ -33107,6 +33266,7 @@ function parseConventionDoc(doc, options = {}) {
33107
33266
  if (sections && sections.length > 0) kind.sections = sections;
33108
33267
  if (freshnessHorizon !== void 0) kind.freshnessHorizon = freshnessHorizon;
33109
33268
  if (browseCollapsed !== void 0) kind.browseCollapsed = browseCollapsed;
33269
+ if (order !== void 0) kind.order = order;
33110
33270
  if (claim !== void 0) kind.claim = claim;
33111
33271
  return {
33112
33272
  ok: true,
@@ -33257,7 +33417,7 @@ function isAnyEntryKey(entry) {
33257
33417
  return isPageEntryKey(entry) || isViewEntryKey(entry);
33258
33418
  }
33259
33419
  function isViewEntryVersion(value) {
33260
- return typeof value === "string" && /^sha256:[0-9a-f]{64}$/.test(value);
33420
+ return isContentVersion(value);
33261
33421
  }
33262
33422
  function parseRegistration(id, frontmatter2) {
33263
33423
  if (!isAnyRegistryId(id) || !isPageTypeName(frontmatter2.type) || !isAnyEntryKey(frontmatter2.entry)) return null;
@@ -42193,6 +42353,35 @@ function isBareLinkBlock(node2, fromId) {
42193
42353
  function safeLanguageClass(lang) {
42194
42354
  return typeof lang === "string" && /^[\w+-]{1,24}$/.test(lang) ? `doc-code-${lang}` : void 0;
42195
42355
  }
42356
+ function isIpLiteralHost(hostname2) {
42357
+ return hostname2.startsWith("[") || /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname2);
42358
+ }
42359
+ function normalizeAllowedHost(entry) {
42360
+ if (typeof entry !== "string" || entry === "" || /[\s/?#@:\\]/.test(entry)) return null;
42361
+ let url;
42362
+ try {
42363
+ url = new URL(`https://${entry}/`);
42364
+ } catch {
42365
+ return null;
42366
+ }
42367
+ if (url.href !== `https://${url.hostname}/` || isIpLiteralHost(url.hostname)) return null;
42368
+ return url.hostname;
42369
+ }
42370
+ function admitExternalLink(raw, hosts) {
42371
+ if (!Array.isArray(hosts) || hosts.length === 0) return null;
42372
+ let url;
42373
+ try {
42374
+ url = new URL(raw);
42375
+ } catch {
42376
+ return null;
42377
+ }
42378
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "") return null;
42379
+ if (isIpLiteralHost(url.hostname)) return null;
42380
+ for (const entry of hosts) {
42381
+ if (normalizeAllowedHost(entry) === url.hostname) return url.href;
42382
+ }
42383
+ return null;
42384
+ }
42196
42385
  function documentHref(id, options) {
42197
42386
  const href = options.hrefForDoc?.(id) ?? `?view=doc&id=${encodeURIComponent(id)}`;
42198
42387
  const base = "https://renderer.invalid/";
@@ -42267,9 +42456,14 @@ function renderNode(node2, state, depth, index2) {
42267
42456
  case "break":
42268
42457
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}, index2);
42269
42458
  case "link": {
42270
- const resolved = resolveConceptId(state.options.fromId, node2.url ?? "");
42459
+ const raw = node2.url ?? "";
42460
+ const resolved = resolveConceptId(state.options.fromId, raw);
42271
42461
  const children = renderChildren(node2, state, depth);
42272
42462
  if (resolved === null) {
42463
+ const external = admitExternalLink(raw, state.options.externalLinkHosts);
42464
+ if (external !== null) {
42465
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: external, rel: "noopener noreferrer", target: "_blank", className: "doc-link-external", children }, index2);
42466
+ }
42273
42467
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
42274
42468
  "span",
42275
42469
  {
@@ -42413,8 +42607,11 @@ function renderMarkdownToStaticHtml(body, options) {
42413
42607
  );
42414
42608
  return { html, bounded: rendered.bounded, limits: rendered.limits };
42415
42609
  }
42416
- function renderDocumentToStaticHtml(document3) {
42417
- const rendered = renderMarkdownToStaticHtml(document3.body, { fromId: document3.id });
42610
+ function renderDocumentToStaticHtml(document3, options = {}) {
42611
+ const rendered = renderMarkdownToStaticHtml(document3.body, {
42612
+ fromId: document3.id,
42613
+ externalLinkHosts: options.externalLinkHosts
42614
+ });
42418
42615
  return { html: rendered.html, bounded: rendered.bounded };
42419
42616
  }
42420
42617
 
@@ -42447,9 +42644,45 @@ var MAX_QUERY_ROWS = 500;
42447
42644
  var MAX_EDGE_ROWS = 1e3;
42448
42645
  var MAX_DOCUMENT_BODY_BYTES = 1024 * 1024;
42449
42646
  var MAX_REPLY_BYTES = 2 * 1024 * 1024;
42647
+ var GRAPH_MAX_DOCUMENTS = 1e3;
42648
+ var GRAPH_MAX_RELATIONSHIPS = 1e4;
42450
42649
  var MAX_CHANGE_ROWS = 100;
42451
42650
  var MAX_CHANGE_BYTES = 256 * 1024;
42452
42651
  var MAX_SUBSCRIPTION_HEADS = 1e4;
42652
+ var MAX_HOST_CAPABILITY_BYTES = 128;
42653
+ var MAX_HOST_REQUEST_BYTES = 64 * 1024;
42654
+ var HOST_CAPABILITY_NAME = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;
42655
+ var BRIDGE_SERVICE_LIMITS = Object.freeze({
42656
+ query: MAX_QUERY_ROWS,
42657
+ edges: MAX_EDGE_ROWS,
42658
+ graphDocuments: GRAPH_MAX_DOCUMENTS,
42659
+ graphRelationships: GRAPH_MAX_RELATIONSHIPS,
42660
+ replyBytes: MAX_REPLY_BYTES
42661
+ });
42662
+ var BRIDGE_HOST_CAPABILITIES = Object.freeze({
42663
+ queryKindProjection: "query.kind-projection",
42664
+ queryFieldOr: "query.field-or",
42665
+ queryOpen: "query.open",
42666
+ queryCount: "query.count",
42667
+ edges: "edges",
42668
+ renderDocument: "render-document",
42669
+ openPage: "open-page",
42670
+ subscribeDeltas: "subscribe-deltas",
42671
+ graph: "graph",
42672
+ graphModel: "graph.model",
42673
+ recordOpen: "record.open",
42674
+ frameResize: "frame.resize"
42675
+ });
42676
+ var BRIDGE_SERVICE_CAPABILITIES = Object.freeze([
42677
+ BRIDGE_HOST_CAPABILITIES.queryKindProjection,
42678
+ BRIDGE_HOST_CAPABILITIES.queryFieldOr,
42679
+ BRIDGE_HOST_CAPABILITIES.queryOpen,
42680
+ BRIDGE_HOST_CAPABILITIES.queryCount,
42681
+ BRIDGE_HOST_CAPABILITIES.edges,
42682
+ BRIDGE_HOST_CAPABILITIES.graph,
42683
+ BRIDGE_HOST_CAPABILITIES.renderDocument
42684
+ ]);
42685
+ var V0_REQUEST_TYPES = /* @__PURE__ */ new Set(["hello", "query", "read", "render-document", "edges", "graph", "subscribe", "host", "open-page"]);
42453
42686
  function isPlainRecord(value) {
42454
42687
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
42455
42688
  const prototype = Object.getPrototypeOf(value);
@@ -42582,6 +42815,45 @@ function parseBridgeRequest(value) {
42582
42815
  const params = normalizeEdgeParams(value.params);
42583
42816
  return params ? { bridge: BRIDGE_PROTOCOL, type: "edges", id, params } : null;
42584
42817
  }
42818
+ if (value.type === "graph") {
42819
+ const expected = value.includeBodies === void 0 ? ["bridge", "type", "id"] : ["bridge", "type", "id", "includeBodies"];
42820
+ if (!exactKeys(value, expected)) return null;
42821
+ if (value.includeBodies !== void 0 && typeof value.includeBodies !== "boolean") return null;
42822
+ return { bridge: BRIDGE_PROTOCOL, type: "graph", id, includeBodies: value.includeBodies === true };
42823
+ }
42824
+ if (value.type === "host") {
42825
+ const expected = value.input === void 0 ? ["bridge", "type", "id", "capability"] : ["bridge", "type", "id", "capability", "input"];
42826
+ if (!exactKeys(value, expected)) return null;
42827
+ const capability = boundedString(value.capability, MAX_HOST_CAPABILITY_BYTES);
42828
+ if (!capability || !HOST_CAPABILITY_NAME.test(capability)) return null;
42829
+ if (value.input !== void 0 && !isPlainRecord(value.input)) return null;
42830
+ let bytes;
42831
+ try {
42832
+ bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
42833
+ } catch {
42834
+ return null;
42835
+ }
42836
+ if (bytes > MAX_HOST_REQUEST_BYTES) return null;
42837
+ return {
42838
+ bridge: BRIDGE_PROTOCOL,
42839
+ type: "host",
42840
+ id,
42841
+ capability,
42842
+ ...value.input === void 0 ? {} : { input: value.input }
42843
+ };
42844
+ }
42845
+ return null;
42846
+ }
42847
+ function unsupportedRequest(value) {
42848
+ if (!isPlainRecord(value) || typeof value.type !== "string") return null;
42849
+ if (value.bridge === BRIDGE_PROTOCOL) {
42850
+ return V0_REQUEST_TYPES.has(value.type) ? null : { bridge: BRIDGE_PROTOCOL, id: requestId(value.id) ?? void 0 };
42851
+ }
42852
+ if (value.bridge === ACTION_BRIDGE_PROTOCOL) {
42853
+ if (value.type === "read-versioned") return null;
42854
+ const id = value.type === "action.propose" ? value.requestId : value.id;
42855
+ return { bridge: ACTION_BRIDGE_PROTOCOL, id: requestId(id) ?? void 0 };
42856
+ }
42585
42857
  return null;
42586
42858
  }
42587
42859
  function ok3(id, bridge, type, result) {
@@ -42606,6 +42878,17 @@ var BridgeService = class {
42606
42878
  async handle(launchId, rawRequest) {
42607
42879
  const request = parseBridgeRequest(rawRequest);
42608
42880
  if (!request) {
42881
+ const unsupported = unsupportedRequest(rawRequest);
42882
+ if (unsupported) {
42883
+ return {
42884
+ reply: fail(
42885
+ unsupported.id,
42886
+ unsupported.bridge,
42887
+ "FORBIDDEN",
42888
+ "this host does not offer the requested bridge operation"
42889
+ )
42890
+ };
42891
+ }
42609
42892
  return {
42610
42893
  reply: fail(
42611
42894
  invalidV0RequestId(rawRequest),
@@ -42615,16 +42898,6 @@ var BridgeService = class {
42615
42898
  )
42616
42899
  };
42617
42900
  }
42618
- if (request.bridge === ACTION_BRIDGE_PROTOCOL && this.options.allowActionProtocol === false) {
42619
- return {
42620
- reply: fail(
42621
- request.id,
42622
- request.bridge,
42623
- "FORBIDDEN",
42624
- "this host admits only the read-only v0 View bridge"
42625
- )
42626
- };
42627
- }
42628
42901
  const dataBearing = request.type !== "open-page";
42629
42902
  const before = await this.options.launches.resolve(launchId, dataBearing);
42630
42903
  if (!before) {
@@ -42730,6 +43003,73 @@ var BridgeService = class {
42730
43003
  [...rows].sort((a, b) => a.id.localeCompare(b.id)).map((row) => [row.id, row.version])
42731
43004
  );
42732
43005
  }
43006
+ /**
43007
+ * Whole-bundle projection for graph-shaped Views. The documents come from one head scan and
43008
+ * the relationships from `queryEdges`, which is a second full-bundle scan; the two scans are
43009
+ * accepted because the reply is bounded by the exported graph limits and the host never
43010
+ * caches bundle state on a View's behalf. No `model` or `definitions` are answered here: the
43011
+ * owner of that shape is undecided, and a host that has one declares the `graph.model`
43012
+ * capability before adding them.
43013
+ */
43014
+ async graph(launch, request) {
43015
+ const heads = await queryHeads2(this.options.bundle, {});
43016
+ if (heads.length > GRAPH_MAX_DOCUMENTS) {
43017
+ return {
43018
+ reply: fail(request.id, request.bridge, "TOO_LARGE", `the graph exceeded ${GRAPH_MAX_DOCUMENTS} documents`)
43019
+ };
43020
+ }
43021
+ const [registry, declaredOkfVersion, edges] = await Promise.all([
43022
+ loadKinds(this.options.bundle),
43023
+ readBundleOkfVersion2(this.options.bundle),
43024
+ queryEdges2(this.options.bundle, {})
43025
+ ]);
43026
+ if (edges.length > GRAPH_MAX_RELATIONSHIPS) {
43027
+ return {
43028
+ reply: fail(request.id, request.bridge, "TOO_LARGE", `the graph exceeded ${GRAPH_MAX_RELATIONSHIPS} relationships`)
43029
+ };
43030
+ }
43031
+ const okfVersion = declaredOkfVersion ?? "0.1";
43032
+ const includeBodies = request.includeBodies && (launch.capability === "bundle-read" || launch.capability === "bundle-propose");
43033
+ const documents = [];
43034
+ for (const head of heads) {
43035
+ const source = includeBodies ? await readDocVersioned2(this.options.bundle, head.id).then((result) => ({
43036
+ version: result.version,
43037
+ frontmatter: result.doc.frontmatter,
43038
+ body: result.doc.body
43039
+ })) : { version: head.version, frontmatter: head.frontmatter };
43040
+ if (source.body !== void 0 && Buffer.byteLength(source.body, "utf8") > MAX_DOCUMENT_BODY_BYTES) {
43041
+ return { reply: fail(request.id, request.bridge, "TOO_LARGE", "a document body exceeded the 1 MiB View limit") };
43042
+ }
43043
+ const kind = registry.kinds.get(String(source.frontmatter.type ?? ""));
43044
+ const frontmatter2 = kind ? projectLogicalKindFields(okfVersion, kind, source.frontmatter) : source.frontmatter;
43045
+ documents.push({
43046
+ id: head.id,
43047
+ version: source.version,
43048
+ frontmatter: frontmatter2,
43049
+ ...source.body === void 0 ? {} : { body: source.body }
43050
+ });
43051
+ }
43052
+ const relationships = edges.map(({ from, to, text: text4 }) => ({ from, to, text: text4 }));
43053
+ return {
43054
+ reply: ok3(request.id, request.bridge, request.type, {
43055
+ okfVersion,
43056
+ documents,
43057
+ relationships,
43058
+ counts: { documents: documents.length, relationships: relationships.length }
43059
+ })
43060
+ };
43061
+ }
43062
+ hostDescriptor() {
43063
+ const declared = new Set(this.options.host.capabilities);
43064
+ for (const name of Object.keys(this.options.hostHandlers ?? {})) declared.add(name);
43065
+ return {
43066
+ kind: this.options.host.kind,
43067
+ capabilities: [...declared].sort(),
43068
+ limits: { ...this.options.host.limits },
43069
+ ...this.options.host.frame ? { frame: { ...this.options.host.frame } } : {},
43070
+ ...this.options.host.theme ? { theme: { ...this.options.host.theme } } : {}
43071
+ };
43072
+ }
42733
43073
  async execute(launch, request) {
42734
43074
  if (request.type === "open-page") {
42735
43075
  if (this.options.consumeOpenPage === true) {
@@ -42753,10 +43093,24 @@ var BridgeService = class {
42753
43093
  bundle: { root: config.root, name: config.name },
42754
43094
  mode: config.mode,
42755
43095
  protocol: BRIDGE_PROTOCOL,
42756
- grant: launch.capability === "bundle-propose" ? "propose" : "read"
43096
+ grant: launch.capability === "bundle-propose" ? "propose" : "read",
43097
+ host: this.hostDescriptor()
42757
43098
  })
42758
43099
  };
42759
43100
  }
43101
+ if (request.type === "host") {
43102
+ const handlers = this.options.hostHandlers ?? {};
43103
+ if (!Object.hasOwn(handlers, request.capability)) {
43104
+ return { reply: fail(request.id, request.bridge, "FORBIDDEN", "this host does not offer the requested capability") };
43105
+ }
43106
+ const outcome = await handlers[request.capability]({
43107
+ capability: request.capability,
43108
+ input: request.input,
43109
+ launch
43110
+ });
43111
+ if (!outcome.ok) return { reply: fail(request.id, request.bridge, outcome.code, outcome.message) };
43112
+ return { reply: ok3(request.id, request.bridge, request.type, { capability: request.capability, output: outcome.output }) };
43113
+ }
42760
43114
  if (request.type === "query") {
42761
43115
  const rows = await queryHeads2(this.options.bundle, {
42762
43116
  ...request.params.type ? { type: request.params.type } : {},
@@ -42834,6 +43188,9 @@ var BridgeService = class {
42834
43188
  const projected = edges.map(({ from, to, text: text4 }) => ({ from, to, text: text4 }));
42835
43189
  return { reply: ok3(request.id, request.bridge, request.type, { edges: projected, count: projected.length }) };
42836
43190
  }
43191
+ if (request.type === "graph") {
43192
+ return this.graph(launch, request);
43193
+ }
42837
43194
  if (this.options.enablePolling) {
42838
43195
  this.subscriptions.set(launch.launchId, {
42839
43196
  baseline: await this.subscriptionSnapshot()
@@ -43291,7 +43648,7 @@ function buildSnapshot(first) {
43291
43648
  const snapshotDigest = sha256(utf8(canonicalJson(withoutDigest)));
43292
43649
  return new SnapshotHandle({ ...withoutDigest, snapshotDigest }, objects);
43293
43650
  }
43294
- async function capturePublicationSnapshot(options) {
43651
+ async function capturePublicationSnapshot(options, runtimeOptions = {}) {
43295
43652
  if (options.schema !== PUBLICATION_SNAPSHOT_V1) {
43296
43653
  throw new PublicationError("CAPABILITY_UNAVAILABLE", "the requested publication snapshot schema is unsupported");
43297
43654
  }
@@ -43302,6 +43659,7 @@ async function capturePublicationSnapshot(options) {
43302
43659
  throw new PublicationError("INVALID_OBJECT_IDENTITY", "the filesystem publication root must be absolute");
43303
43660
  }
43304
43661
  const requestedRoot = path5.resolve(options.source.root);
43662
+ const backend = new FilesystemBackend(requestedRoot, { hostPolicy: runtimeOptions.filesystemHostPolicy });
43305
43663
  let rootIdentity;
43306
43664
  try {
43307
43665
  rootIdentity = await authorizePublicationRoot(requestedRoot);
@@ -43324,7 +43682,6 @@ async function capturePublicationSnapshot(options) {
43324
43682
  }
43325
43683
  }
43326
43684
  const maxAttempts = options.maxAttempts ?? 2;
43327
- const backend = new FilesystemBackend(root);
43328
43685
  let lastError;
43329
43686
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
43330
43687
  let handle;
@@ -43466,6 +43823,10 @@ var STATIC_LAUNCH_ID = "publication-snapshot";
43466
43823
  function digest(bytes) {
43467
43824
  return `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
43468
43825
  }
43826
+ var STATIC_BRIDGE_CAPABILITIES = Object.freeze([
43827
+ ...BRIDGE_SERVICE_CAPABILITIES,
43828
+ BRIDGE_HOST_CAPABILITIES.openPage
43829
+ ]);
43469
43830
  function createPublicationBridge(options) {
43470
43831
  if (options.protocol !== PUBLICATION_BRIDGE_V0) {
43471
43832
  throw new PublicationError("CAPABILITY_UNAVAILABLE", "the requested publication bridge protocol is unsupported");
@@ -43513,7 +43874,11 @@ function createPublicationBridge(options) {
43513
43874
  if (!rendered || rendered.id !== id) throw new Error(`document '${id}' is not available`);
43514
43875
  return { html: rendered.html, bounded: rendered.bounded };
43515
43876
  },
43516
- allowActionProtocol: false,
43877
+ host: {
43878
+ kind: options.host?.kind ?? "oss",
43879
+ capabilities: options.host?.capabilities ?? STATIC_BRIDGE_CAPABILITIES,
43880
+ limits: BRIDGE_SERVICE_LIMITS
43881
+ },
43517
43882
  enablePolling: false,
43518
43883
  consumeOpenPage: false
43519
43884
  });