superbee 0.2.1 → 0.3.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -7
- package/SKILL.md +6 -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 +565 -217
- package/dist/publication.mjs +620 -233
- package/dist/superbee.mjs +114401 -101544
- package/package.json +11 -8
- package/references/hosted-checkout.md +130 -0
- 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,14 +31494,32 @@ 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;
|
|
31467
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);
|
|
31518
|
+
}
|
|
31519
|
+
}
|
|
31520
|
+
async function readOwner(lockPath) {
|
|
31521
|
+
const record = await readOwnerRecord(lockPath);
|
|
31522
|
+
return record.state === "record" ? record.owner : null;
|
|
31468
31523
|
}
|
|
31469
31524
|
function processExists(pid) {
|
|
31470
31525
|
try {
|
|
@@ -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,50 +31612,50 @@ 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);
|
|
31601
31655
|
await delay(pollMs);
|
|
31602
31656
|
continue;
|
|
31603
31657
|
}
|
|
31658
|
+
const claimed = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
|
|
31604
31659
|
try {
|
|
31605
31660
|
await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}
|
|
31606
31661
|
`, {
|
|
@@ -31609,28 +31664,113 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs) {
|
|
|
31609
31664
|
mode: 384
|
|
31610
31665
|
});
|
|
31611
31666
|
} catch (err) {
|
|
31612
|
-
|
|
31667
|
+
const code2 = err.code;
|
|
31668
|
+
if (code2 === "EEXIST") continue;
|
|
31669
|
+
await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => {
|
|
31613
31670
|
});
|
|
31671
|
+
if (code2 === "ENOENT" || code2 === "ENOTDIR") continue;
|
|
31614
31672
|
throw err;
|
|
31615
31673
|
}
|
|
31616
|
-
|
|
31617
|
-
|
|
31618
|
-
if (
|
|
31619
|
-
|
|
31620
|
-
|
|
31621
|
-
|
|
31622
|
-
);
|
|
31674
|
+
let inFlight;
|
|
31675
|
+
return () => {
|
|
31676
|
+
if (inFlight) return inFlight;
|
|
31677
|
+
inFlight = (async () => {
|
|
31678
|
+
const started2 = Date.now();
|
|
31679
|
+
const current = await resolveOwnerRecord(lockPath, started2, waitMs, pollMs);
|
|
31680
|
+
if (current.state === "unreadable") throw unreadableOwnerRefusal(lockPath, current.error);
|
|
31681
|
+
if (current.state !== "record" || current.owner.token !== owner.token) {
|
|
31682
|
+
throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
|
|
31683
|
+
}
|
|
31684
|
+
await removeReleasedLock(lockPath, owner, started2, waitMs, pollMs, policy);
|
|
31685
|
+
})();
|
|
31686
|
+
return inFlight.finally(() => {
|
|
31687
|
+
inFlight = void 0;
|
|
31688
|
+
});
|
|
31689
|
+
};
|
|
31690
|
+
}
|
|
31691
|
+
}
|
|
31692
|
+
async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy) {
|
|
31693
|
+
const started = Date.now();
|
|
31694
|
+
const record = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
|
|
31695
|
+
if (record.state === "record") {
|
|
31696
|
+
if (record.owner.token === owner.token) await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
|
|
31697
|
+
return;
|
|
31698
|
+
}
|
|
31699
|
+
if (record.state === "unreadable" || !await isClaimedDirectory(lockPath, claimed)) return;
|
|
31700
|
+
const remnant = releasedLockRemnantPath(lockPath, owner);
|
|
31701
|
+
await fs.rename(lockPath, remnant);
|
|
31702
|
+
await fs.rm(remnant, { recursive: true, force: true });
|
|
31703
|
+
}
|
|
31704
|
+
async function isClaimedDirectory(lockPath, claimed) {
|
|
31705
|
+
if (claimed === null || claimed.birthtimeNs <= 0n) return false;
|
|
31706
|
+
const current = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
|
|
31707
|
+
return current !== null && current.isDirectory() && current.dev === claimed.dev && current.ino === claimed.ino && current.birthtimeNs === claimed.birthtimeNs;
|
|
31708
|
+
}
|
|
31709
|
+
function changedOwnerRefusal(lockPath, current) {
|
|
31710
|
+
return new FilesystemMutationLockError(
|
|
31711
|
+
`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
|
|
31712
|
+
{ lockPath, owner: current, stale: false, malformed: current === null }
|
|
31713
|
+
);
|
|
31714
|
+
}
|
|
31715
|
+
function unreadableOwnerRefusal(lockPath, error) {
|
|
31716
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31717
|
+
return new FilesystemMutationLockError(
|
|
31718
|
+
`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.`,
|
|
31719
|
+
{ lockPath, owner: null, stale: false, malformed: true }
|
|
31720
|
+
);
|
|
31721
|
+
}
|
|
31722
|
+
function releasedLockRemnantPath(lockPath, owner) {
|
|
31723
|
+
const tokenHash = createHash("sha256").update(owner.token).digest("hex");
|
|
31724
|
+
return `${lockPath}.released-${tokenHash}`;
|
|
31725
|
+
}
|
|
31726
|
+
function removalFailure(inspectPath, owner, err, attempts, detail) {
|
|
31727
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31728
|
+
const suffix = attempts > 1 ? ` after ${attempts} bounded attempts` : "";
|
|
31729
|
+
return new FilesystemMutationLockError(
|
|
31730
|
+
`mutation completed but ${detail}${suffix} (${message}); inspect the lock before retrying.`,
|
|
31731
|
+
{ lockPath: inspectPath, owner, stale: false, malformed: false }
|
|
31732
|
+
);
|
|
31733
|
+
}
|
|
31734
|
+
async function removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy) {
|
|
31735
|
+
const remnant = releasedLockRemnantPath(lockPath, owner);
|
|
31736
|
+
let attempts = 0;
|
|
31737
|
+
while (true) {
|
|
31738
|
+
try {
|
|
31739
|
+
await fs.rename(lockPath, remnant);
|
|
31740
|
+
break;
|
|
31741
|
+
} catch (err) {
|
|
31742
|
+
attempts += 1;
|
|
31743
|
+
if (attempts > 1 && err.code === "ENOENT") return;
|
|
31744
|
+
if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
|
|
31745
|
+
throw removalFailure(lockPath, owner, err, attempts, `filesystem lock '${lockPath}' could not be removed`);
|
|
31623
31746
|
}
|
|
31624
|
-
|
|
31625
|
-
|
|
31626
|
-
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
31747
|
+
}
|
|
31748
|
+
await delay(pollMs);
|
|
31749
|
+
const current = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
|
|
31750
|
+
if (current.state === "record" && current.owner.token === owner.token) continue;
|
|
31751
|
+
if (current.state === "unreadable") throw unreadableOwnerRefusal(lockPath, current.error);
|
|
31752
|
+
if (current.state === "absent" && !await pathExists(lockPath)) return;
|
|
31753
|
+
throw changedOwnerRefusal(lockPath, current.state === "record" ? current.owner : null);
|
|
31754
|
+
}
|
|
31755
|
+
attempts = 0;
|
|
31756
|
+
while (true) {
|
|
31757
|
+
try {
|
|
31758
|
+
await fs.rm(remnant, { recursive: true, force: false });
|
|
31759
|
+
return;
|
|
31760
|
+
} catch (err) {
|
|
31761
|
+
attempts += 1;
|
|
31762
|
+
if (err.code === "ENOENT") return;
|
|
31763
|
+
if (!policy.isDirectoryContentionError(err) || Date.now() - started >= waitMs) {
|
|
31764
|
+
throw removalFailure(
|
|
31765
|
+
remnant,
|
|
31766
|
+
owner,
|
|
31767
|
+
err,
|
|
31768
|
+
attempts,
|
|
31769
|
+
`filesystem lock '${lockPath}' was released yet its remnant '${remnant}' could not be removed`
|
|
31631
31770
|
);
|
|
31632
31771
|
}
|
|
31633
|
-
}
|
|
31772
|
+
}
|
|
31773
|
+
await delay(pollMs);
|
|
31634
31774
|
}
|
|
31635
31775
|
}
|
|
31636
31776
|
function newOwner(target) {
|
|
@@ -31648,21 +31788,20 @@ function assertIdentityKey(key) {
|
|
|
31648
31788
|
}
|
|
31649
31789
|
async function acquireFilesystemIdentityLock(key, identity, options = {}) {
|
|
31650
31790
|
assertIdentityKey(key);
|
|
31791
|
+
const policy = captureFilesystemHostPolicy(options.hostPolicy);
|
|
31651
31792
|
const waitMs = positiveOption(options.waitMs, DEFAULT_WAIT_MS, "waitMs");
|
|
31652
31793
|
const pollMs = positiveOption(options.pollMs, DEFAULT_POLL_MS, "pollMs");
|
|
31653
31794
|
const owner = newOwner(identity);
|
|
31654
|
-
const lockRoot = await selectLockRoot(options);
|
|
31655
|
-
return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs);
|
|
31795
|
+
const lockRoot = await selectLockRoot(options, policy);
|
|
31796
|
+
return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs, policy);
|
|
31656
31797
|
}
|
|
31657
31798
|
|
|
31658
31799
|
// ../core/src/filesystem-identity.ts
|
|
31659
|
-
var WINDOWS_REPLACE_CONFLICT_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
|
|
31660
31800
|
async function replaceExistingLeaf(port, tmp, target, rel) {
|
|
31661
31801
|
try {
|
|
31662
31802
|
await port.rename(tmp, target);
|
|
31663
31803
|
} catch (err) {
|
|
31664
|
-
|
|
31665
|
-
if ((port.platform ?? process.platform) === "win32" && WINDOWS_REPLACE_CONFLICT_CODES.has(code2)) {
|
|
31804
|
+
if (port.isReplacementConflict?.(err)) {
|
|
31666
31805
|
throw new ConcurrentReplacementError(rel, 1, 10);
|
|
31667
31806
|
}
|
|
31668
31807
|
throw err;
|
|
@@ -31829,7 +31968,7 @@ async function observeExact(port, root, rel, read) {
|
|
|
31829
31968
|
opened = await port.open(target);
|
|
31830
31969
|
} catch (err) {
|
|
31831
31970
|
if (isAbsentPathError(err)) return { state: "absent" };
|
|
31832
|
-
if (
|
|
31971
|
+
if (port.isTransientOpenError?.(err)) {
|
|
31833
31972
|
restarts = countRestart(restarts, rel);
|
|
31834
31973
|
continue;
|
|
31835
31974
|
}
|
|
@@ -31863,7 +32002,15 @@ async function probeExact(port, root, rel) {
|
|
|
31863
32002
|
restarts = countRestart(restarts, rel);
|
|
31864
32003
|
}
|
|
31865
32004
|
}
|
|
31866
|
-
var linkUnsupportedRoots = /* @__PURE__ */ new
|
|
32005
|
+
var linkUnsupportedRoots = /* @__PURE__ */ new WeakMap();
|
|
32006
|
+
function unsupportedLinkRoots(port) {
|
|
32007
|
+
let roots = linkUnsupportedRoots.get(port);
|
|
32008
|
+
if (!roots) {
|
|
32009
|
+
roots = /* @__PURE__ */ new Set();
|
|
32010
|
+
linkUnsupportedRoots.set(port, roots);
|
|
32011
|
+
}
|
|
32012
|
+
return roots;
|
|
32013
|
+
}
|
|
31867
32014
|
var queues = /* @__PURE__ */ new Map();
|
|
31868
32015
|
function enqueue(key, fn) {
|
|
31869
32016
|
const tail = queues.get(key) ?? Promise.resolve();
|
|
@@ -31945,7 +32092,7 @@ async function createExactLeaf(port, tmp, target, rel, leaf, rootResolved) {
|
|
|
31945
32092
|
return;
|
|
31946
32093
|
}
|
|
31947
32094
|
if (outcome === "unsupported") {
|
|
31948
|
-
|
|
32095
|
+
unsupportedLinkRoots(port).add(rootResolved);
|
|
31949
32096
|
await port.rename(tmp, target);
|
|
31950
32097
|
return;
|
|
31951
32098
|
}
|
|
@@ -31981,7 +32128,7 @@ async function mutateExact(port, root, rel, body) {
|
|
|
31981
32128
|
await replaceExistingLeaf(port, tmp, target, rel);
|
|
31982
32129
|
return;
|
|
31983
32130
|
}
|
|
31984
|
-
if (
|
|
32131
|
+
if (unsupportedLinkRoots(port).has(rootResolved)) {
|
|
31985
32132
|
await port.rename(tmp, target);
|
|
31986
32133
|
return;
|
|
31987
32134
|
}
|
|
@@ -32008,80 +32155,84 @@ function kindOf(stats) {
|
|
|
32008
32155
|
if (stats.isFile()) return "file";
|
|
32009
32156
|
return "other";
|
|
32010
32157
|
}
|
|
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
|
-
|
|
32158
|
+
function createNodeFilesystemIdentityPort(hostPolicy) {
|
|
32159
|
+
const policy = captureFilesystemHostPolicy(hostPolicy);
|
|
32160
|
+
return Object.freeze({
|
|
32161
|
+
isTransientOpenError: policy.isTransientOpenError,
|
|
32162
|
+
isReplacementConflict: policy.isReplacementConflict,
|
|
32163
|
+
async probe(target) {
|
|
32164
|
+
let lstats;
|
|
32165
|
+
try {
|
|
32166
|
+
lstats = await fs2.lstat(target);
|
|
32167
|
+
} catch (err) {
|
|
32168
|
+
if (isAbsentPathError(err)) return null;
|
|
32169
|
+
throw err;
|
|
32170
|
+
}
|
|
32171
|
+
return { kind: kindOf(lstats), dev: lstats.dev, ino: lstats.ino };
|
|
32172
|
+
},
|
|
32173
|
+
async entries(dir) {
|
|
32174
|
+
try {
|
|
32175
|
+
const dirents = await fs2.readdir(dir, { withFileTypes: true });
|
|
32176
|
+
return dirents.map((dirent) => ({ name: dirent.name, kind: kindOf(dirent) }));
|
|
32177
|
+
} catch (err) {
|
|
32178
|
+
if (isAbsentPathError(err)) return null;
|
|
32179
|
+
throw err;
|
|
32180
|
+
}
|
|
32181
|
+
},
|
|
32182
|
+
async open(target) {
|
|
32183
|
+
const handle = await fs2.open(target, "r");
|
|
32184
|
+
try {
|
|
32185
|
+
const stats = await handle.stat();
|
|
32186
|
+
return { handle, dev: stats.dev, ino: stats.ino };
|
|
32187
|
+
} catch (err) {
|
|
32188
|
+
await handle.close().catch(() => {
|
|
32189
|
+
});
|
|
32190
|
+
throw err;
|
|
32191
|
+
}
|
|
32192
|
+
},
|
|
32193
|
+
readAll(handle) {
|
|
32194
|
+
return handle.readFile();
|
|
32195
|
+
},
|
|
32196
|
+
close(handle) {
|
|
32197
|
+
return handle.close();
|
|
32198
|
+
},
|
|
32199
|
+
async stat(target) {
|
|
32200
|
+
return { mtime: (await fs2.stat(target)).mtime };
|
|
32201
|
+
},
|
|
32202
|
+
async mkdir(dir) {
|
|
32203
|
+
try {
|
|
32204
|
+
await fs2.mkdir(dir);
|
|
32205
|
+
return "created";
|
|
32206
|
+
} catch (err) {
|
|
32207
|
+
if (err.code === "EEXIST") return "exists";
|
|
32208
|
+
throw err;
|
|
32209
|
+
}
|
|
32210
|
+
},
|
|
32211
|
+
async writeTemp(dir, name, bytes) {
|
|
32212
|
+
await fs2.writeFile(path2.join(dir, name), bytes);
|
|
32213
|
+
},
|
|
32214
|
+
async link(from, to) {
|
|
32215
|
+
try {
|
|
32216
|
+
await fs2.link(from, to);
|
|
32217
|
+
return "linked";
|
|
32218
|
+
} catch (err) {
|
|
32219
|
+
const code2 = err.code;
|
|
32220
|
+
if (code2 === "EEXIST") return "exists";
|
|
32221
|
+
if (code2 === "EPERM" || code2 === "ENOTSUP" || code2 === "EOPNOTSUPP" || code2 === "EXDEV") return "unsupported";
|
|
32222
|
+
throw err;
|
|
32223
|
+
}
|
|
32224
|
+
},
|
|
32225
|
+
async rename(from, to) {
|
|
32226
|
+
await fs2.rename(from, to);
|
|
32227
|
+
},
|
|
32228
|
+
async unlink(target) {
|
|
32229
|
+
await fs2.unlink(target);
|
|
32230
|
+
},
|
|
32231
|
+
claim(key, identity) {
|
|
32232
|
+
return acquireFilesystemIdentityLock(key, `${identity.root}:${identity.rel}`, { portableRoot: identity.root, hostPolicy: policy });
|
|
32073
32233
|
}
|
|
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
|
-
});
|
|
32234
|
+
});
|
|
32235
|
+
}
|
|
32085
32236
|
|
|
32086
32237
|
// ../core/src/sha256.ts
|
|
32087
32238
|
var K = new Uint32Array([
|
|
@@ -32240,6 +32391,9 @@ function sha256HexOfUtf8(input) {
|
|
|
32240
32391
|
}
|
|
32241
32392
|
|
|
32242
32393
|
// ../core/src/version-transport.ts
|
|
32394
|
+
function isContentVersion(value) {
|
|
32395
|
+
return typeof value === "string" && /^sha256:[0-9a-f]{64}$/.test(value);
|
|
32396
|
+
}
|
|
32243
32397
|
var VersionConflict = class extends Error {
|
|
32244
32398
|
name = "VersionConflict";
|
|
32245
32399
|
id;
|
|
@@ -32271,7 +32425,7 @@ function defaultActor() {
|
|
|
32271
32425
|
}
|
|
32272
32426
|
|
|
32273
32427
|
// ../core/src/backend.ts
|
|
32274
|
-
var
|
|
32428
|
+
var publicationContexts = /* @__PURE__ */ new WeakMap();
|
|
32275
32429
|
function firstString(...vals) {
|
|
32276
32430
|
for (const v of vals) {
|
|
32277
32431
|
if (typeof v === "string" && v.trim() !== "") return v;
|
|
@@ -32290,22 +32444,22 @@ function notFound(root, rel) {
|
|
|
32290
32444
|
err.path = target;
|
|
32291
32445
|
return err;
|
|
32292
32446
|
}
|
|
32293
|
-
async function readBytes(handle) {
|
|
32447
|
+
async function readBytes(port, handle) {
|
|
32294
32448
|
try {
|
|
32295
|
-
return await
|
|
32449
|
+
return await port.readAll(handle);
|
|
32296
32450
|
} catch (err) {
|
|
32297
32451
|
if (isAbsentFileError(err)) return null;
|
|
32298
32452
|
throw err;
|
|
32299
32453
|
}
|
|
32300
32454
|
}
|
|
32301
|
-
async function walkFiles(root, keep, sub = "") {
|
|
32302
|
-
const entries = await
|
|
32455
|
+
async function walkFiles(port, root, keep, sub = "") {
|
|
32456
|
+
const entries = await port.entries(path3.join(root, sub)) ?? [];
|
|
32303
32457
|
const out = [];
|
|
32304
32458
|
for (const entry of entries) {
|
|
32305
32459
|
if (entry.name.startsWith(".")) continue;
|
|
32306
32460
|
const rel = sub === "" ? entry.name : `${sub}/${entry.name}`;
|
|
32307
32461
|
if (entry.kind === "directory") {
|
|
32308
|
-
out.push(...await walkFiles(root, keep, rel));
|
|
32462
|
+
out.push(...await walkFiles(port, root, keep, rel));
|
|
32309
32463
|
} else if (entry.kind === "file" && keep(entry.name)) {
|
|
32310
32464
|
out.push(rel);
|
|
32311
32465
|
}
|
|
@@ -32313,19 +32467,20 @@ async function walkFiles(root, keep, sub = "") {
|
|
|
32313
32467
|
return out;
|
|
32314
32468
|
}
|
|
32315
32469
|
function reservedPath(dir, name) {
|
|
32316
|
-
|
|
32317
|
-
return d === "" ? name : `${d}/${name}`;
|
|
32470
|
+
return dir === "" ? name : `${dir}/${name}`;
|
|
32318
32471
|
}
|
|
32319
32472
|
var FilesystemBackend = class _FilesystemBackend {
|
|
32320
32473
|
#root;
|
|
32474
|
+
#port;
|
|
32321
32475
|
/**
|
|
32322
32476
|
* The root is resolved once here: a relative root would otherwise re-resolve against the
|
|
32323
32477
|
* process's current directory on every operation, so a later `chdir` would silently move the
|
|
32324
32478
|
* bundle and derive a different identity key.
|
|
32325
32479
|
*/
|
|
32326
|
-
constructor(root) {
|
|
32480
|
+
constructor(root, options = {}) {
|
|
32481
|
+
this.#port = createNodeFilesystemIdentityPort(options.hostPolicy);
|
|
32327
32482
|
this.#root = path3.resolve(root);
|
|
32328
|
-
|
|
32483
|
+
publicationContexts.set(this, { root: this.#root, port: this.#port });
|
|
32329
32484
|
}
|
|
32330
32485
|
async read(id) {
|
|
32331
32486
|
return this.#readWithEdition(id, () => this.#readEdition());
|
|
@@ -32341,7 +32496,7 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32341
32496
|
async #readWithEdition(id, edition) {
|
|
32342
32497
|
assertSafeConceptId(id);
|
|
32343
32498
|
const rel = pathFromConceptId(id);
|
|
32344
|
-
const observed = await observeExact(
|
|
32499
|
+
const observed = await observeExact(this.#port, this.#root, rel, (handle) => readBytes(this.#port, handle));
|
|
32345
32500
|
if (observed.state === "absent") throw notFound(this.#root, rel);
|
|
32346
32501
|
const raw = observed.value.toString("utf8");
|
|
32347
32502
|
const { frontmatter: frontmatter2, body } = parseMarkdown(raw, rel, { okfVersion: await edition() });
|
|
@@ -32363,7 +32518,7 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32363
32518
|
assertSafeConceptId(id);
|
|
32364
32519
|
const raw = stringifyDoc(doc.frontmatter, doc.body ?? "");
|
|
32365
32520
|
const rel = pathFromConceptId(id);
|
|
32366
|
-
return mutateExact(
|
|
32521
|
+
return mutateExact(this.#port, this.#root, rel, async (target) => {
|
|
32367
32522
|
if (options.expectedVersion !== void 0) {
|
|
32368
32523
|
const bytes = await target.current();
|
|
32369
32524
|
const current = bytes === null ? null : versionOfBytes(bytes.toString("utf8"));
|
|
@@ -32377,7 +32532,7 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32377
32532
|
}
|
|
32378
32533
|
async delete(id, options = {}) {
|
|
32379
32534
|
assertSafeConceptId(id);
|
|
32380
|
-
return mutateExact(
|
|
32535
|
+
return mutateExact(this.#port, this.#root, pathFromConceptId(id), async (target) => {
|
|
32381
32536
|
const bytes = await target.current();
|
|
32382
32537
|
if (bytes === null) return false;
|
|
32383
32538
|
const current = versionOfBytes(bytes.toString("utf8"));
|
|
@@ -32391,10 +32546,10 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32391
32546
|
async versions(id) {
|
|
32392
32547
|
assertSafeConceptId(id);
|
|
32393
32548
|
const rel = pathFromConceptId(id);
|
|
32394
|
-
const observed = await observeExact(
|
|
32549
|
+
const observed = await observeExact(this.#port, this.#root, rel, async (handle, target) => {
|
|
32395
32550
|
try {
|
|
32396
|
-
const bytes = await
|
|
32397
|
-
const { mtime: mtime2 } = await
|
|
32551
|
+
const bytes = await this.#port.readAll(handle);
|
|
32552
|
+
const { mtime: mtime2 } = await this.#port.stat(target);
|
|
32398
32553
|
return { raw: bytes.toString("utf8"), mtime: mtime2 };
|
|
32399
32554
|
} catch {
|
|
32400
32555
|
return null;
|
|
@@ -32409,10 +32564,10 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32409
32564
|
}
|
|
32410
32565
|
async exists(id) {
|
|
32411
32566
|
assertSafeConceptId(id);
|
|
32412
|
-
return (await probeExact(
|
|
32567
|
+
return (await probeExact(this.#port, this.#root, pathFromConceptId(id))).state === "exact";
|
|
32413
32568
|
}
|
|
32414
32569
|
async list(prefix) {
|
|
32415
|
-
const files = await walkFiles(this.#root, (name) => name.endsWith(".md"));
|
|
32570
|
+
const files = await walkFiles(this.#port, this.#root, (name) => name.endsWith(".md"));
|
|
32416
32571
|
const ids = [];
|
|
32417
32572
|
for (const rel of files) {
|
|
32418
32573
|
if (isReservedFile(rel)) continue;
|
|
@@ -32426,20 +32581,22 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32426
32581
|
if (prefix && !id.startsWith(prefix)) continue;
|
|
32427
32582
|
ids.push(id);
|
|
32428
32583
|
}
|
|
32429
|
-
ids.sort(
|
|
32584
|
+
ids.sort(compareStorageKeys);
|
|
32430
32585
|
return ids;
|
|
32431
32586
|
}
|
|
32432
32587
|
async readReserved(dir, name) {
|
|
32433
32588
|
assertSafeReservedDir(dir);
|
|
32434
|
-
|
|
32589
|
+
assertSafeReservedFilename(name);
|
|
32590
|
+
const observed = await observeExact(this.#port, this.#root, reservedPath(dir, name), (handle) => readBytes(this.#port, handle));
|
|
32435
32591
|
if (observed.state === "absent") return null;
|
|
32436
32592
|
const content3 = observed.value.toString("utf8");
|
|
32437
32593
|
return { content: content3, version: versionOfBytes(content3) };
|
|
32438
32594
|
}
|
|
32439
32595
|
async writeReserved(dir, name, content3, options = {}) {
|
|
32440
32596
|
assertSafeReservedDir(dir);
|
|
32597
|
+
assertSafeReservedFilename(name);
|
|
32441
32598
|
const rel = reservedPath(dir, name);
|
|
32442
|
-
return mutateExact(
|
|
32599
|
+
return mutateExact(this.#port, this.#root, rel, async (target) => {
|
|
32443
32600
|
if (options.expectedVersion !== void 0) {
|
|
32444
32601
|
const bytes = await target.current();
|
|
32445
32602
|
const current = bytes === null ? null : versionOfBytes(bytes.toString("utf8"));
|
|
@@ -32454,14 +32611,14 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32454
32611
|
// ── blobs: opaque bytes + a content-type ──────────────────────────────────
|
|
32455
32612
|
async readBlob(key) {
|
|
32456
32613
|
assertSafeBlobKey(key);
|
|
32457
|
-
const observed = await observeExact(
|
|
32614
|
+
const observed = await observeExact(this.#port, this.#root, key, (handle) => readBytes(this.#port, handle));
|
|
32458
32615
|
if (observed.state === "absent") return null;
|
|
32459
32616
|
const bytes = observed.value;
|
|
32460
32617
|
return { bytes, contentType: resolveContentType(key), version: blobVersion(bytes) };
|
|
32461
32618
|
}
|
|
32462
32619
|
async writeBlob(key, bytes, _contentType, options = {}) {
|
|
32463
32620
|
assertSafeBlobKey(key);
|
|
32464
|
-
return mutateExact(
|
|
32621
|
+
return mutateExact(this.#port, this.#root, key, async (target) => {
|
|
32465
32622
|
if (options.expectedVersion !== void 0) {
|
|
32466
32623
|
const existing = await target.current();
|
|
32467
32624
|
const current = existing === null ? null : blobVersion(existing);
|
|
@@ -32475,7 +32632,7 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32475
32632
|
}
|
|
32476
32633
|
async deleteBlob(key, options = {}) {
|
|
32477
32634
|
assertSafeBlobKey(key);
|
|
32478
|
-
return mutateExact(
|
|
32635
|
+
return mutateExact(this.#port, this.#root, key, async (target) => {
|
|
32479
32636
|
const existing = await target.current();
|
|
32480
32637
|
if (existing === null) return false;
|
|
32481
32638
|
const current = blobVersion(existing);
|
|
@@ -32488,14 +32645,14 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32488
32645
|
}
|
|
32489
32646
|
async existsBlob(key) {
|
|
32490
32647
|
assertSafeBlobKey(key);
|
|
32491
|
-
const observed = await probeExact(
|
|
32648
|
+
const observed = await probeExact(this.#port, this.#root, key);
|
|
32492
32649
|
return observed.state === "exact" && observed.value.kind === "file";
|
|
32493
32650
|
}
|
|
32494
32651
|
async listBlobs(prefix) {
|
|
32495
|
-
const files = await walkFiles(this.#root, (name) => !name.toLowerCase().endsWith(".md"));
|
|
32652
|
+
const files = await walkFiles(this.#port, this.#root, (name) => !name.toLowerCase().endsWith(".md"));
|
|
32496
32653
|
const keys = files.map(toPosix);
|
|
32497
32654
|
const filtered = prefix ? keys.filter((k) => k.startsWith(prefix)) : keys;
|
|
32498
|
-
filtered.sort(
|
|
32655
|
+
filtered.sort(compareStorageKeys);
|
|
32499
32656
|
return filtered;
|
|
32500
32657
|
}
|
|
32501
32658
|
capabilities() {
|
|
@@ -32508,24 +32665,25 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
32508
32665
|
};
|
|
32509
32666
|
}
|
|
32510
32667
|
};
|
|
32511
|
-
function
|
|
32512
|
-
const
|
|
32513
|
-
if (!
|
|
32514
|
-
return
|
|
32668
|
+
function publicationContext(backend) {
|
|
32669
|
+
const context = publicationContexts.get(backend);
|
|
32670
|
+
if (!context) throw new InvalidInputError("Unknown filesystem backend publication source.");
|
|
32671
|
+
return context;
|
|
32515
32672
|
}
|
|
32516
32673
|
async function readRawFilesystemDocument(backend, id) {
|
|
32517
32674
|
assertSafeConceptId(id);
|
|
32518
|
-
const root =
|
|
32675
|
+
const { root, port } = publicationContext(backend);
|
|
32519
32676
|
const rel = pathFromConceptId(id);
|
|
32520
|
-
const observed = await observeExact(
|
|
32677
|
+
const observed = await observeExact(port, root, rel, (handle) => readBytes(port, handle));
|
|
32521
32678
|
if (observed.state === "absent") throw notFound(root, rel);
|
|
32522
32679
|
const raw = new TextDecoder("utf-8", { fatal: true }).decode(observed.value);
|
|
32523
32680
|
return { bytes: observed.value.slice(), version: versionOfBytes(raw) };
|
|
32524
32681
|
}
|
|
32525
32682
|
async function readRawFilesystemReserved(backend, dir, name) {
|
|
32526
32683
|
assertSafeReservedDir(dir);
|
|
32527
|
-
|
|
32528
|
-
const
|
|
32684
|
+
assertSafeReservedFilename(name);
|
|
32685
|
+
const { root, port } = publicationContext(backend);
|
|
32686
|
+
const observed = await observeExact(port, root, reservedPath(dir, name), (handle) => readBytes(port, handle));
|
|
32529
32687
|
if (observed.state === "absent") return null;
|
|
32530
32688
|
return {
|
|
32531
32689
|
bytes: observed.value.slice(),
|
|
@@ -32533,15 +32691,16 @@ async function readRawFilesystemReserved(backend, dir, name) {
|
|
|
32533
32691
|
};
|
|
32534
32692
|
}
|
|
32535
32693
|
async function listFilesystemReservedObjects(backend) {
|
|
32536
|
-
const root =
|
|
32537
|
-
const files = await walkFiles(root, (name) => name === "index.md" || name === "log.md");
|
|
32694
|
+
const { root, port } = publicationContext(backend);
|
|
32695
|
+
const files = await walkFiles(port, root, (name) => name === "index.md" || name === "log.md");
|
|
32538
32696
|
return files.map((relative) => {
|
|
32539
32697
|
const slash = relative.lastIndexOf("/");
|
|
32540
32698
|
const dir = slash === -1 ? "" : relative.slice(0, slash);
|
|
32541
32699
|
const name = relative.slice(slash + 1);
|
|
32542
32700
|
assertSafeReservedDir(dir);
|
|
32701
|
+
assertSafeReservedFilename(name);
|
|
32543
32702
|
return { dir, name };
|
|
32544
|
-
}).sort((a, b) => reservedPath(a.dir, a.name)
|
|
32703
|
+
}).sort((a, b) => compareStorageKeys(reservedPath(a.dir, a.name), reservedPath(b.dir, b.name)));
|
|
32545
32704
|
}
|
|
32546
32705
|
|
|
32547
32706
|
// ../core/src/bundle-ops.ts
|
|
@@ -32581,7 +32740,7 @@ setDefaultBackendFactory((root) => new FilesystemBackend(root));
|
|
|
32581
32740
|
var CONVENTIONS_PREFIX = "conventions/";
|
|
32582
32741
|
var CONVENTION_TYPE = "Convention";
|
|
32583
32742
|
function buildKindRegistry(docs, initialWarnings = [], options = {}) {
|
|
32584
|
-
const
|
|
32743
|
+
const accepted = /* @__PURE__ */ new Map();
|
|
32585
32744
|
const warnings = [...initialWarnings];
|
|
32586
32745
|
for (const doc of [...docs].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
32587
32746
|
const parsed = parseConventionDoc(doc, options);
|
|
@@ -32604,10 +32763,10 @@ function buildKindRegistry(docs, initialWarnings = [], options = {}) {
|
|
|
32604
32763
|
severity: "warning"
|
|
32605
32764
|
});
|
|
32606
32765
|
}
|
|
32607
|
-
if (
|
|
32766
|
+
if (accepted.has(kind.governs)) {
|
|
32608
32767
|
warnings.push({
|
|
32609
32768
|
code: "KIND_DUPLICATE_GOVERNS",
|
|
32610
|
-
message: `duplicate kind convention for '${kind.governs}': '${doc.id}' ignored, keeping the first-declared '${
|
|
32769
|
+
message: `duplicate kind convention for '${kind.governs}': '${doc.id}' ignored, keeping the first-declared '${accepted.get(kind.governs).id}'.`,
|
|
32611
32770
|
field: kind.governs,
|
|
32612
32771
|
severity: "warning"
|
|
32613
32772
|
});
|
|
@@ -32621,10 +32780,18 @@ function buildKindRegistry(docs, initialWarnings = [], options = {}) {
|
|
|
32621
32780
|
severity: "warning"
|
|
32622
32781
|
});
|
|
32623
32782
|
}
|
|
32624
|
-
|
|
32783
|
+
accepted.set(kind.governs, kind);
|
|
32625
32784
|
}
|
|
32785
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
32786
|
+
for (const kind of [...accepted.values()].sort(compareReadingOrder)) kinds.set(kind.governs, kind);
|
|
32626
32787
|
return { kinds, warnings };
|
|
32627
32788
|
}
|
|
32789
|
+
function compareReadingOrder(a, b) {
|
|
32790
|
+
if (a.order !== void 0 && b.order !== void 0 && a.order !== b.order) return a.order - b.order;
|
|
32791
|
+
if (a.order !== void 0 && b.order === void 0) return -1;
|
|
32792
|
+
if (a.order === void 0 && b.order !== void 0) return 1;
|
|
32793
|
+
return a.id.localeCompare(b.id);
|
|
32794
|
+
}
|
|
32628
32795
|
var PROGRESS_STATUS_FIELD = "progress_status";
|
|
32629
32796
|
var SUPERBEE_PROGRESS_STATUS_FIELD = "superbee_progress_status";
|
|
32630
32797
|
function progressStatusStorageField(okfVersion) {
|
|
@@ -33093,6 +33260,19 @@ function parseConventionDoc(doc, options = {}) {
|
|
|
33093
33260
|
const path6 = typeof fm.path === "string" && fm.path.trim() !== "" ? fm.path.trim() : void 0;
|
|
33094
33261
|
const freshnessHorizon = typeof fm.freshness_horizon === "string" && fm.freshness_horizon.trim() !== "" ? fm.freshness_horizon.trim() : void 0;
|
|
33095
33262
|
const browseCollapsed = fm.browse_collapsed === true ? true : void 0;
|
|
33263
|
+
let order;
|
|
33264
|
+
if (fm.order !== void 0) {
|
|
33265
|
+
if (typeof fm.order === "number" && Number.isFinite(fm.order)) {
|
|
33266
|
+
order = fm.order;
|
|
33267
|
+
} else {
|
|
33268
|
+
warnings.push({
|
|
33269
|
+
code: "KIND_CONVENTION_BAD_SHAPE",
|
|
33270
|
+
message: `kind convention '${doc.id}' has an invalid 'order' (${describeShape(fm.order)}; expected a finite number such as 10); ignoring it.`,
|
|
33271
|
+
field: "order",
|
|
33272
|
+
severity: "warning"
|
|
33273
|
+
});
|
|
33274
|
+
}
|
|
33275
|
+
}
|
|
33096
33276
|
const kind = {
|
|
33097
33277
|
id: doc.id,
|
|
33098
33278
|
title,
|
|
@@ -33107,6 +33287,7 @@ function parseConventionDoc(doc, options = {}) {
|
|
|
33107
33287
|
if (sections && sections.length > 0) kind.sections = sections;
|
|
33108
33288
|
if (freshnessHorizon !== void 0) kind.freshnessHorizon = freshnessHorizon;
|
|
33109
33289
|
if (browseCollapsed !== void 0) kind.browseCollapsed = browseCollapsed;
|
|
33290
|
+
if (order !== void 0) kind.order = order;
|
|
33110
33291
|
if (claim !== void 0) kind.claim = claim;
|
|
33111
33292
|
return {
|
|
33112
33293
|
ok: true,
|
|
@@ -33257,7 +33438,7 @@ function isAnyEntryKey(entry) {
|
|
|
33257
33438
|
return isPageEntryKey(entry) || isViewEntryKey(entry);
|
|
33258
33439
|
}
|
|
33259
33440
|
function isViewEntryVersion(value) {
|
|
33260
|
-
return
|
|
33441
|
+
return isContentVersion(value);
|
|
33261
33442
|
}
|
|
33262
33443
|
function parseRegistration(id, frontmatter2) {
|
|
33263
33444
|
if (!isAnyRegistryId(id) || !isPageTypeName(frontmatter2.type) || !isAnyEntryKey(frontmatter2.entry)) return null;
|
|
@@ -42193,6 +42374,35 @@ function isBareLinkBlock(node2, fromId) {
|
|
|
42193
42374
|
function safeLanguageClass(lang) {
|
|
42194
42375
|
return typeof lang === "string" && /^[\w+-]{1,24}$/.test(lang) ? `doc-code-${lang}` : void 0;
|
|
42195
42376
|
}
|
|
42377
|
+
function isIpLiteralHost(hostname2) {
|
|
42378
|
+
return hostname2.startsWith("[") || /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname2);
|
|
42379
|
+
}
|
|
42380
|
+
function normalizeAllowedHost(entry) {
|
|
42381
|
+
if (typeof entry !== "string" || entry === "" || /[\s/?#@:\\]/.test(entry)) return null;
|
|
42382
|
+
let url;
|
|
42383
|
+
try {
|
|
42384
|
+
url = new URL(`https://${entry}/`);
|
|
42385
|
+
} catch {
|
|
42386
|
+
return null;
|
|
42387
|
+
}
|
|
42388
|
+
if (url.href !== `https://${url.hostname}/` || isIpLiteralHost(url.hostname)) return null;
|
|
42389
|
+
return url.hostname;
|
|
42390
|
+
}
|
|
42391
|
+
function admitExternalLink(raw, hosts) {
|
|
42392
|
+
if (!Array.isArray(hosts) || hosts.length === 0) return null;
|
|
42393
|
+
let url;
|
|
42394
|
+
try {
|
|
42395
|
+
url = new URL(raw);
|
|
42396
|
+
} catch {
|
|
42397
|
+
return null;
|
|
42398
|
+
}
|
|
42399
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "") return null;
|
|
42400
|
+
if (isIpLiteralHost(url.hostname)) return null;
|
|
42401
|
+
for (const entry of hosts) {
|
|
42402
|
+
if (normalizeAllowedHost(entry) === url.hostname) return url.href;
|
|
42403
|
+
}
|
|
42404
|
+
return null;
|
|
42405
|
+
}
|
|
42196
42406
|
function documentHref(id, options) {
|
|
42197
42407
|
const href = options.hrefForDoc?.(id) ?? `?view=doc&id=${encodeURIComponent(id)}`;
|
|
42198
42408
|
const base = "https://renderer.invalid/";
|
|
@@ -42267,9 +42477,14 @@ function renderNode(node2, state, depth, index2) {
|
|
|
42267
42477
|
case "break":
|
|
42268
42478
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}, index2);
|
|
42269
42479
|
case "link": {
|
|
42270
|
-
const
|
|
42480
|
+
const raw = node2.url ?? "";
|
|
42481
|
+
const resolved = resolveConceptId(state.options.fromId, raw);
|
|
42271
42482
|
const children = renderChildren(node2, state, depth);
|
|
42272
42483
|
if (resolved === null) {
|
|
42484
|
+
const external = admitExternalLink(raw, state.options.externalLinkHosts);
|
|
42485
|
+
if (external !== null) {
|
|
42486
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: external, rel: "noopener noreferrer", target: "_blank", className: "doc-link-external", children }, index2);
|
|
42487
|
+
}
|
|
42273
42488
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
42274
42489
|
"span",
|
|
42275
42490
|
{
|
|
@@ -42413,8 +42628,11 @@ function renderMarkdownToStaticHtml(body, options) {
|
|
|
42413
42628
|
);
|
|
42414
42629
|
return { html, bounded: rendered.bounded, limits: rendered.limits };
|
|
42415
42630
|
}
|
|
42416
|
-
function renderDocumentToStaticHtml(document3) {
|
|
42417
|
-
const rendered = renderMarkdownToStaticHtml(document3.body, {
|
|
42631
|
+
function renderDocumentToStaticHtml(document3, options = {}) {
|
|
42632
|
+
const rendered = renderMarkdownToStaticHtml(document3.body, {
|
|
42633
|
+
fromId: document3.id,
|
|
42634
|
+
externalLinkHosts: options.externalLinkHosts
|
|
42635
|
+
});
|
|
42418
42636
|
return { html: rendered.html, bounded: rendered.bounded };
|
|
42419
42637
|
}
|
|
42420
42638
|
|
|
@@ -42447,9 +42665,45 @@ var MAX_QUERY_ROWS = 500;
|
|
|
42447
42665
|
var MAX_EDGE_ROWS = 1e3;
|
|
42448
42666
|
var MAX_DOCUMENT_BODY_BYTES = 1024 * 1024;
|
|
42449
42667
|
var MAX_REPLY_BYTES = 2 * 1024 * 1024;
|
|
42668
|
+
var GRAPH_MAX_DOCUMENTS = 1e3;
|
|
42669
|
+
var GRAPH_MAX_RELATIONSHIPS = 1e4;
|
|
42450
42670
|
var MAX_CHANGE_ROWS = 100;
|
|
42451
42671
|
var MAX_CHANGE_BYTES = 256 * 1024;
|
|
42452
42672
|
var MAX_SUBSCRIPTION_HEADS = 1e4;
|
|
42673
|
+
var MAX_HOST_CAPABILITY_BYTES = 128;
|
|
42674
|
+
var MAX_HOST_REQUEST_BYTES = 64 * 1024;
|
|
42675
|
+
var HOST_CAPABILITY_NAME = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;
|
|
42676
|
+
var BRIDGE_SERVICE_LIMITS = Object.freeze({
|
|
42677
|
+
query: MAX_QUERY_ROWS,
|
|
42678
|
+
edges: MAX_EDGE_ROWS,
|
|
42679
|
+
graphDocuments: GRAPH_MAX_DOCUMENTS,
|
|
42680
|
+
graphRelationships: GRAPH_MAX_RELATIONSHIPS,
|
|
42681
|
+
replyBytes: MAX_REPLY_BYTES
|
|
42682
|
+
});
|
|
42683
|
+
var BRIDGE_HOST_CAPABILITIES = Object.freeze({
|
|
42684
|
+
queryKindProjection: "query.kind-projection",
|
|
42685
|
+
queryFieldOr: "query.field-or",
|
|
42686
|
+
queryOpen: "query.open",
|
|
42687
|
+
queryCount: "query.count",
|
|
42688
|
+
edges: "edges",
|
|
42689
|
+
renderDocument: "render-document",
|
|
42690
|
+
openPage: "open-page",
|
|
42691
|
+
subscribeDeltas: "subscribe-deltas",
|
|
42692
|
+
graph: "graph",
|
|
42693
|
+
graphModel: "graph.model",
|
|
42694
|
+
recordOpen: "record.open",
|
|
42695
|
+
frameResize: "frame.resize"
|
|
42696
|
+
});
|
|
42697
|
+
var BRIDGE_SERVICE_CAPABILITIES = Object.freeze([
|
|
42698
|
+
BRIDGE_HOST_CAPABILITIES.queryKindProjection,
|
|
42699
|
+
BRIDGE_HOST_CAPABILITIES.queryFieldOr,
|
|
42700
|
+
BRIDGE_HOST_CAPABILITIES.queryOpen,
|
|
42701
|
+
BRIDGE_HOST_CAPABILITIES.queryCount,
|
|
42702
|
+
BRIDGE_HOST_CAPABILITIES.edges,
|
|
42703
|
+
BRIDGE_HOST_CAPABILITIES.graph,
|
|
42704
|
+
BRIDGE_HOST_CAPABILITIES.renderDocument
|
|
42705
|
+
]);
|
|
42706
|
+
var V0_REQUEST_TYPES = /* @__PURE__ */ new Set(["hello", "query", "read", "render-document", "edges", "graph", "subscribe", "host", "open-page"]);
|
|
42453
42707
|
function isPlainRecord(value) {
|
|
42454
42708
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
42455
42709
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -42582,6 +42836,45 @@ function parseBridgeRequest(value) {
|
|
|
42582
42836
|
const params = normalizeEdgeParams(value.params);
|
|
42583
42837
|
return params ? { bridge: BRIDGE_PROTOCOL, type: "edges", id, params } : null;
|
|
42584
42838
|
}
|
|
42839
|
+
if (value.type === "graph") {
|
|
42840
|
+
const expected = value.includeBodies === void 0 ? ["bridge", "type", "id"] : ["bridge", "type", "id", "includeBodies"];
|
|
42841
|
+
if (!exactKeys(value, expected)) return null;
|
|
42842
|
+
if (value.includeBodies !== void 0 && typeof value.includeBodies !== "boolean") return null;
|
|
42843
|
+
return { bridge: BRIDGE_PROTOCOL, type: "graph", id, includeBodies: value.includeBodies === true };
|
|
42844
|
+
}
|
|
42845
|
+
if (value.type === "host") {
|
|
42846
|
+
const expected = value.input === void 0 ? ["bridge", "type", "id", "capability"] : ["bridge", "type", "id", "capability", "input"];
|
|
42847
|
+
if (!exactKeys(value, expected)) return null;
|
|
42848
|
+
const capability = boundedString(value.capability, MAX_HOST_CAPABILITY_BYTES);
|
|
42849
|
+
if (!capability || !HOST_CAPABILITY_NAME.test(capability)) return null;
|
|
42850
|
+
if (value.input !== void 0 && !isPlainRecord(value.input)) return null;
|
|
42851
|
+
let bytes;
|
|
42852
|
+
try {
|
|
42853
|
+
bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
42854
|
+
} catch {
|
|
42855
|
+
return null;
|
|
42856
|
+
}
|
|
42857
|
+
if (bytes > MAX_HOST_REQUEST_BYTES) return null;
|
|
42858
|
+
return {
|
|
42859
|
+
bridge: BRIDGE_PROTOCOL,
|
|
42860
|
+
type: "host",
|
|
42861
|
+
id,
|
|
42862
|
+
capability,
|
|
42863
|
+
...value.input === void 0 ? {} : { input: value.input }
|
|
42864
|
+
};
|
|
42865
|
+
}
|
|
42866
|
+
return null;
|
|
42867
|
+
}
|
|
42868
|
+
function unsupportedRequest(value) {
|
|
42869
|
+
if (!isPlainRecord(value) || typeof value.type !== "string") return null;
|
|
42870
|
+
if (value.bridge === BRIDGE_PROTOCOL) {
|
|
42871
|
+
return V0_REQUEST_TYPES.has(value.type) ? null : { bridge: BRIDGE_PROTOCOL, id: requestId(value.id) ?? void 0 };
|
|
42872
|
+
}
|
|
42873
|
+
if (value.bridge === ACTION_BRIDGE_PROTOCOL) {
|
|
42874
|
+
if (value.type === "read-versioned") return null;
|
|
42875
|
+
const id = value.type === "action.propose" ? value.requestId : value.id;
|
|
42876
|
+
return { bridge: ACTION_BRIDGE_PROTOCOL, id: requestId(id) ?? void 0 };
|
|
42877
|
+
}
|
|
42585
42878
|
return null;
|
|
42586
42879
|
}
|
|
42587
42880
|
function ok3(id, bridge, type, result) {
|
|
@@ -42606,6 +42899,17 @@ var BridgeService = class {
|
|
|
42606
42899
|
async handle(launchId, rawRequest) {
|
|
42607
42900
|
const request = parseBridgeRequest(rawRequest);
|
|
42608
42901
|
if (!request) {
|
|
42902
|
+
const unsupported = unsupportedRequest(rawRequest);
|
|
42903
|
+
if (unsupported) {
|
|
42904
|
+
return {
|
|
42905
|
+
reply: fail(
|
|
42906
|
+
unsupported.id,
|
|
42907
|
+
unsupported.bridge,
|
|
42908
|
+
"FORBIDDEN",
|
|
42909
|
+
"this host does not offer the requested bridge operation"
|
|
42910
|
+
)
|
|
42911
|
+
};
|
|
42912
|
+
}
|
|
42609
42913
|
return {
|
|
42610
42914
|
reply: fail(
|
|
42611
42915
|
invalidV0RequestId(rawRequest),
|
|
@@ -42615,16 +42919,6 @@ var BridgeService = class {
|
|
|
42615
42919
|
)
|
|
42616
42920
|
};
|
|
42617
42921
|
}
|
|
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
42922
|
const dataBearing = request.type !== "open-page";
|
|
42629
42923
|
const before = await this.options.launches.resolve(launchId, dataBearing);
|
|
42630
42924
|
if (!before) {
|
|
@@ -42730,6 +43024,73 @@ var BridgeService = class {
|
|
|
42730
43024
|
[...rows].sort((a, b) => a.id.localeCompare(b.id)).map((row) => [row.id, row.version])
|
|
42731
43025
|
);
|
|
42732
43026
|
}
|
|
43027
|
+
/**
|
|
43028
|
+
* Whole-bundle projection for graph-shaped Views. The documents come from one head scan and
|
|
43029
|
+
* the relationships from `queryEdges`, which is a second full-bundle scan; the two scans are
|
|
43030
|
+
* accepted because the reply is bounded by the exported graph limits and the host never
|
|
43031
|
+
* caches bundle state on a View's behalf. No `model` or `definitions` are answered here: the
|
|
43032
|
+
* owner of that shape is undecided, and a host that has one declares the `graph.model`
|
|
43033
|
+
* capability before adding them.
|
|
43034
|
+
*/
|
|
43035
|
+
async graph(launch, request) {
|
|
43036
|
+
const heads = await queryHeads2(this.options.bundle, {});
|
|
43037
|
+
if (heads.length > GRAPH_MAX_DOCUMENTS) {
|
|
43038
|
+
return {
|
|
43039
|
+
reply: fail(request.id, request.bridge, "TOO_LARGE", `the graph exceeded ${GRAPH_MAX_DOCUMENTS} documents`)
|
|
43040
|
+
};
|
|
43041
|
+
}
|
|
43042
|
+
const [registry, declaredOkfVersion, edges] = await Promise.all([
|
|
43043
|
+
loadKinds(this.options.bundle),
|
|
43044
|
+
readBundleOkfVersion2(this.options.bundle),
|
|
43045
|
+
queryEdges2(this.options.bundle, {})
|
|
43046
|
+
]);
|
|
43047
|
+
if (edges.length > GRAPH_MAX_RELATIONSHIPS) {
|
|
43048
|
+
return {
|
|
43049
|
+
reply: fail(request.id, request.bridge, "TOO_LARGE", `the graph exceeded ${GRAPH_MAX_RELATIONSHIPS} relationships`)
|
|
43050
|
+
};
|
|
43051
|
+
}
|
|
43052
|
+
const okfVersion = declaredOkfVersion ?? "0.1";
|
|
43053
|
+
const includeBodies = request.includeBodies && (launch.capability === "bundle-read" || launch.capability === "bundle-propose");
|
|
43054
|
+
const documents = [];
|
|
43055
|
+
for (const head of heads) {
|
|
43056
|
+
const source = includeBodies ? await readDocVersioned2(this.options.bundle, head.id).then((result) => ({
|
|
43057
|
+
version: result.version,
|
|
43058
|
+
frontmatter: result.doc.frontmatter,
|
|
43059
|
+
body: result.doc.body
|
|
43060
|
+
})) : { version: head.version, frontmatter: head.frontmatter };
|
|
43061
|
+
if (source.body !== void 0 && Buffer.byteLength(source.body, "utf8") > MAX_DOCUMENT_BODY_BYTES) {
|
|
43062
|
+
return { reply: fail(request.id, request.bridge, "TOO_LARGE", "a document body exceeded the 1 MiB View limit") };
|
|
43063
|
+
}
|
|
43064
|
+
const kind = registry.kinds.get(String(source.frontmatter.type ?? ""));
|
|
43065
|
+
const frontmatter2 = kind ? projectLogicalKindFields(okfVersion, kind, source.frontmatter) : source.frontmatter;
|
|
43066
|
+
documents.push({
|
|
43067
|
+
id: head.id,
|
|
43068
|
+
version: source.version,
|
|
43069
|
+
frontmatter: frontmatter2,
|
|
43070
|
+
...source.body === void 0 ? {} : { body: source.body }
|
|
43071
|
+
});
|
|
43072
|
+
}
|
|
43073
|
+
const relationships = edges.map(({ from, to, text: text4 }) => ({ from, to, text: text4 }));
|
|
43074
|
+
return {
|
|
43075
|
+
reply: ok3(request.id, request.bridge, request.type, {
|
|
43076
|
+
okfVersion,
|
|
43077
|
+
documents,
|
|
43078
|
+
relationships,
|
|
43079
|
+
counts: { documents: documents.length, relationships: relationships.length }
|
|
43080
|
+
})
|
|
43081
|
+
};
|
|
43082
|
+
}
|
|
43083
|
+
hostDescriptor() {
|
|
43084
|
+
const declared = new Set(this.options.host.capabilities);
|
|
43085
|
+
for (const name of Object.keys(this.options.hostHandlers ?? {})) declared.add(name);
|
|
43086
|
+
return {
|
|
43087
|
+
kind: this.options.host.kind,
|
|
43088
|
+
capabilities: [...declared].sort(),
|
|
43089
|
+
limits: { ...this.options.host.limits },
|
|
43090
|
+
...this.options.host.frame ? { frame: { ...this.options.host.frame } } : {},
|
|
43091
|
+
...this.options.host.theme ? { theme: { ...this.options.host.theme } } : {}
|
|
43092
|
+
};
|
|
43093
|
+
}
|
|
42733
43094
|
async execute(launch, request) {
|
|
42734
43095
|
if (request.type === "open-page") {
|
|
42735
43096
|
if (this.options.consumeOpenPage === true) {
|
|
@@ -42753,10 +43114,25 @@ var BridgeService = class {
|
|
|
42753
43114
|
bundle: { root: config.root, name: config.name },
|
|
42754
43115
|
mode: config.mode,
|
|
42755
43116
|
protocol: BRIDGE_PROTOCOL,
|
|
42756
|
-
grant: launch.capability === "bundle-propose" ? "propose" : "read"
|
|
43117
|
+
grant: launch.capability === "bundle-propose" ? "propose" : "read",
|
|
43118
|
+
...config.mode === "dir" && this.options.host.kind === "oss" && launch.capability === "bundle-propose" ? { actionProtocol: "v1", actions: ["document.set-field", "document.set-body", "document.update"] } : {},
|
|
43119
|
+
host: this.hostDescriptor()
|
|
42757
43120
|
})
|
|
42758
43121
|
};
|
|
42759
43122
|
}
|
|
43123
|
+
if (request.type === "host") {
|
|
43124
|
+
const handlers = this.options.hostHandlers ?? {};
|
|
43125
|
+
if (!Object.hasOwn(handlers, request.capability)) {
|
|
43126
|
+
return { reply: fail(request.id, request.bridge, "FORBIDDEN", "this host does not offer the requested capability") };
|
|
43127
|
+
}
|
|
43128
|
+
const outcome = await handlers[request.capability]({
|
|
43129
|
+
capability: request.capability,
|
|
43130
|
+
input: request.input,
|
|
43131
|
+
launch
|
|
43132
|
+
});
|
|
43133
|
+
if (!outcome.ok) return { reply: fail(request.id, request.bridge, outcome.code, outcome.message) };
|
|
43134
|
+
return { reply: ok3(request.id, request.bridge, request.type, { capability: request.capability, output: outcome.output }) };
|
|
43135
|
+
}
|
|
42760
43136
|
if (request.type === "query") {
|
|
42761
43137
|
const rows = await queryHeads2(this.options.bundle, {
|
|
42762
43138
|
...request.params.type ? { type: request.params.type } : {},
|
|
@@ -42834,6 +43210,9 @@ var BridgeService = class {
|
|
|
42834
43210
|
const projected = edges.map(({ from, to, text: text4 }) => ({ from, to, text: text4 }));
|
|
42835
43211
|
return { reply: ok3(request.id, request.bridge, request.type, { edges: projected, count: projected.length }) };
|
|
42836
43212
|
}
|
|
43213
|
+
if (request.type === "graph") {
|
|
43214
|
+
return this.graph(launch, request);
|
|
43215
|
+
}
|
|
42837
43216
|
if (this.options.enablePolling) {
|
|
42838
43217
|
this.subscriptions.set(launch.launchId, {
|
|
42839
43218
|
baseline: await this.subscriptionSnapshot()
|
|
@@ -43291,7 +43670,7 @@ function buildSnapshot(first) {
|
|
|
43291
43670
|
const snapshotDigest = sha256(utf8(canonicalJson(withoutDigest)));
|
|
43292
43671
|
return new SnapshotHandle({ ...withoutDigest, snapshotDigest }, objects);
|
|
43293
43672
|
}
|
|
43294
|
-
async function capturePublicationSnapshot(options) {
|
|
43673
|
+
async function capturePublicationSnapshot(options, runtimeOptions = {}) {
|
|
43295
43674
|
if (options.schema !== PUBLICATION_SNAPSHOT_V1) {
|
|
43296
43675
|
throw new PublicationError("CAPABILITY_UNAVAILABLE", "the requested publication snapshot schema is unsupported");
|
|
43297
43676
|
}
|
|
@@ -43302,6 +43681,7 @@ async function capturePublicationSnapshot(options) {
|
|
|
43302
43681
|
throw new PublicationError("INVALID_OBJECT_IDENTITY", "the filesystem publication root must be absolute");
|
|
43303
43682
|
}
|
|
43304
43683
|
const requestedRoot = path5.resolve(options.source.root);
|
|
43684
|
+
const backend = new FilesystemBackend(requestedRoot, { hostPolicy: runtimeOptions.filesystemHostPolicy });
|
|
43305
43685
|
let rootIdentity;
|
|
43306
43686
|
try {
|
|
43307
43687
|
rootIdentity = await authorizePublicationRoot(requestedRoot);
|
|
@@ -43324,7 +43704,6 @@ async function capturePublicationSnapshot(options) {
|
|
|
43324
43704
|
}
|
|
43325
43705
|
}
|
|
43326
43706
|
const maxAttempts = options.maxAttempts ?? 2;
|
|
43327
|
-
const backend = new FilesystemBackend(root);
|
|
43328
43707
|
let lastError;
|
|
43329
43708
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
43330
43709
|
let handle;
|
|
@@ -43466,6 +43845,10 @@ var STATIC_LAUNCH_ID = "publication-snapshot";
|
|
|
43466
43845
|
function digest(bytes) {
|
|
43467
43846
|
return `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
|
|
43468
43847
|
}
|
|
43848
|
+
var STATIC_BRIDGE_CAPABILITIES = Object.freeze([
|
|
43849
|
+
...BRIDGE_SERVICE_CAPABILITIES,
|
|
43850
|
+
BRIDGE_HOST_CAPABILITIES.openPage
|
|
43851
|
+
]);
|
|
43469
43852
|
function createPublicationBridge(options) {
|
|
43470
43853
|
if (options.protocol !== PUBLICATION_BRIDGE_V0) {
|
|
43471
43854
|
throw new PublicationError("CAPABILITY_UNAVAILABLE", "the requested publication bridge protocol is unsupported");
|
|
@@ -43513,7 +43896,11 @@ function createPublicationBridge(options) {
|
|
|
43513
43896
|
if (!rendered || rendered.id !== id) throw new Error(`document '${id}' is not available`);
|
|
43514
43897
|
return { html: rendered.html, bounded: rendered.bounded };
|
|
43515
43898
|
},
|
|
43516
|
-
|
|
43899
|
+
host: {
|
|
43900
|
+
kind: options.host?.kind ?? "oss",
|
|
43901
|
+
capabilities: options.host?.capabilities ?? STATIC_BRIDGE_CAPABILITIES,
|
|
43902
|
+
limits: BRIDGE_SERVICE_LIMITS
|
|
43903
|
+
},
|
|
43517
43904
|
enablePolling: false,
|
|
43518
43905
|
consumeOpenPage: false
|
|
43519
43906
|
});
|