@riebeckite/core 0.0.19 → 0.0.20

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 CHANGED
@@ -37,7 +37,7 @@ export default defineConfig({
37
37
  ```
38
38
 
39
39
  The content directory is resolved relative to the application root. See
40
- [Configuration](../../docs/docs/reference/configuration.en.md) for the full field list and the
40
+ [Configuration](../../docs/docs/reference/configuration.md) for the full field list and the
41
41
  `projectRoot` / `appRoot` / `configRoot` / `contentRoot` resolution rules.
42
42
 
43
43
  ## Public API
@@ -88,8 +88,8 @@ The content directory is resolved relative to the application root. See
88
88
 
89
89
  ## See also
90
90
 
91
- - [Framework Reference](../../docs/docs/reference/README.en.md)
92
- - [Content System](../../docs/docs/framework/content-system.en.md)
93
- - [Plugin System](../../docs/docs/reference/plugin-api.en.md) / [Theme System](../../docs/docs/reference/theme-api.en.md)
94
- - [Architecture](../../docs/docs/framework/architecture.en.md)
91
+ - [Framework Reference](../../docs/docs/reference/README.md)
92
+ - [Content System](../../docs/docs/framework/content-system.md)
93
+ - [Plugin System](../../docs/docs/reference/plugin-api.md) / [Theme System](../../docs/docs/reference/theme-api.md)
94
+ - [Architecture](../../docs/docs/framework/architecture.md)
95
95
 
package/README_ja.md CHANGED
@@ -29,7 +29,7 @@ export default defineConfig({
29
29
  });
30
30
  ```
31
31
 
32
- コンテンツディレクトリはアプリケーションルート基準で解決します。設定項目の全体と `projectRoot` / `appRoot` / `configRoot` / `contentRoot` の解決規則は [Configuration](../../docs/docs/reference/configuration.md) を参照してください。
32
+ コンテンツディレクトリはアプリケーションルート基準で解決します。設定項目の全体と `projectRoot` / `appRoot` / `configRoot` / `contentRoot` の解決規則は [Configuration](../../docs/docs/reference/configuration.ja.md) を参照してください。
33
33
 
34
34
  ## 公開 API
35
35
 
@@ -69,8 +69,8 @@ export default defineConfig({
69
69
 
70
70
  ## 関連資料
71
71
 
72
- - [Framework Reference](../../docs/docs/reference/README.md)
73
- - [Content System](../../docs/docs/framework/content-system.md)
74
- - [Plugin System](../../docs/docs/reference/plugin-api.md) / [Theme System](../../docs/docs/reference/theme-api.md)
75
- - [Architecture](../../docs/docs/framework/architecture.md)
72
+ - [Framework Reference](../../docs/docs/reference/README.ja.md)
73
+ - [Content System](../../docs/docs/framework/content-system.ja.md)
74
+ - [Plugin System](../../docs/docs/reference/plugin-api.ja.md) / [Theme System](../../docs/docs/reference/theme-api.ja.md)
75
+ - [Architecture](../../docs/docs/framework/architecture.ja.md)
76
76
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export { defineConfig, isExcluded, isPublished, resolveConfig, resolveConfigModule, } from "./src/config.js";
2
- export { type ConfigDeprecationNotice, collectConfigDeprecationDiagnostics, } from "./src/config_deprecations.js";
3
2
  export { ConfigValidationError } from "./src/config_validation.js";
4
3
  export { ATTACHMENTS_BASE_PATH, attachmentUrl, getExtension, isAttachmentPath, isImagePath, isMarkdownPath, normalizeContentPath, } from "./src/content/attachment.js";
5
4
  export type { ContentBuildState, FingerprintedContentEntry, } from "./src/content/content_build_state.js";
@@ -33,13 +32,13 @@ export { buildOutputInventory, determineOutputChanges, htmlOutputPath, } from ".
33
32
  export type { PublishingResolutionOptions, PublishingVisibility, ResolvedPublishingState, } from "./src/content/publishing.js";
34
33
  export { resolvePublishingBuildTime, resolvePublishingState, } from "./src/content/publishing.js";
35
34
  export { readOnlyContentGraph } from "./src/content/read_only_content_graph.js";
36
- export { createDeprecationDiagnostic, type DeprecationKind, type DeprecationNotice, formatDeprecationMessage, } from "./src/deprecation.js";
37
35
  export type { LogContext, Logger, LogLevel, Observability, ObservabilityValue, TraceAttributes, TraceEvent, Tracer, TraceSink, TraceSpan, } from "./src/observability.js";
38
36
  export { CompositeTraceSink, ConsoleLogger, NoopLogger, NoopTracer, SinkTracer, } from "./src/observability.js";
39
37
  export type { PipelineOptions } from "./src/pipeline.js";
40
38
  export { Pipeline } from "./src/pipeline.js";
41
39
  export type { PluginCache } from "./src/plugin/plugin_cache.js";
42
40
  export { PluginDependencyError, type PluginDependencyErrorKind, } from "./src/plugin/plugin_dependency_error.js";
41
+ export { PluginHookError } from "./src/plugin/plugin_lifecycle.js";
43
42
  export type { CreatePluginMemoOptions, PluginMemo, } from "./src/plugin/plugin_memo.js";
44
43
  export { createPluginMemo, stableStringify, } from "./src/plugin/plugin_memo.js";
45
44
  export { resolveBuildOutputDirectory, resolveManagedStateRoot, } from "./src/project/project_layout.js";
@@ -74,5 +73,7 @@ export { defineTheme } from "./src/types/theme_config.js";
74
73
  export { uniqueStrings } from "./src/utils/collections.js";
75
74
  export { attachErrorPath } from "./src/utils/error.js";
76
75
  export { escapeHtml, escapeHtmlAttribute, escapeScriptJson, } from "./src/utils/html.js";
76
+ export type { MarkdownFenceLine } from "./src/utils/markdown.js";
77
+ export { scanMarkdownFenceLines } from "./src/utils/markdown.js";
77
78
  export { normalizeTag } from "./src/utils/tags.js";
78
79
  export { calculateReadingTime, stripHtml } from "./src/utils/text.js";
package/dist/index.js CHANGED
@@ -778,46 +778,6 @@ function globToRegExp(glob) {
778
778
  return new RegExp(`^${re}$`);
779
779
  }
780
780
 
781
- // src/deprecation.ts
782
- function createDeprecationDiagnostic(notice) {
783
- return {
784
- code: `deprecated-${notice.kind}`,
785
- severity: "warning",
786
- message: formatDeprecationMessage(notice),
787
- target: notice.target,
788
- suggestion: notice.replacement ? `Use ${notice.replacement}. ${notice.action}` : notice.action,
789
- meta: { deprecation: notice }
790
- };
791
- }
792
- function formatDeprecationMessage(notice) {
793
- const lines = [
794
- `Deprecated: ${notice.target}`,
795
- `Deprecated since: ${notice.deprecatedSince}`
796
- ];
797
- if (notice.replacement) lines.push(`Use: ${notice.replacement}`);
798
- lines.push(`Migration: ${notice.action}`);
799
- if (notice.documentationUrl) lines.push(`See: ${notice.documentationUrl}`);
800
- if (notice.removedIn) lines.push(`Removal planned: ${notice.removedIn}`);
801
- return lines.join("\n");
802
- }
803
-
804
- // src/config_deprecations.ts
805
- var configDeprecations = [];
806
- function collectConfigDeprecationDiagnostics(config, notices = configDeprecations) {
807
- return notices.filter((notice) => hasConfigPath(config, notice.path)).map(createDeprecationDiagnostic);
808
- }
809
- function hasConfigPath(config, path6) {
810
- let value = config;
811
- for (const segment of path6.split(".")) {
812
- if (!isRecord2(value) || !(segment in value)) return false;
813
- value = value[segment];
814
- }
815
- return value !== void 0;
816
- }
817
- function isRecord2(value) {
818
- return typeof value === "object" && value !== null && !Array.isArray(value);
819
- }
820
-
821
781
  // src/content/image_extensions.ts
822
782
  var IMAGE_EXTENSIONS = [
823
783
  "png",
@@ -895,7 +855,7 @@ async function readContentBuildStateStatus(statePath) {
895
855
  }
896
856
  throw error;
897
857
  }
898
- if (!isRecord3(parsed)) {
858
+ if (!isRecord2(parsed)) {
899
859
  return { kind: "invalid", path: statePath, reason: "invalid-shape" };
900
860
  }
901
861
  if (parsed.version !== CONTENT_BUILD_STATE_VERSION) {
@@ -935,15 +895,15 @@ function resolveContentBuildStatePath(config, contentDirectory) {
935
895
  );
936
896
  }
937
897
  function isContentBuildState(value) {
938
- return isRecord3(value) && value.version === CONTENT_BUILD_STATE_VERSION && isContentBuildStateShape(value);
898
+ return isRecord2(value) && value.version === CONTENT_BUILD_STATE_VERSION && isContentBuildStateShape(value);
939
899
  }
940
900
  function isContentBuildStateShape(value) {
941
- if (!isRecord3(value.entries) || !isRecord3(value.contentIndex) || value.outputs !== void 0 && !isOutputDescriptors(value.outputs)) {
901
+ if (!isRecord2(value.entries) || !isRecord2(value.contentIndex) || value.outputs !== void 0 && !isOutputDescriptors(value.outputs)) {
942
902
  return false;
943
903
  }
944
904
  return Object.values(value.entries).every(
945
- (entry) => isRecord3(entry) && typeof entry.fingerprint === "string" && entry.fingerprint.length > 0 && Array.isArray(entry.aliases) && entry.aliases.every((alias) => typeof alias === "string") && Array.isArray(entry.dependencies) && entry.dependencies.every(
946
- (dependency) => isRecord3(dependency) && (dependency.kind === "content" || dependency.kind === "file") && typeof dependency.id === "string"
905
+ (entry) => isRecord2(entry) && typeof entry.fingerprint === "string" && entry.fingerprint.length > 0 && Array.isArray(entry.aliases) && entry.aliases.every((alias) => typeof alias === "string") && Array.isArray(entry.dependencies) && entry.dependencies.every(
906
+ (dependency) => isRecord2(dependency) && (dependency.kind === "content" || dependency.kind === "file") && typeof dependency.id === "string"
947
907
  )
948
908
  ) && Object.values(value.contentIndex).every(
949
909
  (entry) => typeof entry === "string"
@@ -951,7 +911,7 @@ function isContentBuildStateShape(value) {
951
911
  }
952
912
  function isOutputDescriptors(value) {
953
913
  return Array.isArray(value) && value.every(
954
- (output) => isRecord3(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)
914
+ (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)
955
915
  );
956
916
  }
957
917
  function isOutputPath(value) {
@@ -961,11 +921,11 @@ function isOutputPath(value) {
961
921
  return value.split("/").every((segment) => segment !== "." && segment !== "..");
962
922
  }
963
923
  function isOutputDependency(value) {
964
- if (!isRecord3(value) || typeof value.type !== "string") return false;
924
+ if (!isRecord2(value) || typeof value.type !== "string") return false;
965
925
  if (value.type === "global" || value.type === "unknown") return true;
966
926
  return value.type === "content" && typeof value.slug === "string" || value.type === "tag" && typeof value.tag === "string" || value.type === "folder" && typeof value.folder === "string";
967
927
  }
968
- function isRecord3(value) {
928
+ function isRecord2(value) {
969
929
  return typeof value === "object" && value !== null && !Array.isArray(value);
970
930
  }
971
931
  function isNotFoundError(error) {
@@ -1727,44 +1687,25 @@ async function writeAtomically(targetPath, dir, serialized) {
1727
1687
  throw error;
1728
1688
  }
1729
1689
  }
1690
+ function isMissingFileError(error) {
1691
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1692
+ }
1730
1693
  function createPersistentContentCache(options) {
1731
1694
  const { config, logger, tracer } = options;
1732
1695
  const baseDir = cacheDirectory(config);
1733
1696
  const versionDir = versionDirectory(baseDir);
1734
- return {
1735
- async get(key) {
1736
- if (!config.cache?.enabled) return void 0;
1697
+ const cache = {
1698
+ async lookup(key) {
1699
+ if (!config.cache?.enabled) {
1700
+ return { status: "miss", reason: "disabled" };
1701
+ }
1737
1702
  const filePath = cacheFilePath(baseDir, key);
1703
+ let raw;
1738
1704
  try {
1739
- const raw = await fs2.readFile(filePath, "utf8");
1740
- const entry = JSON.parse(raw);
1741
- if (entry.schemaVersion !== CONTENT_CACHE_SCHEMA_VERSION) {
1742
- tracer?.event("persistentContentCache.schemaMismatch", { key });
1743
- return void 0;
1744
- }
1745
- if (entry.key !== key) {
1746
- tracer?.event("persistentContentCache.keyMismatch", { key });
1747
- return void 0;
1748
- }
1749
- if (!isValidCacheEntry(entry)) {
1750
- tracer?.event("persistentContentCache.invalidShape", { key });
1751
- return void 0;
1752
- }
1753
- tracer?.event("persistentContentCache.found", { key });
1754
- return entry;
1705
+ raw = await fs2.readFile(filePath, "utf8");
1755
1706
  } catch (error) {
1756
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
1757
- tracer?.event("persistentContentCache.miss", { key });
1758
- return void 0;
1759
- }
1760
- if (error instanceof SyntaxError) {
1761
- logger?.warn(
1762
- "Persistent content cache entry is corrupted and will be ignored.",
1763
- {
1764
- key
1765
- }
1766
- );
1767
- return void 0;
1707
+ if (isMissingFileError(error)) {
1708
+ return { status: "miss", reason: "no-entry" };
1768
1709
  }
1769
1710
  logger?.warn(
1770
1711
  "Persistent content cache entry could not be read and will be ignored.",
@@ -1773,8 +1714,38 @@ function createPersistentContentCache(options) {
1773
1714
  error: error instanceof Error ? error.message : String(error)
1774
1715
  }
1775
1716
  );
1776
- return void 0;
1717
+ return { status: "miss", reason: "unreadable" };
1777
1718
  }
1719
+ let entry;
1720
+ try {
1721
+ entry = JSON.parse(raw);
1722
+ } catch (error) {
1723
+ logger?.warn(
1724
+ "Persistent content cache entry is corrupted and will be ignored.",
1725
+ {
1726
+ key,
1727
+ error: error instanceof Error ? error.message : String(error)
1728
+ }
1729
+ );
1730
+ return { status: "miss", reason: "corrupted" };
1731
+ }
1732
+ if (typeof entry !== "object" || entry === null) {
1733
+ return { status: "miss", reason: "invalid-entry" };
1734
+ }
1735
+ if (entry.schemaVersion !== CONTENT_CACHE_SCHEMA_VERSION) {
1736
+ return { status: "miss", reason: "schema-mismatch" };
1737
+ }
1738
+ if (entry.key !== key) {
1739
+ return { status: "miss", reason: "key-mismatch" };
1740
+ }
1741
+ if (!isValidCacheEntry(entry)) {
1742
+ return { status: "miss", reason: "invalid-entry" };
1743
+ }
1744
+ return { status: "hit", entry };
1745
+ },
1746
+ async get(key) {
1747
+ const result = await cache.lookup(key);
1748
+ return result.status === "hit" ? result.entry : void 0;
1778
1749
  },
1779
1750
  async set(key, value) {
1780
1751
  if (!config.cache?.enabled) return;
@@ -1805,6 +1776,7 @@ function createPersistentContentCache(options) {
1805
1776
  }
1806
1777
  }
1807
1778
  };
1779
+ return cache;
1808
1780
  }
1809
1781
  function isValidCacheEntry(entry) {
1810
1782
  return typeof entry === "object" && entry !== null && typeof entry.key === "string" && typeof entry.schemaVersion === "number" && Array.isArray(entry.dependencies) && entry.dependencies.every(isValidDependency) && typeof entry.value === "object" && entry.value !== null && typeof entry.value.html === "string" && typeof entry.value.frontmatter === "object" && entry.value.frontmatter !== null && !Array.isArray(entry.value.frontmatter);
@@ -2153,41 +2125,15 @@ var Pipeline = class {
2153
2125
  frontmatter,
2154
2126
  pipelineFingerprint: this.pipelineFingerprint
2155
2127
  });
2156
- const cachedEntry = await this.persistentCache.get(cacheKey);
2157
- if (cachedEntry) {
2158
- const validationTracker = createContentDependencyTracker(
2159
- this.options.contentSource
2128
+ const lookup = await this.persistentCache.lookup(cacheKey);
2129
+ if (lookup.status === "hit") {
2130
+ const invalidDependency = await this.findInvalidDependency(
2131
+ lookup.entry.dependencies
2160
2132
  );
2161
- let valid = true;
2162
- for (const dep of cachedEntry.dependencies) {
2163
- if (dep.kind === "content") {
2164
- const content = await validationTracker.readContent(
2165
- dep.id,
2166
- async () => await this.getMarkdownBySlug?.(dep.id) ?? ""
2167
- );
2168
- if (fingerprintContent(content) !== dep.fingerprint) {
2169
- valid = false;
2170
- break;
2171
- }
2172
- } else if (dep.kind === "file") {
2173
- const fileContent = await validationTracker.contentSource.read({
2174
- path: dep.id
2175
- });
2176
- if (fingerprintContent(fileContent) !== dep.fingerprint) {
2177
- valid = false;
2178
- break;
2179
- }
2180
- } else if (dep.kind === "link") {
2181
- if (fingerprintContent(this.resolveLinkDependency(dep.id)) !== dep.fingerprint) {
2182
- valid = false;
2183
- break;
2184
- }
2185
- }
2186
- }
2187
- if (valid) {
2133
+ if (!invalidDependency) {
2188
2134
  this.reportContentDependencies(
2189
2135
  sourceSlug,
2190
- cachedEntry.dependencies
2136
+ lookup.entry.dependencies
2191
2137
  );
2192
2138
  this.options.observability?.tracer?.event(
2193
2139
  "persistentContentCache.hit",
@@ -2195,16 +2141,32 @@ var Pipeline = class {
2195
2141
  );
2196
2142
  this.options.onPersistentContentCacheResult?.("hit");
2197
2143
  return {
2198
- frontmatter: cachedEntry.value.frontmatter,
2199
- html: cachedEntry.value.html
2144
+ frontmatter: lookup.entry.value.frontmatter,
2145
+ html: lookup.entry.value.html
2200
2146
  };
2201
2147
  }
2148
+ this.options.observability?.tracer?.event(
2149
+ "persistentContentCache.miss",
2150
+ {
2151
+ key: cacheKey,
2152
+ slug: sourceSlug,
2153
+ reason: "dependency-changed",
2154
+ dependencyKind: invalidDependency.kind,
2155
+ dependencyId: invalidDependency.id
2156
+ }
2157
+ );
2158
+ this.options.onPersistentContentCacheResult?.("miss");
2159
+ } else {
2160
+ this.options.observability?.tracer?.event(
2161
+ "persistentContentCache.miss",
2162
+ {
2163
+ key: cacheKey,
2164
+ slug: sourceSlug,
2165
+ reason: lookup.reason
2166
+ }
2167
+ );
2168
+ this.options.onPersistentContentCacheResult?.("miss");
2202
2169
  }
2203
- this.options.observability?.tracer?.event(
2204
- "persistentContentCache.miss",
2205
- { key: cacheKey, slug: sourceSlug }
2206
- );
2207
- this.options.onPersistentContentCacheResult?.("miss");
2208
2170
  } else {
2209
2171
  this.options.observability?.tracer?.event(
2210
2172
  "persistentContentCache.bypass",
@@ -2316,6 +2278,30 @@ var Pipeline = class {
2316
2278
  reportContentDependencies(slug, dependencies) {
2317
2279
  this.options.onContentDependencies?.(slug, dependencies);
2318
2280
  }
2281
+ async findInvalidDependency(dependencies) {
2282
+ const contentSource = this.options.contentSource;
2283
+ if (!contentSource) return null;
2284
+ const validationTracker = createContentDependencyTracker(contentSource);
2285
+ for (const dep of dependencies) {
2286
+ if (dep.kind === "content") {
2287
+ const content = await validationTracker.readContent(
2288
+ dep.id,
2289
+ async () => await this.getMarkdownBySlug?.(dep.id) ?? ""
2290
+ );
2291
+ if (fingerprintContent(content) !== dep.fingerprint) return dep;
2292
+ } else if (dep.kind === "file") {
2293
+ const fileContent = await validationTracker.contentSource.read({
2294
+ path: dep.id
2295
+ });
2296
+ if (fingerprintContent(fileContent) !== dep.fingerprint) return dep;
2297
+ } else if (dep.kind === "link") {
2298
+ if (fingerprintContent(this.resolveLinkDependency(dep.id)) !== dep.fingerprint) {
2299
+ return dep;
2300
+ }
2301
+ }
2302
+ }
2303
+ return null;
2304
+ }
2319
2305
  getPermalink(slug) {
2320
2306
  const permalink = this.permalinks.get(slug);
2321
2307
  if (!permalink) {
@@ -3848,10 +3834,12 @@ var FileSystemContentSource = class {
3848
3834
  async scanWithExclusions() {
3849
3835
  const exclusions = [];
3850
3836
  try {
3837
+ const templaterTemplateFolder = await this.resolveTemplaterTemplateFolder();
3851
3838
  const entries = await this.scanDirectory(
3852
3839
  this.contentDirectory,
3853
3840
  "",
3854
- exclusions
3841
+ exclusions,
3842
+ templaterTemplateFolder
3855
3843
  );
3856
3844
  return { entries, exclusions };
3857
3845
  } catch (error) {
@@ -3869,7 +3857,7 @@ var FileSystemContentSource = class {
3869
3857
  }
3870
3858
  return await fs4.readFile(filePath);
3871
3859
  }
3872
- async scanDirectory(directory, parentPath2, exclusions) {
3860
+ async scanDirectory(directory, parentPath2, exclusions, templaterTemplateFolder) {
3873
3861
  const directoryEntries = await fs4.readdir(directory, {
3874
3862
  withFileTypes: true
3875
3863
  });
@@ -3886,9 +3874,21 @@ var FileSystemContentSource = class {
3886
3874
  exclusions.push({ path: logicalPath, pattern });
3887
3875
  continue;
3888
3876
  }
3877
+ if (templaterTemplateFolder && isTemplaterTemplatePath(logicalPath, templaterTemplateFolder)) {
3878
+ exclusions.push({
3879
+ path: logicalPath,
3880
+ pattern: `Obsidian Templater templates_folder: ${templaterTemplateFolder}`
3881
+ });
3882
+ continue;
3883
+ }
3889
3884
  if (directoryEntry.isDirectory()) {
3890
3885
  entries.push(
3891
- ...await this.scanDirectory(filePath, logicalPath, exclusions)
3886
+ ...await this.scanDirectory(
3887
+ filePath,
3888
+ logicalPath,
3889
+ exclusions,
3890
+ templaterTemplateFolder
3891
+ )
3892
3892
  );
3893
3893
  continue;
3894
3894
  }
@@ -3913,6 +3913,28 @@ var FileSystemContentSource = class {
3913
3913
  }
3914
3914
  return filePath;
3915
3915
  }
3916
+ async resolveTemplaterTemplateFolder() {
3917
+ const settingsPath = path4.join(
3918
+ this.contentDirectory,
3919
+ ".obsidian",
3920
+ "plugins",
3921
+ "templater-obsidian",
3922
+ "data.json"
3923
+ );
3924
+ let raw;
3925
+ try {
3926
+ raw = await fs4.readFile(settingsPath, "utf8");
3927
+ } catch (error) {
3928
+ if (isFileNotFoundError(error)) return void 0;
3929
+ throw error;
3930
+ }
3931
+ try {
3932
+ const settings = JSON.parse(raw);
3933
+ return normalizeTemplaterTemplateFolder(settings.templates_folder);
3934
+ } catch {
3935
+ return void 0;
3936
+ }
3937
+ }
3916
3938
  };
3917
3939
  function isIgnoredContentPath(logicalPath, exclude = []) {
3918
3940
  if (isExcluded(INTERNAL_CONTENT_IGNORE_PATTERNS, logicalPath)) return true;
@@ -3925,6 +3947,17 @@ function matchContentExcludePattern(logicalPath, exclude = []) {
3925
3947
  const withoutExtension = logicalPath.slice(0, -3);
3926
3948
  return exclude.find((pattern) => isExcluded([pattern], withoutExtension));
3927
3949
  }
3950
+ function isTemplaterTemplatePath(logicalPath, templateFolder) {
3951
+ return logicalPath === templateFolder || logicalPath.startsWith(`${templateFolder}/`);
3952
+ }
3953
+ function normalizeTemplaterTemplateFolder(value) {
3954
+ if (typeof value !== "string" || value.trim().length === 0) return void 0;
3955
+ const normalized = normalizeContentPath(value.trim()).replace(/\/+$/, "");
3956
+ if (normalized.length === 0 || normalized.startsWith("/") || path4.isAbsolute(normalized) || path4.win32.isAbsolute(normalized) || normalized.split("/").some((segment) => segment === "..")) {
3957
+ return void 0;
3958
+ }
3959
+ return normalized;
3960
+ }
3928
3961
  function normalizeLogicalPath(logicalPath) {
3929
3962
  const normalizedPath = normalizeContentPath(logicalPath);
3930
3963
  if (normalizedPath.length === 0 || normalizedPath.startsWith("/") || path4.isAbsolute(normalizedPath) || path4.win32.isAbsolute(normalizedPath) || normalizedPath.split("/").some((segment) => segment === "..")) {
@@ -3935,6 +3968,9 @@ function normalizeLogicalPath(logicalPath) {
3935
3968
  function isMissingDirectoryError(error, directory) {
3936
3969
  return error instanceof Error && "code" in error && error.code === "ENOENT" && "path" in error && error.path === directory;
3937
3970
  }
3971
+ function isFileNotFoundError(error) {
3972
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
3973
+ }
3938
3974
  function missingContentDirectoryError(directory) {
3939
3975
  const relative = path4.relative(process.cwd(), directory);
3940
3976
  const display = relative && !relative.startsWith("..") ? relative : directory;
@@ -4370,6 +4406,7 @@ var ContentManager = class {
4370
4406
  routableSlugs = null;
4371
4407
  routableSlugsPromise = null;
4372
4408
  processedContentCount = 0;
4409
+ reusedContentCount = 0;
4373
4410
  outputChangeSet = null;
4374
4411
  trackedContentDependencies = /* @__PURE__ */ new Map();
4375
4412
  async getAllPosts() {
@@ -4597,6 +4634,12 @@ var ContentManager = class {
4597
4634
  async build(options = {}) {
4598
4635
  return await this.getManifest(options);
4599
4636
  }
4637
+ getProcessedContentCount() {
4638
+ return this.processedContentCount;
4639
+ }
4640
+ getReusedContentCount() {
4641
+ return this.reusedContentCount;
4642
+ }
4600
4643
  async getOutputChangeSet(options = {}) {
4601
4644
  await this.getManifest(options);
4602
4645
  if (!this.outputChangeSet) {
@@ -4735,6 +4778,7 @@ var ContentManager = class {
4735
4778
  if (preparation.affectedContent.dependent.has(slug)) return null;
4736
4779
  const entry = preparation.previousManifestEntriesBySlug.get(slug);
4737
4780
  if (!entry) return null;
4781
+ this.reusedContentCount += 1;
4738
4782
  this.observability().tracer.event("content.reuse", { slug });
4739
4783
  return cloneManifestEntry(entry);
4740
4784
  }
@@ -5218,6 +5262,32 @@ function escapeScriptJson(value) {
5218
5262
  return value.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
5219
5263
  }
5220
5264
 
5265
+ // src/utils/markdown.ts
5266
+ var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
5267
+ var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
5268
+ function scanMarkdownFenceLines(source) {
5269
+ const lines = [];
5270
+ let fence = null;
5271
+ for (const value of source.split("\n")) {
5272
+ if (fence === null) {
5273
+ const open = FENCE_OPEN.exec(value);
5274
+ if (open && !(open[1][0] === "`" && open[2].includes("`"))) {
5275
+ fence = { marker: open[1][0], length: open[1].length };
5276
+ }
5277
+ lines.push({ value, inFence: false });
5278
+ continue;
5279
+ }
5280
+ const close = FENCE_CLOSE.exec(value);
5281
+ if (close && close[1][0] === fence.marker && close[1].length >= fence.length) {
5282
+ fence = null;
5283
+ lines.push({ value, inFence: false });
5284
+ continue;
5285
+ }
5286
+ lines.push({ value, inFence: true });
5287
+ }
5288
+ return lines;
5289
+ }
5290
+
5221
5291
  // src/utils/text.ts
5222
5292
  function stripHtml(html) {
5223
5293
  return html.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
@@ -5243,6 +5313,7 @@ export {
5243
5313
  NoopTracer,
5244
5314
  Pipeline,
5245
5315
  PluginDependencyError,
5316
+ PluginHookError,
5246
5317
  SinkTracer,
5247
5318
  appendContentBodySlot,
5248
5319
  attachErrorPath,
@@ -5251,11 +5322,9 @@ export {
5251
5322
  buildGraphEdges,
5252
5323
  buildOutputInventory,
5253
5324
  calculateReadingTime,
5254
- collectConfigDeprecationDiagnostics,
5255
5325
  computePipelineFingerprint,
5256
5326
  createClientEntry,
5257
5327
  createContentGraph,
5258
- createDeprecationDiagnostic,
5259
5328
  createPluginMemo,
5260
5329
  createStyleAsset,
5261
5330
  createUnavailableGeneratedOutputSink,
@@ -5269,7 +5338,6 @@ export {
5269
5338
  escapeScriptJson,
5270
5339
  extractFrontmatterAliases,
5271
5340
  fingerprintContentEntries,
5272
- formatDeprecationMessage,
5273
5341
  getContentSourceEntry,
5274
5342
  getEntryLanguage,
5275
5343
  getExtension,
@@ -5312,6 +5380,7 @@ export {
5312
5380
  resolvePublicFolderLocation,
5313
5381
  resolvePublishingBuildTime,
5314
5382
  resolvePublishingState,
5383
+ scanMarkdownFenceLines,
5315
5384
  selectEntriesByLanguage,
5316
5385
  serializePublicClientConfig,
5317
5386
  stableStringify,
@@ -44,6 +44,7 @@ export declare class ContentManager {
44
44
  private routableSlugs;
45
45
  private routableSlugsPromise;
46
46
  private processedContentCount;
47
+ private reusedContentCount;
47
48
  private outputChangeSet;
48
49
  private trackedContentDependencies;
49
50
  constructor(content: string | ContentSource, exclude?: string[], pipelineOptions?: PipelineOptions);
@@ -56,6 +57,8 @@ export declare class ContentManager {
56
57
  getManifest(options?: ContentBuildOptions): Promise<ContentManifest>;
57
58
  private buildManifest;
58
59
  build(options?: ContentBuildOptions): Promise<ContentManifest>;
60
+ getProcessedContentCount(): number;
61
+ getReusedContentCount(): number;
59
62
  getOutputChangeSet(options?: ContentBuildOptions): Promise<OutputChangeSet>;
60
63
  getBacklinks(targetSlug: string): Promise<Backlink[]>;
61
64
  getContentGraph(): Promise<ContentGraph>;
@@ -18,16 +18,21 @@ export type PersistentContentCacheOptions = {
18
18
  };
19
19
  export declare const CONTENT_CACHE_SCHEMA_VERSION = 3;
20
20
  export declare const CONTENT_CACHE_COMPATIBILITY_VERSION = 1;
21
- export type PersistentContentCache = {
22
- get(key: string): Promise<PersistentContentCacheEntry | undefined>;
23
- set(key: string, value: PersistentContentCacheEntry): Promise<void>;
24
- clear(): Promise<void>;
21
+ export type PersistentContentCacheMissReason = "disabled" | "no-entry" | "corrupted" | "unreadable" | "schema-mismatch" | "key-mismatch" | "invalid-entry";
22
+ export type PersistentContentCacheLookup = {
23
+ status: "hit";
24
+ entry: PersistentContentCacheEntry;
25
+ } | {
26
+ status: "miss";
27
+ reason: PersistentContentCacheMissReason;
25
28
  };
26
- export declare function createPersistentContentCache(options: PersistentContentCacheOptions): {
29
+ export type PersistentContentCache = {
30
+ lookup(key: string): Promise<PersistentContentCacheLookup>;
27
31
  get(key: string): Promise<PersistentContentCacheEntry | undefined>;
28
32
  set(key: string, value: PersistentContentCacheEntry): Promise<void>;
29
33
  clear(): Promise<void>;
30
34
  };
35
+ export declare function createPersistentContentCache(options: PersistentContentCacheOptions): PersistentContentCache;
31
36
  export type CacheKeyInputs = {
32
37
  slug: string;
33
38
  source: string;
@@ -17,6 +17,7 @@ export declare class FileSystemContentSource implements ContentSource {
17
17
  read(entry: ContentSourceEntry): Promise<ContentSourceContent>;
18
18
  private scanDirectory;
19
19
  private resolveFilePath;
20
+ private resolveTemplaterTemplateFolder;
20
21
  }
21
22
  export declare function isIgnoredContentPath(logicalPath: string, exclude?: readonly string[]): boolean;
22
23
  export declare function matchContentExcludePattern(logicalPath: string, exclude?: readonly string[]): string | undefined;
@@ -27,6 +27,7 @@ export declare class Pipeline {
27
27
  execute(markDownContent: string, context?: MarkdownExecutionContext): Promise<PostContent>;
28
28
  private executeWithEmbedState;
29
29
  private reportContentDependencies;
30
+ private findInvalidDependency;
30
31
  private getPermalink;
31
32
  private resolveLinkDependency;
32
33
  private recordNoteLinkDependencies;
@@ -114,15 +114,17 @@ export type ContentLocationInput = {
114
114
  markdown: string;
115
115
  };
116
116
  export type ContentManifest = {
117
+ /**
118
+ * Raw entries for every scanned content source, including drafts, scheduled,
119
+ * private, and otherwise nonpublic entries. This is not a publication view.
120
+ */
117
121
  entries: ContentManifestEntry[];
118
122
  /**
119
- * Entries that are routable and therefore get a generated page: `public`
120
- * and `unlisted` notes. `entries` keeps every scanned note for backward
121
- * compatibility; plugins that emit generated output should read this view
122
- * so drafts and scheduled notes never reach generated output.
123
+ * Routable entries for generated pages: `public` and `unlisted` notes.
124
+ * Plugins that emit public output must use this view rather than `entries`.
123
125
  */
124
126
  publicEntries: ContentManifestEntry[];
125
- /** Entries that may appear on discovery surfaces such as navigation, search, feeds, taxonomy, and public graphs. */
127
+ /** Public entries allowed on discovery surfaces such as navigation, search, feeds, taxonomy, and public graphs. */
126
128
  discoverableEntries: ContentManifestEntry[];
127
129
  bySlug: Map<string, ContentManifestEntry>;
128
130
  /** Contains only entries with an explicit source-authored content ID. */
@@ -26,7 +26,7 @@ export type RenderableFeedEntry = ContentManifestEntry & {
26
26
  };
27
27
  export type PluginSeoExtension = {
28
28
  buildArticleSeo(config: ResolvedRiebeckiteConfig, permalink: string, post: PostContent, headTags?: readonly PluginHeadTag[], language?: string): SeoMetadata;
29
- buildWebsiteSeo(config: ResolvedRiebeckiteConfig, input: WebsiteSeoInput, headTags?: readonly PluginHeadTag[]): SeoMetadata;
29
+ buildWebsiteSeo(config: ResolvedRiebeckiteConfig, input: WebsiteSeoInput, headTags?: readonly PluginHeadTag[], language?: string): SeoMetadata;
30
30
  buildAbsoluteUrl(config: ResolvedRiebeckiteConfig, pathOrUrl: string): string;
31
31
  buildPostUrl(config: ResolvedRiebeckiteConfig, permalink: string): string;
32
32
  getDescription(post: Pick<PostContent, "frontmatter" | "html">): string;
@@ -0,0 +1,5 @@
1
+ export type MarkdownFenceLine = {
2
+ readonly value: string;
3
+ readonly inFence: boolean;
4
+ };
5
+ export declare function scanMarkdownFenceLines(source: string): MarkdownFenceLine[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/core",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "tsx": "^4.22.4",
52
- "@riebeckite/test": "0.0.19"
52
+ "@riebeckite/test": "0.0.20"
53
53
  },
54
54
  "repository": {
55
55
  "type": "git",
@@ -1,8 +0,0 @@
1
- import { type DeprecationNotice } from "./deprecation.js";
2
- import type { Diagnostic } from "./types/diagnostic.js";
3
- import type { RiebeckiteConfig } from "./types/riebeckite_config.js";
4
- export type ConfigDeprecationNotice = DeprecationNotice & {
5
- readonly kind: "config";
6
- readonly path: string;
7
- };
8
- export declare function collectConfigDeprecationDiagnostics(config: RiebeckiteConfig, notices?: readonly ConfigDeprecationNotice[]): Diagnostic[];
@@ -1,14 +0,0 @@
1
- import type { Diagnostic } from "./types/diagnostic.js";
2
- export type DeprecationKind = "config" | "plugin-api" | "theme-api" | "cli" | "scaffold" | "package";
3
- export type DeprecationNotice = {
4
- readonly kind: DeprecationKind;
5
- readonly id: string;
6
- readonly target: string;
7
- readonly deprecatedSince: string;
8
- readonly action: string;
9
- readonly replacement?: string;
10
- readonly documentationUrl?: string;
11
- readonly removedIn?: string;
12
- };
13
- export declare function createDeprecationDiagnostic(notice: DeprecationNotice): Diagnostic;
14
- export declare function formatDeprecationMessage(notice: DeprecationNotice): string;