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.
- package/README.md +20 -6
- package/SKILL.md +1 -1
- package/dist/publication/bridge.d.ts +9 -0
- package/dist/publication/capture.d.ts +5 -1
- package/dist/publication/index.d.ts +1 -0
- package/dist/publication-bridge.mjs +542 -216
- package/dist/publication.mjs +597 -232
- package/dist/superbee.mjs +59738 -57909
- package/package.json +11 -8
- package/references/recipes/review-workflow/references/view-authoring-v0.md +101 -206
- package/references/views/references/view-authoring-v0.md +101 -206
package/dist/publication.mjs
CHANGED
|
@@ -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(
|
|
30925
|
+
parsed = yamlEngine.parse(source);
|
|
30923
30926
|
} catch (err) {
|
|
30924
30927
|
throw new MalformedDocumentError(context, err);
|
|
30925
30928
|
}
|
|
30926
|
-
|
|
30927
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31040
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
31497
|
+
async function readOwnerRecord(lockPath) {
|
|
31498
|
+
let raw;
|
|
31461
31499
|
try {
|
|
31462
|
-
|
|
31463
|
-
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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 (
|
|
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-
|
|
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
|
|
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-
|
|
31589
|
-
if (
|
|
31590
|
-
|
|
31642
|
+
if (failure === "unwitnessed-contention-error") {
|
|
31643
|
+
if (unwitnessedRetryUsed) throw err;
|
|
31644
|
+
unwitnessedRetryUsed = true;
|
|
31591
31645
|
continue;
|
|
31592
31646
|
} else {
|
|
31593
|
-
|
|
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
|
-
|
|
31617
|
-
|
|
31618
|
-
if (
|
|
31619
|
-
|
|
31620
|
-
|
|
31621
|
-
|
|
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
|
-
|
|
31625
|
-
|
|
31626
|
-
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
32012
|
-
|
|
32013
|
-
|
|
32014
|
-
|
|
32015
|
-
|
|
32016
|
-
|
|
32017
|
-
|
|
32018
|
-
|
|
32019
|
-
|
|
32020
|
-
|
|
32021
|
-
|
|
32022
|
-
|
|
32023
|
-
|
|
32024
|
-
|
|
32025
|
-
|
|
32026
|
-
|
|
32027
|
-
|
|
32028
|
-
|
|
32029
|
-
|
|
32030
|
-
|
|
32031
|
-
|
|
32032
|
-
|
|
32033
|
-
|
|
32034
|
-
|
|
32035
|
-
|
|
32036
|
-
|
|
32037
|
-
|
|
32038
|
-
|
|
32039
|
-
|
|
32040
|
-
|
|
32041
|
-
|
|
32042
|
-
|
|
32043
|
-
|
|
32044
|
-
|
|
32045
|
-
|
|
32046
|
-
|
|
32047
|
-
|
|
32048
|
-
|
|
32049
|
-
|
|
32050
|
-
|
|
32051
|
-
|
|
32052
|
-
|
|
32053
|
-
|
|
32054
|
-
|
|
32055
|
-
|
|
32056
|
-
|
|
32057
|
-
|
|
32058
|
-
|
|
32059
|
-
|
|
32060
|
-
|
|
32061
|
-
|
|
32062
|
-
|
|
32063
|
-
|
|
32064
|
-
|
|
32065
|
-
|
|
32066
|
-
|
|
32067
|
-
|
|
32068
|
-
|
|
32069
|
-
|
|
32070
|
-
|
|
32071
|
-
|
|
32072
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
32528
|
+
const observed = await observeExact(this.#port, this.#root, rel, async (handle, target) => {
|
|
32395
32529
|
try {
|
|
32396
|
-
const bytes = await
|
|
32397
|
-
const { mtime: mtime2 } = await
|
|
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(
|
|
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(
|
|
32563
|
+
ids.sort(compareStorageKeys);
|
|
32430
32564
|
return ids;
|
|
32431
32565
|
}
|
|
32432
32566
|
async readReserved(dir, name) {
|
|
32433
32567
|
assertSafeReservedDir(dir);
|
|
32434
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
32512
|
-
const
|
|
32513
|
-
if (!
|
|
32514
|
-
return
|
|
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 =
|
|
32654
|
+
const { root, port } = publicationContext(backend);
|
|
32519
32655
|
const rel = pathFromConceptId(id);
|
|
32520
|
-
const observed = await observeExact(
|
|
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
|
-
|
|
32528
|
-
const
|
|
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 =
|
|
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)
|
|
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
|
|
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 (
|
|
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 '${
|
|
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
|
-
|
|
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
|
|
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
|
|
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, {
|
|
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
|
-
|
|
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
|
});
|