superbee 0.4.6-pre.1 → 0.4.6-pre.3
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 +7 -2
- package/SKILL.md +13 -5
- package/dist/THIRD-PARTY-NOTICES.txt +2 -2
- package/dist/portable-export.mjs +1 -1
- package/dist/publication/types.d.ts +8 -0
- package/dist/publication-bridge.mjs +330 -144
- package/dist/publication.mjs +347 -146
- package/dist/superbee.mjs +84205 -78550
- package/package.json +2 -2
- package/references/hosted-checkout.md +1 -1
- package/references/modeling-and-delivery.md +1 -1
- package/references/recipes/review-workflow/references/view-authoring-v0.md +1 -2
- package/references/views/references/view-authoring-v0.md +1 -2
|
@@ -2890,15 +2890,30 @@ var FrontmatterRefusedError = class extends InvalidInputError {
|
|
|
2890
2890
|
}
|
|
2891
2891
|
};
|
|
2892
2892
|
var FilesystemIdentityAliasError = class extends InvalidInputError {
|
|
2893
|
+
reason = "filesystem_alias";
|
|
2893
2894
|
rel;
|
|
2894
2895
|
segment;
|
|
2895
|
-
|
|
2896
|
+
existing;
|
|
2897
|
+
suggestedPath;
|
|
2898
|
+
/**
|
|
2899
|
+
* `noun` names the existing entry ("folder" or "file"); `index` is the position of `segment`
|
|
2900
|
+
* in `rel`. Both default from the position of the first segment spelled `segment`.
|
|
2901
|
+
*/
|
|
2902
|
+
constructor(rel, segment, existing, noun, index) {
|
|
2903
|
+
const segments = rel.split("/").filter((part) => part !== "" && part !== ".");
|
|
2904
|
+
const at = index ?? segments.indexOf(segment);
|
|
2905
|
+
const kind = noun ?? (at === segments.length - 1 ? "file" : "folder");
|
|
2906
|
+
const clash = existing === void 0 ? `but a ${kind} with that name in a different case or Unicode form already exists` : existing.normalize("NFC") === segment.normalize("NFC") ? `but ${kind} '${existing}' already exists, written in a different Unicode form` : `but ${kind} '${existing}' already exists`;
|
|
2907
|
+
const difference = existing === void 0 ? "case or Unicode form" : existing.normalize("NFC") === segment.normalize("NFC") ? "Unicode form" : existing.toLowerCase() === segment.toLowerCase() ? "case" : "case and Unicode form";
|
|
2896
2908
|
super(
|
|
2897
|
-
`Path '${rel}'
|
|
2909
|
+
`Path '${rel}' uses ${kind} '${segment}', ${clash}, and this disk treats names that differ only by ${difference} as the same name. Use the existing spelling.`
|
|
2898
2910
|
);
|
|
2899
2911
|
this.name = "FilesystemIdentityAliasError";
|
|
2900
2912
|
this.rel = rel;
|
|
2901
2913
|
this.segment = segment;
|
|
2914
|
+
this.existing = existing;
|
|
2915
|
+
const unusable = existing === void 0 || at < 0 || segments[at] !== segment || kind === "file" && at < segments.length - 1 || existing.normalize("NFC") === segment.normalize("NFC");
|
|
2916
|
+
this.suggestedPath = unusable ? void 0 : segments.map((part, position) => position === at ? existing : part).join("/");
|
|
2902
2917
|
}
|
|
2903
2918
|
};
|
|
2904
2919
|
var ConcurrentReplacementError = class extends Error {
|
|
@@ -3103,6 +3118,9 @@ function delimiterLineEnd(input, offset) {
|
|
|
3103
3118
|
if (input[suffix] === "\r" && input[suffix + 1] === "\n") return suffix + 2;
|
|
3104
3119
|
return null;
|
|
3105
3120
|
}
|
|
3121
|
+
function startsWithFrontmatterSyntax(text) {
|
|
3122
|
+
return text.startsWith("\uFEFF") || delimiterLineEnd(text, 0) !== null;
|
|
3123
|
+
}
|
|
3106
3124
|
function splitLeadingFrontmatter(raw, context) {
|
|
3107
3125
|
const input = raw.startsWith("\uFEFF") ? raw.slice(1) : raw;
|
|
3108
3126
|
const openingEnd = delimiterLineEnd(input, 0);
|
|
@@ -3208,7 +3226,10 @@ function stringifyWithSerializedFrontmatter(dumped, body) {
|
|
|
3208
3226
|
const content = body ?? "";
|
|
3209
3227
|
const newline = (value) => value.endsWith("\n") ? value : `${value}
|
|
3210
3228
|
`;
|
|
3211
|
-
if (dumped === "{}")
|
|
3229
|
+
if (dumped === "{}") {
|
|
3230
|
+
const stored = normalizeDocumentBodyForStorage(content);
|
|
3231
|
+
return `${startsWithFrontmatterSyntax(stored) ? "---\n---\n" : ""}${stored}`;
|
|
3232
|
+
}
|
|
3212
3233
|
return `---
|
|
3213
3234
|
${newline(dumped)}---
|
|
3214
3235
|
${normalizeDocumentBodyForStorage(content)}`;
|
|
@@ -3644,6 +3665,8 @@ function extractMarkdownLinkSpans(source, options = {}) {
|
|
|
3644
3665
|
const body = options.skipCode ? maskMarkdownCode(source) : source;
|
|
3645
3666
|
const out = [];
|
|
3646
3667
|
let textOpen = -1;
|
|
3668
|
+
let codeOpen = -1;
|
|
3669
|
+
let masked;
|
|
3647
3670
|
let afterClose;
|
|
3648
3671
|
let hrefFirst;
|
|
3649
3672
|
let hrefRest;
|
|
@@ -3662,6 +3685,7 @@ function extractMarkdownLinkSpans(source, options = {}) {
|
|
|
3662
3685
|
image
|
|
3663
3686
|
});
|
|
3664
3687
|
textOpen = -1;
|
|
3688
|
+
codeOpen = -1;
|
|
3665
3689
|
afterClose = void 0;
|
|
3666
3690
|
hrefFirst = void 0;
|
|
3667
3691
|
hrefRest = void 0;
|
|
@@ -3682,10 +3706,22 @@ function extractMarkdownLinkSpans(source, options = {}) {
|
|
|
3682
3706
|
afterClose = void 0;
|
|
3683
3707
|
}
|
|
3684
3708
|
if (textOpen >= 0 && char === "]") {
|
|
3685
|
-
|
|
3709
|
+
const open = codeOpen >= 0 && masked[cursor] !== "]" ? codeOpen : textOpen;
|
|
3710
|
+
afterClose = { open, close: cursor };
|
|
3686
3711
|
textOpen = -1;
|
|
3712
|
+
codeOpen = -1;
|
|
3713
|
+
}
|
|
3714
|
+
if (char === "[") {
|
|
3715
|
+
if (textOpen >= 0 && !options.skipCode) {
|
|
3716
|
+
masked ??= maskMarkdownCode(source);
|
|
3717
|
+
if (masked[cursor] !== "[" && masked[textOpen] === "[") {
|
|
3718
|
+
codeOpen = cursor;
|
|
3719
|
+
continue;
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
textOpen = cursor;
|
|
3723
|
+
codeOpen = -1;
|
|
3687
3724
|
}
|
|
3688
|
-
if (char === "[" && textOpen < 0) textOpen = cursor;
|
|
3689
3725
|
}
|
|
3690
3726
|
return out;
|
|
3691
3727
|
}
|
|
@@ -4019,36 +4055,57 @@ function hasSourceIdentity(sources, identity) {
|
|
|
4019
4055
|
return Array.isArray(sources) && sources.some((entry) => matchesSourceIdentity(entry, identity));
|
|
4020
4056
|
}
|
|
4021
4057
|
|
|
4058
|
+
// ../core/src/skip-reasons.ts
|
|
4059
|
+
var PERMISSION_DENIED_PREFIX = "permission denied (";
|
|
4060
|
+
function permissionDeniedReason(code) {
|
|
4061
|
+
return `${PERMISSION_DENIED_PREFIX}${code})`;
|
|
4062
|
+
}
|
|
4063
|
+
function isPermissionDeniedSkip(skip) {
|
|
4064
|
+
return skip.reason.startsWith(PERMISSION_DENIED_PREFIX);
|
|
4065
|
+
}
|
|
4066
|
+
|
|
4022
4067
|
// ../core/src/document-scan.ts
|
|
4023
4068
|
function isEnoent(err) {
|
|
4024
4069
|
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
4025
4070
|
}
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
onMalformed({ id, reason: candidate.detail });
|
|
4041
|
-
continue;
|
|
4042
|
-
}
|
|
4043
|
-
if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
|
|
4044
|
-
onAliased({ id, reason: candidate.message });
|
|
4045
|
-
continue;
|
|
4046
|
-
}
|
|
4047
|
-
throw candidate;
|
|
4071
|
+
function deniedReason(err) {
|
|
4072
|
+
const code = typeof err === "object" && err !== null ? err.code : void 0;
|
|
4073
|
+
return code === "EACCES" || code === "EPERM" ? permissionDeniedReason(code) : void 0;
|
|
4074
|
+
}
|
|
4075
|
+
async function readManyExisting(backend, ids, onMalformed, onAliased, onDenied) {
|
|
4076
|
+
const tolerated = (err) => isEnoent(err) || err instanceof MalformedDocumentError && onMalformed !== void 0 || err instanceof FilesystemIdentityAliasError && onAliased !== void 0 || deniedReason(err) !== void 0 && onDenied !== void 0;
|
|
4077
|
+
const readOne = async (id) => {
|
|
4078
|
+
try {
|
|
4079
|
+
return [await backend.read(id)];
|
|
4080
|
+
} catch (candidate) {
|
|
4081
|
+
if (isEnoent(candidate)) return [];
|
|
4082
|
+
if (candidate instanceof MalformedDocumentError && onMalformed) {
|
|
4083
|
+
onMalformed({ id, reason: candidate.detail });
|
|
4084
|
+
return [];
|
|
4048
4085
|
}
|
|
4086
|
+
if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
|
|
4087
|
+
onAliased({ id, reason: candidate.message });
|
|
4088
|
+
return [];
|
|
4089
|
+
}
|
|
4090
|
+
const reason = deniedReason(candidate);
|
|
4091
|
+
if (reason !== void 0 && onDenied) {
|
|
4092
|
+
onDenied({ id, reason });
|
|
4093
|
+
return [];
|
|
4094
|
+
}
|
|
4095
|
+
throw candidate;
|
|
4049
4096
|
}
|
|
4050
|
-
|
|
4051
|
-
|
|
4097
|
+
};
|
|
4098
|
+
const readBatch = async (batch) => {
|
|
4099
|
+
try {
|
|
4100
|
+
return await backend.readMany(batch);
|
|
4101
|
+
} catch (err) {
|
|
4102
|
+
if (!tolerated(err)) throw err;
|
|
4103
|
+
if (batch.length === 1) return readOne(batch[0]);
|
|
4104
|
+
const half = Math.ceil(batch.length / 2);
|
|
4105
|
+
return [...await readBatch(batch.slice(0, half)), ...await readBatch(batch.slice(half))];
|
|
4106
|
+
}
|
|
4107
|
+
};
|
|
4108
|
+
return readBatch(ids);
|
|
4052
4109
|
}
|
|
4053
4110
|
|
|
4054
4111
|
// ../core/src/engine.ts
|
|
@@ -4072,7 +4129,7 @@ async function readDocVersioned(backend, id) {
|
|
|
4072
4129
|
async function scanMatching(backend, filter, onSkip) {
|
|
4073
4130
|
const ids = await backend.list(filter.prefix);
|
|
4074
4131
|
const results = [];
|
|
4075
|
-
for (const result of await readManyExisting(backend, ids, onSkip)) {
|
|
4132
|
+
for (const result of await readManyExisting(backend, ids, onSkip, void 0, onSkip)) {
|
|
4076
4133
|
if (matchesFilter(result.doc, filter)) results.push(result);
|
|
4077
4134
|
}
|
|
4078
4135
|
results.sort((a, b) => compareCodeUnits(a.doc.id, b.doc.id));
|
|
@@ -4197,7 +4254,7 @@ function assertStorableReservedWrite(dir, name, content) {
|
|
|
4197
4254
|
// ../core/src/filesystem-identity.ts
|
|
4198
4255
|
import { promises as fs2 } from "node:fs";
|
|
4199
4256
|
import path2 from "node:path";
|
|
4200
|
-
import { createHash as createHash2
|
|
4257
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
4201
4258
|
|
|
4202
4259
|
// ../core/src/filesystem-lock.ts
|
|
4203
4260
|
import { promises as fs, realpathSync } from "node:fs";
|
|
@@ -4252,6 +4309,8 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
4252
4309
|
var OWNER_FILE = "owner.json";
|
|
4253
4310
|
var DEFAULT_WAIT_MS = 5e3;
|
|
4254
4311
|
var DEFAULT_POLL_MS = 25;
|
|
4312
|
+
var UNREADABLE_OWNER_POLL_MS = 2e3;
|
|
4313
|
+
var ABANDONED_CLAIM_AGE_MS = 2e3;
|
|
4255
4314
|
var FilesystemMutationLockError = class extends Error {
|
|
4256
4315
|
lockPath;
|
|
4257
4316
|
owner;
|
|
@@ -4339,9 +4398,13 @@ async function readOwnerRecord(lockPath) {
|
|
|
4339
4398
|
return owner === null ? { state: "absent" } : { state: "record", owner };
|
|
4340
4399
|
}
|
|
4341
4400
|
async function resolveOwnerRecord(lockPath, started, waitMs, pollMs) {
|
|
4401
|
+
let firstUnreadable;
|
|
4342
4402
|
while (true) {
|
|
4343
4403
|
const record2 = await readOwnerRecord(lockPath);
|
|
4344
|
-
if (record2.state !== "unreadable"
|
|
4404
|
+
if (record2.state !== "unreadable") return record2;
|
|
4405
|
+
const now = Date.now();
|
|
4406
|
+
firstUnreadable ??= now;
|
|
4407
|
+
if (now - started >= waitMs || now - firstUnreadable >= UNREADABLE_OWNER_POLL_MS) return record2;
|
|
4345
4408
|
await delay(pollMs);
|
|
4346
4409
|
}
|
|
4347
4410
|
}
|
|
@@ -4392,6 +4455,17 @@ async function quarantineStaleLock(lockPath, owner, policy) {
|
|
|
4392
4455
|
throw err;
|
|
4393
4456
|
}
|
|
4394
4457
|
}
|
|
4458
|
+
async function removeAbandonedClaim(lockPath) {
|
|
4459
|
+
try {
|
|
4460
|
+
const info = await fs.lstat(lockPath);
|
|
4461
|
+
const age = Date.now() - info.mtimeMs;
|
|
4462
|
+
if (!info.isDirectory() || age < ABANDONED_CLAIM_AGE_MS) return false;
|
|
4463
|
+
await fs.rmdir(lockPath);
|
|
4464
|
+
return true;
|
|
4465
|
+
} catch {
|
|
4466
|
+
return false;
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4395
4469
|
function delay(ms) {
|
|
4396
4470
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4397
4471
|
}
|
|
@@ -4442,7 +4516,7 @@ async function timeoutError(lockPath, snapshot, guarded) {
|
|
|
4442
4516
|
const sameHost = owner?.hostname === hostname();
|
|
4443
4517
|
let message;
|
|
4444
4518
|
if (malformed) {
|
|
4445
|
-
message = `timed out waiting for filesystem mutation lock '${lockPath}'; its owner metadata is missing or malformed.
|
|
4519
|
+
message = `timed out waiting for filesystem mutation lock '${lockPath}'; its owner metadata is missing or malformed. Remove the lock directory once no Superbee process is running, then retry.`;
|
|
4446
4520
|
} else if (stale) {
|
|
4447
4521
|
message = `stale filesystem mutation lock '${lockPath}' belongs to absent PID ${owner.pid} on ${owner.hostname}. Inspect and remove the lock, then retry.`;
|
|
4448
4522
|
} else if (!sameHost) {
|
|
@@ -4491,7 +4565,9 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
|
|
|
4491
4565
|
} else {
|
|
4492
4566
|
unwitnessedRetryUsed = false;
|
|
4493
4567
|
}
|
|
4494
|
-
|
|
4568
|
+
const existing = await readOwnerRecord(lockPath);
|
|
4569
|
+
let existingOwner = existing.state === "record" ? existing.owner : null;
|
|
4570
|
+
if (existing.state === "absent" && await removeAbandonedClaim(lockPath)) continue;
|
|
4495
4571
|
if (existingOwner !== null) {
|
|
4496
4572
|
if (await quarantineStaleLock(lockPath, existingOwner, policy)) continue;
|
|
4497
4573
|
existingOwner = await readOwner(lockPath);
|
|
@@ -4511,14 +4587,13 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
|
|
|
4511
4587
|
});
|
|
4512
4588
|
} catch (err) {
|
|
4513
4589
|
const code = err.code;
|
|
4514
|
-
if (code === "EEXIST") {
|
|
4590
|
+
if (code === "EEXIST" || code === "ENOENT" || code === "ENOTDIR" || await claimDirectoryLost(lockPath, claimed)) {
|
|
4515
4591
|
heldTokens.delete(owner.token);
|
|
4516
4592
|
continue;
|
|
4517
4593
|
}
|
|
4518
4594
|
await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => {
|
|
4519
4595
|
});
|
|
4520
4596
|
heldTokens.delete(owner.token);
|
|
4521
|
-
if (code === "ENOENT" || code === "ENOTDIR") continue;
|
|
4522
4597
|
throw err;
|
|
4523
4598
|
}
|
|
4524
4599
|
let inFlight;
|
|
@@ -4548,9 +4623,18 @@ async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy
|
|
|
4548
4623
|
return;
|
|
4549
4624
|
}
|
|
4550
4625
|
if (record2.state === "unreadable" || !await isClaimedDirectory(lockPath, claimed)) return;
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4626
|
+
await fs.rmdir(lockPath);
|
|
4627
|
+
}
|
|
4628
|
+
async function claimDirectoryLost(lockPath, claimed) {
|
|
4629
|
+
try {
|
|
4630
|
+
const current = await fs.lstat(lockPath, { bigint: true });
|
|
4631
|
+
if (claimed === null) return false;
|
|
4632
|
+
const reborn = claimed.birthtimeNs > 0n && current.birthtimeNs > 0n && current.birthtimeNs !== claimed.birthtimeNs;
|
|
4633
|
+
return current.dev !== claimed.dev || current.ino !== claimed.ino || reborn;
|
|
4634
|
+
} catch (err) {
|
|
4635
|
+
const code = err.code;
|
|
4636
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
4637
|
+
}
|
|
4554
4638
|
}
|
|
4555
4639
|
async function isClaimedDirectory(lockPath, claimed) {
|
|
4556
4640
|
if (claimed === null || claimed.birthtimeNs <= 0n) return false;
|
|
@@ -4647,9 +4731,20 @@ async function acquireFilesystemIdentityLock(key, identity, options = {}) {
|
|
|
4647
4731
|
return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs, policy);
|
|
4648
4732
|
}
|
|
4649
4733
|
|
|
4734
|
+
// ../core/src/path-fold.ts
|
|
4735
|
+
function foldPath(path4) {
|
|
4736
|
+
return path4.normalize("NFD").toLowerCase().toUpperCase().toLowerCase().normalize("NFC");
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4650
4739
|
// ../core/src/identity-fold.ts
|
|
4651
4740
|
function foldSegment(segment) {
|
|
4652
|
-
return segment.normalize("
|
|
4741
|
+
return foldPath(segment).normalize("NFD");
|
|
4742
|
+
}
|
|
4743
|
+
|
|
4744
|
+
// ../core/src/filesystem-temp-name.ts
|
|
4745
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
4746
|
+
function identityTempName(leaf) {
|
|
4747
|
+
return `.${leaf}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
|
|
4653
4748
|
}
|
|
4654
4749
|
|
|
4655
4750
|
// ../core/src/filesystem-identity.ts
|
|
@@ -4715,7 +4810,10 @@ async function existingAnchor(resolved) {
|
|
|
4715
4810
|
async function foldedPhysicalRoot(root) {
|
|
4716
4811
|
const { anchor, tail } = await existingAnchor(path2.resolve(root));
|
|
4717
4812
|
const anchorSegments = anchor.split(path2.sep).filter((segment, index) => index === 0 || segment !== "");
|
|
4718
|
-
return [...anchorSegments, ...tail].map(
|
|
4813
|
+
return [...anchorSegments, ...tail].map(foldRootSegment).join("/");
|
|
4814
|
+
}
|
|
4815
|
+
function foldRootSegment(segment) {
|
|
4816
|
+
return segment.normalize("NFKD").toLowerCase().toUpperCase().toLowerCase();
|
|
4719
4817
|
}
|
|
4720
4818
|
async function identityKey(root, rel) {
|
|
4721
4819
|
const foldedRel = relSegments(rel).map(foldSegment).join("/");
|
|
@@ -4796,10 +4894,32 @@ async function probeSegment(port, candidate, rel, segment) {
|
|
|
4796
4894
|
function hasExact(listing, segment) {
|
|
4797
4895
|
return listing.some((entry) => entry.name === segment);
|
|
4798
4896
|
}
|
|
4897
|
+
function existingSpelling(names, segment) {
|
|
4898
|
+
const folded = foldSegment(segment);
|
|
4899
|
+
const matches = [...names].filter((name) => name !== segment && foldSegment(name) === folded);
|
|
4900
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
4901
|
+
}
|
|
4902
|
+
function entryNoun(kind, leaf) {
|
|
4903
|
+
if (kind === void 0) return leaf ? "file" : "folder";
|
|
4904
|
+
return kind === "directory" ? "folder" : "file";
|
|
4905
|
+
}
|
|
4906
|
+
function aliasRefusal(rel, segments, index, names, kind) {
|
|
4907
|
+
const segment = segments[index];
|
|
4908
|
+
return new FilesystemIdentityAliasError(rel, segment, existingSpelling(names, segment), entryNoun(kind, index === segments.length - 1), index);
|
|
4909
|
+
}
|
|
4910
|
+
function aliasRefusalFromListing(rel, index, listing, leaf) {
|
|
4911
|
+
const segment = relSegments(rel)[index];
|
|
4912
|
+
const existing = existingSpelling(listing.map((entry) => entry.name), segment);
|
|
4913
|
+
const kind = existing === void 0 ? void 0 : listing.find((entry) => entry.name === existing)?.kind;
|
|
4914
|
+
return new FilesystemIdentityAliasError(rel, segment, existing, entryNoun(kind, leaf), index);
|
|
4915
|
+
}
|
|
4916
|
+
function listingView(names) {
|
|
4917
|
+
return { has: (name) => names.has(name), names: () => names };
|
|
4918
|
+
}
|
|
4799
4919
|
function freshListings(port) {
|
|
4800
4920
|
return async (dir) => {
|
|
4801
4921
|
const listing = await port.entries(dir);
|
|
4802
|
-
return listing === null ? null : { has: (name) => hasExact(listing, name) };
|
|
4922
|
+
return listing === null ? null : { has: (name) => hasExact(listing, name), names: () => listing.map((entry) => entry.name) };
|
|
4803
4923
|
};
|
|
4804
4924
|
}
|
|
4805
4925
|
async function walkExact(port, rootResolved, segments, rel) {
|
|
@@ -4811,7 +4931,7 @@ async function walkExact(port, rootResolved, segments, rel) {
|
|
|
4811
4931
|
if (probed === null) return { state: "absent", depth: index };
|
|
4812
4932
|
const listing = await port.entries(parent);
|
|
4813
4933
|
if (listing === null) return { state: "absent", depth: index };
|
|
4814
|
-
if (!hasExact(listing, segment)) throw
|
|
4934
|
+
if (!hasExact(listing, segment)) throw aliasRefusal(rel, segments, index, listing.map((entry) => entry.name), probed.kind);
|
|
4815
4935
|
if (index === segments.length - 1) return { state: "exact", leaf: probed };
|
|
4816
4936
|
if (probed.kind !== "directory") return { state: "shape-mismatch", segment };
|
|
4817
4937
|
parent = candidate;
|
|
@@ -4826,35 +4946,53 @@ async function recordWalk(port, rootResolved, segments, rel) {
|
|
|
4826
4946
|
const segment = segments[index];
|
|
4827
4947
|
const candidate = path2.join(parent, segment);
|
|
4828
4948
|
const probed = await probeSegment(port, candidate, rel, segment);
|
|
4829
|
-
if (probed === null) return
|
|
4830
|
-
if (index < segments.length - 1 && probed.kind !== "directory") return null;
|
|
4949
|
+
if (probed === null) return { recorded, complete: false };
|
|
4831
4950
|
recorded.push(probed);
|
|
4951
|
+
if (index < segments.length - 1 && probed.kind !== "directory") return { recorded, complete: false };
|
|
4832
4952
|
parent = candidate;
|
|
4833
4953
|
}
|
|
4834
|
-
return recorded;
|
|
4954
|
+
return { recorded, complete: true };
|
|
4835
4955
|
}
|
|
4836
|
-
async function confirmedWalk(port, rootResolved, segments, rel, recorded, handle, listings = freshListings(port)) {
|
|
4956
|
+
async function confirmedWalk(port, rootResolved, segments, rel, recorded, handle, listings = freshListings(port), stopAt = segments.length) {
|
|
4837
4957
|
let parent = rootResolved;
|
|
4838
|
-
for (let index = 0; index <
|
|
4958
|
+
for (let index = 0; index < stopAt; index++) {
|
|
4839
4959
|
const segment = segments[index];
|
|
4840
4960
|
const candidate = path2.join(parent, segment);
|
|
4841
4961
|
const listing = await listings(parent);
|
|
4842
4962
|
if (listing === null) return "absent";
|
|
4843
4963
|
const listed = listing.has(segment);
|
|
4844
4964
|
if (handle !== null && index === segments.length - 1) {
|
|
4845
|
-
const
|
|
4846
|
-
|
|
4965
|
+
const probe = await probeSegment(port, candidate, rel, segment);
|
|
4966
|
+
const verdict = classifyLeaf({ listed, probe, handle });
|
|
4967
|
+
if (verdict === "aliased") throw aliasRefusal(rel, segments, index, listing.names(), probe?.kind);
|
|
4847
4968
|
return verdict === "replaced" ? "restart" : verdict;
|
|
4848
4969
|
}
|
|
4849
4970
|
if (index < segments.length - 1 || !listed) {
|
|
4850
|
-
const
|
|
4851
|
-
|
|
4971
|
+
const probe = await probeSegment(port, candidate, rel, segment);
|
|
4972
|
+
const verdict = confirmAlias(listed, probe, recorded[index]);
|
|
4973
|
+
if (verdict === "aliased") throw aliasRefusal(rel, segments, index, listing.names(), probe?.kind);
|
|
4852
4974
|
if (verdict !== "continue") return verdict;
|
|
4853
4975
|
}
|
|
4854
4976
|
parent = candidate;
|
|
4855
4977
|
}
|
|
4856
4978
|
return "continue";
|
|
4857
4979
|
}
|
|
4980
|
+
async function confirmPrefix(port, rootResolved, segments, rel, recorded) {
|
|
4981
|
+
if (recorded.length === 0) return "absent";
|
|
4982
|
+
let kept = prefixListings.get(port);
|
|
4983
|
+
if (kept === void 0) prefixListings.set(port, kept = /* @__PURE__ */ new Map());
|
|
4984
|
+
const cached = witnessedListings(port, kept, Date.now, PREFIX_LISTING_CAPACITY);
|
|
4985
|
+
let verdict;
|
|
4986
|
+
try {
|
|
4987
|
+
verdict = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, cached.source, recorded.length);
|
|
4988
|
+
} catch (error) {
|
|
4989
|
+
if (!cached.reused()) throw error;
|
|
4990
|
+
kept.clear();
|
|
4991
|
+
verdict = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, freshListings(port), recorded.length);
|
|
4992
|
+
}
|
|
4993
|
+
if (verdict === "restart" && cached.reused()) kept.clear();
|
|
4994
|
+
return verdict === "restart" ? "restart" : "absent";
|
|
4995
|
+
}
|
|
4858
4996
|
function countRestart(restarts, rel) {
|
|
4859
4997
|
if (restarts >= MAX_RESTARTS) throw new ConcurrentReplacementError(rel, restarts + 1);
|
|
4860
4998
|
return restarts + 1;
|
|
@@ -4866,8 +5004,14 @@ async function observeWith(port, rootResolved, rel, read, listings, onExact) {
|
|
|
4866
5004
|
const segments = relSegments(rel);
|
|
4867
5005
|
const target = path2.join(rootResolved, ...segments);
|
|
4868
5006
|
for (let restarts = 0; ; ) {
|
|
4869
|
-
const
|
|
4870
|
-
if (
|
|
5007
|
+
const walk = await recordWalk(port, rootResolved, segments, rel);
|
|
5008
|
+
if (!walk.complete) {
|
|
5009
|
+
const prefix = await confirmPrefix(port, rootResolved, segments, rel, walk.recorded);
|
|
5010
|
+
if (prefix === "absent") return { state: "absent" };
|
|
5011
|
+
restarts = countRestart(restarts, rel);
|
|
5012
|
+
continue;
|
|
5013
|
+
}
|
|
5014
|
+
const recorded = walk.recorded;
|
|
4871
5015
|
const verified = await confirmedWalk(port, rootResolved, segments, rel, recorded, null, listings);
|
|
4872
5016
|
if (verified === "absent") return { state: "absent" };
|
|
4873
5017
|
if (verified === "restart") {
|
|
@@ -4906,8 +5050,14 @@ async function probeExact(port, root, rel) {
|
|
|
4906
5050
|
const segments = relSegments(rel);
|
|
4907
5051
|
const rootResolved = path2.resolve(root);
|
|
4908
5052
|
for (let restarts = 0; ; ) {
|
|
4909
|
-
const
|
|
4910
|
-
if (
|
|
5053
|
+
const walk = await recordWalk(port, rootResolved, segments, rel);
|
|
5054
|
+
if (!walk.complete) {
|
|
5055
|
+
const prefix = await confirmPrefix(port, rootResolved, segments, rel, walk.recorded);
|
|
5056
|
+
if (prefix === "absent") return { state: "absent" };
|
|
5057
|
+
restarts = countRestart(restarts, rel);
|
|
5058
|
+
continue;
|
|
5059
|
+
}
|
|
5060
|
+
const recorded = walk.recorded;
|
|
4911
5061
|
const leaf = recorded[recorded.length - 1];
|
|
4912
5062
|
const verified = await confirmedWalk(port, rootResolved, segments, rel, recorded, null);
|
|
4913
5063
|
const verdict = verified === "continue" ? await confirmedWalk(port, rootResolved, segments, rel, recorded, leaf) : verified;
|
|
@@ -4917,45 +5067,56 @@ async function probeExact(port, root, rel) {
|
|
|
4917
5067
|
}
|
|
4918
5068
|
}
|
|
4919
5069
|
var SETTLED_AFTER_NS = 3000000000n;
|
|
5070
|
+
function witnessCanDetectChange(witness) {
|
|
5071
|
+
return witness.mtimeNs > 0n && witness.ctimeNs > 0n;
|
|
5072
|
+
}
|
|
4920
5073
|
function witnessKey(witness) {
|
|
4921
5074
|
return `${witness.dev}:${witness.ino}:${witness.mtimeNs}:${witness.ctimeNs}`;
|
|
4922
5075
|
}
|
|
4923
|
-
function
|
|
4924
|
-
|
|
4925
|
-
const
|
|
4926
|
-
|
|
4927
|
-
const
|
|
4928
|
-
|
|
5076
|
+
function witnessedListings(port, kept, now, capacity = Infinity) {
|
|
5077
|
+
let reused = false;
|
|
5078
|
+
const portWitness = port.directoryWitness?.bind(port);
|
|
5079
|
+
if (portWitness === void 0) return { source: freshListings(port), reused: () => false };
|
|
5080
|
+
const witnessOf = async (dir) => {
|
|
5081
|
+
const witness = await portWitness(dir);
|
|
5082
|
+
return witness !== null && witnessCanDetectChange(witness) ? witness : null;
|
|
5083
|
+
};
|
|
4929
5084
|
const settled = (witness) => {
|
|
4930
5085
|
const nowNs = BigInt(Math.floor(now())) * 1000000n;
|
|
4931
5086
|
const latest = witness.mtimeNs > witness.ctimeNs ? witness.mtimeNs : witness.ctimeNs;
|
|
4932
5087
|
return nowNs - latest >= SETTLED_AFTER_NS;
|
|
4933
5088
|
};
|
|
4934
|
-
const
|
|
4935
|
-
|
|
4936
|
-
if (
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
return names;
|
|
4956
|
-
};
|
|
4957
|
-
return { source, reused: () => reused };
|
|
5089
|
+
const source = async (dir) => {
|
|
5090
|
+
const keep = kept.get(dir);
|
|
5091
|
+
if (keep !== void 0) {
|
|
5092
|
+
const current = await witnessOf(dir);
|
|
5093
|
+
if (current !== null && witnessKey(current) === keep.witness) {
|
|
5094
|
+
reused = true;
|
|
5095
|
+
return listingView(keep.names);
|
|
5096
|
+
}
|
|
5097
|
+
kept.delete(dir);
|
|
5098
|
+
}
|
|
5099
|
+
const before = await witnessOf(dir);
|
|
5100
|
+
const listing = await port.entries(dir);
|
|
5101
|
+
if (listing === null) return null;
|
|
5102
|
+
const names = new Set(listing.map((entry) => entry.name));
|
|
5103
|
+
const after = await witnessOf(dir);
|
|
5104
|
+
if (before !== null && after !== null && witnessKey(before) === witnessKey(after) && settled(after)) {
|
|
5105
|
+
kept.delete(dir);
|
|
5106
|
+
kept.set(dir, { witness: witnessKey(after), names });
|
|
5107
|
+
while (kept.size > capacity) kept.delete(kept.keys().next().value);
|
|
5108
|
+
}
|
|
5109
|
+
return listingView(names);
|
|
4958
5110
|
};
|
|
5111
|
+
return { source, reused: () => reused };
|
|
5112
|
+
}
|
|
5113
|
+
var prefixListings = /* @__PURE__ */ new WeakMap();
|
|
5114
|
+
var PREFIX_LISTING_CAPACITY = 64;
|
|
5115
|
+
function openObservationPass(port, root, now = Date.now) {
|
|
5116
|
+
const rootResolved = path2.resolve(root);
|
|
5117
|
+
const kept = /* @__PURE__ */ new Map();
|
|
5118
|
+
const reusedVerdicts = [];
|
|
5119
|
+
const listingsFor = () => witnessedListings(port, kept, now);
|
|
4959
5120
|
return {
|
|
4960
5121
|
async observe(rel, read) {
|
|
4961
5122
|
const listings = listingsFor();
|
|
@@ -5058,7 +5219,7 @@ async function readWhole(port, target) {
|
|
|
5058
5219
|
await port.close(opened.handle);
|
|
5059
5220
|
}
|
|
5060
5221
|
}
|
|
5061
|
-
async function mkdirExact(port, parent, segment, isTail, rel) {
|
|
5222
|
+
async function mkdirExact(port, parent, segment, isTail, rel, index = -1) {
|
|
5062
5223
|
const outcome = await port.mkdir(path2.join(parent, segment));
|
|
5063
5224
|
if (outcome === "created") return;
|
|
5064
5225
|
const listing = await port.entries(parent);
|
|
@@ -5067,7 +5228,9 @@ async function mkdirExact(port, parent, segment, isTail, rel) {
|
|
|
5067
5228
|
throw new FilesystemSymlinkEntryError(rel, segment);
|
|
5068
5229
|
}
|
|
5069
5230
|
const verdict = classifyMkdir(outcome, listing, segment, isTail);
|
|
5070
|
-
if (verdict === "aliased")
|
|
5231
|
+
if (verdict === "aliased") {
|
|
5232
|
+
throw aliasRefusalFromListing(rel, index, listing, false);
|
|
5233
|
+
}
|
|
5071
5234
|
if (verdict === "shape-mismatch") throw new FilesystemShapeMismatchError(rel, segment);
|
|
5072
5235
|
}
|
|
5073
5236
|
async function ensureExactDirectories(port, rootResolved, segments, existingDepth, rel) {
|
|
@@ -5087,8 +5250,9 @@ async function ensureExactDirectories(port, rootResolved, segments, existingDept
|
|
|
5087
5250
|
}
|
|
5088
5251
|
}
|
|
5089
5252
|
let parent = path2.join(rootResolved, ...segments.slice(0, existingDepth));
|
|
5090
|
-
for (
|
|
5091
|
-
|
|
5253
|
+
for (let index = existingDepth; index < segments.length - 1; index++) {
|
|
5254
|
+
const segment = segments[index];
|
|
5255
|
+
await mkdirExact(port, parent, segment, false, rel, index);
|
|
5092
5256
|
parent = path2.join(parent, segment);
|
|
5093
5257
|
}
|
|
5094
5258
|
}
|
|
@@ -5106,7 +5270,7 @@ async function createExactLeaf(port, tmp, target, rel, leaf, rootResolved) {
|
|
|
5106
5270
|
}
|
|
5107
5271
|
const listing = await port.entries(path2.dirname(target)) ?? [];
|
|
5108
5272
|
if (hasExact(listing, leaf)) throw new ConcurrentReplacementError(rel, 1);
|
|
5109
|
-
throw
|
|
5273
|
+
throw aliasRefusalFromListing(rel, relSegments(rel).length - 1, listing, true);
|
|
5110
5274
|
}
|
|
5111
5275
|
async function mutateExact(port, root, rel, body) {
|
|
5112
5276
|
const segments = relSegments(rel);
|
|
@@ -5128,11 +5292,13 @@ async function mutateExact(port, root, rel, body) {
|
|
|
5128
5292
|
async replace(bytes) {
|
|
5129
5293
|
await ensureExactDirectories(port, rootResolved, segments, existingDepth, rel);
|
|
5130
5294
|
const dir = path2.dirname(target);
|
|
5131
|
-
const tmpName =
|
|
5295
|
+
const tmpName = identityTempName(leaf);
|
|
5132
5296
|
const tmp = path2.join(dir, tmpName);
|
|
5133
5297
|
await port.writeTemp(dir, tmpName, bytes);
|
|
5134
5298
|
try {
|
|
5135
5299
|
if (realized.state === "exact") {
|
|
5300
|
+
await port.copyMode?.(target, tmp).catch(() => {
|
|
5301
|
+
});
|
|
5136
5302
|
await replaceExistingLeaf(port, tmp, target, rel);
|
|
5137
5303
|
return;
|
|
5138
5304
|
}
|
|
@@ -5292,6 +5458,9 @@ function createNodeFilesystemIdentityPort(hostPolicy) {
|
|
|
5292
5458
|
async rename(from, to) {
|
|
5293
5459
|
await fs2.rename(from, to);
|
|
5294
5460
|
},
|
|
5461
|
+
async copyMode(from, to) {
|
|
5462
|
+
await fs2.chmod(to, (await fs2.lstat(from)).mode & 511);
|
|
5463
|
+
},
|
|
5295
5464
|
async unlink(target) {
|
|
5296
5465
|
await fs2.unlink(target);
|
|
5297
5466
|
},
|
|
@@ -5314,6 +5483,7 @@ function blobVersion(bytes) {
|
|
|
5314
5483
|
|
|
5315
5484
|
// ../core/src/backend.ts
|
|
5316
5485
|
var publicationContexts = /* @__PURE__ */ new WeakMap();
|
|
5486
|
+
var fileMutations = /* @__PURE__ */ new WeakMap();
|
|
5317
5487
|
function firstString(...vals) {
|
|
5318
5488
|
for (const v of vals) {
|
|
5319
5489
|
if (typeof v === "string" && v.trim() !== "") return v;
|
|
@@ -5430,6 +5600,7 @@ var FilesystemBackend = class _FilesystemBackend {
|
|
|
5430
5600
|
this.#root = path3.resolve(root);
|
|
5431
5601
|
this.#coordinator = createFilesystemMutationCoordinator(this.#port, this.#root, options.hostPolicy);
|
|
5432
5602
|
publicationContexts.set(this, { root: this.#root, port: this.#port });
|
|
5603
|
+
fileMutations.set(this, (rel, body) => this.#mutateExact(rel, body));
|
|
5433
5604
|
}
|
|
5434
5605
|
async read(id) {
|
|
5435
5606
|
return this.#readWithEdition(id, () => this.#readEdition());
|
|
@@ -5785,6 +5956,7 @@ var KIND_FIELD_TYPE_SET = new Set(KIND_FIELD_TYPES);
|
|
|
5785
5956
|
function isKindFieldType(value) {
|
|
5786
5957
|
return typeof value === "string" && KIND_FIELD_TYPE_SET.has(value);
|
|
5787
5958
|
}
|
|
5959
|
+
var UNDECLARED_FIELDS_POLICIES = ["warn", "error"];
|
|
5788
5960
|
function buildKindRegistry(docs, initialWarnings = [], options = {}) {
|
|
5789
5961
|
const accepted = /* @__PURE__ */ new Map();
|
|
5790
5962
|
const warnings = [...initialWarnings];
|
|
@@ -6379,6 +6551,19 @@ function parseConventionDoc(doc, options = {}) {
|
|
|
6379
6551
|
});
|
|
6380
6552
|
}
|
|
6381
6553
|
}
|
|
6554
|
+
let undeclaredFields;
|
|
6555
|
+
if (fm.undeclared_fields !== void 0) {
|
|
6556
|
+
if (UNDECLARED_FIELDS_POLICIES.includes(fm.undeclared_fields)) {
|
|
6557
|
+
undeclaredFields = fm.undeclared_fields;
|
|
6558
|
+
} else {
|
|
6559
|
+
warnings.push({
|
|
6560
|
+
code: "KIND_CONVENTION_BAD_SHAPE",
|
|
6561
|
+
message: `kind convention '${doc.id}' has an invalid 'undeclared_fields' (${describeShape(fm.undeclared_fields)}; expected warn or error); using warn.`,
|
|
6562
|
+
field: "undeclared_fields",
|
|
6563
|
+
severity: "warning"
|
|
6564
|
+
});
|
|
6565
|
+
}
|
|
6566
|
+
}
|
|
6382
6567
|
const kind = {
|
|
6383
6568
|
id: doc.id,
|
|
6384
6569
|
title,
|
|
@@ -6396,6 +6581,7 @@ function parseConventionDoc(doc, options = {}) {
|
|
|
6396
6581
|
if (browseCollapsed !== void 0) kind.browseCollapsed = browseCollapsed;
|
|
6397
6582
|
if (order !== void 0) kind.order = order;
|
|
6398
6583
|
if (claim !== void 0) kind.claim = claim;
|
|
6584
|
+
if (undeclaredFields !== void 0) kind.undeclaredFields = undeclaredFields;
|
|
6399
6585
|
if (Object.hasOwn(fm, "deprecated")) {
|
|
6400
6586
|
kind.deprecated = parseKindDeprecation(fm.deprecated, kind, RESERVED_KIND_FIELD_NAMES, claimCoordinates(options.okfVersion, kind)?.fields ?? []);
|
|
6401
6587
|
warnings.push(...kind.deprecated.problems ?? []);
|
|
@@ -6432,58 +6618,6 @@ function isTerminal(kind, frontmatter2) {
|
|
|
6432
6618
|
return false;
|
|
6433
6619
|
}
|
|
6434
6620
|
|
|
6435
|
-
// ../core/src/query-selection.ts
|
|
6436
|
-
function applyQuerySelectionFilters(rows, params, kinds = []) {
|
|
6437
|
-
let out = rows;
|
|
6438
|
-
const byGoverns = new Map(kinds.map((kind) => [kind.governs, kind]));
|
|
6439
|
-
if (params.field) {
|
|
6440
|
-
const eq = params.field.indexOf("=");
|
|
6441
|
-
if (eq > 0) {
|
|
6442
|
-
const key = params.field.slice(0, eq).trim();
|
|
6443
|
-
const values = params.field.slice(eq + 1).split(",").map((value) => value.trim()).filter(Boolean);
|
|
6444
|
-
out = out.filter((row) => {
|
|
6445
|
-
if (key !== PROGRESS_STATUS_FIELD) {
|
|
6446
|
-
return values.some((value) => matchesFilter(row, { fields: { [key]: value } }));
|
|
6447
|
-
}
|
|
6448
|
-
const kind = byGoverns.get(String(row.frontmatter.type ?? ""));
|
|
6449
|
-
if (!kind) return false;
|
|
6450
|
-
const raw = readKindField(params.okfVersion, kind, row.frontmatter, key);
|
|
6451
|
-
const actual = raw === void 0 || raw === null ? [] : (Array.isArray(raw) ? raw : [raw]).map((value) => String(value));
|
|
6452
|
-
return values.some((value) => actual.includes(value));
|
|
6453
|
-
});
|
|
6454
|
-
}
|
|
6455
|
-
}
|
|
6456
|
-
if (params.open) {
|
|
6457
|
-
out = out.filter((row) => {
|
|
6458
|
-
const kind = byGoverns.get(String(row.frontmatter.type ?? ""));
|
|
6459
|
-
return !kind || !isTerminal(kind, row.frontmatter);
|
|
6460
|
-
});
|
|
6461
|
-
}
|
|
6462
|
-
const count = out.length;
|
|
6463
|
-
if (typeof params.limit === "number" && params.limit > 0) {
|
|
6464
|
-
out = out.slice(0, params.limit);
|
|
6465
|
-
}
|
|
6466
|
-
return { rows: out, count };
|
|
6467
|
-
}
|
|
6468
|
-
|
|
6469
|
-
// ../core/src/kinds-load.ts
|
|
6470
|
-
async function loadKinds(bundle) {
|
|
6471
|
-
const warnings = [];
|
|
6472
|
-
const docs = await query2(bundle, { prefix: CONVENTIONS_PREFIX, type: CONVENTION_TYPE }, {
|
|
6473
|
-
onSkip: ({ id, reason }) => warnings.push({
|
|
6474
|
-
code: "KIND_CONVENTION_MALFORMED",
|
|
6475
|
-
message: `skipped kind convention '${id}' with unparseable frontmatter: ${reason}`,
|
|
6476
|
-
field: id,
|
|
6477
|
-
severity: "warning"
|
|
6478
|
-
})
|
|
6479
|
-
});
|
|
6480
|
-
const okfVersion = await readBundleOkfVersion2(bundle).catch((error) => {
|
|
6481
|
-
if (!(error instanceof MalformedDocumentError)) throw error;
|
|
6482
|
-
return void 0;
|
|
6483
|
-
});
|
|
6484
|
-
return buildKindRegistry(docs, warnings, { okfVersion });
|
|
6485
|
-
}
|
|
6486
|
-
|
|
6487
6621
|
// ../core/src/page.ts
|
|
6488
6622
|
var PAGE_REGISTRY_PREFIX = "pages-registry/";
|
|
6489
6623
|
var PAGE_ENTRY_PREFIX = "pages/";
|
|
@@ -6556,6 +6690,58 @@ function parseRegistration(id, frontmatter2) {
|
|
|
6556
6690
|
};
|
|
6557
6691
|
}
|
|
6558
6692
|
|
|
6693
|
+
// ../core/src/query-selection.ts
|
|
6694
|
+
function applyQuerySelectionFilters(rows, params, kinds = []) {
|
|
6695
|
+
let out = rows;
|
|
6696
|
+
const byGoverns = new Map(kinds.map((kind) => [kind.governs, kind]));
|
|
6697
|
+
if (params.field) {
|
|
6698
|
+
const eq = params.field.indexOf("=");
|
|
6699
|
+
if (eq > 0) {
|
|
6700
|
+
const key = params.field.slice(0, eq).trim();
|
|
6701
|
+
const values = params.field.slice(eq + 1).split(",").map((value) => value.trim()).filter(Boolean);
|
|
6702
|
+
out = out.filter((row) => {
|
|
6703
|
+
if (key !== PROGRESS_STATUS_FIELD) {
|
|
6704
|
+
return values.some((value) => matchesFilter(row, { fields: { [key]: value } }));
|
|
6705
|
+
}
|
|
6706
|
+
const kind = byGoverns.get(String(row.frontmatter.type ?? ""));
|
|
6707
|
+
if (!kind) return false;
|
|
6708
|
+
const raw = readKindField(params.okfVersion, kind, row.frontmatter, key);
|
|
6709
|
+
const actual = raw === void 0 || raw === null ? [] : (Array.isArray(raw) ? raw : [raw]).map((value) => String(value));
|
|
6710
|
+
return values.some((value) => actual.includes(value));
|
|
6711
|
+
});
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
6714
|
+
if (params.open) {
|
|
6715
|
+
out = out.filter((row) => {
|
|
6716
|
+
const kind = byGoverns.get(String(row.frontmatter.type ?? ""));
|
|
6717
|
+
return !kind || !isTerminal(kind, row.frontmatter);
|
|
6718
|
+
});
|
|
6719
|
+
}
|
|
6720
|
+
const count = out.length;
|
|
6721
|
+
if (typeof params.limit === "number" && params.limit > 0) {
|
|
6722
|
+
out = out.slice(0, params.limit);
|
|
6723
|
+
}
|
|
6724
|
+
return { rows: out, count };
|
|
6725
|
+
}
|
|
6726
|
+
|
|
6727
|
+
// ../core/src/kinds-load.ts
|
|
6728
|
+
async function loadKinds(bundle) {
|
|
6729
|
+
const warnings = [];
|
|
6730
|
+
const docs = await query2(bundle, { prefix: CONVENTIONS_PREFIX, type: CONVENTION_TYPE }, {
|
|
6731
|
+
onSkip: ({ id, reason }) => warnings.push({
|
|
6732
|
+
code: "KIND_CONVENTION_MALFORMED",
|
|
6733
|
+
message: isPermissionDeniedSkip({ reason }) ? `skipped kind convention '${id}' that could not be read: ${reason}` : `skipped kind convention '${id}' with unparseable frontmatter: ${reason}`,
|
|
6734
|
+
field: id,
|
|
6735
|
+
severity: "warning"
|
|
6736
|
+
})
|
|
6737
|
+
});
|
|
6738
|
+
const okfVersion = await readBundleOkfVersion2(bundle).catch((error) => {
|
|
6739
|
+
if (!(error instanceof MalformedDocumentError)) throw error;
|
|
6740
|
+
return void 0;
|
|
6741
|
+
});
|
|
6742
|
+
return buildKindRegistry(docs, warnings, { okfVersion });
|
|
6743
|
+
}
|
|
6744
|
+
|
|
6559
6745
|
// ../core/src/query-order.ts
|
|
6560
6746
|
function meaningfulChangeOrderKey(id, frontmatter2, okfVersion) {
|
|
6561
6747
|
const value = meaningfulChangeTimeValue(frontmatter2);
|