superbee 0.4.4 → 0.4.5
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/dist/publication/capture.d.ts +2 -2
- package/dist/publication/external-link-policy.d.ts +16 -0
- package/dist/publication/index.d.ts +1 -1
- package/dist/publication/types.d.ts +22 -1
- package/dist/publication.mjs +130 -32
- package/dist/superbee.mjs +37256 -37134
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { FilesystemHostPolicy } from "@superbee/core/filesystem";
|
|
2
|
-
import { type CapturePublicationSnapshotOptionsV1, type
|
|
2
|
+
import { type CapturePublicationSnapshotOptionsV1, type PublicationCapturedSnapshotHandleV1 } from "./types.js";
|
|
3
3
|
export interface CapturePublicationRuntimeOptions {
|
|
4
4
|
readonly filesystemHostPolicy?: FilesystemHostPolicy;
|
|
5
5
|
}
|
|
6
6
|
/** Capture one coherent immutable filesystem publication snapshot. */
|
|
7
|
-
export declare function capturePublicationSnapshot(options: CapturePublicationSnapshotOptionsV1, runtimeOptions?: CapturePublicationRuntimeOptions): Promise<
|
|
7
|
+
export declare function capturePublicationSnapshot(options: CapturePublicationSnapshotOptionsV1, runtimeOptions?: CapturePublicationRuntimeOptions): Promise<PublicationCapturedSnapshotHandleV1>;
|
|
8
8
|
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which external links become anchors. This file stays free of imports because `superbee/publication`
|
|
3
|
+
* ships its declaration on its own, without the private renderer package or React types.
|
|
4
|
+
*
|
|
5
|
+
* - `"none"` keeps every external target as inert text. Absent and unknown values mean the same.
|
|
6
|
+
* - `"https"` admits any `https:` URL with no userinfo.
|
|
7
|
+
* - `{ hosts }` also requires the default port and an exact match on one listed bare host name.
|
|
8
|
+
*
|
|
9
|
+
* The page owner's code sets this. Never read it from a document, Kind, recipe, View manifest,
|
|
10
|
+
* bundle record or tool argument. The one exception is a host's sealed room, whose host list comes
|
|
11
|
+
* from content that host validated and sealed.
|
|
12
|
+
*/
|
|
13
|
+
export type ExternalLinkPolicy = "none" | "https" | {
|
|
14
|
+
readonly hosts: readonly string[];
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=external-link-policy.d.ts.map
|
|
@@ -3,7 +3,7 @@ export { createPublicationBridge, type CreatePublicationBridgeOptionsV1 } from "
|
|
|
3
3
|
export { PublicationError, isPublicationError } from "./errors.js";
|
|
4
4
|
export type { PublicationErrorCodeV1, PublicationErrorOptions } from "./errors.js";
|
|
5
5
|
export { PUBLICATION_SNAPSHOT_SCHEMA_V1 } from "./schema.js";
|
|
6
|
-
export { PUBLICATION_BRIDGE_V0, PUBLICATION_SNAPSHOT_V1, type CapturePublicationSnapshotOptionsV1, type PublicationBridgeAdmissionV1, type PublicationBridgeOutcomeV1, type PublicationBridgeV1, type PublicationJsonValue, type PublicationObjectRefV1, type PublicationSnapshotHandleV1, type PublicationSnapshotV1, } from "./types.js";
|
|
6
|
+
export { PUBLICATION_BRIDGE_V0, PUBLICATION_SNAPSHOT_V1, type CapturePublicationSnapshotOptionsV1, type ExternalLinkPolicy, type PublicationBridgeAdmissionV1, type PublicationBridgeOutcomeV1, type PublicationBridgeV1, type PublicationCapturedSnapshotHandleV1, type PublicationJsonValue, type PublicationObjectRefV1, type PublicationRenderDocumentOptionsV1, type PublicationRenderedDocumentV1, type PublicationSnapshotHandleV1, type PublicationSnapshotV1, } from "./types.js";
|
|
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";
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { ExternalLinkPolicy } from "./external-link-policy.js";
|
|
1
2
|
import type { JsonValue, ObjectRef, PublicationSnapshotV1 as GeneratedPublicationSnapshotV1 } from "./generated/publication-snapshot-v1.js";
|
|
2
3
|
type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer Item)[] ? readonly DeepReadonly<Item>[] : T extends object ? {
|
|
3
4
|
readonly [Key in keyof T]: DeepReadonly<T[Key]>;
|
|
4
5
|
} : T;
|
|
5
6
|
export declare const PUBLICATION_SNAPSHOT_V1: "https://getsuperbee.com/schemas/publication-snapshot/v1";
|
|
6
7
|
export declare const PUBLICATION_BRIDGE_V0: "v0";
|
|
8
|
+
export type { ExternalLinkPolicy };
|
|
7
9
|
export type PublicationJsonValue = DeepReadonly<JsonValue>;
|
|
8
10
|
export type PublicationObjectRefV1 = DeepReadonly<ObjectRef>;
|
|
9
11
|
export type PublicationSnapshotV1 = DeepReadonly<GeneratedPublicationSnapshotV1>;
|
|
@@ -26,6 +28,26 @@ export interface PublicationSnapshotHandleV1 {
|
|
|
26
28
|
serializeManifest(): Uint8Array;
|
|
27
29
|
close(): Promise<void>;
|
|
28
30
|
}
|
|
31
|
+
export interface PublicationRenderDocumentOptionsV1 {
|
|
32
|
+
/** Set by the caller's own code. Absent or unknown means `none`. */
|
|
33
|
+
externalLinks?: ExternalLinkPolicy;
|
|
34
|
+
}
|
|
35
|
+
export interface PublicationRenderedDocumentV1 {
|
|
36
|
+
html: string;
|
|
37
|
+
bounded: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The handle capture returns. Handles that only serve stored bytes, such as a deployed site's
|
|
41
|
+
* reader, implement {@link PublicationSnapshotHandleV1} and need no renderer.
|
|
42
|
+
*/
|
|
43
|
+
export interface PublicationCapturedSnapshotHandleV1 extends PublicationSnapshotHandleV1 {
|
|
44
|
+
/**
|
|
45
|
+
* Render one document again from its digest-checked source with the caller's link policy. The
|
|
46
|
+
* snapshot and its stored inert HTML do not change, and the View bridge never calls this.
|
|
47
|
+
* With no policy the HTML equals the stored object byte for byte.
|
|
48
|
+
*/
|
|
49
|
+
renderDocument(id: string, options?: PublicationRenderDocumentOptionsV1): Promise<PublicationRenderedDocumentV1>;
|
|
50
|
+
}
|
|
29
51
|
export interface PublicationBridgeAdmissionV1 {
|
|
30
52
|
id: string;
|
|
31
53
|
entry: string;
|
|
@@ -40,5 +62,4 @@ export interface PublicationBridgeOutcomeV1 {
|
|
|
40
62
|
export interface PublicationBridgeV1 {
|
|
41
63
|
handle(rawRequest: unknown): Promise<PublicationBridgeOutcomeV1>;
|
|
42
64
|
}
|
|
43
|
-
export {};
|
|
44
65
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/publication.mjs
CHANGED
|
@@ -43438,6 +43438,72 @@ function gfmFromMarkdown() {
|
|
|
43438
43438
|
];
|
|
43439
43439
|
}
|
|
43440
43440
|
|
|
43441
|
+
// ../markdown-renderer/src/parse.ts
|
|
43442
|
+
var MAX_BLOCK_SCORE = 44e7;
|
|
43443
|
+
var MAX_BLOCK_EMPHASIS = 4e3;
|
|
43444
|
+
var MAX_EMPHASIS_SCORE = 1e8;
|
|
43445
|
+
var MAX_PIPES = 25e3;
|
|
43446
|
+
var MAX_LINE_MARKERS = 14e3;
|
|
43447
|
+
var MAX_TABLES = 2500;
|
|
43448
|
+
var MAX_LINE_NESTING = 64;
|
|
43449
|
+
var MAX_LINE_PREFIX = 512;
|
|
43450
|
+
var CONTAINER_MARKER = />|[-+*](?=[ \t]|$)|\d{1,9}[.)](?=[ \t]|$)|\[\^[^[\]\s]+\]:/my;
|
|
43451
|
+
function withinParseLimits(source) {
|
|
43452
|
+
let emphasisSum = 0, score = 0, block = 0, emphasis = 0, pipes = 0, markers = 0, tables = 0;
|
|
43453
|
+
let lineStart = 0, linePipes = 0, lineDashes = 0, blank = true, delimiterRow = true;
|
|
43454
|
+
for (let i = 0; i <= source.length; i++) {
|
|
43455
|
+
const code2 = i < source.length ? source.charCodeAt(i) : 10;
|
|
43456
|
+
if (code2 === 13 && source.charCodeAt(i + 1) === 10) continue;
|
|
43457
|
+
if (code2 === 10 || code2 === 13) {
|
|
43458
|
+
let nesting = 0, prefix = 0, position2 = lineStart;
|
|
43459
|
+
for (; ; ) {
|
|
43460
|
+
while (source.charCodeAt(position2) === 32 || source.charCodeAt(position2) === 9) position2++;
|
|
43461
|
+
CONTAINER_MARKER.lastIndex = position2;
|
|
43462
|
+
if (!CONTAINER_MARKER.test(source)) break;
|
|
43463
|
+
position2 = CONTAINER_MARKER.lastIndex;
|
|
43464
|
+
prefix = position2 - lineStart;
|
|
43465
|
+
nesting++;
|
|
43466
|
+
}
|
|
43467
|
+
if (nesting > MAX_LINE_NESTING || prefix > MAX_LINE_PREFIX) return false;
|
|
43468
|
+
markers += nesting;
|
|
43469
|
+
if (delimiterRow && linePipes > 0 && lineDashes > 0) tables++;
|
|
43470
|
+
if (blank) {
|
|
43471
|
+
score += block * block;
|
|
43472
|
+
emphasisSum += emphasis * emphasis;
|
|
43473
|
+
block = 0;
|
|
43474
|
+
emphasis = 0;
|
|
43475
|
+
} else {
|
|
43476
|
+
block += 2;
|
|
43477
|
+
}
|
|
43478
|
+
lineStart = i + 1;
|
|
43479
|
+
linePipes = lineDashes = 0;
|
|
43480
|
+
blank = delimiterRow = true;
|
|
43481
|
+
continue;
|
|
43482
|
+
}
|
|
43483
|
+
if (code2 === 32 || code2 === 9) continue;
|
|
43484
|
+
blank = false;
|
|
43485
|
+
if (code2 === 124) linePipes++, pipes++;
|
|
43486
|
+
else if (code2 === 45) lineDashes++;
|
|
43487
|
+
else if (code2 !== 58 && code2 !== 62) delimiterRow = false;
|
|
43488
|
+
const alphanumeric = code2 >= 48 && code2 <= 57 || code2 >= 65 && code2 <= 90 || code2 >= 97 && code2 <= 122 || code2 >= 128;
|
|
43489
|
+
if (!alphanumeric) block += code2 === 93 ? 2 : 1;
|
|
43490
|
+
if ((code2 === 42 || code2 === 95 || code2 === 126) && ++emphasis > MAX_BLOCK_EMPHASIS) return false;
|
|
43491
|
+
}
|
|
43492
|
+
return (emphasisSum + emphasis * emphasis) / MAX_EMPHASIS_SCORE + (score + block * block) / MAX_BLOCK_SCORE + (markers / MAX_LINE_MARKERS) ** 2 + (tables / MAX_TABLES) ** 2 + (pipes / MAX_PIPES) ** 2 <= 1;
|
|
43493
|
+
}
|
|
43494
|
+
function isCallStackExhausted(error) {
|
|
43495
|
+
return error instanceof Error && (error.name === "RangeError" || error.name === "InternalError") && /call stack|recursion/i.test(error.message);
|
|
43496
|
+
}
|
|
43497
|
+
function parseWithinLimits(source) {
|
|
43498
|
+
if (!withinParseLimits(source)) return null;
|
|
43499
|
+
try {
|
|
43500
|
+
return fromMarkdown(source, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] });
|
|
43501
|
+
} catch (error) {
|
|
43502
|
+
if (isCallStackExhausted(error)) return null;
|
|
43503
|
+
throw error;
|
|
43504
|
+
}
|
|
43505
|
+
}
|
|
43506
|
+
|
|
43441
43507
|
// ../markdown-renderer/src/index.tsx
|
|
43442
43508
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
43443
43509
|
function renderableConceptId(fromId, raw) {
|
|
@@ -43467,13 +43533,20 @@ function staticImageLookup(options) {
|
|
|
43467
43533
|
}
|
|
43468
43534
|
return lookup;
|
|
43469
43535
|
}
|
|
43470
|
-
function textOf(
|
|
43471
|
-
|
|
43472
|
-
|
|
43536
|
+
function textOf(root) {
|
|
43537
|
+
const parts = [];
|
|
43538
|
+
const pending = [root];
|
|
43539
|
+
while (pending.length > 0) {
|
|
43540
|
+
const node2 = pending.pop();
|
|
43541
|
+
if (node2.type === "text" || node2.type === "inlineCode" || node2.type === "html") {
|
|
43542
|
+
parts.push(node2.value ?? "");
|
|
43543
|
+
continue;
|
|
43544
|
+
}
|
|
43545
|
+
const children = node2.children;
|
|
43546
|
+
if (!Array.isArray(children)) continue;
|
|
43547
|
+
for (let i = children.length - 1; i >= 0; i--) pending.push(children[i]);
|
|
43473
43548
|
}
|
|
43474
|
-
|
|
43475
|
-
if (!Array.isArray(children)) return "";
|
|
43476
|
-
return children.map((child) => textOf(child)).join("");
|
|
43549
|
+
return parts.join("");
|
|
43477
43550
|
}
|
|
43478
43551
|
function renderTextFallback(node2, index2) {
|
|
43479
43552
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "doc-markdown-fallback", children: textOf(node2) }, index2);
|
|
@@ -43526,16 +43599,19 @@ function admitFragment(raw) {
|
|
|
43526
43599
|
}
|
|
43527
43600
|
return fragment.length <= MAX_FRAGMENT_LENGTH && SAFE_FRAGMENT.test(fragment) ? fragment : null;
|
|
43528
43601
|
}
|
|
43529
|
-
function admitExternalLink(raw,
|
|
43530
|
-
|
|
43602
|
+
function admitExternalLink(raw, policy) {
|
|
43603
|
+
const anyHost = policy === "https";
|
|
43604
|
+
const hosts = typeof policy === "object" && policy !== null ? policy.hosts : void 0;
|
|
43605
|
+
if (!anyHost && (!Array.isArray(hosts) || hosts.length === 0)) return null;
|
|
43531
43606
|
let url;
|
|
43532
43607
|
try {
|
|
43533
43608
|
url = new URL(raw);
|
|
43534
43609
|
} catch {
|
|
43535
43610
|
return null;
|
|
43536
43611
|
}
|
|
43537
|
-
if (url.protocol !== "https:" || url.username !== "" || url.password !== ""
|
|
43538
|
-
if (
|
|
43612
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "") return null;
|
|
43613
|
+
if (anyHost) return url.href;
|
|
43614
|
+
if (url.port !== "") return null;
|
|
43539
43615
|
for (const entry of hosts) {
|
|
43540
43616
|
if (normalizeAllowedHost(entry) === url.hostname) return url.href;
|
|
43541
43617
|
}
|
|
@@ -43617,10 +43693,14 @@ function renderNode(node2, state, depth, index2) {
|
|
|
43617
43693
|
case "link": {
|
|
43618
43694
|
const raw = node2.url ?? "";
|
|
43619
43695
|
const resolved = renderableConceptId(state.options.fromId, raw);
|
|
43696
|
+
const insideLink = state.insideLink;
|
|
43697
|
+
state.insideLink = true;
|
|
43620
43698
|
const children = renderChildren(node2, state, depth);
|
|
43699
|
+
state.insideLink = insideLink;
|
|
43621
43700
|
if (resolved === null) {
|
|
43622
|
-
const
|
|
43623
|
-
|
|
43701
|
+
const hasContent = /\S/.test(textOf(node2)) || node2.children.some((child) => child.type === "image");
|
|
43702
|
+
const external = admitExternalLink(raw, state.options.externalLinks);
|
|
43703
|
+
if (external !== null && !insideLink && hasContent) {
|
|
43624
43704
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: external, rel: "noopener noreferrer", target: "_blank", className: "doc-link-external", children }, index2);
|
|
43625
43705
|
}
|
|
43626
43706
|
const fragment = admitFragment(raw);
|
|
@@ -43742,11 +43822,15 @@ function renderMarkdown(body, options) {
|
|
|
43742
43822
|
source = source.slice(0, maxBodyChars);
|
|
43743
43823
|
bounded = true;
|
|
43744
43824
|
}
|
|
43745
|
-
const tree =
|
|
43746
|
-
|
|
43747
|
-
|
|
43748
|
-
|
|
43749
|
-
|
|
43825
|
+
const tree = parseWithinLimits(source);
|
|
43826
|
+
if (tree === null) {
|
|
43827
|
+
return {
|
|
43828
|
+
element: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "doc-code doc-markdown-fallback", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: source }) }),
|
|
43829
|
+
bounded: true,
|
|
43830
|
+
limits: { maxBodyChars, maxNodes }
|
|
43831
|
+
};
|
|
43832
|
+
}
|
|
43833
|
+
const state = { count: 0, bounded: false, insideLink: false, options, maxNodes, imageAssets: staticImageLookup(options) };
|
|
43750
43834
|
const children = tree.children;
|
|
43751
43835
|
const nodes = [];
|
|
43752
43836
|
let i = 0;
|
|
@@ -43783,11 +43867,8 @@ function renderMarkdownToStaticHtml(body, options) {
|
|
|
43783
43867
|
);
|
|
43784
43868
|
return { html, bounded: rendered.bounded, limits: rendered.limits };
|
|
43785
43869
|
}
|
|
43786
|
-
function renderDocumentToStaticHtml(document3
|
|
43787
|
-
const rendered = renderMarkdownToStaticHtml(document3.body, {
|
|
43788
|
-
fromId: document3.id,
|
|
43789
|
-
externalLinkHosts: options.externalLinkHosts
|
|
43790
|
-
});
|
|
43870
|
+
function renderDocumentToStaticHtml(document3) {
|
|
43871
|
+
const rendered = renderMarkdownToStaticHtml(document3.body, { fromId: document3.id });
|
|
43791
43872
|
return { html: rendered.html, bounded: rendered.bounded };
|
|
43792
43873
|
}
|
|
43793
43874
|
|
|
@@ -44685,6 +44766,17 @@ async function readInventory(backend, limits) {
|
|
|
44685
44766
|
}
|
|
44686
44767
|
return { documents, reserved, blobs };
|
|
44687
44768
|
}
|
|
44769
|
+
function parseDocumentSource(bytes, id, okfEdition) {
|
|
44770
|
+
try {
|
|
44771
|
+
const raw = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
44772
|
+
return parseMarkdown(raw, `${id}.md`, { okfVersion: okfEdition });
|
|
44773
|
+
} catch (error) {
|
|
44774
|
+
throw new PublicationError("MALFORMED_DOCUMENT", "a publication document is malformed", {
|
|
44775
|
+
subject: id,
|
|
44776
|
+
cause: error
|
|
44777
|
+
});
|
|
44778
|
+
}
|
|
44779
|
+
}
|
|
44688
44780
|
var SnapshotHandle = class {
|
|
44689
44781
|
constructor(manifest, objects) {
|
|
44690
44782
|
this.objects = objects;
|
|
@@ -44718,6 +44810,21 @@ var SnapshotHandle = class {
|
|
|
44718
44810
|
}
|
|
44719
44811
|
return bytes.slice();
|
|
44720
44812
|
}
|
|
44813
|
+
async renderDocument(id, options = {}) {
|
|
44814
|
+
if (this.closed) throw new PublicationError("HANDLE_CLOSED", "the publication snapshot handle is closed");
|
|
44815
|
+
const row = this.manifest.documents.find((document3) => document3.id === id);
|
|
44816
|
+
if (!row) throw new PublicationError("OBJECT_MISSING", "the document is not part of this snapshot", { subject: id });
|
|
44817
|
+
const parsed = parseDocumentSource(await this.readObject(row.source), id, this.manifest.source.okfEdition);
|
|
44818
|
+
try {
|
|
44819
|
+
const rendered = renderMarkdownToStaticHtml(parsed.body, { fromId: id, externalLinks: options.externalLinks });
|
|
44820
|
+
return { html: rendered.html, bounded: rendered.bounded };
|
|
44821
|
+
} catch (error) {
|
|
44822
|
+
throw new PublicationError("RENDER_FAILED", "a publication document could not be rendered", {
|
|
44823
|
+
subject: id,
|
|
44824
|
+
cause: error
|
|
44825
|
+
});
|
|
44826
|
+
}
|
|
44827
|
+
}
|
|
44721
44828
|
serializeManifest() {
|
|
44722
44829
|
if (this.closed) throw new PublicationError("HANDLE_CLOSED", "the publication snapshot handle is closed");
|
|
44723
44830
|
return this.serializedManifest.slice();
|
|
@@ -44781,16 +44888,7 @@ function buildSnapshot(first) {
|
|
|
44781
44888
|
}
|
|
44782
44889
|
}
|
|
44783
44890
|
for (const row of first.documents) {
|
|
44784
|
-
|
|
44785
|
-
try {
|
|
44786
|
-
const raw = new TextDecoder("utf-8", { fatal: true }).decode(row.bytes);
|
|
44787
|
-
parsed = parseMarkdown(raw, `${row.id}.md`, { okfVersion: okfEdition });
|
|
44788
|
-
} catch (error) {
|
|
44789
|
-
throw new PublicationError("MALFORMED_DOCUMENT", "a publication document is malformed", {
|
|
44790
|
-
subject: row.id,
|
|
44791
|
-
cause: error
|
|
44792
|
-
});
|
|
44793
|
-
}
|
|
44891
|
+
const parsed = parseDocumentSource(row.bytes, row.id, okfEdition);
|
|
44794
44892
|
if (typeof parsed.frontmatter.type !== "string" || !parsed.frontmatter.type.trim()) {
|
|
44795
44893
|
throw new PublicationError("MALFORMED_DOCUMENT", "a publication document requires a non-empty type", {
|
|
44796
44894
|
subject: row.id
|