superbee 0.4.4-pre.1 → 0.4.4-pre.11
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/NOTICE +4 -0
- package/README.md +5 -0
- package/SKILL.md +10 -10
- package/dist/THIRD-PARTY-NOTICES.txt +2347 -0
- package/dist/portable-export.mjs +7 -3
- package/dist/publication/index.d.ts +3 -0
- package/dist/publication/site-manifest-core.d.ts +49 -0
- package/dist/publication/site-manifest-entry.d.ts +5 -0
- package/dist/publication/site-manifest.d.ts +5 -0
- package/dist/publication-bridge.mjs +42 -19
- package/dist/publication-site-manifest.mjs +212 -0
- package/dist/publication.mjs +246 -46
- package/dist/superbee.mjs +12708 -8181
- package/package.json +7 -3
- package/references/author-an-integration.md +87 -1
- package/references/hosted-checkout.md +40 -7
- package/references/modeling-and-delivery.md +3 -2
- package/references/recipes/review-workflow/references/view-authoring-v0.md +18 -0
- package/references/views/references/view-authoring-v0.md +18 -0
package/dist/portable-export.mjs
CHANGED
|
@@ -3,6 +3,11 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
|
+
// ../core/src/path-fold.ts
|
|
7
|
+
function foldPath(path2) {
|
|
8
|
+
return path2.normalize("NFC").toLowerCase().toUpperCase().toLowerCase().normalize("NFC");
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
// ../core/src/errors.ts
|
|
7
12
|
var InvalidInputError = class extends Error {
|
|
8
13
|
constructor(message) {
|
|
@@ -175,7 +180,6 @@ function readStoredZip(archive) {
|
|
|
175
180
|
return entries;
|
|
176
181
|
}
|
|
177
182
|
var WINDOWS_DEVICE_NAMES = /* @__PURE__ */ new Set(["CON", "PRN", "AUX", "NUL", ...Array.from({ length: 9 }, (_, i) => `COM${i + 1}`), ...Array.from({ length: 9 }, (_, i) => `LPT${i + 1}`)]);
|
|
178
|
-
var fold = (segment) => segment.normalize("NFC").toLowerCase();
|
|
179
183
|
function classifyEntryPath(entryPath) {
|
|
180
184
|
const unsafe = (why) => new ExportArchiveError("unsafe_path", `the archive holds a path a bundle cannot keep (${why}): ${JSON.stringify(entryPath.slice(0, 200))}`);
|
|
181
185
|
if (entryPath === "" || entryPath.length > 4096) throw unsafe("empty or too long");
|
|
@@ -209,13 +213,13 @@ function findEntryCollision(paths) {
|
|
|
209
213
|
const dirs = /* @__PURE__ */ new Map();
|
|
210
214
|
for (const entryPath of paths) {
|
|
211
215
|
const segments = entryPath.split("/");
|
|
212
|
-
const key = segments.map(
|
|
216
|
+
const key = segments.map(foldPath).join("/");
|
|
213
217
|
const file = files.get(key) ?? dirs.get(key)?.by;
|
|
214
218
|
if (file !== void 0) return [file, entryPath];
|
|
215
219
|
files.set(key, entryPath);
|
|
216
220
|
for (let depth = 1; depth < segments.length; depth += 1) {
|
|
217
221
|
const spelling = segments.slice(0, depth).join("/");
|
|
218
|
-
const dirKey = segments.slice(0, depth).map(
|
|
222
|
+
const dirKey = segments.slice(0, depth).map(foldPath).join("/");
|
|
219
223
|
const asFile = files.get(dirKey);
|
|
220
224
|
if (asFile !== void 0) return [asFile, entryPath];
|
|
221
225
|
const seen = dirs.get(dirKey);
|
|
@@ -7,4 +7,7 @@ export { PUBLICATION_BRIDGE_V0, PUBLICATION_SNAPSHOT_V1, type CapturePublication
|
|
|
7
7
|
export type { CapturePublicationRuntimeOptions } from "./capture.js";
|
|
8
8
|
export { assertPublicationSourceExclusions } from "./source-exclusions.js";
|
|
9
9
|
export type { PublicationSourceInventory, PublicationSourceExclusions } from "./source-exclusions.js";
|
|
10
|
+
export { parseSiteManifestDoc, isSiteLabel, SITE_LABEL, SITE_TYPE, SITES_PREFIX, SITE_DOCUMENT_KEYS } from "./site-manifest-core.js";
|
|
11
|
+
export type { SiteManifest, SiteManifestError, SiteManifestParseResult, SitePresentationReference } from "./site-manifest-core.js";
|
|
12
|
+
export { readPublicationSiteManifest } from "./site-manifest.js";
|
|
10
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const SITE_TYPE = "Site";
|
|
2
|
+
export declare const SITES_PREFIX = "sites/";
|
|
3
|
+
/** One site label leaves room for its workspace label in a combined DNS name. */
|
|
4
|
+
export declare const SITE_LABEL: RegExp;
|
|
5
|
+
export declare const isSiteLabel: (value: unknown) => value is string;
|
|
6
|
+
export declare const SITE_DOCUMENT_KEYS: readonly ["type", "title", "description", "presentation", "landing", "views", "audience", "capabilities", "options", "brand"];
|
|
7
|
+
export type SitePresentationReference = {
|
|
8
|
+
readonly kind: "package";
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly version: string;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: "repository";
|
|
13
|
+
readonly repository: string;
|
|
14
|
+
};
|
|
15
|
+
export interface SiteManifest {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly site: string;
|
|
18
|
+
readonly title: string;
|
|
19
|
+
readonly description?: string;
|
|
20
|
+
readonly presentation: SitePresentationReference;
|
|
21
|
+
readonly landing?: string;
|
|
22
|
+
readonly views: readonly string[];
|
|
23
|
+
readonly audience: "restricted";
|
|
24
|
+
readonly capabilities: readonly string[];
|
|
25
|
+
readonly options: Readonly<Record<string, unknown>>;
|
|
26
|
+
readonly brand?: {
|
|
27
|
+
readonly logo?: string;
|
|
28
|
+
readonly fonts?: readonly string[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface SiteManifestError {
|
|
32
|
+
readonly code: "SITE_MANIFEST_INVALID";
|
|
33
|
+
readonly path: string;
|
|
34
|
+
readonly message: string;
|
|
35
|
+
}
|
|
36
|
+
export type SiteManifestParseResult = {
|
|
37
|
+
readonly ok: true;
|
|
38
|
+
readonly manifest: SiteManifest;
|
|
39
|
+
} | {
|
|
40
|
+
readonly ok: false;
|
|
41
|
+
readonly errors: readonly SiteManifestError[];
|
|
42
|
+
};
|
|
43
|
+
/** Standard OKF and managed metadata survive on the document; only Site fields enter this value.
|
|
44
|
+
* Presentation owners validate option semantics. Builders separately admit trusted resolved code. */
|
|
45
|
+
export declare function parseSiteManifestDoc(doc: {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly frontmatter: Readonly<Record<string, unknown>>;
|
|
48
|
+
}): SiteManifestParseResult;
|
|
49
|
+
//# sourceMappingURL=site-manifest.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Portable declaration surface, with no filesystem capture or credential handling. */
|
|
2
|
+
export { parseSiteManifestDoc, isSiteLabel, SITE_LABEL, SITE_TYPE, SITES_PREFIX, SITE_DOCUMENT_KEYS } from "./site-manifest-core.js";
|
|
3
|
+
export type { SiteManifest, SiteManifestError, SiteManifestParseResult, SitePresentationReference } from "./site-manifest-core.js";
|
|
4
|
+
export { readPublicationSiteManifest } from "./site-manifest.js";
|
|
5
|
+
//# sourceMappingURL=site-manifest-entry.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type SiteManifestParseResult } from "./site-manifest-core.js";
|
|
2
|
+
import type { PublicationSnapshotV1 } from "./types.js";
|
|
3
|
+
/** Resolve only declarations and references in the frozen publication. This never resolves code. */
|
|
4
|
+
export declare function readPublicationSiteManifest(snapshot: Pick<PublicationSnapshotV1, "documents" | "views" | "blobs">, site: string): SiteManifestParseResult;
|
|
5
|
+
//# sourceMappingURL=site-manifest.d.ts.map
|
|
@@ -3397,6 +3397,11 @@ function stringifyDoc(frontmatter2, body) {
|
|
|
3397
3397
|
return stringifyWithData(frontmatter2, body);
|
|
3398
3398
|
}
|
|
3399
3399
|
|
|
3400
|
+
// ../core/src/code-unit-order.ts
|
|
3401
|
+
function compareCodeUnits(a, b) {
|
|
3402
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3400
3405
|
// ../core/src/links.ts
|
|
3401
3406
|
function normalizeSegments(segments) {
|
|
3402
3407
|
const out = [];
|
|
@@ -3418,6 +3423,13 @@ function isExternalHref(href) {
|
|
|
3418
3423
|
return /^[a-z][a-z0-9+.-]*:\/\//i.test(h) || h.startsWith("mailto:") || h.startsWith("#");
|
|
3419
3424
|
}
|
|
3420
3425
|
function extractMarkdownLinks(source, options = {}) {
|
|
3426
|
+
const out = [];
|
|
3427
|
+
for (const span of extractMarkdownLinkSpans(source, options)) {
|
|
3428
|
+
if (!span.image) out.push({ text: span.text, href: span.href });
|
|
3429
|
+
}
|
|
3430
|
+
return out;
|
|
3431
|
+
}
|
|
3432
|
+
function extractMarkdownLinkSpans(source, options = {}) {
|
|
3421
3433
|
const body = options.skipCode ? maskMarkdownCode(source) : source;
|
|
3422
3434
|
const out = [];
|
|
3423
3435
|
let textOpen = -1;
|
|
@@ -3428,12 +3440,16 @@ function extractMarkdownLinks(source, options = {}) {
|
|
|
3428
3440
|
const char = body[cursor];
|
|
3429
3441
|
if (hrefRest) {
|
|
3430
3442
|
if (char === ")") {
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3443
|
+
const image = hrefRest.open > 0 && body[hrefRest.open - 1] === "!";
|
|
3444
|
+
out.push({
|
|
3445
|
+
text: source.slice(hrefRest.open + 1, hrefRest.close),
|
|
3446
|
+
href: source.slice(hrefRest.hrefStart, cursor),
|
|
3447
|
+
start: image ? hrefRest.open - 1 : hrefRest.open,
|
|
3448
|
+
end: cursor + 1,
|
|
3449
|
+
hrefStart: hrefRest.hrefStart,
|
|
3450
|
+
hrefEnd: cursor,
|
|
3451
|
+
image
|
|
3452
|
+
});
|
|
3437
3453
|
textOpen = -1;
|
|
3438
3454
|
afterClose = void 0;
|
|
3439
3455
|
hrefFirst = void 0;
|
|
@@ -3813,12 +3829,13 @@ async function readDocVersioned(backend, id) {
|
|
|
3813
3829
|
function isEnoent(err) {
|
|
3814
3830
|
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
3815
3831
|
}
|
|
3816
|
-
async function readManyExisting(backend, ids, onMalformed) {
|
|
3832
|
+
async function readManyExisting(backend, ids, onMalformed, onAliased) {
|
|
3817
3833
|
try {
|
|
3818
3834
|
return await backend.readMany(ids);
|
|
3819
3835
|
} catch (err) {
|
|
3820
|
-
const malformed = err instanceof MalformedDocumentError;
|
|
3821
|
-
|
|
3836
|
+
const malformed = err instanceof MalformedDocumentError && onMalformed !== void 0;
|
|
3837
|
+
const aliased = err instanceof FilesystemIdentityAliasError && onAliased !== void 0;
|
|
3838
|
+
if (!isEnoent(err) && !malformed && !aliased) throw err;
|
|
3822
3839
|
const out = [];
|
|
3823
3840
|
for (const id of ids) {
|
|
3824
3841
|
try {
|
|
@@ -3829,6 +3846,10 @@ async function readManyExisting(backend, ids, onMalformed) {
|
|
|
3829
3846
|
onMalformed({ id, reason: candidate.detail });
|
|
3830
3847
|
continue;
|
|
3831
3848
|
}
|
|
3849
|
+
if (candidate instanceof FilesystemIdentityAliasError && onAliased) {
|
|
3850
|
+
onAliased({ id, reason: candidate.message });
|
|
3851
|
+
continue;
|
|
3852
|
+
}
|
|
3832
3853
|
throw candidate;
|
|
3833
3854
|
}
|
|
3834
3855
|
}
|
|
@@ -3841,7 +3862,7 @@ async function scanMatching(backend, filter, onSkip) {
|
|
|
3841
3862
|
for (const result of await readManyExisting(backend, ids, onSkip)) {
|
|
3842
3863
|
if (matchesFilter(result.doc, filter)) results.push(result);
|
|
3843
3864
|
}
|
|
3844
|
-
results.sort((a, b) => a.doc.id
|
|
3865
|
+
results.sort((a, b) => compareCodeUnits(a.doc.id, b.doc.id));
|
|
3845
3866
|
return results;
|
|
3846
3867
|
}
|
|
3847
3868
|
async function query(backend, filter = {}, options = {}) {
|
|
@@ -3850,7 +3871,7 @@ async function query(backend, filter = {}, options = {}) {
|
|
|
3850
3871
|
async function queryHeads(backend, filter = {}, options = {}) {
|
|
3851
3872
|
if (backend.queryHeads) {
|
|
3852
3873
|
const rows = (await backend.queryHeads(filter, options.onSkip ? { onSkip: options.onSkip } : void 0)).filter((row) => matchesFilter(row, filter));
|
|
3853
|
-
rows.sort((a, b) => a.id
|
|
3874
|
+
rows.sort((a, b) => compareCodeUnits(a.id, b.id));
|
|
3854
3875
|
return rows;
|
|
3855
3876
|
}
|
|
3856
3877
|
return (await scanMatching(backend, filter, options.onSkip)).map(({ doc, version: version2 }) => ({
|
|
@@ -3890,7 +3911,7 @@ async function queryEdges(backend, filter = {}, options = {}) {
|
|
|
3890
3911
|
edges.push(link);
|
|
3891
3912
|
}
|
|
3892
3913
|
}
|
|
3893
|
-
edges.sort((a, b) => a.from
|
|
3914
|
+
edges.sort((a, b) => compareCodeUnits(a.from, b.from) || compareCodeUnits(a.to, b.to) || compareCodeUnits(a.text, b.text));
|
|
3894
3915
|
return edges;
|
|
3895
3916
|
}
|
|
3896
3917
|
|
|
@@ -4406,6 +4427,11 @@ async function acquireFilesystemIdentityLock(key, identity, options = {}) {
|
|
|
4406
4427
|
return claimLockPath(path.join(lockRoot, `${key}.lock`), owner, waitMs, pollMs, policy);
|
|
4407
4428
|
}
|
|
4408
4429
|
|
|
4430
|
+
// ../core/src/identity-fold.ts
|
|
4431
|
+
function foldSegment(segment) {
|
|
4432
|
+
return segment.normalize("NFKD").toLowerCase().toUpperCase().toLowerCase();
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4409
4435
|
// ../core/src/filesystem-identity.ts
|
|
4410
4436
|
async function replaceExistingLeaf(port, tmp, target, rel) {
|
|
4411
4437
|
try {
|
|
@@ -4451,9 +4477,6 @@ function relSegments(rel) {
|
|
|
4451
4477
|
}
|
|
4452
4478
|
return segments;
|
|
4453
4479
|
}
|
|
4454
|
-
function foldSegment(segment) {
|
|
4455
|
-
return segment.normalize("NFKD").toLowerCase().toUpperCase().toLowerCase();
|
|
4456
|
-
}
|
|
4457
4480
|
async function existingAnchor(resolved) {
|
|
4458
4481
|
const tail = [];
|
|
4459
4482
|
let candidate = resolved;
|
|
@@ -6328,7 +6351,7 @@ function compareByMeaningfulChange(a, b) {
|
|
|
6328
6351
|
} else if (b.timestampMs !== null) {
|
|
6329
6352
|
return 1;
|
|
6330
6353
|
}
|
|
6331
|
-
return a.id
|
|
6354
|
+
return compareCodeUnits(a.id, b.id);
|
|
6332
6355
|
}
|
|
6333
6356
|
function sortByMeaningfulChange(rows, okfVersion) {
|
|
6334
6357
|
return rows.map((row) => ({ row, key: meaningfulChangeOrderKey(row.id, row.frontmatter, okfVersion) })).sort((a, b) => compareByMeaningfulChange(a.key, b.key)).map(({ row }) => row);
|
|
@@ -6575,7 +6598,7 @@ function replyWithinLimit(reply) {
|
|
|
6575
6598
|
}
|
|
6576
6599
|
function skippedReply(skipped) {
|
|
6577
6600
|
if (skipped.length === 0) return {};
|
|
6578
|
-
return { skipped: [...skipped].sort((a, b) => a.id
|
|
6601
|
+
return { skipped: [...skipped].sort((a, b) => compareCodeUnits(a.id, b.id)).slice(0, MAX_QUERY_ROWS) };
|
|
6579
6602
|
}
|
|
6580
6603
|
function boundedRows(rows, params, kinds) {
|
|
6581
6604
|
const requested = params.limit === 0 || params.limit === void 0 ? MAX_QUERY_ROWS : Math.min(params.limit, MAX_QUERY_ROWS);
|
|
@@ -6690,7 +6713,7 @@ var BridgeService = class {
|
|
|
6690
6713
|
for (const id of subscription.baseline.keys()) {
|
|
6691
6714
|
if (!next.has(id)) removed.push(id);
|
|
6692
6715
|
}
|
|
6693
|
-
changes.sort((a, b) => a.id
|
|
6716
|
+
changes.sort((a, b) => compareCodeUnits(a.id, b.id));
|
|
6694
6717
|
removed.sort();
|
|
6695
6718
|
if (changes.length === 0 && removed.length === 0) return { status: "unchanged" };
|
|
6696
6719
|
if (changes.length > MAX_CHANGE_ROWS || removed.length > MAX_CHANGE_ROWS) {
|
|
@@ -6719,7 +6742,7 @@ var BridgeService = class {
|
|
|
6719
6742
|
throw new Error("the bundle is too large for the experimental View polling snapshot");
|
|
6720
6743
|
}
|
|
6721
6744
|
return new Map(
|
|
6722
|
-
[...rows].sort((a, b) => a.id
|
|
6745
|
+
[...rows].sort((a, b) => compareCodeUnits(a.id, b.id)).map((row) => [row.id, row.version])
|
|
6723
6746
|
);
|
|
6724
6747
|
}
|
|
6725
6748
|
/**
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// ../core/src/errors.ts
|
|
2
|
+
var InvalidInputError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "InvalidInputError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// ../core/src/paths.ts
|
|
10
|
+
function assertPortablePath(value, label) {
|
|
11
|
+
if (/[\u0000-\u001f\u007f]/u.test(value) || /^[a-z]:/i.test(value)) {
|
|
12
|
+
throw new InvalidInputError(`${label} must not contain controls or start with a drive prefix.`);
|
|
13
|
+
}
|
|
14
|
+
if (value.includes("\\") || value.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
15
|
+
throw new InvalidInputError(`${label} must be canonical and bundle-relative.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function assertSafeConceptId(id) {
|
|
19
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
20
|
+
throw new InvalidInputError("Concept id must be a non-empty string.");
|
|
21
|
+
}
|
|
22
|
+
assertPortablePath(id, "Concept id");
|
|
23
|
+
const segments = id.split("/");
|
|
24
|
+
if (segments.slice(0, -1).some((seg) => seg.toLowerCase().endsWith(".md"))) {
|
|
25
|
+
throw new InvalidInputError(
|
|
26
|
+
`Concept id must not contain a non-final segment ending in '.md': '${id}' (it collides with a concept file at that path).`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
var FORMAT_AT_EDGE = /^\p{Cf}|\p{Cf}$/u;
|
|
31
|
+
var FORMAT_INSIDE = /(?![\u200c\u200d])\p{Cf}/u;
|
|
32
|
+
function nonCanonicalDocumentId(id) {
|
|
33
|
+
const segments = id.split("/");
|
|
34
|
+
if (segments.some((segment) => segment !== segment.trim())) return "segment_whitespace";
|
|
35
|
+
if (segments.some((segment) => FORMAT_AT_EDGE.test(segment) || FORMAT_INSIDE.test(segment))) return "format_character";
|
|
36
|
+
if (id !== id.normalize("NFC")) return "not_nfc";
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function assertSafeBlobKey(key) {
|
|
40
|
+
if (typeof key !== "string" || key.trim() === "") {
|
|
41
|
+
throw new InvalidInputError("Blob key must be a non-empty string.");
|
|
42
|
+
}
|
|
43
|
+
assertPortablePath(key, "Blob key");
|
|
44
|
+
const segments = key.split("/");
|
|
45
|
+
if (segments.some((seg) => seg.startsWith("."))) {
|
|
46
|
+
throw new InvalidInputError(`Blob key must not contain dot-prefixed segments: '${key}'.`);
|
|
47
|
+
}
|
|
48
|
+
if (segments.some((seg) => seg.toLowerCase().endsWith(".md"))) {
|
|
49
|
+
throw new InvalidInputError(
|
|
50
|
+
`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.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ../core/src/json-data.ts
|
|
56
|
+
function isJsonData(value, depth = 0) {
|
|
57
|
+
if (depth > 32) return false;
|
|
58
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
59
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
60
|
+
if (Array.isArray(value)) return value.every((item) => isJsonData(item, depth + 1));
|
|
61
|
+
if (value === null || typeof value !== "object") return false;
|
|
62
|
+
const proto = Object.getPrototypeOf(value);
|
|
63
|
+
if (proto !== null && Object.getPrototypeOf(proto) !== null) return false;
|
|
64
|
+
return Object.values(value).every((item) => item !== void 0 && isJsonData(item, depth + 1));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ../core/src/okf-standard-fields.ts
|
|
68
|
+
var OKF_STANDARD_FIELD_NAMES = ["type", "title", "description", "resource", "tags", "status", "sources", "generated", "verified", "stale_after", "usage_window", "runtime", "computation", "parameters", "executor", "attester", "okf_version"];
|
|
69
|
+
function isOkfStandardField(field) {
|
|
70
|
+
return OKF_STANDARD_FIELD_NAMES.includes(field);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ../core/src/managed-document-metadata.ts
|
|
74
|
+
var MANAGED_DOCUMENT_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
75
|
+
"generated",
|
|
76
|
+
"verified",
|
|
77
|
+
"superbee_updated_by",
|
|
78
|
+
"actor",
|
|
79
|
+
"timestamp"
|
|
80
|
+
]);
|
|
81
|
+
function isManagedDocumentMetadataField(field) {
|
|
82
|
+
return MANAGED_DOCUMENT_METADATA_FIELDS.has(field);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ../core/src/site-manifest.ts
|
|
86
|
+
var SITE_TYPE = "Site";
|
|
87
|
+
var SITES_PREFIX = "sites/";
|
|
88
|
+
var SITE_LABEL = /^[a-z][a-z0-9-]{1,30}[a-z0-9]$/;
|
|
89
|
+
var isSiteLabel = (value) => typeof value === "string" && SITE_LABEL.test(value) && !value.includes("--");
|
|
90
|
+
var SITE_DOCUMENT_KEYS = ["type", "title", "description", "presentation", "landing", "views", "audience", "capabilities", "options", "brand"];
|
|
91
|
+
var record = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
92
|
+
var packageName = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
93
|
+
var exactVersion = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
|
94
|
+
var encoder = new TextEncoder();
|
|
95
|
+
function presentationReference(raw) {
|
|
96
|
+
if (typeof raw !== "string" || raw.length > 512) return void 0;
|
|
97
|
+
if (raw.startsWith("repo:")) {
|
|
98
|
+
const repository = raw.slice(5);
|
|
99
|
+
return /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/.test(repository) && !repository.endsWith(".git") ? { kind: "repository", repository } : void 0;
|
|
100
|
+
}
|
|
101
|
+
const at = raw.lastIndexOf("@");
|
|
102
|
+
if (at < 1) return void 0;
|
|
103
|
+
const name = raw.slice(0, at), version = raw.slice(at + 1), match = exactVersion.exec(version);
|
|
104
|
+
if (name.length > 214 || !packageName.test(name) || !match || !match.slice(1, 4).every((part) => Number.isSafeInteger(Number(part))) || match[4]?.split(".").some((part) => /^\d+$/.test(part) && (part.length > 1 && part.startsWith("0") || !Number.isSafeInteger(Number(part))))) return void 0;
|
|
105
|
+
return { kind: "package", name, version };
|
|
106
|
+
}
|
|
107
|
+
var concept = (raw) => {
|
|
108
|
+
if (typeof raw !== "string") return false;
|
|
109
|
+
try {
|
|
110
|
+
assertSafeConceptId(raw);
|
|
111
|
+
return nonCanonicalDocumentId(raw) === null;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var blob = (raw) => {
|
|
117
|
+
if (typeof raw !== "string") return false;
|
|
118
|
+
try {
|
|
119
|
+
assertSafeBlobKey(raw);
|
|
120
|
+
return true;
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
function parseSiteManifestDoc(doc) {
|
|
126
|
+
const errors = [];
|
|
127
|
+
const fail = (path, message) => {
|
|
128
|
+
errors.push({ code: "SITE_MANIFEST_INVALID", path, message });
|
|
129
|
+
};
|
|
130
|
+
const fm = record(doc?.frontmatter) ? doc.frontmatter : {};
|
|
131
|
+
const site = typeof doc?.id === "string" && doc.id.startsWith(SITES_PREFIX) ? doc.id.slice(SITES_PREFIX.length) : "";
|
|
132
|
+
if (!isSiteLabel(site)) fail("/id", "a Site lives at sites/<site>, using one site label");
|
|
133
|
+
if (fm.type !== SITE_TYPE) fail("/type", "type must be Site");
|
|
134
|
+
if (Object.keys(fm).some((key) => !SITE_DOCUMENT_KEYS.includes(key) && !isOkfStandardField(key) && !isManagedDocumentMetadataField(key)))
|
|
135
|
+
fail("", "unrecognized Site fields");
|
|
136
|
+
if (typeof fm.title !== "string" || fm.title.trim() === "" || encoder.encode(fm.title).byteLength > 512)
|
|
137
|
+
fail("/title", "title must be a nonempty string of at most 512 bytes");
|
|
138
|
+
if (fm.description !== void 0 && (typeof fm.description !== "string" || encoder.encode(fm.description).byteLength > 8192))
|
|
139
|
+
fail("/description", "description must be a string of at most 8192 bytes");
|
|
140
|
+
const presentation = presentationReference(fm.presentation);
|
|
141
|
+
if (!presentation) fail("/presentation", "presentation must name an exact npm version or repo:owner/name");
|
|
142
|
+
if (fm.audience !== "restricted") fail("/audience", "only the restricted audience is supported");
|
|
143
|
+
const list = (raw, path, maximum, valid) => {
|
|
144
|
+
if (!Array.isArray(raw) || raw.length > maximum || !raw.every(valid) || new Set(raw).size !== raw.length) {
|
|
145
|
+
fail(path, "expected a bounded list of unique valid references");
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
return [...raw];
|
|
149
|
+
};
|
|
150
|
+
const views = list(fm.views, "/views", 64, concept);
|
|
151
|
+
const capability = (v) => typeof v === "string" && /^[a-z][a-z0-9-]{0,63}$/.test(v);
|
|
152
|
+
const capabilities = list(fm.capabilities ?? [], "/capabilities", 16, capability);
|
|
153
|
+
if (fm.landing !== void 0 && !concept(fm.landing)) fail("/landing", "landing must be a canonical document or View id");
|
|
154
|
+
const options = fm.options ?? {};
|
|
155
|
+
if (!record(options) || !isJsonData(options) || encoder.encode(JSON.stringify(options)).byteLength > 16384)
|
|
156
|
+
fail("/options", "options must be JSON data of at most 16384 bytes");
|
|
157
|
+
let brand;
|
|
158
|
+
if (fm.brand !== void 0) {
|
|
159
|
+
if (!record(fm.brand) || Object.keys(fm.brand).some((key) => key !== "logo" && key !== "fonts")) fail("/brand", "brand may only name a logo and fonts");
|
|
160
|
+
else {
|
|
161
|
+
if (fm.brand.logo !== void 0 && !blob(fm.brand.logo)) fail("/brand/logo", "logo must be a bundle blob reference");
|
|
162
|
+
const fonts = fm.brand.fonts === void 0 ? void 0 : list(fm.brand.fonts, "/brand/fonts", 16, blob);
|
|
163
|
+
brand = { ...typeof fm.brand.logo === "string" ? { logo: fm.brand.logo } : {}, ...fonts ? { fonts } : {} };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (errors.length) return { ok: false, errors };
|
|
167
|
+
return { ok: true, manifest: {
|
|
168
|
+
id: doc.id,
|
|
169
|
+
site,
|
|
170
|
+
title: fm.title,
|
|
171
|
+
presentation,
|
|
172
|
+
audience: "restricted",
|
|
173
|
+
views,
|
|
174
|
+
capabilities,
|
|
175
|
+
options: JSON.parse(JSON.stringify(options)),
|
|
176
|
+
...fm.description === void 0 ? {} : { description: fm.description },
|
|
177
|
+
...fm.landing === void 0 ? {} : { landing: fm.landing },
|
|
178
|
+
...brand ? { brand } : {}
|
|
179
|
+
} };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ../publication/src/site-manifest.ts
|
|
183
|
+
function readPublicationSiteManifest(snapshot, site) {
|
|
184
|
+
const declarations = snapshot.documents.filter((doc) => doc.frontmatter.type === SITE_TYPE);
|
|
185
|
+
if (declarations.length !== 1 || declarations[0].id !== `${SITES_PREFIX}${site}`)
|
|
186
|
+
return { ok: false, errors: [{ code: "SITE_MANIFEST_INVALID", path: "/id", message: "the publication requires exactly one Site at sites/<site>" }] };
|
|
187
|
+
const parsed = parseSiteManifestDoc(declarations[0]);
|
|
188
|
+
if (!parsed.ok) return parsed;
|
|
189
|
+
const { manifest } = parsed;
|
|
190
|
+
const views = new Set(snapshot.views.map((view) => view.id));
|
|
191
|
+
const documents = new Set(snapshot.documents.map((doc) => doc.id));
|
|
192
|
+
const blobs = new Set(snapshot.blobs.map((blob2) => blob2.key));
|
|
193
|
+
const errors = [];
|
|
194
|
+
const fail = (path, message) => {
|
|
195
|
+
errors.push({ code: "SITE_MANIFEST_INVALID", path, message });
|
|
196
|
+
};
|
|
197
|
+
if (manifest.views.some((id) => !views.has(id))) fail("/views", "every declared View must be registered in this publication");
|
|
198
|
+
if (manifest.landing !== void 0 && !documents.has(manifest.landing)) fail("/landing", "landing must name a document or View in this publication");
|
|
199
|
+
if (manifest.landing !== void 0 && views.has(manifest.landing) && !manifest.views.includes(manifest.landing)) fail("/landing", "a landing View must be declared by this Site");
|
|
200
|
+
if (manifest.brand?.logo !== void 0 && !blobs.has(manifest.brand.logo)) fail("/brand/logo", "the logo must be a blob in this publication");
|
|
201
|
+
if (manifest.brand?.fonts?.some((key) => !blobs.has(key))) fail("/brand/fonts", "every font must be a blob in this publication");
|
|
202
|
+
return errors.length ? { ok: false, errors } : parsed;
|
|
203
|
+
}
|
|
204
|
+
export {
|
|
205
|
+
SITES_PREFIX,
|
|
206
|
+
SITE_DOCUMENT_KEYS,
|
|
207
|
+
SITE_LABEL,
|
|
208
|
+
SITE_TYPE,
|
|
209
|
+
isSiteLabel,
|
|
210
|
+
parseSiteManifestDoc,
|
|
211
|
+
readPublicationSiteManifest
|
|
212
|
+
};
|