@riebeckite/core 0.0.3 → 0.0.6
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 +1 -1
- package/README_ja.md +1 -1
- package/dist/index.d.ts +14 -15
- package/dist/index.js +112 -98
- package/dist/src/types/plugin_endpoint.d.ts +23 -1
- package/dist/src/types/plugin_pipeline.d.ts +1 -2
- package/dist/src/utils/html.d.ts +9 -0
- package/package.json +3 -2
package/README.md
CHANGED
package/README_ja.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -4,61 +4,60 @@ export { ATTACHMENTS_BASE_PATH, attachmentUrl, getExtension, isAttachmentPath, i
|
|
|
4
4
|
export type { ContentBuildState, FingerprintedContentEntry, } from "./src/content/content_build_state.js";
|
|
5
5
|
export type { ContentBuildStateInvalidReason, ContentBuildStateStatus, } from "./src/content/content_build_state_store.js";
|
|
6
6
|
export { loadContentBuildState, readContentBuildStateStatus, resolveContentBuildStatePath, } from "./src/content/content_build_state_store.js";
|
|
7
|
-
export { fingerprintContentEntries } from "./src/content/content_fingerprint.js";
|
|
8
7
|
export type { ContentCollection, ContentCollectionContext, ContentCollectionDefinition, ContentCollectionPage, } from "./src/content/content_collection.js";
|
|
9
8
|
export { buildContentCollections } from "./src/content/content_collection.js";
|
|
10
|
-
export
|
|
11
|
-
export { groupContentEntries, queryContentEntries, queryContentPage, resolveContentQueryPagination, } from "./src/content/content_query.js";
|
|
9
|
+
export { fingerprintContentEntries } from "./src/content/content_fingerprint.js";
|
|
12
10
|
export type { ContentGraph, ContentGraphNeighbors, } from "./src/content/content_graph.js";
|
|
13
11
|
export { createContentGraph } from "./src/content/content_graph.js";
|
|
14
|
-
export { buildGraphEdges, layoutRadialGraph, } from "./src/content/graph_layout.js";
|
|
15
|
-
export type { GraphEdge, GraphLayoutNode, LinkableGraphNode, RadialGraphLayoutOptions, } from "./src/content/graph_layout.js";
|
|
16
12
|
export { resolveDefaultContentLocation } from "./src/content/content_location.js";
|
|
17
13
|
export type { ContentBuildOptions, ContentInspection, } from "./src/content/content_manager.js";
|
|
18
14
|
export { ContentManager } from "./src/content/content_manager.js";
|
|
19
15
|
export { extractFrontmatterAliases } from "./src/content/content_metadata.js";
|
|
16
|
+
export type { ContentQueryDateFilter, ContentQueryDateGranularity, ContentQueryFilter, ContentQueryFrontmatterFilter, ContentQueryGroup, ContentQueryGroupBy, ContentQueryGroupOptions, ContentQueryPage, ContentQueryPagination, ContentQueryScalar, ContentQuerySort, ContentQuerySortOrder, ContentQuerySpec, ContentQueryTagFilter, } from "./src/content/content_query.js";
|
|
17
|
+
export { groupContentEntries, queryContentEntries, queryContentPage, resolveContentQueryPagination, } from "./src/content/content_query.js";
|
|
20
18
|
export type { ContentSource, ContentSourceContent, ContentSourceEntry, ContentSourceMetadata, } from "./src/content/content_source.js";
|
|
21
19
|
export { getContentSourceEntry, readContentSourceEntry, } from "./src/content/content_source.js";
|
|
22
20
|
export { resolveContentStableId } from "./src/content/content_stable_id.js";
|
|
23
21
|
export { FileSystemContentSource } from "./src/content/file_system_content_source.js";
|
|
22
|
+
export type { GraphEdge, GraphLayoutNode, LinkableGraphNode, RadialGraphLayoutOptions, } from "./src/content/graph_layout.js";
|
|
23
|
+
export { buildGraphEdges, layoutRadialGraph, } from "./src/content/graph_layout.js";
|
|
24
24
|
export { IMAGE_EXTENSIONS } from "./src/content/image_extensions.js";
|
|
25
25
|
export { readOnlyContentGraph } from "./src/content/read_only_content_graph.js";
|
|
26
|
-
export { uniqueStrings } from "./src/utils/collections.js";
|
|
27
|
-
export { escapeHtml, escapeHtmlAttribute } from "./src/utils/html.js";
|
|
28
|
-
export { normalizeTag } from "./src/utils/tags.js";
|
|
29
|
-
export { calculateReadingTime, stripHtml } from "./src/utils/text.js";
|
|
30
26
|
export type { LogContext, Logger, LogLevel, Observability, ObservabilityValue, TraceAttributes, TraceEvent, Tracer, TraceSink, TraceSpan, } from "./src/observability.js";
|
|
31
27
|
export { CompositeTraceSink, ConsoleLogger, NoopLogger, NoopTracer, SinkTracer, } from "./src/observability.js";
|
|
32
28
|
export type { PipelineOptions } from "./src/pipeline.js";
|
|
33
29
|
export { Pipeline } from "./src/pipeline.js";
|
|
34
|
-
export type { JsonValue } from "./src/types/json_value.js";
|
|
35
30
|
export type { PluginCache } from "./src/plugin/plugin_cache.js";
|
|
31
|
+
export { PluginDependencyError, type PluginDependencyErrorKind, } from "./src/plugin/plugin_dependency_error.js";
|
|
36
32
|
export type { CreatePluginMemoOptions, PluginMemo, } from "./src/plugin/plugin_memo.js";
|
|
37
33
|
export { createPluginMemo, stableStringify, } from "./src/plugin/plugin_memo.js";
|
|
38
|
-
export { PluginDependencyError, type PluginDependencyErrorKind, } from "./src/plugin/plugin_dependency_error.js";
|
|
39
34
|
export type { ConfigValidationIssue, PluginOptionsValidator, } from "./src/types/config_validation.js";
|
|
40
35
|
export type { ContentAsset, ContentBodySlot, ContentLink, ContentLinkKind, ContentLocationInput, ContentManifest, ContentManifestEntry, ContentManifestPluginClientEntry, ContentPublicLocation, ContentRedirect, } from "./src/types/content_manifest.js";
|
|
41
36
|
export { appendContentBodySlot } from "./src/types/content_manifest.js";
|
|
42
37
|
export type { Diagnostic, DiagnosticCode, DiagnosticSeverity, } from "./src/types/diagnostic.js";
|
|
43
38
|
export type { GeneratedOutput, GeneratedOutputContent, GeneratedOutputInput, GeneratedOutputSink, } from "./src/types/generated_output.js";
|
|
44
39
|
export { createUnavailableGeneratedOutputSink, normalizeGeneratedOutputPath, } from "./src/types/generated_output.js";
|
|
40
|
+
export type { JsonValue } from "./src/types/json_value.js";
|
|
45
41
|
export type { PluginInput, ResolvedPluginMetadata, RiebeckitePlugin, } from "./src/types/plugin.js";
|
|
46
42
|
export { definePlugin, getResolvedPluginMetadata, resolvePlugins, } from "./src/types/plugin.js";
|
|
47
43
|
export type { PluginAsset, PluginAssetKind, PluginClientEntry, } from "./src/types/plugin_asset.js";
|
|
48
44
|
export { createClientEntry, createStyleAsset, serializePublicClientConfig, } from "./src/types/plugin_asset.js";
|
|
49
45
|
export type { PluginContentContext, PluginContentLocationAugmentContext, PluginContentLocationContext, PluginContentLocationResolver, PluginContentRenderer, PluginContext, PluginGeneratedHtml, PluginGeneratedHtmlInspector, PluginGraphContext, PluginLifecycleContext, PluginManifestContext, PluginPostContext, PluginRenderContext, PluginRenderInput, PluginRenderTarget, } from "./src/types/plugin_context.js";
|
|
50
46
|
export type { PluginDiagnostic, PluginDiagnosticLevel, } from "./src/types/plugin_diagnostic.js";
|
|
51
|
-
export type {
|
|
52
|
-
export type { PluginEndpoint, PluginEndpointContext, PluginEndpointMethod, PluginEndpointResponse, PluginEndpointOptions, } from "./src/types/plugin_endpoint.js";
|
|
47
|
+
export type { PluginEndpoint, PluginEndpointContext, PluginEndpointMethod, PluginEndpointOptions, PluginEndpointRequest, PluginEndpointResponse, } from "./src/types/plugin_endpoint.js";
|
|
53
48
|
export { defineEndpoint } from "./src/types/plugin_endpoint.js";
|
|
49
|
+
export type { PluginHeadTag } from "./src/types/plugin_head.js";
|
|
54
50
|
export type { HtmlPipeline, MarkdownEmbedFragment, MarkdownPipeline, MarkdownPipelineContext, PipelinePlugin, } from "./src/types/plugin_pipeline.js";
|
|
55
51
|
export type { PluginSeoExtension, RenderableFeedEntry, SeoMetadata, WebsiteSeoInput, } from "./src/types/plugin_seo.js";
|
|
56
52
|
export type { PostContent, PostFrontmatter } from "./src/types/post_content.js";
|
|
57
|
-
export type { PublishStrategy } from "./src/types/publish_strategy.js";
|
|
58
|
-
export type { PublishFrontmatter } from "./src/types/publish_strategy.js";
|
|
53
|
+
export type { PublishFrontmatter, PublishStrategy, } from "./src/types/publish_strategy.js";
|
|
59
54
|
export { isPublishable } from "./src/types/publish_strategy.js";
|
|
60
55
|
export type { ResolvedRiebeckiteConfig } from "./src/types/resolved_riebeckite_config.js";
|
|
61
56
|
export type { RiebeckiteConfig } from "./src/types/riebeckite_config.js";
|
|
62
57
|
export type { SiteConfig } from "./src/types/site_config.js";
|
|
63
58
|
export type { RiebeckiteTheme, ThemeArticleLayoutPreset, ThemeAttributes, ThemeColorMode, ThemeConfig, ThemeDesignTokens, ThemeInput, ThemeStyle, ThemeTypographyPreset, } from "./src/types/theme_config.js";
|
|
64
59
|
export { defineTheme } from "./src/types/theme_config.js";
|
|
60
|
+
export { uniqueStrings } from "./src/utils/collections.js";
|
|
61
|
+
export { escapeHtml, escapeHtmlAttribute, escapeScriptJson, } from "./src/utils/html.js";
|
|
62
|
+
export { normalizeTag } from "./src/utils/tags.js";
|
|
63
|
+
export { calculateReadingTime, stripHtml } from "./src/utils/text.js";
|
package/dist/index.js
CHANGED
|
@@ -382,16 +382,18 @@ function collectProviders(plugins) {
|
|
|
382
382
|
function collectDependencies(plugins, providers) {
|
|
383
383
|
const availableCapabilities = [...providers.keys()];
|
|
384
384
|
return plugins.map((plugin, pluginIndex) => {
|
|
385
|
-
const requiredDependencies = (plugin.requires ?? []).map(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
385
|
+
const requiredDependencies = (plugin.requires ?? []).map(
|
|
386
|
+
(rawCapability) => {
|
|
387
|
+
const capability = normalize(rawCapability);
|
|
388
|
+
return resolveDependency({
|
|
389
|
+
pluginIndex,
|
|
390
|
+
pluginName: plugin.name,
|
|
391
|
+
capability,
|
|
392
|
+
availableCapabilities,
|
|
393
|
+
providers
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
);
|
|
395
397
|
const optionalDependencies = (plugin.optional ?? []).flatMap(
|
|
396
398
|
(rawCapability) => {
|
|
397
399
|
const capability = normalize(rawCapability);
|
|
@@ -824,23 +826,6 @@ function isNotFoundError(error) {
|
|
|
824
826
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
825
827
|
}
|
|
826
828
|
|
|
827
|
-
// src/content/content_fingerprint.ts
|
|
828
|
-
import { createHash } from "node:crypto";
|
|
829
|
-
async function fingerprintContentEntries(entries, read) {
|
|
830
|
-
return await Promise.all(
|
|
831
|
-
[...entries].sort((left, right) => left.path.localeCompare(right.path)).map(async (entry) => ({
|
|
832
|
-
entry,
|
|
833
|
-
fingerprint: await fingerprintContentEntry(entry, read)
|
|
834
|
-
}))
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
async function fingerprintContentEntry(entry, read) {
|
|
838
|
-
if (entry.metadata?.hash) return `hash:${entry.metadata.hash}`;
|
|
839
|
-
if (entry.metadata?.etag) return `etag:${entry.metadata.etag}`;
|
|
840
|
-
const content = await read(entry);
|
|
841
|
-
return `sha256:${createHash("sha256").update(typeof content === "string" ? content : content).digest("hex")}`;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
829
|
// src/content/content_query.ts
|
|
845
830
|
function queryContentEntries(entries, spec = {}) {
|
|
846
831
|
const filter = spec.filter ?? {};
|
|
@@ -1172,6 +1157,23 @@ function normalizePath(value) {
|
|
|
1172
1157
|
return absolute.replace(/\/{2,}/g, "/").replace(/\/+$/, "") || "/";
|
|
1173
1158
|
}
|
|
1174
1159
|
|
|
1160
|
+
// src/content/content_fingerprint.ts
|
|
1161
|
+
import { createHash } from "node:crypto";
|
|
1162
|
+
async function fingerprintContentEntries(entries, read) {
|
|
1163
|
+
return await Promise.all(
|
|
1164
|
+
[...entries].sort((left, right) => left.path.localeCompare(right.path)).map(async (entry) => ({
|
|
1165
|
+
entry,
|
|
1166
|
+
fingerprint: await fingerprintContentEntry(entry, read)
|
|
1167
|
+
}))
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
async function fingerprintContentEntry(entry, read) {
|
|
1171
|
+
if (entry.metadata?.hash) return `hash:${entry.metadata.hash}`;
|
|
1172
|
+
if (entry.metadata?.etag) return `etag:${entry.metadata.etag}`;
|
|
1173
|
+
const content = await read(entry);
|
|
1174
|
+
return `sha256:${createHash("sha256").update(typeof content === "string" ? content : content).digest("hex")}`;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1175
1177
|
// src/utils/collections.ts
|
|
1176
1178
|
function uniqueStrings(values) {
|
|
1177
1179
|
return Array.from(new Set(values));
|
|
@@ -1220,46 +1222,6 @@ function isResolvedNoteLink(link) {
|
|
|
1220
1222
|
return link.kind === "note" && link.slug !== null;
|
|
1221
1223
|
}
|
|
1222
1224
|
|
|
1223
|
-
// src/content/graph_layout.ts
|
|
1224
|
-
function buildGraphEdges(nodes, visibleSlugs = new Set(nodes.map((node) => node.slug))) {
|
|
1225
|
-
const edges = /* @__PURE__ */ new Map();
|
|
1226
|
-
for (const node of nodes) {
|
|
1227
|
-
for (const target of node.outgoing) {
|
|
1228
|
-
if (!visibleSlugs.has(node.slug) || !visibleSlugs.has(target)) continue;
|
|
1229
|
-
const key = `${node.slug}\0${target}`;
|
|
1230
|
-
edges.set(key, { source: node.slug, target });
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return Array.from(edges.values());
|
|
1234
|
-
}
|
|
1235
|
-
function layoutRadialGraph(nodes, options) {
|
|
1236
|
-
const layout = /* @__PURE__ */ new Map();
|
|
1237
|
-
const centerX = options.width / 2;
|
|
1238
|
-
const centerY = options.height / 2;
|
|
1239
|
-
const radius = Math.min(options.width, options.height) * (options.radiusRatio ?? 0.36);
|
|
1240
|
-
const minNodeRadius = options.minNodeRadius ?? 8;
|
|
1241
|
-
const maxNodeRadius = options.maxNodeRadius ?? 14;
|
|
1242
|
-
const selectedIndex = Math.max(
|
|
1243
|
-
nodes.findIndex((node) => node.slug === options.centerSlug),
|
|
1244
|
-
0
|
|
1245
|
-
);
|
|
1246
|
-
nodes.forEach((node, index) => {
|
|
1247
|
-
const shiftedIndex = (index - selectedIndex + nodes.length) % nodes.length;
|
|
1248
|
-
const angle = shiftedIndex / Math.max(nodes.length, 1) * Math.PI * 2;
|
|
1249
|
-
const linkCount = node.outgoing.length + node.backlinks.length;
|
|
1250
|
-
const isCenter = node.slug === options.centerSlug;
|
|
1251
|
-
layout.set(node.slug, {
|
|
1252
|
-
x: isCenter ? centerX : centerX + Math.cos(angle) * radius,
|
|
1253
|
-
y: isCenter ? centerY : centerY + Math.sin(angle) * radius,
|
|
1254
|
-
radius: clamp(7 + linkCount, minNodeRadius, maxNodeRadius)
|
|
1255
|
-
});
|
|
1256
|
-
});
|
|
1257
|
-
return layout;
|
|
1258
|
-
}
|
|
1259
|
-
function clamp(value, min, max) {
|
|
1260
|
-
return Math.min(Math.max(value, min), max);
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
1225
|
// src/content/content_location.ts
|
|
1264
1226
|
function resolveDefaultContentLocation(content) {
|
|
1265
1227
|
return {
|
|
@@ -2360,12 +2322,12 @@ function extractFrontmatterAliases(markdown) {
|
|
|
2360
2322
|
const lines = frontmatter[1].split(/\r?\n/);
|
|
2361
2323
|
for (let i = 0; i < lines.length; i++) {
|
|
2362
2324
|
const line = lines[i] ?? "";
|
|
2363
|
-
const inlineMatch = line.match(/^
|
|
2325
|
+
const inlineMatch = line.match(/^alias(?:es)?:\s*(.+)$/i);
|
|
2364
2326
|
if (inlineMatch?.[1]) {
|
|
2365
2327
|
aliases.push(...parseYamlScalarOrList(inlineMatch[1]));
|
|
2366
2328
|
continue;
|
|
2367
2329
|
}
|
|
2368
|
-
if (/^
|
|
2330
|
+
if (/^alias(?:es)?:\s*$/i.test(line)) {
|
|
2369
2331
|
for (let j = i + 1; j < lines.length; j++) {
|
|
2370
2332
|
const itemMatch = lines[j]?.match(/^\s*-\s*(.+)$/);
|
|
2371
2333
|
if (!itemMatch?.[1]) break;
|
|
@@ -2960,6 +2922,46 @@ async function getContentSourceEntry(source, logicalPath) {
|
|
|
2960
2922
|
return (await source.scan()).find((candidate) => candidate.path === logicalPath) ?? null;
|
|
2961
2923
|
}
|
|
2962
2924
|
|
|
2925
|
+
// src/content/graph_layout.ts
|
|
2926
|
+
function buildGraphEdges(nodes, visibleSlugs = new Set(nodes.map((node) => node.slug))) {
|
|
2927
|
+
const edges = /* @__PURE__ */ new Map();
|
|
2928
|
+
for (const node of nodes) {
|
|
2929
|
+
for (const target of node.outgoing) {
|
|
2930
|
+
if (!visibleSlugs.has(node.slug) || !visibleSlugs.has(target)) continue;
|
|
2931
|
+
const key = `${node.slug}\0${target}`;
|
|
2932
|
+
edges.set(key, { source: node.slug, target });
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
return Array.from(edges.values());
|
|
2936
|
+
}
|
|
2937
|
+
function layoutRadialGraph(nodes, options) {
|
|
2938
|
+
const layout = /* @__PURE__ */ new Map();
|
|
2939
|
+
const centerX = options.width / 2;
|
|
2940
|
+
const centerY = options.height / 2;
|
|
2941
|
+
const radius = Math.min(options.width, options.height) * (options.radiusRatio ?? 0.36);
|
|
2942
|
+
const minNodeRadius = options.minNodeRadius ?? 8;
|
|
2943
|
+
const maxNodeRadius = options.maxNodeRadius ?? 14;
|
|
2944
|
+
const selectedIndex = Math.max(
|
|
2945
|
+
nodes.findIndex((node) => node.slug === options.centerSlug),
|
|
2946
|
+
0
|
|
2947
|
+
);
|
|
2948
|
+
nodes.forEach((node, index) => {
|
|
2949
|
+
const shiftedIndex = (index - selectedIndex + nodes.length) % nodes.length;
|
|
2950
|
+
const angle = shiftedIndex / Math.max(nodes.length, 1) * Math.PI * 2;
|
|
2951
|
+
const linkCount = node.outgoing.length + node.backlinks.length;
|
|
2952
|
+
const isCenter = node.slug === options.centerSlug;
|
|
2953
|
+
layout.set(node.slug, {
|
|
2954
|
+
x: isCenter ? centerX : centerX + Math.cos(angle) * radius,
|
|
2955
|
+
y: isCenter ? centerY : centerY + Math.sin(angle) * radius,
|
|
2956
|
+
radius: clamp(7 + linkCount, minNodeRadius, maxNodeRadius)
|
|
2957
|
+
});
|
|
2958
|
+
});
|
|
2959
|
+
return layout;
|
|
2960
|
+
}
|
|
2961
|
+
function clamp(value, min, max) {
|
|
2962
|
+
return Math.min(Math.max(value, min), max);
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2963
2965
|
// src/content/read_only_content_graph.ts
|
|
2964
2966
|
async function readOnlyContentGraph(source, locations) {
|
|
2965
2967
|
const entries = await source.scan();
|
|
@@ -3000,26 +3002,6 @@ async function readText2(source, entry) {
|
|
|
3000
3002
|
return typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
3001
3003
|
}
|
|
3002
3004
|
|
|
3003
|
-
// src/utils/html.ts
|
|
3004
|
-
function escapeHtml(value) {
|
|
3005
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3006
|
-
}
|
|
3007
|
-
function escapeHtmlAttribute(value) {
|
|
3008
|
-
return escapeHtml(value);
|
|
3009
|
-
}
|
|
3010
|
-
|
|
3011
|
-
// src/utils/text.ts
|
|
3012
|
-
function stripHtml(html) {
|
|
3013
|
-
return html.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
|
|
3014
|
-
}
|
|
3015
|
-
function calculateReadingTime(html) {
|
|
3016
|
-
const text = stripHtml(html).trim();
|
|
3017
|
-
if (!text) return 1;
|
|
3018
|
-
const cjkCharacters = text.match(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu)?.length ?? 0;
|
|
3019
|
-
const latinWords = text.replace(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu, " ").match(/[\p{L}\p{N}]+/gu)?.length ?? 0;
|
|
3020
|
-
return Math.max(1, Math.ceil(cjkCharacters / 500 + latinWords / 220));
|
|
3021
|
-
}
|
|
3022
|
-
|
|
3023
3005
|
// src/plugin/plugin_memo.ts
|
|
3024
3006
|
import { createHash as createHash3 } from "node:crypto";
|
|
3025
3007
|
function createPluginMemo(cache, options = {}) {
|
|
@@ -3030,13 +3012,23 @@ function createPluginMemo(cache, options = {}) {
|
|
|
3030
3012
|
const cached = await cache.get(memoKey);
|
|
3031
3013
|
if (cached !== void 0) return cached;
|
|
3032
3014
|
} catch (error) {
|
|
3033
|
-
warn(
|
|
3015
|
+
warn(
|
|
3016
|
+
options.logger,
|
|
3017
|
+
"Plugin cache read failed; recomputing.",
|
|
3018
|
+
key,
|
|
3019
|
+
error
|
|
3020
|
+
);
|
|
3034
3021
|
}
|
|
3035
3022
|
const value = await compute();
|
|
3036
3023
|
try {
|
|
3037
3024
|
await cache.set(memoKey, value);
|
|
3038
3025
|
} catch (error) {
|
|
3039
|
-
warn(
|
|
3026
|
+
warn(
|
|
3027
|
+
options.logger,
|
|
3028
|
+
"Plugin cache write failed; result not cached.",
|
|
3029
|
+
key,
|
|
3030
|
+
error
|
|
3031
|
+
);
|
|
3040
3032
|
}
|
|
3041
3033
|
return value;
|
|
3042
3034
|
}
|
|
@@ -3079,16 +3071,37 @@ function defineEndpoint(path4, handler, options = {}) {
|
|
|
3079
3071
|
path: path4,
|
|
3080
3072
|
handler: async (context) => {
|
|
3081
3073
|
const response = await handler(context);
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
headers
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
}
|
|
3088
|
-
};
|
|
3074
|
+
const headers = { ...response.headers };
|
|
3075
|
+
if (options.cacheControl !== void 0) {
|
|
3076
|
+
headers["Cache-Control"] = options.cacheControl;
|
|
3077
|
+
}
|
|
3078
|
+
return { ...response, headers };
|
|
3089
3079
|
}
|
|
3090
3080
|
};
|
|
3091
3081
|
}
|
|
3082
|
+
|
|
3083
|
+
// src/utils/html.ts
|
|
3084
|
+
function escapeHtml(value) {
|
|
3085
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3086
|
+
}
|
|
3087
|
+
function escapeHtmlAttribute(value) {
|
|
3088
|
+
return escapeHtml(value);
|
|
3089
|
+
}
|
|
3090
|
+
function escapeScriptJson(value) {
|
|
3091
|
+
return value.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
// src/utils/text.ts
|
|
3095
|
+
function stripHtml(html) {
|
|
3096
|
+
return html.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
|
|
3097
|
+
}
|
|
3098
|
+
function calculateReadingTime(html) {
|
|
3099
|
+
const text = stripHtml(html).trim();
|
|
3100
|
+
if (!text) return 1;
|
|
3101
|
+
const cjkCharacters = text.match(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu)?.length ?? 0;
|
|
3102
|
+
const latinWords = text.replace(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu, " ").match(/[\p{L}\p{N}]+/gu)?.length ?? 0;
|
|
3103
|
+
return Math.max(1, Math.ceil(cjkCharacters / 500 + latinWords / 220));
|
|
3104
|
+
}
|
|
3092
3105
|
export {
|
|
3093
3106
|
ATTACHMENTS_BASE_PATH,
|
|
3094
3107
|
CompositeTraceSink,
|
|
@@ -3118,6 +3131,7 @@ export {
|
|
|
3118
3131
|
defineTheme,
|
|
3119
3132
|
escapeHtml,
|
|
3120
3133
|
escapeHtmlAttribute,
|
|
3134
|
+
escapeScriptJson,
|
|
3121
3135
|
extractFrontmatterAliases,
|
|
3122
3136
|
fingerprintContentEntries,
|
|
3123
3137
|
getContentSourceEntry,
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import type { ContentManifest } from "./content_manifest.js";
|
|
2
2
|
import type { ResolvedRiebeckiteConfig } from "./resolved_riebeckite_config.js";
|
|
3
|
-
export type PluginEndpointMethod = "GET";
|
|
3
|
+
export type PluginEndpointMethod = "GET" | "POST";
|
|
4
|
+
/**
|
|
5
|
+
* Framework-neutral snapshot of an incoming HTTP request.
|
|
6
|
+
*
|
|
7
|
+
* Integrations extract only these fields from their host router and pass them
|
|
8
|
+
* to endpoint handlers. Endpoints never receive a Hono, Vite, or Node request
|
|
9
|
+
* object, so a plugin stays usable across runtimes.
|
|
10
|
+
*/
|
|
11
|
+
export type PluginEndpointRequest = {
|
|
12
|
+
method: PluginEndpointMethod;
|
|
13
|
+
/** Absolute request URL. */
|
|
14
|
+
url: string;
|
|
15
|
+
/** URL path, for example `/webmentions`. */
|
|
16
|
+
path: string;
|
|
17
|
+
/** First value of every query-string parameter. */
|
|
18
|
+
query: Readonly<Record<string, string>>;
|
|
19
|
+
/** Request headers keyed by lower-cased name. */
|
|
20
|
+
headers: Readonly<Record<string, string>>;
|
|
21
|
+
/** Raw request body text. Empty when the request has no body. */
|
|
22
|
+
body: string;
|
|
23
|
+
};
|
|
4
24
|
export type PluginEndpointContext = {
|
|
5
25
|
config: ResolvedRiebeckiteConfig;
|
|
6
26
|
manifest: ContentManifest;
|
|
27
|
+
request: PluginEndpointRequest;
|
|
7
28
|
};
|
|
8
29
|
export type PluginEndpointResponse = {
|
|
9
30
|
status?: number;
|
|
@@ -13,6 +34,7 @@ export type PluginEndpointResponse = {
|
|
|
13
34
|
};
|
|
14
35
|
export type PluginEndpoint = {
|
|
15
36
|
path: string;
|
|
37
|
+
/** Defaults to `GET` when omitted. */
|
|
16
38
|
method?: PluginEndpointMethod;
|
|
17
39
|
handler(context: PluginEndpointContext): PluginEndpointResponse | Promise<PluginEndpointResponse>;
|
|
18
40
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Plugin } from "unified";
|
|
2
|
-
import type { Node } from "unist";
|
|
3
2
|
import type { ContentSource } from "../content/content_source.js";
|
|
4
3
|
import type { PluginRenderInput } from "./plugin_context.js";
|
|
5
|
-
export type PipelinePlugin = Plugin<[
|
|
4
|
+
export type PipelinePlugin = Plugin<any[], any, any>;
|
|
6
5
|
export type MarkdownPipeline = {
|
|
7
6
|
use(plugin: PipelinePlugin, options?: unknown): void;
|
|
8
7
|
};
|
package/dist/src/utils/html.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
export declare function escapeHtml(value: string): string;
|
|
2
2
|
export declare function escapeHtmlAttribute(value: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Escapes JSON so it can be embedded inside a `<script>` element (e.g. as
|
|
5
|
+
* `application/ld+json` or `application/json`). `JSON.stringify` leaves `<`,
|
|
6
|
+
* `>`, `&`, U+2028 and U+2029 untouched; inside a script tag `<` terminates
|
|
7
|
+
* the element, which lets an attacker break out and inject markup or code.
|
|
8
|
+
* Replacing them with the equivalent `\uXXXX` escapes keeps the JSON valid
|
|
9
|
+
* while neutralizing the dangerous characters.
|
|
10
|
+
*/
|
|
11
|
+
export declare function escapeScriptJson(value: string): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"vfile-matter": "^5.0.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"tsx": "^4.22.4"
|
|
45
|
+
"tsx": "^4.22.4",
|
|
46
|
+
"@riebeckite/test": "0.0.6"
|
|
46
47
|
},
|
|
47
48
|
"repository": {
|
|
48
49
|
"type": "git",
|