@riebeckite/core 0.0.22 → 0.0.24
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/index.d.ts +7 -3
- package/dist/index.js +1052 -180
- package/dist/src/content/content_build_coordinator.d.ts +6 -16
- package/dist/src/content/content_build_plan.d.ts +24 -0
- package/dist/src/content/content_build_state.d.ts +22 -1
- package/dist/src/content/content_manager.d.ts +17 -2
- package/dist/src/content/content_persistent_cache.d.ts +9 -4
- package/dist/src/content/external_fingerprint.d.ts +1 -0
- package/dist/src/content/file_system_content_source.d.ts +1 -1
- package/dist/src/content/manifest_hook_cache.d.ts +7 -0
- package/dist/src/content/output_dependency.d.ts +3 -0
- package/dist/src/content/public_content_view.d.ts +30 -0
- package/dist/src/pipeline.d.ts +2 -0
- package/dist/src/plugin/generated_output_registry.d.ts +1 -0
- package/dist/src/plugin/plugin_runtime.d.ts +36 -1
- package/dist/src/types/content_manifest.d.ts +0 -36
- package/dist/src/types/external_dependency.d.ts +31 -0
- package/dist/src/types/output_dependency.d.ts +6 -0
- package/dist/src/types/plugin.d.ts +34 -2
- package/dist/src/types/plugin_asset.d.ts +2 -2
- package/dist/src/types/plugin_component.d.ts +2 -0
- package/dist/src/types/plugin_context.d.ts +2 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -825,17 +825,15 @@ function getFileName(filePath) {
|
|
|
825
825
|
return normalizedPath.slice(normalizedPath.lastIndexOf("/") + 1);
|
|
826
826
|
}
|
|
827
827
|
|
|
828
|
-
// src/content/content_build_state_store.ts
|
|
829
|
-
import { randomUUID } from "node:crypto";
|
|
830
|
-
import fs from "node:fs/promises";
|
|
831
|
-
import path from "node:path";
|
|
832
|
-
|
|
833
828
|
// src/content/content_build_state.ts
|
|
834
|
-
var CONTENT_BUILD_STATE_VERSION =
|
|
829
|
+
var CONTENT_BUILD_STATE_VERSION = 16;
|
|
835
830
|
var CONTENT_BUILD_STATE_DIRECTORY = ".riebeckite";
|
|
836
831
|
var CONTENT_BUILD_STATE_EXCLUDE = `${CONTENT_BUILD_STATE_DIRECTORY}/**`;
|
|
837
832
|
|
|
838
833
|
// src/content/content_build_state_store.ts
|
|
834
|
+
import { randomUUID } from "node:crypto";
|
|
835
|
+
import fs from "node:fs/promises";
|
|
836
|
+
import path from "node:path";
|
|
839
837
|
async function loadContentBuildState(statePath) {
|
|
840
838
|
try {
|
|
841
839
|
const parsed = JSON.parse(await fs.readFile(statePath, "utf8"));
|
|
@@ -876,13 +874,26 @@ async function saveContentBuildState(statePath, state2) {
|
|
|
876
874
|
const temporaryPath = `${statePath}.${randomUUID()}.tmp`;
|
|
877
875
|
await fs.mkdir(directory, { recursive: true });
|
|
878
876
|
try {
|
|
877
|
+
await waitForFaultInjection("content-build-state-save-start");
|
|
879
878
|
await fs.writeFile(temporaryPath, JSON.stringify(state2), "utf8");
|
|
880
879
|
await fs.rename(temporaryPath, statePath);
|
|
880
|
+
await waitForFaultInjection("content-build-state-committed");
|
|
881
881
|
} catch (error) {
|
|
882
882
|
await fs.rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
883
883
|
throw error;
|
|
884
884
|
}
|
|
885
885
|
}
|
|
886
|
+
async function waitForFaultInjection(point) {
|
|
887
|
+
if (process.env.NODE_ENV !== "test" || process.env.RIEBECKITE_SSG_FAULT_INJECTION !== "1" || process.env.RIEBECKITE_SSG_FAULT_POINT !== point) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const signalFile = process.env.RIEBECKITE_SSG_FAULT_SIGNAL_FILE;
|
|
891
|
+
if (!signalFile) return;
|
|
892
|
+
await fs.writeFile(signalFile, `${point}
|
|
893
|
+
`, "utf8");
|
|
894
|
+
await new Promise(() => {
|
|
895
|
+
});
|
|
896
|
+
}
|
|
886
897
|
function resolveContentBuildStatePath(config, contentDirectory) {
|
|
887
898
|
if (config?.buildDirectory) {
|
|
888
899
|
return path.join(config.buildDirectory, "build", "content-state.json");
|
|
@@ -898,7 +909,7 @@ function isContentBuildState(value) {
|
|
|
898
909
|
return isRecord2(value) && value.version === CONTENT_BUILD_STATE_VERSION && isContentBuildStateShape(value);
|
|
899
910
|
}
|
|
900
911
|
function isContentBuildStateShape(value) {
|
|
901
|
-
if (!isRecord2(value.entries) || !isRecord2(value.contentIndex) || value.outputs !== void 0 && !isOutputDescriptors(value.outputs)) {
|
|
912
|
+
if (!isRecord2(value.entries) || !isRecord2(value.contentIndex) || !isExternalDependencies(value.externalDependencies) || value.outputs !== void 0 && !isOutputDescriptors(value.outputs) || value.manifestHookCache !== void 0 && !isManifestHookCache(value.manifestHookCache) || value.nextPublishingChangeAt !== void 0 && (typeof value.nextPublishingChangeAt !== "string" || Number.isNaN(Date.parse(value.nextPublishingChangeAt)))) {
|
|
902
913
|
return false;
|
|
903
914
|
}
|
|
904
915
|
return Object.values(value.entries).every(
|
|
@@ -909,6 +920,11 @@ function isContentBuildStateShape(value) {
|
|
|
909
920
|
(entry) => typeof entry === "string"
|
|
910
921
|
);
|
|
911
922
|
}
|
|
923
|
+
function isManifestHookCache(value) {
|
|
924
|
+
return Array.isArray(value) && value.every(
|
|
925
|
+
(entry) => isRecord2(entry) && typeof entry.pluginName === "string" && typeof entry.cacheable === "boolean" && (entry.pipelineFingerprint === void 0 || typeof entry.pipelineFingerprint === "string") && typeof entry.inputFingerprint === "string" && typeof entry.outputFingerprint === "string" && isRecord2(entry.snapshot) && Array.isArray(entry.snapshot.entries)
|
|
926
|
+
);
|
|
927
|
+
}
|
|
912
928
|
function isOutputDescriptors(value) {
|
|
913
929
|
return Array.isArray(value) && value.every(
|
|
914
930
|
(output) => isRecord2(output) && (output.kind === "content" || output.kind === "redirect" || output.kind === "plugin-page" || output.kind === "generated") && isOutputPath(output.path) && typeof output.producer === "string" && Array.isArray(output.dependencies) && output.dependencies.every(isOutputDependency)
|
|
@@ -923,7 +939,17 @@ function isOutputPath(value) {
|
|
|
923
939
|
function isOutputDependency(value) {
|
|
924
940
|
if (!isRecord2(value) || typeof value.type !== "string") return false;
|
|
925
941
|
if (value.type === "global" || value.type === "unknown") return true;
|
|
926
|
-
return value.type === "content" && typeof value.slug === "string" || value.type === "tag" && typeof value.tag === "string" || value.type === "folder" && typeof value.folder === "string";
|
|
942
|
+
return value.type === "content" && typeof value.slug === "string" || value.type === "tag" && typeof value.tag === "string" || value.type === "folder" && typeof value.folder === "string" || value.type === "file" && typeof value.path === "string" || value.type === "external" && typeof value.id === "string" || value.type === "projection" && (value.projection === "navigation" || value.projection === "recent-posts" || value.projection === "taxonomy-tags" || value.projection === "taxonomy-folders");
|
|
943
|
+
}
|
|
944
|
+
function isExternalDependencies(value) {
|
|
945
|
+
return Array.isArray(value) && value.every(
|
|
946
|
+
(dependency) => isRecord2(dependency) && typeof dependency.id === "string" && dependency.id.length > 0 && typeof dependency.fingerprint === "string" && dependency.fingerprint.length > 0 && isExternalScope(dependency.scope) && (dependency.expiresAt === void 0 || typeof dependency.expiresAt === "string" && !Number.isNaN(Date.parse(dependency.expiresAt)))
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
function isExternalScope(value) {
|
|
950
|
+
if (!isRecord2(value) || typeof value.type !== "string") return false;
|
|
951
|
+
if (value.type === "content" || value.type === "all") return true;
|
|
952
|
+
return value.type === "output" && Array.isArray(value.paths) && value.paths.every((path6) => isOutputPath(path6));
|
|
927
953
|
}
|
|
928
954
|
function isRecord2(value) {
|
|
929
955
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1652,8 +1678,8 @@ function warn(logger, message, key, error) {
|
|
|
1652
1678
|
|
|
1653
1679
|
// src/content/content_persistent_cache.ts
|
|
1654
1680
|
var CONTENT_CACHE_NAMESPACE = "content";
|
|
1655
|
-
var CONTENT_CACHE_SCHEMA_VERSION =
|
|
1656
|
-
var CONTENT_CACHE_COMPATIBILITY_VERSION =
|
|
1681
|
+
var CONTENT_CACHE_SCHEMA_VERSION = 5;
|
|
1682
|
+
var CONTENT_CACHE_COMPATIBILITY_VERSION = 3;
|
|
1657
1683
|
function hash2(value) {
|
|
1658
1684
|
return createHash4("sha256").update(value).digest("hex");
|
|
1659
1685
|
}
|
|
@@ -1813,17 +1839,35 @@ function normalizeFrontmatterForCache(frontmatter) {
|
|
|
1813
1839
|
}
|
|
1814
1840
|
return normalized;
|
|
1815
1841
|
}
|
|
1816
|
-
function computePipelineFingerprint(config) {
|
|
1842
|
+
function computePipelineFingerprint(config, contentScopeFingerprint) {
|
|
1817
1843
|
const plugins = config.plugins;
|
|
1818
1844
|
const fingerprintData = {
|
|
1819
1845
|
compatibilityVersion: CONTENT_CACHE_COMPATIBILITY_VERSION,
|
|
1820
1846
|
plugins: plugins.map((p) => pluginFingerprint(p)),
|
|
1821
1847
|
content: {
|
|
1822
1848
|
filters: config.content.filters
|
|
1823
|
-
}
|
|
1849
|
+
},
|
|
1850
|
+
contentScopeFingerprint
|
|
1824
1851
|
};
|
|
1825
1852
|
return hash2(stableStringify(fingerprintData));
|
|
1826
1853
|
}
|
|
1854
|
+
function computeContentScopeFingerprint(locations) {
|
|
1855
|
+
return hash2(
|
|
1856
|
+
stableStringify(
|
|
1857
|
+
[...locations.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([slug, location]) => ({
|
|
1858
|
+
slug,
|
|
1859
|
+
permalink: location.permalink,
|
|
1860
|
+
...location.language ? { language: location.language } : {},
|
|
1861
|
+
...location.redirects ? {
|
|
1862
|
+
redirects: location.redirects.map((redirect) => ({
|
|
1863
|
+
...redirect
|
|
1864
|
+
}))
|
|
1865
|
+
} : {},
|
|
1866
|
+
...location.metadata ? { metadata: { ...location.metadata } } : {}
|
|
1867
|
+
}))
|
|
1868
|
+
)
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1827
1871
|
var pluginFingerprints = /* @__PURE__ */ new WeakMap();
|
|
1828
1872
|
function pluginFingerprint(plugin) {
|
|
1829
1873
|
const cached = pluginFingerprints.get(plugin);
|
|
@@ -1859,23 +1903,25 @@ function sanitizeForFingerprint(value) {
|
|
|
1859
1903
|
}
|
|
1860
1904
|
return value;
|
|
1861
1905
|
}
|
|
1862
|
-
function isPersistentlyCacheable(
|
|
1863
|
-
const
|
|
1864
|
-
(p) => p.name === "l10n" && p.enabled !== false
|
|
1865
|
-
);
|
|
1866
|
-
if (hasL10n) {
|
|
1867
|
-
return { cacheable: false, reason: "l10n plugin is enabled" };
|
|
1868
|
-
}
|
|
1906
|
+
function isPersistentlyCacheable(slug, config, source) {
|
|
1907
|
+
const context = source === void 0 ? void 0 : { slug, source };
|
|
1869
1908
|
const unsafePlugin = config.plugins.find((plugin) => {
|
|
1870
1909
|
if (plugin.enabled === false) return false;
|
|
1871
1910
|
if (!canAffectProcessedContent(plugin)) return false;
|
|
1872
|
-
|
|
1911
|
+
const contract = plugin.processedContentCache;
|
|
1912
|
+
if (!contract) return true;
|
|
1913
|
+
if (!context) return contract.dependencyMode === "unsafe";
|
|
1914
|
+
const applicability = getApplicability(contract.appliesToContent, context);
|
|
1915
|
+
if (applicability === "does-not-apply") return false;
|
|
1916
|
+
if (applicability === "unknown") return true;
|
|
1917
|
+
if (contract.dependencyMode === "unsafe") return true;
|
|
1918
|
+
return getContractDependencyFingerprint(contract, context) === void 0;
|
|
1873
1919
|
});
|
|
1874
1920
|
if (unsafePlugin) {
|
|
1875
1921
|
if (unsafePlugin.processedContentCache?.dependencyMode === "unsafe") {
|
|
1876
1922
|
return {
|
|
1877
1923
|
cacheable: false,
|
|
1878
|
-
reason: `plugin ${unsafePlugin.name} explicitly marks processed content cache as unsafe`
|
|
1924
|
+
reason: `plugin ${unsafePlugin.name} explicitly marks processed content cache as unsafe and cannot prove safety for this content`
|
|
1879
1925
|
};
|
|
1880
1926
|
}
|
|
1881
1927
|
return {
|
|
@@ -1885,6 +1931,68 @@ function isPersistentlyCacheable(_slug, config) {
|
|
|
1885
1931
|
}
|
|
1886
1932
|
return { cacheable: true };
|
|
1887
1933
|
}
|
|
1934
|
+
function computeContentCacheFingerprint(slug, source, config) {
|
|
1935
|
+
const context = { slug, source };
|
|
1936
|
+
const plugins = config.plugins.filter(
|
|
1937
|
+
(plugin) => plugin.enabled !== false && canAffectProcessedContent(plugin)
|
|
1938
|
+
).map((plugin) => {
|
|
1939
|
+
const contract = plugin.processedContentCache;
|
|
1940
|
+
if (!contract) return { name: plugin.name, applicability: "unknown" };
|
|
1941
|
+
const applicability = getApplicability(
|
|
1942
|
+
contract.appliesToContent,
|
|
1943
|
+
context
|
|
1944
|
+
);
|
|
1945
|
+
return {
|
|
1946
|
+
name: plugin.name,
|
|
1947
|
+
applicability,
|
|
1948
|
+
dependencyFingerprint: applicability === "applies" ? getContractDependencyFingerprint(contract, context) : void 0
|
|
1949
|
+
};
|
|
1950
|
+
});
|
|
1951
|
+
return hash2(stableStringify(plugins));
|
|
1952
|
+
}
|
|
1953
|
+
function getApplicability(appliesToContent, context) {
|
|
1954
|
+
if (!context) return "unknown";
|
|
1955
|
+
if (!appliesToContent) return "applies";
|
|
1956
|
+
try {
|
|
1957
|
+
const applicability = appliesToContent(context);
|
|
1958
|
+
return applicability === "applies" || applicability === "does-not-apply" || applicability === "unknown" ? applicability : "unknown";
|
|
1959
|
+
} catch {
|
|
1960
|
+
return "unknown";
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function getContractDependencyFingerprint(contract, context) {
|
|
1964
|
+
if (!context) return void 0;
|
|
1965
|
+
try {
|
|
1966
|
+
const content = contract.contentDependencyFingerprint?.(context);
|
|
1967
|
+
if (contract.contentDependencyFingerprint && content === void 0)
|
|
1968
|
+
return void 0;
|
|
1969
|
+
const environment = contract.environmentFingerprint?.();
|
|
1970
|
+
if (contract.environmentFingerprint && environment === void 0)
|
|
1971
|
+
return void 0;
|
|
1972
|
+
return hash2(
|
|
1973
|
+
stableStringify({
|
|
1974
|
+
content: content ?? null,
|
|
1975
|
+
environment: environment ?? null
|
|
1976
|
+
})
|
|
1977
|
+
);
|
|
1978
|
+
} catch {
|
|
1979
|
+
return void 0;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
function hasGlobalContentDependency(config) {
|
|
1983
|
+
return config.plugins.some(
|
|
1984
|
+
(plugin) => plugin.enabled !== false && canAffectProcessedContent(plugin) && plugin.processedContentCache?.dependencyMode === "global"
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
function requiresFullContentRegeneration(config) {
|
|
1988
|
+
return config.plugins.some((plugin) => {
|
|
1989
|
+
if (plugin.enabled === false || !canAffectProcessedContent(plugin)) {
|
|
1990
|
+
return false;
|
|
1991
|
+
}
|
|
1992
|
+
const contract = plugin.processedContentCache;
|
|
1993
|
+
return !contract || contract.dependencyMode === "unsafe" && !contract.appliesToContent;
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1888
1996
|
function canAffectProcessedContent(plugin) {
|
|
1889
1997
|
return Boolean(
|
|
1890
1998
|
plugin.remarkPlugins?.length || plugin.rehypePlugins?.length || plugin.extendMarkdownPipeline || plugin.extendHtmlPipeline || plugin.renderers?.length || plugin.onContentLoaded || plugin.onPostParsed || plugin.onPostProcessed
|
|
@@ -2197,7 +2305,8 @@ var Pipeline = class {
|
|
|
2197
2305
|
tracer: this.options.observability?.tracer
|
|
2198
2306
|
});
|
|
2199
2307
|
this.pipelineFingerprint = computePipelineFingerprint(
|
|
2200
|
-
this.options.config
|
|
2308
|
+
this.options.config,
|
|
2309
|
+
this.options.contentScopeFingerprint
|
|
2201
2310
|
);
|
|
2202
2311
|
}
|
|
2203
2312
|
}
|
|
@@ -2224,9 +2333,11 @@ var Pipeline = class {
|
|
|
2224
2333
|
const effectiveContentSource = dependencyTracker ? dependencyTracker.contentSource : this.options.contentSource;
|
|
2225
2334
|
const sourceSlug = context.sourceSlug;
|
|
2226
2335
|
if (embedDepth === 0 && this.persistentCache && this.pipelineFingerprint && sourceSlug && this.options.contentSource && this.options.config) {
|
|
2336
|
+
const persistentCache = this.persistentCache;
|
|
2227
2337
|
const cacheable = isPersistentlyCacheable(
|
|
2228
2338
|
sourceSlug,
|
|
2229
|
-
this.options.config
|
|
2339
|
+
this.options.config,
|
|
2340
|
+
markDownContent
|
|
2230
2341
|
);
|
|
2231
2342
|
if (cacheable.cacheable) {
|
|
2232
2343
|
const frontmatter = extractFrontmatter(markDownContent);
|
|
@@ -2234,12 +2345,21 @@ var Pipeline = class {
|
|
|
2234
2345
|
slug: sourceSlug,
|
|
2235
2346
|
source: markDownContent,
|
|
2236
2347
|
frontmatter,
|
|
2237
|
-
pipelineFingerprint: this.
|
|
2348
|
+
pipelineFingerprint: this.contentPipelineFingerprint(
|
|
2349
|
+
sourceSlug,
|
|
2350
|
+
markDownContent
|
|
2351
|
+
)
|
|
2238
2352
|
});
|
|
2239
|
-
const lookup = await this.
|
|
2353
|
+
const lookup = await (this.options.observability ?? noopObservability).tracer.span(
|
|
2354
|
+
"persistentContentCache.lookup",
|
|
2355
|
+
{},
|
|
2356
|
+
() => persistentCache.lookup(cacheKey)
|
|
2357
|
+
);
|
|
2240
2358
|
if (lookup.status === "hit") {
|
|
2241
|
-
const invalidDependency = await this.
|
|
2242
|
-
|
|
2359
|
+
const invalidDependency = await (this.options.observability ?? noopObservability).tracer.span(
|
|
2360
|
+
"persistentContentCache.validation",
|
|
2361
|
+
{ dependencyCount: lookup.entry.dependencies.length },
|
|
2362
|
+
() => this.findInvalidDependency(lookup.entry.dependencies)
|
|
2243
2363
|
);
|
|
2244
2364
|
if (!invalidDependency) {
|
|
2245
2365
|
this.reportContentDependencies(
|
|
@@ -2350,7 +2470,11 @@ var Pipeline = class {
|
|
|
2350
2470
|
this.use(processor, rehypeKatex, { output: "mathml", strict: false });
|
|
2351
2471
|
this.use(processor, rehypeFormat);
|
|
2352
2472
|
this.use(processor, rehypeStringify, { allowDangerousHtml: true });
|
|
2353
|
-
const file = await
|
|
2473
|
+
const file = await (this.options.observability ?? noopObservability).tracer.span(
|
|
2474
|
+
"content.markdown",
|
|
2475
|
+
{ embedded: embedDepth > 0 },
|
|
2476
|
+
async () => await processor.process(markDownContent.trim())
|
|
2477
|
+
);
|
|
2354
2478
|
const result = {
|
|
2355
2479
|
frontmatter: file.data.matter || {},
|
|
2356
2480
|
html: String(file.value)
|
|
@@ -2367,7 +2491,8 @@ var Pipeline = class {
|
|
|
2367
2491
|
if (embedDepth === 0 && this.persistentCache && this.pipelineFingerprint && sourceSlug && dependencyTracker && this.options.config) {
|
|
2368
2492
|
const cacheable = isPersistentlyCacheable(
|
|
2369
2493
|
sourceSlug,
|
|
2370
|
-
this.options.config
|
|
2494
|
+
this.options.config,
|
|
2495
|
+
markDownContent
|
|
2371
2496
|
);
|
|
2372
2497
|
if (cacheable.cacheable) {
|
|
2373
2498
|
const frontmatter = extractFrontmatter(markDownContent);
|
|
@@ -2375,7 +2500,10 @@ var Pipeline = class {
|
|
|
2375
2500
|
slug: sourceSlug,
|
|
2376
2501
|
source: markDownContent,
|
|
2377
2502
|
frontmatter,
|
|
2378
|
-
pipelineFingerprint: this.
|
|
2503
|
+
pipelineFingerprint: this.contentPipelineFingerprint(
|
|
2504
|
+
sourceSlug,
|
|
2505
|
+
markDownContent
|
|
2506
|
+
)
|
|
2379
2507
|
});
|
|
2380
2508
|
const dependencies = dependencyTracker.dependencies();
|
|
2381
2509
|
const entry = {
|
|
@@ -2392,6 +2520,11 @@ var Pipeline = class {
|
|
|
2392
2520
|
reportContentDependencies(slug, dependencies) {
|
|
2393
2521
|
this.options.onContentDependencies?.(slug, dependencies);
|
|
2394
2522
|
}
|
|
2523
|
+
contentPipelineFingerprint(slug, source) {
|
|
2524
|
+
const config = this.options.config;
|
|
2525
|
+
if (!config || !this.pipelineFingerprint) return "";
|
|
2526
|
+
return `${this.pipelineFingerprint}:${computeContentCacheFingerprint(slug, source, config)}`;
|
|
2527
|
+
}
|
|
2395
2528
|
async findInvalidDependency(dependencies) {
|
|
2396
2529
|
const contentSource = this.options.contentSource;
|
|
2397
2530
|
if (!contentSource) return null;
|
|
@@ -2677,18 +2810,141 @@ function escapeRegExp(value) {
|
|
|
2677
2810
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2678
2811
|
}
|
|
2679
2812
|
|
|
2813
|
+
// src/content/manifest_hook_cache.ts
|
|
2814
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
2815
|
+
function fingerprintManifestHookInput(manifest) {
|
|
2816
|
+
return fingerprintManifest(manifestFingerprintValue(manifest));
|
|
2817
|
+
}
|
|
2818
|
+
function createManifestHookCacheEntry(pluginName, cacheable, pipelineFingerprint, inputFingerprint, manifest) {
|
|
2819
|
+
const snapshot = {
|
|
2820
|
+
entries: JSON.parse(
|
|
2821
|
+
JSON.stringify(manifest.entries)
|
|
2822
|
+
)
|
|
2823
|
+
};
|
|
2824
|
+
return {
|
|
2825
|
+
pluginName,
|
|
2826
|
+
cacheable,
|
|
2827
|
+
pipelineFingerprint,
|
|
2828
|
+
inputFingerprint,
|
|
2829
|
+
outputFingerprint: fingerprintManifestHookSnapshot(snapshot, manifest),
|
|
2830
|
+
snapshot
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
function fingerprintManifestHookSideEffects(manifest) {
|
|
2834
|
+
const value = {
|
|
2835
|
+
...manifest,
|
|
2836
|
+
entries: []
|
|
2837
|
+
};
|
|
2838
|
+
return fingerprintManifest(manifestFingerprintValue(value));
|
|
2839
|
+
}
|
|
2840
|
+
function fingerprintManifestHookSnapshot(snapshot, manifest) {
|
|
2841
|
+
return fingerprintManifest(
|
|
2842
|
+
manifestFingerprintValue({
|
|
2843
|
+
...manifest,
|
|
2844
|
+
entries: [...snapshot.entries]
|
|
2845
|
+
})
|
|
2846
|
+
);
|
|
2847
|
+
}
|
|
2848
|
+
function applyManifestHookSnapshot(manifest, snapshot) {
|
|
2849
|
+
try {
|
|
2850
|
+
const entries = structuredClone(snapshot.entries);
|
|
2851
|
+
if (!entries.every(isManifestEntry)) return false;
|
|
2852
|
+
const entriesBySlug = new Map(entries.map((entry) => [entry.slug, entry]));
|
|
2853
|
+
if (entriesBySlug.size !== entries.length) return false;
|
|
2854
|
+
manifest.entries = entries;
|
|
2855
|
+
manifest.publicEntries = manifest.publicEntries.map((entry) => entriesBySlug.get(entry.slug)).filter((entry) => entry !== void 0);
|
|
2856
|
+
manifest.discoverableEntries = manifest.discoverableEntries.map((entry) => entriesBySlug.get(entry.slug)).filter((entry) => entry !== void 0);
|
|
2857
|
+
manifest.bySlug = new Map(entries.map((entry) => [entry.slug, entry]));
|
|
2858
|
+
manifest.byContentId = new Map(
|
|
2859
|
+
entries.filter((entry) => entry.contentId !== void 0).map((entry) => [entry.contentId, entry])
|
|
2860
|
+
);
|
|
2861
|
+
manifest.byPermalink = new Map(
|
|
2862
|
+
entries.map((entry) => [entry.permalink, entry])
|
|
2863
|
+
);
|
|
2864
|
+
manifest.byRoutablePermalink = new Map(
|
|
2865
|
+
manifest.publicEntries.map((entry) => [entry.permalink, entry])
|
|
2866
|
+
);
|
|
2867
|
+
manifest.byAlias = /* @__PURE__ */ new Map();
|
|
2868
|
+
manifest.byTag = /* @__PURE__ */ new Map();
|
|
2869
|
+
manifest.byAsset = /* @__PURE__ */ new Map();
|
|
2870
|
+
manifest.outgoingLinks = /* @__PURE__ */ new Map();
|
|
2871
|
+
manifest.incomingLinks = /* @__PURE__ */ new Map();
|
|
2872
|
+
for (const entry of entries) {
|
|
2873
|
+
manifest.outgoingLinks.set(entry.slug, entry.links);
|
|
2874
|
+
for (const alias of entry.aliases ?? []) {
|
|
2875
|
+
const aliases = manifest.byAlias.get(alias.toLowerCase()) ?? [];
|
|
2876
|
+
aliases.push(entry);
|
|
2877
|
+
manifest.byAlias.set(alias.toLowerCase(), aliases);
|
|
2878
|
+
}
|
|
2879
|
+
for (const tag of entry.tags) {
|
|
2880
|
+
const tagged = manifest.byTag.get(tag) ?? [];
|
|
2881
|
+
tagged.push(entry);
|
|
2882
|
+
manifest.byTag.set(tag, tagged);
|
|
2883
|
+
}
|
|
2884
|
+
for (const asset of entry.assets) {
|
|
2885
|
+
const assets = manifest.byAsset.get(asset.path) ?? [];
|
|
2886
|
+
assets.push(entry);
|
|
2887
|
+
manifest.byAsset.set(asset.path, assets);
|
|
2888
|
+
}
|
|
2889
|
+
for (const link of entry.links) {
|
|
2890
|
+
if (link.kind !== "note" || link.slug === null || link.slug === entry.slug)
|
|
2891
|
+
continue;
|
|
2892
|
+
const incoming = manifest.incomingLinks.get(link.slug) ?? [];
|
|
2893
|
+
if (!incoming.includes(entry.slug)) incoming.push(entry.slug);
|
|
2894
|
+
manifest.incomingLinks.set(link.slug, incoming);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
for (const entry of entries)
|
|
2898
|
+
entry.backlinks = manifest.incomingLinks.get(entry.slug) ?? [];
|
|
2899
|
+
return true;
|
|
2900
|
+
} catch {
|
|
2901
|
+
return false;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
function manifestFingerprintValue(manifest) {
|
|
2905
|
+
return {
|
|
2906
|
+
entries: manifest.entries,
|
|
2907
|
+
publicEntries: manifest.publicEntries.map((entry) => entry.slug),
|
|
2908
|
+
discoverableEntries: manifest.discoverableEntries.map(
|
|
2909
|
+
(entry) => entry.slug
|
|
2910
|
+
),
|
|
2911
|
+
redirects: [...manifest.redirects.entries()].sort(
|
|
2912
|
+
([left], [right]) => left.localeCompare(right)
|
|
2913
|
+
),
|
|
2914
|
+
publicRedirects: [...manifest.publicRedirects.entries()].sort(
|
|
2915
|
+
([left], [right]) => left.localeCompare(right)
|
|
2916
|
+
),
|
|
2917
|
+
assets: manifest.assets,
|
|
2918
|
+
clientEntries: manifest.clientEntries,
|
|
2919
|
+
diagnostics: manifest.diagnostics,
|
|
2920
|
+
generatedOutputs: manifest.generatedOutputs,
|
|
2921
|
+
folderLocations: [...manifest.folderLocations.entries()].sort(
|
|
2922
|
+
([left], [right]) => left.localeCompare(right)
|
|
2923
|
+
),
|
|
2924
|
+
pageRoutes: manifest.pageRoutes,
|
|
2925
|
+
pagePaths: manifest.pagePaths
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
function fingerprintManifest(value) {
|
|
2929
|
+
const normalized = JSON.parse(JSON.stringify(value));
|
|
2930
|
+
return createHash6("sha256").update(stableStringify(normalized)).digest("hex");
|
|
2931
|
+
}
|
|
2932
|
+
function isManifestEntry(value) {
|
|
2933
|
+
return typeof value === "object" && value !== null && typeof value.slug === "string" && typeof value.html === "string";
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2680
2936
|
// src/types/plugin_asset.ts
|
|
2681
|
-
function createStyleAsset(pluginName) {
|
|
2937
|
+
function createStyleAsset(pluginName, moduleSpecifier) {
|
|
2682
2938
|
return {
|
|
2683
2939
|
pluginName,
|
|
2684
2940
|
kind: "style",
|
|
2685
|
-
moduleSpecifier
|
|
2941
|
+
moduleSpecifier
|
|
2686
2942
|
};
|
|
2687
2943
|
}
|
|
2688
|
-
function createClientEntry(pluginName, exportName, publicConfig) {
|
|
2944
|
+
function createClientEntry(pluginName, moduleSpecifier, exportName, publicConfig) {
|
|
2689
2945
|
return {
|
|
2690
2946
|
pluginName,
|
|
2691
|
-
moduleSpecifier
|
|
2947
|
+
moduleSpecifier,
|
|
2692
2948
|
...exportName === void 0 ? {} : { exportName },
|
|
2693
2949
|
...publicConfig === void 0 ? {} : { publicConfig }
|
|
2694
2950
|
};
|
|
@@ -2760,6 +3016,9 @@ var GeneratedOutputRegistry = class {
|
|
|
2760
3016
|
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
2761
3017
|
);
|
|
2762
3018
|
}
|
|
3019
|
+
count() {
|
|
3020
|
+
return this.outputs.length;
|
|
3021
|
+
}
|
|
2763
3022
|
};
|
|
2764
3023
|
|
|
2765
3024
|
// src/plugin/plugin_lifecycle.ts
|
|
@@ -2841,12 +3100,46 @@ var PluginRuntime = class {
|
|
|
2841
3100
|
isBuildTime = false;
|
|
2842
3101
|
resolvedPlugins = null;
|
|
2843
3102
|
pageDeclarations = /* @__PURE__ */ new WeakMap();
|
|
3103
|
+
externalDependencies = /* @__PURE__ */ new Map();
|
|
3104
|
+
externalDependencyFailures = [];
|
|
3105
|
+
externalDependenciesResolved = false;
|
|
3106
|
+
manifestHooksExecuted = 0;
|
|
3107
|
+
manifestHookDurationMs = 0;
|
|
3108
|
+
manifestHooksReused = 0;
|
|
3109
|
+
manifestHookCache = [];
|
|
3110
|
+
manifestHookMetricsByPlugin = /* @__PURE__ */ new Map();
|
|
2844
3111
|
getDiagnostics() {
|
|
2845
3112
|
return this.diagnostics;
|
|
2846
3113
|
}
|
|
2847
3114
|
getBuildStage() {
|
|
2848
3115
|
return this.buildStage;
|
|
2849
3116
|
}
|
|
3117
|
+
getManifestHookMetrics() {
|
|
3118
|
+
return {
|
|
3119
|
+
manifestHooksExecuted: this.manifestHooksExecuted,
|
|
3120
|
+
manifestHooksReused: this.manifestHooksReused,
|
|
3121
|
+
manifestHookCacheHit: this.manifestHooksReused,
|
|
3122
|
+
manifestHookCacheMiss: this.manifestHooksExecuted,
|
|
3123
|
+
manifestHookDurationMs: this.manifestHookDurationMs,
|
|
3124
|
+
byPlugin: Object.fromEntries(this.manifestHookMetricsByPlugin)
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
getNoChangeFastPathBlockReason(previousCache, pipelineFingerprint) {
|
|
3128
|
+
let cacheIndex = 0;
|
|
3129
|
+
for (const plugin of this.plugins()) {
|
|
3130
|
+
if (plugin.buildEnd) return "plugin-build-end";
|
|
3131
|
+
if (plugin.planGeneratedOutputs) return "plugin-generated-output-plan";
|
|
3132
|
+
if (!plugin.onManifestCreated) continue;
|
|
3133
|
+
const cached = previousCache[cacheIndex];
|
|
3134
|
+
cacheIndex += 1;
|
|
3135
|
+
if (cached?.pluginName !== plugin.name || !cached.cacheable || cached.pipelineFingerprint !== pipelineFingerprint)
|
|
3136
|
+
return "manifest-hook-cache-miss";
|
|
3137
|
+
}
|
|
3138
|
+
return void 0;
|
|
3139
|
+
}
|
|
3140
|
+
getManifestHookCache() {
|
|
3141
|
+
return this.manifestHookCache;
|
|
3142
|
+
}
|
|
2850
3143
|
/**
|
|
2851
3144
|
* Starts the plugin build lifecycle exactly once, regardless of which public
|
|
2852
3145
|
* entry point (content processing or location resolution) reaches it first.
|
|
@@ -2865,6 +3158,43 @@ var PluginRuntime = class {
|
|
|
2865
3158
|
markdown
|
|
2866
3159
|
});
|
|
2867
3160
|
}
|
|
3161
|
+
async resolveExternalDependencies(contentIndex) {
|
|
3162
|
+
if (this.externalDependenciesResolved) {
|
|
3163
|
+
return {
|
|
3164
|
+
snapshots: [...this.externalDependencies.values()],
|
|
3165
|
+
failures: this.externalDependencyFailures
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
await this.startBuild(contentIndex);
|
|
3169
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
3170
|
+
const failures = [];
|
|
3171
|
+
for (const plugin of this.plugins()) {
|
|
3172
|
+
const resolve = plugin.resolveExternalDependencies;
|
|
3173
|
+
if (!resolve) continue;
|
|
3174
|
+
const results = await resolve({
|
|
3175
|
+
config: this.pipelineOptions.config,
|
|
3176
|
+
contentIndex,
|
|
3177
|
+
cache: this.cacheFor(plugin)
|
|
3178
|
+
});
|
|
3179
|
+
for (const result of results) {
|
|
3180
|
+
if (result.kind === "unavailable") {
|
|
3181
|
+
failures.push(result.failure);
|
|
3182
|
+
continue;
|
|
3183
|
+
}
|
|
3184
|
+
const snapshot = result.snapshot;
|
|
3185
|
+
if (snapshots.has(snapshot.id)) {
|
|
3186
|
+
throw new Error(
|
|
3187
|
+
`External dependency ID "${snapshot.id}" is declared more than once.`
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
snapshots.set(snapshot.id, snapshot);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
this.externalDependencies = snapshots;
|
|
3194
|
+
this.externalDependencyFailures = failures;
|
|
3195
|
+
this.externalDependenciesResolved = true;
|
|
3196
|
+
return { snapshots: [...snapshots.values()], failures };
|
|
3197
|
+
}
|
|
2868
3198
|
async resolveContentLocations(entries, contentIndex) {
|
|
2869
3199
|
const context = { ...this.createContext(contentIndex), entries };
|
|
2870
3200
|
const locations = [];
|
|
@@ -2911,15 +3241,86 @@ var PluginRuntime = class {
|
|
|
2911
3241
|
}
|
|
2912
3242
|
);
|
|
2913
3243
|
}
|
|
2914
|
-
async runManifestCreated(manifest, contentIndex) {
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3244
|
+
async runManifestCreated(manifest, contentIndex, previousCache = [], pipelineFingerprint) {
|
|
3245
|
+
const context = { ...this.createContext(contentIndex), manifest };
|
|
3246
|
+
let cacheIndex = 0;
|
|
3247
|
+
for (const plugin of this.plugins()) {
|
|
3248
|
+
const hook = plugin.onManifestCreated;
|
|
3249
|
+
if (!hook) continue;
|
|
3250
|
+
const inputFingerprint = fingerprintManifestHookInput(manifest);
|
|
3251
|
+
const cached = previousCache[cacheIndex];
|
|
3252
|
+
cacheIndex += 1;
|
|
3253
|
+
const reason = manifestHookCacheMissReason(
|
|
3254
|
+
cached,
|
|
3255
|
+
plugin.name,
|
|
3256
|
+
pipelineFingerprint,
|
|
3257
|
+
inputFingerprint
|
|
3258
|
+
);
|
|
3259
|
+
const canRestore = cached?.pluginName === plugin.name && cached.cacheable && cached.pipelineFingerprint === pipelineFingerprint && cached.inputFingerprint === inputFingerprint;
|
|
3260
|
+
const manifestBeforeRestore = canRestore ? {
|
|
3261
|
+
entries: structuredClone(manifest.entries)
|
|
3262
|
+
} : void 0;
|
|
3263
|
+
const restored = canRestore && applyManifestHookSnapshot(manifest, cached.snapshot);
|
|
3264
|
+
const cacheHit = restored && fingerprintManifestHookSnapshot(cached.snapshot, manifest) === cached.outputFingerprint;
|
|
3265
|
+
if (cacheHit) {
|
|
3266
|
+
this.manifestHooksReused += 1;
|
|
3267
|
+
this.manifestHookCache.push(cached);
|
|
3268
|
+
this.recordManifestHookMetric(plugin.name, "reused", "cache-hit", 0);
|
|
3269
|
+
continue;
|
|
2921
3270
|
}
|
|
2922
|
-
|
|
3271
|
+
if (restored && manifestBeforeRestore)
|
|
3272
|
+
applyManifestHookSnapshot(manifest, manifestBeforeRestore);
|
|
3273
|
+
const sideEffectsBefore = fingerprintManifestHookSideEffects(manifest);
|
|
3274
|
+
const outputCountBefore = this.generatedOutputs.count();
|
|
3275
|
+
const diagnosticCountBefore = this.diagnostics.length;
|
|
3276
|
+
const manifestDiagnosticCountBefore = manifest.diagnostics.length;
|
|
3277
|
+
const started = performance.now();
|
|
3278
|
+
this.manifestHooksExecuted += 1;
|
|
3279
|
+
try {
|
|
3280
|
+
await runPluginHook(
|
|
3281
|
+
plugin,
|
|
3282
|
+
"onManifestCreated",
|
|
3283
|
+
this.createPluginContext(plugin, context),
|
|
3284
|
+
hook
|
|
3285
|
+
);
|
|
3286
|
+
} finally {
|
|
3287
|
+
const durationMs = performance.now() - started;
|
|
3288
|
+
this.manifestHookDurationMs += durationMs;
|
|
3289
|
+
this.recordManifestHookMetric(
|
|
3290
|
+
plugin.name,
|
|
3291
|
+
"executed",
|
|
3292
|
+
reason,
|
|
3293
|
+
durationMs
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
3296
|
+
this.manifestHookCache.push(
|
|
3297
|
+
createManifestHookCacheEntry(
|
|
3298
|
+
plugin.name,
|
|
3299
|
+
plugin.resolveExternalDependencies === void 0 && sideEffectsBefore === fingerprintManifestHookSideEffects(manifest) && outputCountBefore === this.generatedOutputs.count() && diagnosticCountBefore === this.diagnostics.length && manifestDiagnosticCountBefore === manifest.diagnostics.length,
|
|
3300
|
+
pipelineFingerprint,
|
|
3301
|
+
inputFingerprint,
|
|
3302
|
+
manifest
|
|
3303
|
+
)
|
|
3304
|
+
);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
recordManifestHookMetric(pluginName, outcome, reason, durationMs) {
|
|
3308
|
+
const previous = this.manifestHookMetricsByPlugin.get(pluginName) ?? {
|
|
3309
|
+
executed: 0,
|
|
3310
|
+
reused: 0,
|
|
3311
|
+
cacheHit: 0,
|
|
3312
|
+
cacheMiss: 0,
|
|
3313
|
+
durationMs: 0,
|
|
3314
|
+
reason
|
|
3315
|
+
};
|
|
3316
|
+
this.manifestHookMetricsByPlugin.set(pluginName, {
|
|
3317
|
+
executed: previous.executed + (outcome === "executed" ? 1 : 0),
|
|
3318
|
+
reused: previous.reused + (outcome === "reused" ? 1 : 0),
|
|
3319
|
+
cacheHit: previous.cacheHit + (outcome === "reused" ? 1 : 0),
|
|
3320
|
+
cacheMiss: previous.cacheMiss + (outcome === "executed" ? 1 : 0),
|
|
3321
|
+
durationMs: previous.durationMs + durationMs,
|
|
3322
|
+
reason
|
|
3323
|
+
});
|
|
2923
3324
|
}
|
|
2924
3325
|
async runBuildEnd(manifest, contentIndex) {
|
|
2925
3326
|
const context = {
|
|
@@ -2931,6 +3332,30 @@ var PluginRuntime = class {
|
|
|
2931
3332
|
(plugin) => this.createPluginContext(plugin, context)
|
|
2932
3333
|
);
|
|
2933
3334
|
}
|
|
3335
|
+
async runGeneratedOutputPlans(manifest) {
|
|
3336
|
+
for (const plugin of this.plugins()) {
|
|
3337
|
+
const hook = plugin.planGeneratedOutputs;
|
|
3338
|
+
if (!hook) continue;
|
|
3339
|
+
const outputs = await hook(
|
|
3340
|
+
freezeGeneratedOutputPlanContext({
|
|
3341
|
+
manifest: {
|
|
3342
|
+
discoverableEntries: manifest.discoverableEntries,
|
|
3343
|
+
routableEntries: manifest.publicEntries
|
|
3344
|
+
},
|
|
3345
|
+
site: this.pipelineOptions.config?.site
|
|
3346
|
+
})
|
|
3347
|
+
);
|
|
3348
|
+
const sink = this.generatedOutputs.sinkFor(plugin.name);
|
|
3349
|
+
for (const output of outputs) {
|
|
3350
|
+
if (output.dependencies.length === 0) {
|
|
3351
|
+
throw new Error(
|
|
3352
|
+
`Generated output plan from "${plugin.name}" must declare at least one dependency.`
|
|
3353
|
+
);
|
|
3354
|
+
}
|
|
3355
|
+
sink.emit(output);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
2934
3359
|
enableBuildTime() {
|
|
2935
3360
|
this.isBuildTime = true;
|
|
2936
3361
|
}
|
|
@@ -3088,7 +3513,9 @@ var PluginRuntime = class {
|
|
|
3088
3513
|
return declarations;
|
|
3089
3514
|
}
|
|
3090
3515
|
getContentOutputDependencies() {
|
|
3091
|
-
return this.plugins().flatMap(
|
|
3516
|
+
return this.plugins().flatMap(
|
|
3517
|
+
(plugin) => plugin.contentOutputDependencies ?? []
|
|
3518
|
+
);
|
|
3092
3519
|
}
|
|
3093
3520
|
async collectDiagnostics(contentIndex) {
|
|
3094
3521
|
const tracer = this.observability().tracer;
|
|
@@ -3151,7 +3578,8 @@ var PluginRuntime = class {
|
|
|
3151
3578
|
config: this.pipelineOptions.config,
|
|
3152
3579
|
contentIndex,
|
|
3153
3580
|
contentSource: this.pipelineOptions.contentSource,
|
|
3154
|
-
diagnostics: this.diagnostics
|
|
3581
|
+
diagnostics: this.diagnostics,
|
|
3582
|
+
externalDependencies: this.externalDependencies
|
|
3155
3583
|
};
|
|
3156
3584
|
}
|
|
3157
3585
|
createPluginContext(plugin, context) {
|
|
@@ -3202,6 +3630,15 @@ var PluginRuntime = class {
|
|
|
3202
3630
|
return this.resolvedPlugins;
|
|
3203
3631
|
}
|
|
3204
3632
|
};
|
|
3633
|
+
function freezeGeneratedOutputPlanContext(context) {
|
|
3634
|
+
return deepFreeze(structuredClone(context));
|
|
3635
|
+
}
|
|
3636
|
+
function deepFreeze(value) {
|
|
3637
|
+
if (value === null || typeof value !== "object") return value;
|
|
3638
|
+
Object.freeze(value);
|
|
3639
|
+
for (const property of Object.values(value)) deepFreeze(property);
|
|
3640
|
+
return value;
|
|
3641
|
+
}
|
|
3205
3642
|
function collectPageTypeOwners(plugins) {
|
|
3206
3643
|
const pageTypes = /* @__PURE__ */ new Map();
|
|
3207
3644
|
for (const plugin of plugins) {
|
|
@@ -3264,6 +3701,16 @@ function normalizeDirectoryPagePath(pathname) {
|
|
|
3264
3701
|
const path6 = normalizePagePath(pathname);
|
|
3265
3702
|
return path6 === "/" ? path6 : `${path6}/`;
|
|
3266
3703
|
}
|
|
3704
|
+
function manifestHookCacheMissReason(cached, pluginName, pipelineFingerprint, inputFingerprint) {
|
|
3705
|
+
if (!cached) return "cache-missing";
|
|
3706
|
+
if (cached.pluginName !== pluginName) return "plugin-order-changed";
|
|
3707
|
+
if (!cached.cacheable) return "uncacheable";
|
|
3708
|
+
if (cached.pipelineFingerprint !== pipelineFingerprint)
|
|
3709
|
+
return "pipeline-fingerprint-changed";
|
|
3710
|
+
if (cached.inputFingerprint !== inputFingerprint)
|
|
3711
|
+
return "manifest-input-changed";
|
|
3712
|
+
return "snapshot-invalid";
|
|
3713
|
+
}
|
|
3267
3714
|
function normalizePagePath(pathname) {
|
|
3268
3715
|
const path6 = `/${pathname.split("/").filter(Boolean).join("/")}`;
|
|
3269
3716
|
return path6 === "/" ? path6 : path6.replace(/\/$/, "");
|
|
@@ -3571,34 +4018,46 @@ var ContentBuildCoordinator = class {
|
|
|
3571
4018
|
this.dependencies = dependencies;
|
|
3572
4019
|
}
|
|
3573
4020
|
dependencies;
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
this.
|
|
4021
|
+
plan = null;
|
|
4022
|
+
getPlan(incremental, pipelineFingerprint, fullContentRegenerationRequired = false, externalDependencies = [], externalDependencyFailures = [], publishingBuildTime = /* @__PURE__ */ new Date()) {
|
|
4023
|
+
if (this.plan) return this.plan;
|
|
4024
|
+
const plan = this.prepare(
|
|
3577
4025
|
incremental,
|
|
3578
4026
|
pipelineFingerprint,
|
|
3579
|
-
fullContentRegenerationRequired
|
|
4027
|
+
fullContentRegenerationRequired,
|
|
4028
|
+
externalDependencies,
|
|
4029
|
+
externalDependencyFailures,
|
|
4030
|
+
publishingBuildTime
|
|
3580
4031
|
);
|
|
3581
|
-
|
|
4032
|
+
this.plan = plan;
|
|
4033
|
+
void plan.catch(() => {
|
|
4034
|
+
if (this.plan === plan) this.plan = null;
|
|
4035
|
+
});
|
|
4036
|
+
return plan;
|
|
3582
4037
|
}
|
|
3583
|
-
async commit(
|
|
4038
|
+
async commit(plan, manifest, pipelineFingerprint, outputs = [], manifestEntries = manifest.entries, trackedDependencies = /* @__PURE__ */ new Map(), externalDependencies = [], processedContentCacheFingerprints = /* @__PURE__ */ new Map(), manifestHookCache = [], publishingBuildTime) {
|
|
3584
4039
|
const entriesBySlug = new Map(
|
|
3585
4040
|
manifest.entries.map((entry) => [entry.slug, entry])
|
|
3586
4041
|
);
|
|
4042
|
+
const nextChangeAt = nextPublishingChangeAt(manifest, publishingBuildTime);
|
|
3587
4043
|
const state2 = {
|
|
3588
4044
|
version: CONTENT_BUILD_STATE_VERSION,
|
|
3589
4045
|
entries: Object.fromEntries(
|
|
3590
|
-
|
|
4046
|
+
plan.currentEntries.map(({ entry, fingerprint }) => {
|
|
3591
4047
|
const manifestEntry = entriesBySlug.get(toSlug(entry.path));
|
|
3592
4048
|
return [
|
|
3593
4049
|
entry.path,
|
|
3594
4050
|
{
|
|
3595
4051
|
fingerprint,
|
|
3596
|
-
aliases:
|
|
4052
|
+
aliases: plan.currentContentAliases.get(entry.path) ?? [],
|
|
3597
4053
|
dependencies: collectDependencies2(
|
|
3598
4054
|
manifestEntry,
|
|
3599
|
-
trackedDependencies.get(toSlug(entry.path)) ??
|
|
4055
|
+
trackedDependencies.get(toSlug(entry.path)) ?? plan.previousState?.entries[entry.path]?.dependencies ?? []
|
|
3600
4056
|
),
|
|
3601
|
-
linkTargets: collectLinkTargets(manifestEntry)
|
|
4057
|
+
linkTargets: collectLinkTargets(manifestEntry),
|
|
4058
|
+
...processedContentCacheFingerprints.get(toSlug(entry.path)) ? {
|
|
4059
|
+
processedContentCacheFingerprint: processedContentCacheFingerprints.get(toSlug(entry.path))
|
|
4060
|
+
} : {}
|
|
3602
4061
|
}
|
|
3603
4062
|
];
|
|
3604
4063
|
})
|
|
@@ -3610,7 +4069,19 @@ var ContentBuildCoordinator = class {
|
|
|
3610
4069
|
),
|
|
3611
4070
|
pipelineFingerprint,
|
|
3612
4071
|
manifestEntries,
|
|
3613
|
-
outputs
|
|
4072
|
+
outputs,
|
|
4073
|
+
externalDependencies: externalDependencies.map(
|
|
4074
|
+
({ id, fingerprint, scope, expiresAt }) => ({
|
|
4075
|
+
id,
|
|
4076
|
+
fingerprint,
|
|
4077
|
+
scope,
|
|
4078
|
+
...expiresAt === void 0 ? {} : { expiresAt }
|
|
4079
|
+
})
|
|
4080
|
+
),
|
|
4081
|
+
manifestHookCache,
|
|
4082
|
+
...nextChangeAt ? {
|
|
4083
|
+
nextPublishingChangeAt: nextChangeAt
|
|
4084
|
+
} : {}
|
|
3614
4085
|
};
|
|
3615
4086
|
try {
|
|
3616
4087
|
await saveContentBuildState(this.dependencies.buildStatePath, state2);
|
|
@@ -3624,7 +4095,7 @@ var ContentBuildCoordinator = class {
|
|
|
3624
4095
|
);
|
|
3625
4096
|
}
|
|
3626
4097
|
}
|
|
3627
|
-
async prepare(incremental, pipelineFingerprint, fullContentRegenerationRequired) {
|
|
4098
|
+
async prepare(incremental, pipelineFingerprint, fullContentRegenerationRequired, externalDependencies, externalDependencyFailures, publishingBuildTime) {
|
|
3628
4099
|
const entries = await this.dependencies.observability.tracer.span(
|
|
3629
4100
|
"content.discovery",
|
|
3630
4101
|
{},
|
|
@@ -3638,12 +4109,20 @@ var ContentBuildCoordinator = class {
|
|
|
3638
4109
|
(entry) => this.dependencies.readEntry(entry)
|
|
3639
4110
|
)
|
|
3640
4111
|
);
|
|
3641
|
-
const previousState =
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
4112
|
+
const previousState = await this.dependencies.observability.tracer.span(
|
|
4113
|
+
"content.build-state.load",
|
|
4114
|
+
{ incremental },
|
|
4115
|
+
async () => incremental ? await loadContentBuildState(this.dependencies.buildStatePath) : void 0
|
|
4116
|
+
);
|
|
4117
|
+
const changeSet = await this.dependencies.observability.tracer.span(
|
|
4118
|
+
"content.change-set",
|
|
4119
|
+
{},
|
|
4120
|
+
() => previousState ? diffContentEntries(previousState, currentEntries) : allContentChanged(currentEntries)
|
|
4121
|
+
);
|
|
4122
|
+
const currentContentAliases = await this.dependencies.observability.tracer.span(
|
|
4123
|
+
"content.aliases",
|
|
4124
|
+
{},
|
|
4125
|
+
() => this.getContentAliases(currentEntries, previousState, changeSet)
|
|
3647
4126
|
);
|
|
3648
4127
|
const {
|
|
3649
4128
|
index: currentContentIndex,
|
|
@@ -3656,26 +4135,54 @@ var ContentBuildCoordinator = class {
|
|
|
3656
4135
|
currentContentAliases
|
|
3657
4136
|
)
|
|
3658
4137
|
);
|
|
3659
|
-
const
|
|
3660
|
-
changeSet,
|
|
4138
|
+
const publishingStateExpired = isPublishingStateExpired(
|
|
3661
4139
|
previousState,
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
4140
|
+
publishingBuildTime
|
|
4141
|
+
);
|
|
4142
|
+
const affected = await this.dependencies.observability.tracer.span(
|
|
4143
|
+
"content.affected",
|
|
4144
|
+
{ fullContentRegenerationRequired },
|
|
4145
|
+
() => !fullContentRegenerationRequired && !publishingStateExpired && previousState?.pipelineFingerprint === pipelineFingerprint ? determineAffectedContent(
|
|
4146
|
+
changeSet,
|
|
4147
|
+
previousState,
|
|
4148
|
+
currentEntries.map(({ entry }) => entry.path),
|
|
4149
|
+
currentContentIndex
|
|
4150
|
+
) : determineAffectedContent(
|
|
4151
|
+
allContentChanged(currentEntries),
|
|
4152
|
+
void 0,
|
|
4153
|
+
currentEntries.map(({ entry }) => entry.path),
|
|
4154
|
+
currentContentIndex
|
|
4155
|
+
)
|
|
3669
4156
|
);
|
|
4157
|
+
const fullRegenerationReasons = [];
|
|
4158
|
+
if (!incremental) fullRegenerationReasons.push("incremental-disabled");
|
|
4159
|
+
else if (!previousState)
|
|
4160
|
+
fullRegenerationReasons.push("cache-missing-or-invalid");
|
|
4161
|
+
if (previousState && previousState.pipelineFingerprint !== pipelineFingerprint)
|
|
4162
|
+
fullRegenerationReasons.push("pipeline-fingerprint-changed");
|
|
4163
|
+
if (fullContentRegenerationRequired)
|
|
4164
|
+
fullRegenerationReasons.push("content-plugin-uncacheable");
|
|
4165
|
+
if (publishingStateExpired)
|
|
4166
|
+
fullRegenerationReasons.push("publishing-state-expired");
|
|
4167
|
+
if (externalDependencyFailures.length > 0)
|
|
4168
|
+
fullRegenerationReasons.push("external-dependency-unavailable");
|
|
3670
4169
|
this.dependencies.observability.tracer.event("build.incremental", {
|
|
3671
|
-
incremental
|
|
4170
|
+
incremental,
|
|
4171
|
+
cacheValid: previousState !== void 0,
|
|
4172
|
+
fullRegenerationReasons: fullRegenerationReasons.join(","),
|
|
4173
|
+
externalDependencyFailures: externalDependencyFailures.length,
|
|
3672
4174
|
added: changeSet.added.length,
|
|
3673
4175
|
changed: changeSet.changed.length,
|
|
3674
4176
|
removed: changeSet.removed.length,
|
|
3675
4177
|
unchanged: changeSet.unchanged.length,
|
|
3676
4178
|
affected: affected.direct.size + affected.dependent.size
|
|
3677
4179
|
});
|
|
4180
|
+
const reusableOutputs = incremental && previousState?.pipelineFingerprint === pipelineFingerprint && externalDependencyFailures.length === 0 && !publishingStateExpired && externalDependenciesEqual(
|
|
4181
|
+
previousState?.externalDependencies ?? [],
|
|
4182
|
+
externalDependencies
|
|
4183
|
+
) && changeSet.added.length === 0 && changeSet.changed.length === 0 && changeSet.removed.length === 0 ? previousState?.outputs : void 0;
|
|
3678
4184
|
return {
|
|
4185
|
+
incremental,
|
|
3679
4186
|
previousState,
|
|
3680
4187
|
previousManifestEntriesBySlug: new Map(
|
|
3681
4188
|
(previousState?.manifestEntries ?? []).map((entry) => [
|
|
@@ -3688,7 +4195,12 @@ var ContentBuildCoordinator = class {
|
|
|
3688
4195
|
currentContentIndex,
|
|
3689
4196
|
currentContentIndexAmbiguities,
|
|
3690
4197
|
changeSet,
|
|
3691
|
-
affectedContent: affected
|
|
4198
|
+
affectedContent: affected,
|
|
4199
|
+
cacheValid: previousState !== void 0,
|
|
4200
|
+
fullRegenerationReasons,
|
|
4201
|
+
reusableOutputs,
|
|
4202
|
+
externalDependencies,
|
|
4203
|
+
externalDependencyFailures
|
|
3692
4204
|
};
|
|
3693
4205
|
}
|
|
3694
4206
|
async getContentAliases(currentEntries, previousState, changeSet) {
|
|
@@ -3711,6 +4223,31 @@ var ContentBuildCoordinator = class {
|
|
|
3711
4223
|
return aliases;
|
|
3712
4224
|
}
|
|
3713
4225
|
};
|
|
4226
|
+
function externalDependenciesEqual(previous, current) {
|
|
4227
|
+
if (previous.length !== current.length) return false;
|
|
4228
|
+
const currentById = new Map(
|
|
4229
|
+
current.map((dependency) => [dependency.id, dependency])
|
|
4230
|
+
);
|
|
4231
|
+
return previous.every((dependency) => {
|
|
4232
|
+
const candidate = currentById.get(dependency.id);
|
|
4233
|
+
return candidate !== void 0 && candidate.fingerprint === dependency.fingerprint && candidate.expiresAt === dependency.expiresAt && !isExpired(candidate.expiresAt);
|
|
4234
|
+
});
|
|
4235
|
+
}
|
|
4236
|
+
function isExpired(expiresAt) {
|
|
4237
|
+
return expiresAt !== void 0 && Date.parse(expiresAt) <= Date.now();
|
|
4238
|
+
}
|
|
4239
|
+
function isPublishingStateExpired(state2, buildTime) {
|
|
4240
|
+
return state2?.nextPublishingChangeAt !== void 0 && Date.parse(state2.nextPublishingChangeAt) <= buildTime.getTime();
|
|
4241
|
+
}
|
|
4242
|
+
function nextPublishingChangeAt(manifest, buildTime) {
|
|
4243
|
+
const next = manifest.entries.filter((entry) => entry.publishing.visibility === "scheduled").map((entry) => entry.publishing.publishAt?.getTime()).filter(
|
|
4244
|
+
(time) => time !== void 0 && time > buildTime.getTime()
|
|
4245
|
+
).reduce(
|
|
4246
|
+
(earliest, time) => earliest === void 0 || time < earliest ? time : earliest,
|
|
4247
|
+
void 0
|
|
4248
|
+
);
|
|
4249
|
+
return next === void 0 ? void 0 : new Date(next).toISOString();
|
|
4250
|
+
}
|
|
3714
4251
|
function readText2(content) {
|
|
3715
4252
|
return typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
3716
4253
|
}
|
|
@@ -3763,7 +4300,27 @@ function collectLinkTargets(manifestEntry) {
|
|
|
3763
4300
|
].sort();
|
|
3764
4301
|
}
|
|
3765
4302
|
|
|
4303
|
+
// src/content/content_build_plan.ts
|
|
4304
|
+
function reusableOutputChangeSet(outputs) {
|
|
4305
|
+
const unchanged = [...outputs].sort(
|
|
4306
|
+
(left, right) => left.path.localeCompare(right.path)
|
|
4307
|
+
);
|
|
4308
|
+
return {
|
|
4309
|
+
affected: [],
|
|
4310
|
+
removed: [],
|
|
4311
|
+
unchanged,
|
|
4312
|
+
fullRegenerationRequired: unchanged.some(
|
|
4313
|
+
(output) => output.dependencies.some((dependency) => dependency.type === "unknown")
|
|
4314
|
+
),
|
|
4315
|
+
candidateOutputCount: unchanged.length,
|
|
4316
|
+
affectedOutputCount: 0,
|
|
4317
|
+
removedOutputCount: 0,
|
|
4318
|
+
unchangedOutputCount: unchanged.length
|
|
4319
|
+
};
|
|
4320
|
+
}
|
|
4321
|
+
|
|
3766
4322
|
// src/content/content_entry_reader.ts
|
|
4323
|
+
var textDecoder = new TextDecoder();
|
|
3767
4324
|
var ContentEntryReader = class {
|
|
3768
4325
|
constructor(source, observability) {
|
|
3769
4326
|
this.source = source;
|
|
@@ -3802,8 +4359,18 @@ var ContentEntryReader = class {
|
|
|
3802
4359
|
read(entry) {
|
|
3803
4360
|
const cached = this.texts.get(entry.path);
|
|
3804
4361
|
if (cached) return cached;
|
|
3805
|
-
const content = this.
|
|
3806
|
-
|
|
4362
|
+
const content = this.observability.tracer.span(
|
|
4363
|
+
"content.read",
|
|
4364
|
+
{ extension: entry.path.endsWith(".md") ? "markdown" : "asset" },
|
|
4365
|
+
async () => {
|
|
4366
|
+
const value = await this.source.read(entry);
|
|
4367
|
+
const text = typeof value === "string" ? value : textDecoder.decode(value);
|
|
4368
|
+
this.observability.tracer.event("content.read.bytes", {
|
|
4369
|
+
bytes: Buffer.byteLength(text, "utf8"),
|
|
4370
|
+
extension: entry.path.endsWith(".md") ? "markdown" : "asset"
|
|
4371
|
+
});
|
|
4372
|
+
return text;
|
|
4373
|
+
}
|
|
3807
4374
|
);
|
|
3808
4375
|
const retain = entry.path.endsWith(".md");
|
|
3809
4376
|
const tracked = retain ? content : content.finally(() => this.texts.delete(entry.path));
|
|
@@ -3930,6 +4497,8 @@ async function mapConcurrent2(values, concurrency, map) {
|
|
|
3930
4497
|
import fs4 from "node:fs/promises";
|
|
3931
4498
|
import path4 from "node:path";
|
|
3932
4499
|
var INTERNAL_CONTENT_IGNORE_PATTERNS = [
|
|
4500
|
+
".riebeckite",
|
|
4501
|
+
".riebeckite/**",
|
|
3933
4502
|
".git",
|
|
3934
4503
|
".git/**",
|
|
3935
4504
|
".github",
|
|
@@ -3979,9 +4548,9 @@ var FileSystemContentSource = class {
|
|
|
3979
4548
|
return await fs4.readFile(filePath);
|
|
3980
4549
|
}
|
|
3981
4550
|
async scanDirectory(directory, parentPath2, exclusions, templaterTemplateFolder) {
|
|
3982
|
-
const directoryEntries = await fs4.readdir(directory, {
|
|
4551
|
+
const directoryEntries = (await fs4.readdir(directory, {
|
|
3983
4552
|
withFileTypes: true
|
|
3984
|
-
});
|
|
4553
|
+
})).toSorted((left, right) => left.name.localeCompare(right.name, "en"));
|
|
3985
4554
|
const entries = [];
|
|
3986
4555
|
for (const directoryEntry of directoryEntries) {
|
|
3987
4556
|
if (directoryEntry.isSymbolicLink()) continue;
|
|
@@ -4280,6 +4849,8 @@ function determineOutputChanges(input) {
|
|
|
4280
4849
|
input.manifest,
|
|
4281
4850
|
changedContent
|
|
4282
4851
|
);
|
|
4852
|
+
const changedExternal = changedExternalDependencies(input);
|
|
4853
|
+
const changedExternalScopes = changedExternalDependencyScopes(input);
|
|
4283
4854
|
const affected = /* @__PURE__ */ new Map();
|
|
4284
4855
|
const unchanged = [];
|
|
4285
4856
|
for (const output of current) {
|
|
@@ -4288,7 +4859,9 @@ function determineOutputChanges(input) {
|
|
|
4288
4859
|
changedContent,
|
|
4289
4860
|
changedTags,
|
|
4290
4861
|
changedFolders,
|
|
4291
|
-
changedFiles
|
|
4862
|
+
changedFiles,
|
|
4863
|
+
changedExternal,
|
|
4864
|
+
changedExternalScopes
|
|
4292
4865
|
)) {
|
|
4293
4866
|
affected.set(output.path, output);
|
|
4294
4867
|
} else {
|
|
@@ -4305,7 +4878,7 @@ function determineOutputChanges(input) {
|
|
|
4305
4878
|
const removed = [...previous.values()].filter(
|
|
4306
4879
|
(output) => !currentPaths.has(output.path)
|
|
4307
4880
|
);
|
|
4308
|
-
const fullRegenerationRequired = current.some(
|
|
4881
|
+
const fullRegenerationRequired = (input.externalDependencyFailures?.length ?? 0) > 0 || current.some(
|
|
4309
4882
|
(output) => output.dependencies.some((dependency) => dependency.type === "unknown")
|
|
4310
4883
|
);
|
|
4311
4884
|
const affectedValues = [...affected.values()].sort(compareOutput);
|
|
@@ -4335,7 +4908,10 @@ function buildOutputInventory(manifest, pluginPageOutputs = [], contentOutputDep
|
|
|
4335
4908
|
});
|
|
4336
4909
|
}
|
|
4337
4910
|
for (const output of pluginPageOutputs) {
|
|
4338
|
-
addOutput(outputs, {
|
|
4911
|
+
addOutput(outputs, {
|
|
4912
|
+
...output,
|
|
4913
|
+
path: htmlOutputPath(output.path)
|
|
4914
|
+
});
|
|
4339
4915
|
}
|
|
4340
4916
|
for (const output of manifest.generatedOutputs) {
|
|
4341
4917
|
addOutput(outputs, {
|
|
@@ -4362,10 +4938,22 @@ function contentOutput(entry, dependencies) {
|
|
|
4362
4938
|
dependencies: [{ type: "content", slug: entry.slug }, ...dependencies]
|
|
4363
4939
|
};
|
|
4364
4940
|
}
|
|
4365
|
-
function isOutputAffected(output, changedContent, changedTags, changedFolders, changedFiles) {
|
|
4941
|
+
function isOutputAffected(output, changedContent, changedTags, changedFolders, changedFiles, changedExternal, changedExternalScopes) {
|
|
4942
|
+
if (isAffectedByExternalScope(output, changedExternalScopes)) return true;
|
|
4366
4943
|
for (const dependency of output.dependencies) {
|
|
4367
4944
|
if (dependency.type === "unknown") return true;
|
|
4945
|
+
if (dependency.type === "external" && changedExternal.has(dependency.id))
|
|
4946
|
+
return true;
|
|
4368
4947
|
if (dependency.type === "global") return changedContent.size > 0;
|
|
4948
|
+
if (dependency.type === "projection" && (dependency.projection === "navigation" || dependency.projection === "recent-posts")) {
|
|
4949
|
+
return changedContent.size > 0;
|
|
4950
|
+
}
|
|
4951
|
+
if (dependency.type === "projection" && dependency.projection === "taxonomy-tags") {
|
|
4952
|
+
return changedTags.size > 0;
|
|
4953
|
+
}
|
|
4954
|
+
if (dependency.type === "projection" && dependency.projection === "taxonomy-folders") {
|
|
4955
|
+
return changedFolders.size > 0;
|
|
4956
|
+
}
|
|
4369
4957
|
if (dependency.type === "content" && changedContent.has(dependency.slug)) {
|
|
4370
4958
|
return true;
|
|
4371
4959
|
}
|
|
@@ -4380,6 +4968,52 @@ function isOutputAffected(output, changedContent, changedTags, changedFolders, c
|
|
|
4380
4968
|
}
|
|
4381
4969
|
return false;
|
|
4382
4970
|
}
|
|
4971
|
+
function isAffectedByExternalScope(output, changedExternalScopes) {
|
|
4972
|
+
for (const scope of changedExternalScopes.values()) {
|
|
4973
|
+
if (scope.type === "all") return true;
|
|
4974
|
+
if (scope.type === "content" && output.kind === "content") return true;
|
|
4975
|
+
if (scope.type === "output" && scope.paths.includes(output.path))
|
|
4976
|
+
return true;
|
|
4977
|
+
}
|
|
4978
|
+
return false;
|
|
4979
|
+
}
|
|
4980
|
+
function changedExternalDependencies(input) {
|
|
4981
|
+
const previous = new Map(
|
|
4982
|
+
(input.previousState?.externalDependencies ?? []).map((dependency) => [
|
|
4983
|
+
dependency.id,
|
|
4984
|
+
dependency
|
|
4985
|
+
])
|
|
4986
|
+
);
|
|
4987
|
+
const changed = /* @__PURE__ */ new Set();
|
|
4988
|
+
for (const dependency of input.externalDependencies ?? []) {
|
|
4989
|
+
const prior = previous.get(dependency.id);
|
|
4990
|
+
if (!prior || prior.fingerprint !== dependency.fingerprint || prior.expiresAt !== dependency.expiresAt || isExpired2(dependency.expiresAt))
|
|
4991
|
+
changed.add(dependency.id);
|
|
4992
|
+
previous.delete(dependency.id);
|
|
4993
|
+
}
|
|
4994
|
+
for (const id of previous.keys()) changed.add(id);
|
|
4995
|
+
return changed;
|
|
4996
|
+
}
|
|
4997
|
+
function changedExternalDependencyScopes(input) {
|
|
4998
|
+
const previous = new Map(
|
|
4999
|
+
(input.previousState?.externalDependencies ?? []).map((dependency) => [
|
|
5000
|
+
dependency.id,
|
|
5001
|
+
dependency
|
|
5002
|
+
])
|
|
5003
|
+
);
|
|
5004
|
+
const changed = /* @__PURE__ */ new Map();
|
|
5005
|
+
for (const dependency of input.externalDependencies ?? []) {
|
|
5006
|
+
const prior = previous.get(dependency.id);
|
|
5007
|
+
if (!prior || prior.fingerprint !== dependency.fingerprint || prior.expiresAt !== dependency.expiresAt || isExpired2(dependency.expiresAt)) {
|
|
5008
|
+
changed.set(dependency.id, dependency.scope);
|
|
5009
|
+
}
|
|
5010
|
+
previous.delete(dependency.id);
|
|
5011
|
+
}
|
|
5012
|
+
for (const dependency of previous.values()) {
|
|
5013
|
+
changed.set(dependency.id, dependency.scope);
|
|
5014
|
+
}
|
|
5015
|
+
return changed;
|
|
5016
|
+
}
|
|
4383
5017
|
function collectChangedContent(input) {
|
|
4384
5018
|
const changed = /* @__PURE__ */ new Set([
|
|
4385
5019
|
...input.affectedContent.direct,
|
|
@@ -4402,6 +5036,9 @@ function collectChangedContent(input) {
|
|
|
4402
5036
|
}
|
|
4403
5037
|
return changed;
|
|
4404
5038
|
}
|
|
5039
|
+
function isExpired2(expiresAt) {
|
|
5040
|
+
return expiresAt !== void 0 && Date.parse(expiresAt) <= Date.now();
|
|
5041
|
+
}
|
|
4405
5042
|
function collectChangedTags(previousState, manifest, changedContent) {
|
|
4406
5043
|
const previousBySlug = new Map(
|
|
4407
5044
|
(previousState?.manifestEntries ?? []).map((entry) => [entry.slug, entry])
|
|
@@ -4548,7 +5185,12 @@ var ContentManager = class {
|
|
|
4548
5185
|
processedContentCount = 0;
|
|
4549
5186
|
reusedContentCount = 0;
|
|
4550
5187
|
outputChangeSet = null;
|
|
5188
|
+
buildPlan = null;
|
|
5189
|
+
buildStateCommitRequested = false;
|
|
5190
|
+
buildStateCommitted = false;
|
|
5191
|
+
cacheManifestEntries = [];
|
|
4551
5192
|
trackedContentDependencies = /* @__PURE__ */ new Map();
|
|
5193
|
+
processedContentCacheFingerprints = /* @__PURE__ */ new Map();
|
|
4552
5194
|
async getAllPosts() {
|
|
4553
5195
|
return (await this.scan()).filter((entry) => entry.path.endsWith(".md")).map((entry) => ({ slug: entry.path.replace(/\.md$/, "") }));
|
|
4554
5196
|
}
|
|
@@ -4558,13 +5200,13 @@ var ContentManager = class {
|
|
|
4558
5200
|
async getPost(slug) {
|
|
4559
5201
|
return await this.entryReader.readText(`${slug}.md`);
|
|
4560
5202
|
}
|
|
4561
|
-
async getContentIndex(
|
|
5203
|
+
async getContentIndex(plan) {
|
|
4562
5204
|
if (this.contentIndex) return this.contentIndex;
|
|
4563
|
-
if (!
|
|
5205
|
+
if (!plan && this.contentIndexPromise)
|
|
4564
5206
|
return (await this.contentIndexPromise).index;
|
|
4565
|
-
if (
|
|
4566
|
-
this.contentIndex =
|
|
4567
|
-
this.contentIndexAmbiguities =
|
|
5207
|
+
if (plan) {
|
|
5208
|
+
this.contentIndex = plan.currentContentIndex;
|
|
5209
|
+
this.contentIndexAmbiguities = plan.currentContentIndexAmbiguities;
|
|
4568
5210
|
return this.contentIndex;
|
|
4569
5211
|
}
|
|
4570
5212
|
const promise = this.observability().tracer.span(
|
|
@@ -4575,14 +5217,14 @@ var ContentManager = class {
|
|
|
4575
5217
|
(entry) => this.entryReader.read(entry)
|
|
4576
5218
|
)
|
|
4577
5219
|
);
|
|
4578
|
-
if (!
|
|
5220
|
+
if (!plan) this.contentIndexPromise = promise;
|
|
4579
5221
|
try {
|
|
4580
5222
|
const built = await promise;
|
|
4581
5223
|
this.contentIndex = built.index;
|
|
4582
5224
|
this.contentIndexAmbiguities = built.ambiguities;
|
|
4583
5225
|
return this.contentIndex;
|
|
4584
5226
|
} finally {
|
|
4585
|
-
if (!
|
|
5227
|
+
if (!plan) this.contentIndexPromise = null;
|
|
4586
5228
|
}
|
|
4587
5229
|
}
|
|
4588
5230
|
async getProcessedContent(slug) {
|
|
@@ -4607,14 +5249,27 @@ var ContentManager = class {
|
|
|
4607
5249
|
this.getContentIndex(),
|
|
4608
5250
|
this.getPost(slug)
|
|
4609
5251
|
]);
|
|
5252
|
+
this.getProcessedContentCacheFingerprint(slug, rawPost);
|
|
4610
5253
|
await this.pluginRuntime.startBuild(contentIndex);
|
|
4611
5254
|
await this.pluginRuntime.runContentLoaded(slug, rawPost, contentIndex);
|
|
4612
5255
|
await this.ensureRoutableSlugs();
|
|
5256
|
+
const locations = await this.getContentLocations();
|
|
5257
|
+
if (this.pipelineOptions.config) {
|
|
5258
|
+
this.pipelineFingerprint = this.computePipelineFingerprint(locations);
|
|
5259
|
+
}
|
|
4613
5260
|
this.pipeline ??= new Pipeline(
|
|
4614
5261
|
contentIndex,
|
|
4615
|
-
|
|
5262
|
+
new Map(
|
|
5263
|
+
[...locations.entries()].map(([locationSlug, location]) => [
|
|
5264
|
+
locationSlug,
|
|
5265
|
+
location.permalink
|
|
5266
|
+
])
|
|
5267
|
+
),
|
|
4616
5268
|
(postSlug) => this.getPost(postSlug),
|
|
4617
|
-
|
|
5269
|
+
{
|
|
5270
|
+
...this.pipelineOptions,
|
|
5271
|
+
contentScopeFingerprint: this.pipelineFingerprint ?? void 0
|
|
5272
|
+
},
|
|
4618
5273
|
this.isBuildTime
|
|
4619
5274
|
);
|
|
4620
5275
|
const content = await this.pipeline.execute(rawPost, {
|
|
@@ -4644,6 +5299,7 @@ var ContentManager = class {
|
|
|
4644
5299
|
return content;
|
|
4645
5300
|
}
|
|
4646
5301
|
async getManifest(options) {
|
|
5302
|
+
this.requestBuildStateCommit(options);
|
|
4647
5303
|
if (this.manifest) return this.manifest;
|
|
4648
5304
|
if (this.manifestPromise) return await this.manifestPromise;
|
|
4649
5305
|
this.manifestPromise = this.buildManifest(options);
|
|
@@ -4657,19 +5313,19 @@ var ContentManager = class {
|
|
|
4657
5313
|
async buildManifest(options) {
|
|
4658
5314
|
if (this.manifest) return this.manifest;
|
|
4659
5315
|
this.enableBuildTime();
|
|
4660
|
-
const
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
5316
|
+
const config = this.pipelineOptions.config;
|
|
5317
|
+
const plan = await this.getBuildPlan(options);
|
|
5318
|
+
const locations = await this.getContentLocations();
|
|
5319
|
+
if (config) {
|
|
5320
|
+
this.pipelineFingerprint = this.computePipelineFingerprint(locations);
|
|
5321
|
+
}
|
|
4665
5322
|
return await this.observability().tracer.span(
|
|
4666
5323
|
"content.manifest",
|
|
4667
5324
|
{},
|
|
4668
5325
|
async () => {
|
|
4669
|
-
const [posts, contentIndex
|
|
5326
|
+
const [posts, contentIndex] = await Promise.all([
|
|
4670
5327
|
this.getAllPosts(),
|
|
4671
|
-
this.getContentIndex(
|
|
4672
|
-
this.getContentLocations()
|
|
5328
|
+
this.getContentIndex(plan)
|
|
4673
5329
|
]);
|
|
4674
5330
|
const entries = await this.observability().tracer.span(
|
|
4675
5331
|
"content.manifest.entries",
|
|
@@ -4678,12 +5334,14 @@ var ContentManager = class {
|
|
|
4678
5334
|
posts,
|
|
4679
5335
|
CONTENT_PROCESSING_CONCURRENCY,
|
|
4680
5336
|
async (post) => {
|
|
4681
|
-
const
|
|
5337
|
+
const rawPost = await this.getPost(post.slug);
|
|
5338
|
+
const reused = this.reuseManifestEntry(
|
|
5339
|
+
post.slug,
|
|
5340
|
+
rawPost,
|
|
5341
|
+
plan
|
|
5342
|
+
);
|
|
4682
5343
|
if (reused) return reused;
|
|
4683
|
-
const
|
|
4684
|
-
this.getPost(post.slug),
|
|
4685
|
-
this.getProcessedContent(post.slug)
|
|
4686
|
-
]);
|
|
5344
|
+
const processed = await this.getProcessedContent(post.slug);
|
|
4687
5345
|
const location = locations.get(post.slug);
|
|
4688
5346
|
if (!location) {
|
|
4689
5347
|
throw new Error(
|
|
@@ -4723,7 +5381,12 @@ var ContentManager = class {
|
|
|
4723
5381
|
manifest.pageRoutes = [
|
|
4724
5382
|
...await this.pluginRuntime.getPageRoutes(manifest, contentIndex)
|
|
4725
5383
|
];
|
|
4726
|
-
await this.pluginRuntime.runManifestCreated(
|
|
5384
|
+
await this.pluginRuntime.runManifestCreated(
|
|
5385
|
+
manifest,
|
|
5386
|
+
contentIndex,
|
|
5387
|
+
plan.previousState?.manifestHookCache,
|
|
5388
|
+
this.getPipelineFingerprint()
|
|
5389
|
+
);
|
|
4727
5390
|
this.synchronizeManifestHtml(manifest, cacheManifestEntries);
|
|
4728
5391
|
manifest.assets = this.pluginRuntime.collectAssets();
|
|
4729
5392
|
manifest.clientEntries = this.pluginRuntime.collectClientEntries();
|
|
@@ -4731,21 +5394,29 @@ var ContentManager = class {
|
|
|
4731
5394
|
...this.pluginRuntime.getDiagnostics(),
|
|
4732
5395
|
...await this.pluginRuntime.collectDiagnostics(contentIndex)
|
|
4733
5396
|
];
|
|
5397
|
+
await this.pluginRuntime.runGeneratedOutputPlans(manifest);
|
|
4734
5398
|
await this.pluginRuntime.runBuildEnd(manifest, contentIndex);
|
|
4735
5399
|
await this.emitPublicImageAssets(manifest);
|
|
4736
5400
|
manifest.generatedOutputs = this.pluginRuntime.collectGeneratedOutputs();
|
|
4737
|
-
const pluginPageOutputs = await this.
|
|
4738
|
-
|
|
4739
|
-
|
|
5401
|
+
const pluginPageOutputs = await this.observability().tracer.span(
|
|
5402
|
+
"output.dependencies.plugin",
|
|
5403
|
+
{},
|
|
5404
|
+
() => this.pluginRuntime.getPageOutputs(manifest, contentIndex)
|
|
5405
|
+
);
|
|
5406
|
+
this.outputChangeSet = await this.observability().tracer.span(
|
|
5407
|
+
"output.dependencies.resolve",
|
|
5408
|
+
{},
|
|
5409
|
+
() => determineOutputChanges({
|
|
5410
|
+
manifest,
|
|
5411
|
+
previousState: plan.previousState,
|
|
5412
|
+
changeSet: plan.changeSet,
|
|
5413
|
+
affectedContent: plan.affectedContent,
|
|
5414
|
+
pluginPageOutputs,
|
|
5415
|
+
contentOutputDependencies: this.pluginRuntime.getContentOutputDependencies(),
|
|
5416
|
+
externalDependencies: plan.externalDependencies,
|
|
5417
|
+
externalDependencyFailures: plan.externalDependencyFailures
|
|
5418
|
+
})
|
|
4740
5419
|
);
|
|
4741
|
-
this.outputChangeSet = preparation ? determineOutputChanges({
|
|
4742
|
-
manifest,
|
|
4743
|
-
previousState: preparation.previousState,
|
|
4744
|
-
changeSet: preparation.changeSet,
|
|
4745
|
-
affectedContent: preparation.affectedContent,
|
|
4746
|
-
pluginPageOutputs,
|
|
4747
|
-
contentOutputDependencies: this.pluginRuntime.getContentOutputDependencies()
|
|
4748
|
-
}) : null;
|
|
4749
5420
|
if (this.outputChangeSet) {
|
|
4750
5421
|
this.observability().tracer.event("build.incremental.outputs", {
|
|
4751
5422
|
candidateOutputCount: this.outputChangeSet.candidateOutputCount,
|
|
@@ -4755,19 +5426,9 @@ var ContentManager = class {
|
|
|
4755
5426
|
fullRegenerationRequired: this.outputChangeSet.fullRegenerationRequired
|
|
4756
5427
|
});
|
|
4757
5428
|
}
|
|
4758
|
-
if (preparation)
|
|
4759
|
-
await this.buildCoordinator.commit(
|
|
4760
|
-
preparation,
|
|
4761
|
-
manifest,
|
|
4762
|
-
this.getPipelineFingerprint(),
|
|
4763
|
-
this.outputChangeSet ? [
|
|
4764
|
-
...this.outputChangeSet.affected,
|
|
4765
|
-
...this.outputChangeSet.unchanged
|
|
4766
|
-
] : [],
|
|
4767
|
-
cacheManifestEntries,
|
|
4768
|
-
this.trackedContentDependencies
|
|
4769
|
-
);
|
|
4770
5429
|
this.manifest = manifest;
|
|
5430
|
+
this.cacheManifestEntries = cacheManifestEntries;
|
|
5431
|
+
await this.commitBuildState();
|
|
4771
5432
|
return manifest;
|
|
4772
5433
|
}
|
|
4773
5434
|
);
|
|
@@ -4781,31 +5442,72 @@ var ContentManager = class {
|
|
|
4781
5442
|
getReusedContentCount() {
|
|
4782
5443
|
return this.reusedContentCount;
|
|
4783
5444
|
}
|
|
5445
|
+
getManifestHookMetrics() {
|
|
5446
|
+
return this.pluginRuntime.getManifestHookMetrics();
|
|
5447
|
+
}
|
|
5448
|
+
async getNoChangeFastPathBlockReason(options = {}) {
|
|
5449
|
+
const plan = await this.getBuildPlan(options);
|
|
5450
|
+
if (plan.fullRegenerationReasons.includes("publishing-state-expired"))
|
|
5451
|
+
return "publishing-state-expired";
|
|
5452
|
+
if (plan.reusableOutputs?.some(
|
|
5453
|
+
(output) => output.dependencies.some((dependency) => dependency.type === "unknown")
|
|
5454
|
+
))
|
|
5455
|
+
return "output-dependency-unknown";
|
|
5456
|
+
return this.pluginRuntime.getNoChangeFastPathBlockReason(
|
|
5457
|
+
plan.previousState?.manifestHookCache ?? [],
|
|
5458
|
+
this.getPipelineFingerprint()
|
|
5459
|
+
);
|
|
5460
|
+
}
|
|
4784
5461
|
async getOutputChangeSet(options = {}) {
|
|
4785
|
-
|
|
5462
|
+
this.requestBuildStateCommit(options);
|
|
5463
|
+
if (this.outputChangeSet) {
|
|
5464
|
+
await this.commitBuildState();
|
|
5465
|
+
return this.outputChangeSet;
|
|
5466
|
+
}
|
|
5467
|
+
const plan = await this.getBuildPlan(options);
|
|
5468
|
+
if (plan.reusableOutputs) {
|
|
5469
|
+
this.outputChangeSet = await this.observability().tracer.span(
|
|
5470
|
+
"output.dependencies.reuse",
|
|
5471
|
+
{ outputCount: plan.reusableOutputs.length },
|
|
5472
|
+
async () => reusableOutputChangeSet(plan.reusableOutputs ?? [])
|
|
5473
|
+
);
|
|
5474
|
+
return this.outputChangeSet;
|
|
5475
|
+
}
|
|
5476
|
+
await this.observability().tracer.span(
|
|
5477
|
+
"output.dependencies.manifest",
|
|
5478
|
+
{},
|
|
5479
|
+
() => this.getManifest(options)
|
|
5480
|
+
);
|
|
4786
5481
|
if (!this.outputChangeSet) {
|
|
4787
5482
|
const manifest = await this.getManifest();
|
|
4788
5483
|
const contentIndex = await this.getContentIndex();
|
|
4789
|
-
const pluginPageOutputs = await this.
|
|
4790
|
-
|
|
4791
|
-
|
|
5484
|
+
const pluginPageOutputs = await this.observability().tracer.span(
|
|
5485
|
+
"output.dependencies.plugin",
|
|
5486
|
+
{},
|
|
5487
|
+
() => this.pluginRuntime.getPageOutputs(manifest, contentIndex)
|
|
5488
|
+
);
|
|
5489
|
+
return await this.observability().tracer.span(
|
|
5490
|
+
"output.dependencies.resolve",
|
|
5491
|
+
{},
|
|
5492
|
+
() => determineOutputChanges({
|
|
5493
|
+
manifest,
|
|
5494
|
+
previousState: void 0,
|
|
5495
|
+
changeSet: {
|
|
5496
|
+
added: manifest.entries.map((entry) => `${entry.slug}.md`),
|
|
5497
|
+
changed: [],
|
|
5498
|
+
removed: [],
|
|
5499
|
+
unchanged: []
|
|
5500
|
+
},
|
|
5501
|
+
affectedContent: {
|
|
5502
|
+
direct: new Set(manifest.entries.map((entry) => entry.slug)),
|
|
5503
|
+
dependent: /* @__PURE__ */ new Set()
|
|
5504
|
+
},
|
|
5505
|
+
pluginPageOutputs,
|
|
5506
|
+
contentOutputDependencies: this.pluginRuntime.getContentOutputDependencies(),
|
|
5507
|
+
externalDependencies: plan.externalDependencies,
|
|
5508
|
+
externalDependencyFailures: plan.externalDependencyFailures
|
|
5509
|
+
})
|
|
4792
5510
|
);
|
|
4793
|
-
return determineOutputChanges({
|
|
4794
|
-
manifest,
|
|
4795
|
-
previousState: void 0,
|
|
4796
|
-
changeSet: {
|
|
4797
|
-
added: manifest.entries.map((entry) => `${entry.slug}.md`),
|
|
4798
|
-
changed: [],
|
|
4799
|
-
removed: [],
|
|
4800
|
-
unchanged: []
|
|
4801
|
-
},
|
|
4802
|
-
affectedContent: {
|
|
4803
|
-
direct: new Set(manifest.entries.map((entry) => entry.slug)),
|
|
4804
|
-
dependent: /* @__PURE__ */ new Set()
|
|
4805
|
-
},
|
|
4806
|
-
pluginPageOutputs,
|
|
4807
|
-
contentOutputDependencies: this.pluginRuntime.getContentOutputDependencies()
|
|
4808
|
-
});
|
|
4809
5511
|
}
|
|
4810
5512
|
return this.outputChangeSet;
|
|
4811
5513
|
}
|
|
@@ -4910,29 +5612,113 @@ var ContentManager = class {
|
|
|
4910
5612
|
publishStrategy() {
|
|
4911
5613
|
return this.pipelineOptions.config?.content.filters.publishStrategy ?? "explicit";
|
|
4912
5614
|
}
|
|
4913
|
-
reuseManifestEntry(slug,
|
|
4914
|
-
if (!
|
|
4915
|
-
if (
|
|
5615
|
+
reuseManifestEntry(slug, source, plan) {
|
|
5616
|
+
if (!plan.previousState?.manifestEntries) return null;
|
|
5617
|
+
if (plan.previousState.pipelineFingerprint !== this.getPipelineFingerprint()) {
|
|
5618
|
+
return null;
|
|
5619
|
+
}
|
|
5620
|
+
if (plan.affectedContent.direct.has(slug)) return null;
|
|
5621
|
+
if (plan.affectedContent.dependent.has(slug)) return null;
|
|
5622
|
+
const contentCacheFingerprint = this.getProcessedContentCacheFingerprint(
|
|
5623
|
+
slug,
|
|
5624
|
+
source
|
|
5625
|
+
);
|
|
5626
|
+
if (!contentCacheFingerprint) return null;
|
|
5627
|
+
if (plan.previousState.entries[`${slug}.md`]?.processedContentCacheFingerprint !== contentCacheFingerprint) {
|
|
4916
5628
|
return null;
|
|
4917
5629
|
}
|
|
4918
|
-
|
|
4919
|
-
if (preparation.affectedContent.dependent.has(slug)) return null;
|
|
4920
|
-
const entry = preparation.previousManifestEntriesBySlug.get(slug);
|
|
5630
|
+
const entry = plan.previousManifestEntriesBySlug.get(slug);
|
|
4921
5631
|
if (!entry) return null;
|
|
4922
5632
|
this.reusedContentCount += 1;
|
|
5633
|
+
this.processedContentCacheFingerprints.set(slug, contentCacheFingerprint);
|
|
4923
5634
|
this.observability().tracer.event("content.reuse", { slug });
|
|
4924
5635
|
return cloneManifestEntry(entry);
|
|
4925
5636
|
}
|
|
4926
5637
|
getPipelineFingerprint() {
|
|
4927
5638
|
if (!this.pipelineOptions.config) return void 0;
|
|
4928
|
-
this.pipelineFingerprint ??= computePipelineFingerprint(
|
|
4929
|
-
this.pipelineOptions.config
|
|
4930
|
-
);
|
|
5639
|
+
this.pipelineFingerprint ??= this.computePipelineFingerprint();
|
|
4931
5640
|
return this.pipelineFingerprint;
|
|
4932
5641
|
}
|
|
5642
|
+
requestBuildStateCommit(options) {
|
|
5643
|
+
if (this.pipelineOptions.commitBuildState === false) return;
|
|
5644
|
+
if (options?.incremental === true) this.buildStateCommitRequested = true;
|
|
5645
|
+
}
|
|
5646
|
+
async commitBuildState() {
|
|
5647
|
+
if (this.buildStateCommitted || !this.buildStateCommitRequested || !this.manifest || !this.outputChangeSet) {
|
|
5648
|
+
return;
|
|
5649
|
+
}
|
|
5650
|
+
const manifest = this.manifest;
|
|
5651
|
+
const outputChangeSet = this.outputChangeSet;
|
|
5652
|
+
const plan = await this.getBuildPlan();
|
|
5653
|
+
if (!plan.incremental) return;
|
|
5654
|
+
await this.observability().tracer.span(
|
|
5655
|
+
"content.build-state.save",
|
|
5656
|
+
{},
|
|
5657
|
+
() => this.buildCoordinator.commit(
|
|
5658
|
+
plan,
|
|
5659
|
+
manifest,
|
|
5660
|
+
this.getPipelineFingerprint(),
|
|
5661
|
+
[...outputChangeSet.affected, ...outputChangeSet.unchanged],
|
|
5662
|
+
this.cacheManifestEntries,
|
|
5663
|
+
this.trackedContentDependencies,
|
|
5664
|
+
plan.externalDependencies,
|
|
5665
|
+
this.processedContentCacheFingerprints,
|
|
5666
|
+
this.pluginRuntime.getManifestHookCache(),
|
|
5667
|
+
this.publishingBuildTime
|
|
5668
|
+
)
|
|
5669
|
+
);
|
|
5670
|
+
this.buildStateCommitted = true;
|
|
5671
|
+
}
|
|
5672
|
+
getBuildPlan(options) {
|
|
5673
|
+
if (this.buildPlan) return this.buildPlan;
|
|
5674
|
+
const plan = this.createBuildPlan(options);
|
|
5675
|
+
this.buildPlan = plan;
|
|
5676
|
+
void plan.catch(() => {
|
|
5677
|
+
if (this.buildPlan === plan) this.buildPlan = null;
|
|
5678
|
+
});
|
|
5679
|
+
return plan;
|
|
5680
|
+
}
|
|
5681
|
+
async createBuildPlan(options) {
|
|
5682
|
+
const config = this.pipelineOptions.config;
|
|
5683
|
+
if (config && hasGlobalContentDependency(config)) {
|
|
5684
|
+
this.pipelineFingerprint = this.computePipelineFingerprint(
|
|
5685
|
+
await this.getContentLocations()
|
|
5686
|
+
);
|
|
5687
|
+
}
|
|
5688
|
+
const contentIndex = await this.getContentIndex();
|
|
5689
|
+
const external = await this.observability().tracer.span(
|
|
5690
|
+
"external.dependencies.resolve",
|
|
5691
|
+
{},
|
|
5692
|
+
() => this.pluginRuntime.resolveExternalDependencies(contentIndex)
|
|
5693
|
+
);
|
|
5694
|
+
return this.buildCoordinator.getPlan(
|
|
5695
|
+
options?.incremental !== false,
|
|
5696
|
+
this.getPipelineFingerprint(),
|
|
5697
|
+
this.requiresFullContentRegeneration(),
|
|
5698
|
+
external.snapshots,
|
|
5699
|
+
external.failures,
|
|
5700
|
+
this.publishingBuildTime
|
|
5701
|
+
);
|
|
5702
|
+
}
|
|
5703
|
+
computePipelineFingerprint(locations) {
|
|
5704
|
+
const config = this.pipelineOptions.config;
|
|
5705
|
+
if (!config) return "";
|
|
5706
|
+
const contentScopeFingerprint = locations && hasGlobalContentDependency(config) ? computeContentScopeFingerprint(locations) : void 0;
|
|
5707
|
+
return computePipelineFingerprint(config, contentScopeFingerprint);
|
|
5708
|
+
}
|
|
4933
5709
|
requiresFullContentRegeneration() {
|
|
4934
5710
|
const config = this.pipelineOptions.config;
|
|
4935
|
-
return config ?
|
|
5711
|
+
return config ? requiresFullContentRegeneration(config) : false;
|
|
5712
|
+
}
|
|
5713
|
+
getProcessedContentCacheFingerprint(slug, source) {
|
|
5714
|
+
const config = this.pipelineOptions.config;
|
|
5715
|
+
if (!config || !isPersistentlyCacheable(slug, config, source).cacheable) {
|
|
5716
|
+
this.processedContentCacheFingerprints.set(slug, void 0);
|
|
5717
|
+
return void 0;
|
|
5718
|
+
}
|
|
5719
|
+
const fingerprint = computeContentCacheFingerprint(slug, source, config);
|
|
5720
|
+
this.processedContentCacheFingerprints.set(slug, fingerprint);
|
|
5721
|
+
return fingerprint;
|
|
4936
5722
|
}
|
|
4937
5723
|
isRoutable(slug) {
|
|
4938
5724
|
if (!this.routableSlugs) {
|
|
@@ -5009,8 +5795,7 @@ function cloneManifestEntry(entry) {
|
|
|
5009
5795
|
links: entry.links.map((link) => ({ ...link })),
|
|
5010
5796
|
backlinks: [...entry.backlinks],
|
|
5011
5797
|
assets: entry.assets.map((asset) => ({ ...asset })),
|
|
5012
|
-
...entry.headTags ? { headTags: [...entry.headTags] } : {}
|
|
5013
|
-
...entry.bodySlots ? { bodySlots: { ...entry.bodySlots } } : {}
|
|
5798
|
+
...entry.headTags ? { headTags: [...entry.headTags] } : {}
|
|
5014
5799
|
};
|
|
5015
5800
|
}
|
|
5016
5801
|
async function mapConcurrent3(values, concurrency, map) {
|
|
@@ -5042,6 +5827,12 @@ async function getContentSourceEntry(source, logicalPath) {
|
|
|
5042
5827
|
return (await source.scan()).find((candidate) => candidate.path === logicalPath) ?? null;
|
|
5043
5828
|
}
|
|
5044
5829
|
|
|
5830
|
+
// src/content/external_fingerprint.ts
|
|
5831
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
5832
|
+
function fingerprintExternalValue(value) {
|
|
5833
|
+
return createHash7("sha256").update(stableStringify(value)).digest("hex");
|
|
5834
|
+
}
|
|
5835
|
+
|
|
5045
5836
|
// src/content/folder_entry.ts
|
|
5046
5837
|
function resolveFolderEntry(manifest, folder, options = {}) {
|
|
5047
5838
|
const base = normalizeFolder2(folder);
|
|
@@ -5307,6 +6098,105 @@ function clampFinite(value, min, max) {
|
|
|
5307
6098
|
return clamp(value, min, max);
|
|
5308
6099
|
}
|
|
5309
6100
|
|
|
6101
|
+
// src/content/public_content_view.ts
|
|
6102
|
+
var emptyEntries = Object.freeze([]);
|
|
6103
|
+
function createPublicContentView(manifest) {
|
|
6104
|
+
const routableEntries = manifest.entries.filter(
|
|
6105
|
+
(entry) => entry.publishing.routable
|
|
6106
|
+
);
|
|
6107
|
+
const discoverableEntries = routableEntries.filter(
|
|
6108
|
+
(entry) => entry.publishing.discoverable
|
|
6109
|
+
);
|
|
6110
|
+
const routableSlugs = new Set(routableEntries.map((entry) => entry.slug));
|
|
6111
|
+
const copiesBySlug = new Map(
|
|
6112
|
+
routableEntries.map((entry) => [
|
|
6113
|
+
entry.slug,
|
|
6114
|
+
copyEntry(entry, routableSlugs)
|
|
6115
|
+
])
|
|
6116
|
+
);
|
|
6117
|
+
const discoverableSlugs = new Set(
|
|
6118
|
+
discoverableEntries.map((entry) => entry.slug)
|
|
6119
|
+
);
|
|
6120
|
+
const routableCopies = routableEntries.map((entry) => copiesBySlug.get(entry.slug)).filter(isDefined);
|
|
6121
|
+
const discoverableCopies = discoverableEntries.map((entry) => copiesBySlug.get(entry.slug)).filter(isDefined);
|
|
6122
|
+
const aliases = createAliasIndex2(routableCopies);
|
|
6123
|
+
const tags = createTagIndex(discoverableCopies);
|
|
6124
|
+
const getDiscoverableRelated = (relatedSlugs) => Object.freeze(
|
|
6125
|
+
relatedSlugs.filter((relatedSlug) => discoverableSlugs.has(relatedSlug)).map((relatedSlug) => copiesBySlug.get(relatedSlug)).filter(isDefined)
|
|
6126
|
+
);
|
|
6127
|
+
return Object.freeze({
|
|
6128
|
+
routableEntries: Object.freeze(routableCopies),
|
|
6129
|
+
discoverableEntries: Object.freeze(discoverableCopies),
|
|
6130
|
+
findRoutableBySlug: (slug) => copiesBySlug.get(slug),
|
|
6131
|
+
findDiscoverableBySlug: (slug) => discoverableSlugs.has(slug) ? copiesBySlug.get(slug) : void 0,
|
|
6132
|
+
findRoutableByAlias: (alias) => aliases.get(alias.toLowerCase()) ?? emptyEntries,
|
|
6133
|
+
getDiscoverableEntriesByTag: (tag) => tags.get(tag) ?? emptyEntries,
|
|
6134
|
+
getDiscoverableOutgoing: (slug) => getDiscoverableRelated(manifest.graph.outgoingSlugs(slug)),
|
|
6135
|
+
getDiscoverableIncoming: (slug) => getDiscoverableRelated(manifest.graph.incomingSlugs(slug)),
|
|
6136
|
+
getDiscoverableNeighbors: (slug) => getDiscoverableRelated(manifest.graph.neighborSlugs(slug))
|
|
6137
|
+
});
|
|
6138
|
+
}
|
|
6139
|
+
function copyEntry(entry, routableSlugs) {
|
|
6140
|
+
return deepFreeze2(
|
|
6141
|
+
structuredClone({
|
|
6142
|
+
slug: entry.slug,
|
|
6143
|
+
...entry.contentId === void 0 ? {} : { contentId: entry.contentId },
|
|
6144
|
+
permalink: entry.permalink,
|
|
6145
|
+
publicLocation: entry.publicLocation,
|
|
6146
|
+
title: entry.title,
|
|
6147
|
+
...entry.aliases === void 0 ? {} : { aliases: entry.aliases },
|
|
6148
|
+
publishing: {
|
|
6149
|
+
visibility: entry.publishing.visibility,
|
|
6150
|
+
routable: entry.publishing.routable,
|
|
6151
|
+
discoverable: entry.publishing.discoverable
|
|
6152
|
+
},
|
|
6153
|
+
tags: entry.tags,
|
|
6154
|
+
links: entry.links.filter(
|
|
6155
|
+
(link) => link.kind !== "note" || link.slug === null || routableSlugs.has(link.slug)
|
|
6156
|
+
),
|
|
6157
|
+
assets: entry.assets
|
|
6158
|
+
})
|
|
6159
|
+
);
|
|
6160
|
+
}
|
|
6161
|
+
function createAliasIndex2(entries) {
|
|
6162
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
6163
|
+
for (const entry of entries) {
|
|
6164
|
+
for (const alias of entry.aliases ?? []) {
|
|
6165
|
+
const matches = aliases.get(alias.toLowerCase()) ?? [];
|
|
6166
|
+
matches.push(entry);
|
|
6167
|
+
aliases.set(alias.toLowerCase(), matches);
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
return new Map(
|
|
6171
|
+
[...aliases].map(([alias, entries2]) => [alias, Object.freeze(entries2)])
|
|
6172
|
+
);
|
|
6173
|
+
}
|
|
6174
|
+
function createTagIndex(entries) {
|
|
6175
|
+
const tags = /* @__PURE__ */ new Map();
|
|
6176
|
+
for (const entry of entries) {
|
|
6177
|
+
for (const tag of entry.tags) {
|
|
6178
|
+
const taggedEntries = tags.get(tag) ?? [];
|
|
6179
|
+
taggedEntries.push(entry);
|
|
6180
|
+
tags.set(tag, taggedEntries);
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
return new Map(
|
|
6184
|
+
[...tags].map(([tag, entries2]) => [tag, Object.freeze(entries2)])
|
|
6185
|
+
);
|
|
6186
|
+
}
|
|
6187
|
+
function deepFreeze2(value) {
|
|
6188
|
+
if (value === null || typeof value !== "object") {
|
|
6189
|
+
return value;
|
|
6190
|
+
}
|
|
6191
|
+
for (const child of Object.values(value)) {
|
|
6192
|
+
deepFreeze2(child);
|
|
6193
|
+
}
|
|
6194
|
+
return Object.freeze(value);
|
|
6195
|
+
}
|
|
6196
|
+
function isDefined(value) {
|
|
6197
|
+
return value !== void 0;
|
|
6198
|
+
}
|
|
6199
|
+
|
|
5310
6200
|
// src/content/read_only_content_graph.ts
|
|
5311
6201
|
async function readOnlyContentGraph(source, locations) {
|
|
5312
6202
|
const entries = await source.scan();
|
|
@@ -5370,25 +6260,6 @@ function resolveBuildOutputDirectory(config) {
|
|
|
5370
6260
|
return directory ? path5.resolve(directory) : void 0;
|
|
5371
6261
|
}
|
|
5372
6262
|
|
|
5373
|
-
// src/types/content_manifest.ts
|
|
5374
|
-
var ARTICLE_SLOT = {
|
|
5375
|
-
header: "article.header",
|
|
5376
|
-
metadata: "article.metadata",
|
|
5377
|
-
aside: "article.aside",
|
|
5378
|
-
beforeContent: "article.before-content",
|
|
5379
|
-
afterContent: "article.after-content",
|
|
5380
|
-
footer: "article.footer"
|
|
5381
|
-
};
|
|
5382
|
-
function appendContentBodySlot(entry, slot, html) {
|
|
5383
|
-
if (!html.trim()) return;
|
|
5384
|
-
const previous = entry.bodySlots?.[slot];
|
|
5385
|
-
entry.bodySlots = {
|
|
5386
|
-
...entry.bodySlots,
|
|
5387
|
-
[slot]: previous ? `${previous}
|
|
5388
|
-
${html}` : html
|
|
5389
|
-
};
|
|
5390
|
-
}
|
|
5391
|
-
|
|
5392
6263
|
// src/types/plugin_endpoint.ts
|
|
5393
6264
|
function defineEndpoint(path6, handler, options = {}) {
|
|
5394
6265
|
if (options.cacheControl === void 0) {
|
|
@@ -5456,8 +6327,8 @@ function calculateReadingTime(html) {
|
|
|
5456
6327
|
return Math.max(1, Math.ceil(cjkCharacters / 500 + latinWords / 220));
|
|
5457
6328
|
}
|
|
5458
6329
|
export {
|
|
5459
|
-
ARTICLE_SLOT,
|
|
5460
6330
|
ATTACHMENTS_BASE_PATH,
|
|
6331
|
+
CONTENT_BUILD_STATE_VERSION,
|
|
5461
6332
|
CompositeTraceSink,
|
|
5462
6333
|
ConfigValidationError,
|
|
5463
6334
|
ConsoleLogger,
|
|
@@ -5471,7 +6342,6 @@ export {
|
|
|
5471
6342
|
PluginDependencyError,
|
|
5472
6343
|
PluginHookError,
|
|
5473
6344
|
SinkTracer,
|
|
5474
|
-
appendContentBodySlot,
|
|
5475
6345
|
attachErrorPath,
|
|
5476
6346
|
attachmentUrl,
|
|
5477
6347
|
buildContentCollections,
|
|
@@ -5482,6 +6352,7 @@ export {
|
|
|
5482
6352
|
createClientEntry,
|
|
5483
6353
|
createContentGraph,
|
|
5484
6354
|
createPluginMemo,
|
|
6355
|
+
createPublicContentView,
|
|
5485
6356
|
createStyleAsset,
|
|
5486
6357
|
createUnavailableGeneratedOutputSink,
|
|
5487
6358
|
defineConfig,
|
|
@@ -5494,6 +6365,7 @@ export {
|
|
|
5494
6365
|
escapeScriptJson,
|
|
5495
6366
|
extractFrontmatterAliases,
|
|
5496
6367
|
fingerprintContentEntries,
|
|
6368
|
+
fingerprintExternalValue,
|
|
5497
6369
|
getContentSourceEntry,
|
|
5498
6370
|
getEntryLanguage,
|
|
5499
6371
|
getExtension,
|