@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.
@@ -1,21 +1,11 @@
1
1
  import type { Observability } from "../observability.js";
2
2
  import type { ContentManifest, ContentManifestEntry } from "../types/content_manifest.js";
3
- import { determineAffectedContent } from "./affected_content.js";
4
- import { type ContentBuildState, type FingerprintedContentEntry } from "./content_build_state.js";
5
- import { type ContentChangeSet } from "./content_change_set.js";
3
+ import type { ExternalDependencyFailure, ExternalDependencySnapshot } from "../types/external_dependency.js";
4
+ import type { ContentBuildPlan } from "./content_build_plan.js";
5
+ import { type ManifestHookCacheEntry } from "./content_build_state.js";
6
6
  import { type CachedContentDependency } from "./content_dependency_tracker.js";
7
7
  import type { ContentSourceEntry } from "./content_source.js";
8
8
  import type { OutputDescriptor } from "./output_dependency.js";
9
- export type ContentBuildPreparation = {
10
- readonly previousState: ContentBuildState | undefined;
11
- readonly previousManifestEntriesBySlug: ReadonlyMap<string, ContentManifestEntry>;
12
- readonly currentEntries: readonly FingerprintedContentEntry[];
13
- readonly currentContentAliases: ReadonlyMap<string, readonly string[]>;
14
- readonly currentContentIndex: Map<string, string>;
15
- readonly currentContentIndexAmbiguities: ReadonlyMap<string, readonly string[]>;
16
- readonly changeSet: ContentChangeSet;
17
- readonly affectedContent: ReturnType<typeof determineAffectedContent>;
18
- };
19
9
  type ContentBuildCoordinatorDependencies = {
20
10
  readonly buildStatePath: string;
21
11
  readonly getEntries: () => Promise<readonly ContentSourceEntry[]>;
@@ -24,10 +14,10 @@ type ContentBuildCoordinatorDependencies = {
24
14
  };
25
15
  export declare class ContentBuildCoordinator {
26
16
  private readonly dependencies;
27
- private preparation;
17
+ private plan;
28
18
  constructor(dependencies: ContentBuildCoordinatorDependencies);
29
- getPreparation(incremental: boolean | undefined, pipelineFingerprint: string | undefined, fullContentRegenerationRequired?: boolean): Promise<ContentBuildPreparation>;
30
- commit(preparation: ContentBuildPreparation, manifest: ContentManifest, pipelineFingerprint: string | undefined, outputs?: readonly OutputDescriptor[], manifestEntries?: readonly ContentManifestEntry[], trackedDependencies?: ReadonlyMap<string, readonly CachedContentDependency[]>): Promise<void>;
19
+ getPlan(incremental: boolean, pipelineFingerprint: string | undefined, fullContentRegenerationRequired?: boolean, externalDependencies?: readonly ExternalDependencySnapshot[], externalDependencyFailures?: readonly ExternalDependencyFailure[], publishingBuildTime?: Date): Promise<ContentBuildPlan>;
20
+ commit(plan: ContentBuildPlan, manifest: ContentManifest, pipelineFingerprint: string | undefined, outputs: readonly OutputDescriptor[], manifestEntries: readonly ContentManifestEntry[], trackedDependencies: ReadonlyMap<string, readonly CachedContentDependency[]>, externalDependencies: readonly ExternalDependencySnapshot[], processedContentCacheFingerprints: ReadonlyMap<string, string | undefined>, manifestHookCache: readonly ManifestHookCacheEntry[], publishingBuildTime: Date): Promise<void>;
31
21
  private prepare;
32
22
  private getContentAliases;
33
23
  }
@@ -0,0 +1,24 @@
1
+ import type { ContentManifestEntry } from "../types/content_manifest.js";
2
+ import type { ExternalDependencyFailure, ExternalDependencySnapshot } from "../types/external_dependency.js";
3
+ import type { determineAffectedContent } from "./affected_content.js";
4
+ import type { ContentBuildState, FingerprintedContentEntry } from "./content_build_state.js";
5
+ import type { ContentChangeSet } from "./content_change_set.js";
6
+ import type { OutputChangeSet, OutputDescriptor } from "./output_dependency.js";
7
+ export type FullRegenerationReason = "incremental-disabled" | "cache-missing-or-invalid" | "pipeline-fingerprint-changed" | "content-plugin-uncacheable" | "external-dependency-unavailable" | "publishing-state-expired";
8
+ export type ContentBuildPlan = {
9
+ readonly incremental: boolean;
10
+ readonly previousState: ContentBuildState | undefined;
11
+ readonly previousManifestEntriesBySlug: ReadonlyMap<string, ContentManifestEntry>;
12
+ readonly currentEntries: readonly FingerprintedContentEntry[];
13
+ readonly currentContentAliases: ReadonlyMap<string, readonly string[]>;
14
+ readonly currentContentIndex: Map<string, string>;
15
+ readonly currentContentIndexAmbiguities: ReadonlyMap<string, readonly string[]>;
16
+ readonly changeSet: ContentChangeSet;
17
+ readonly affectedContent: ReturnType<typeof determineAffectedContent>;
18
+ readonly cacheValid: boolean;
19
+ readonly fullRegenerationReasons: readonly FullRegenerationReason[];
20
+ readonly reusableOutputs: readonly OutputDescriptor[] | undefined;
21
+ readonly externalDependencies: readonly ExternalDependencySnapshot[];
22
+ readonly externalDependencyFailures: readonly ExternalDependencyFailure[];
23
+ };
24
+ export declare function reusableOutputChangeSet(outputs: readonly OutputDescriptor[]): OutputChangeSet;
@@ -1,7 +1,7 @@
1
1
  import type { ContentManifestEntry } from "../types/content_manifest.js";
2
2
  import type { ContentSourceEntry } from "./content_source.js";
3
3
  import type { OutputDescriptor } from "./output_dependency.js";
4
- export declare const CONTENT_BUILD_STATE_VERSION = 8;
4
+ export declare const CONTENT_BUILD_STATE_VERSION = 16;
5
5
  /**
6
6
  * Build state lives under `<content directory>/.riebeckite`. That directory is
7
7
  * build-time state, not content, so content sources must exclude it from scans
@@ -14,6 +14,7 @@ export type ContentBuildEntry = {
14
14
  readonly aliases: readonly string[];
15
15
  readonly dependencies: readonly ContentBuildDependency[];
16
16
  readonly linkTargets?: readonly string[];
17
+ readonly processedContentCacheFingerprint?: string;
17
18
  };
18
19
  export type ContentBuildDependency = {
19
20
  readonly kind: "content" | "file";
@@ -26,6 +27,26 @@ export type ContentBuildState = {
26
27
  readonly pipelineFingerprint?: string;
27
28
  readonly manifestEntries?: readonly ContentManifestEntry[];
28
29
  readonly outputs?: readonly OutputDescriptor[];
30
+ readonly externalDependencies?: readonly PersistedExternalDependency[];
31
+ readonly manifestHookCache?: readonly ManifestHookCacheEntry[];
32
+ readonly nextPublishingChangeAt?: string;
33
+ };
34
+ export type ManifestHookCacheEntry = {
35
+ readonly pluginName: string;
36
+ readonly cacheable: boolean;
37
+ readonly pipelineFingerprint: string | undefined;
38
+ readonly inputFingerprint: string;
39
+ readonly outputFingerprint: string;
40
+ readonly snapshot: ManifestHookSnapshot;
41
+ };
42
+ export type ManifestHookSnapshot = {
43
+ readonly entries: readonly ContentManifestEntry[];
44
+ };
45
+ export type PersistedExternalDependency = {
46
+ readonly id: string;
47
+ readonly fingerprint: string;
48
+ readonly scope: import("../types/external_dependency.js").ExternalDependencyScope;
49
+ readonly expiresAt?: string;
29
50
  };
30
51
  export type FingerprintedContentEntry = {
31
52
  readonly entry: ContentSourceEntry;
@@ -1,10 +1,11 @@
1
1
  import type { PipelineOptions } from "../pipeline.js";
2
+ import type { ManifestHookMetrics } from "../plugin/plugin_runtime.js";
2
3
  import type { ContentManifest, ContentPublicLocation } from "../types/content_manifest.js";
3
4
  import type { Diagnostic } from "../types/diagnostic.js";
4
5
  import type { ResolvedPluginPage } from "../types/plugin_page.js";
5
6
  import type { PostContent } from "../types/post_content.js";
6
7
  import type { ResolvedRiebeckiteConfig } from "../types/resolved_riebeckite_config.js";
7
- import { type ContentBuildPreparation } from "./content_build_coordinator.js";
8
+ import { type ContentBuildPlan } from "./content_build_plan.js";
8
9
  import type { ContentGraph } from "./content_graph.js";
9
10
  import type { ContentSource, ContentSourceEntry } from "./content_source.js";
10
11
  import { type OutputChangeSet } from "./output_dependency.js";
@@ -46,12 +47,17 @@ export declare class ContentManager {
46
47
  private processedContentCount;
47
48
  private reusedContentCount;
48
49
  private outputChangeSet;
50
+ private buildPlan;
51
+ private buildStateCommitRequested;
52
+ private buildStateCommitted;
53
+ private cacheManifestEntries;
49
54
  private trackedContentDependencies;
55
+ private processedContentCacheFingerprints;
50
56
  constructor(content: string | ContentSource, exclude?: string[], pipelineOptions?: PipelineOptions);
51
57
  getAllPosts(): Promise<ContentPostReference[]>;
52
58
  scan(): Promise<readonly ContentSourceEntry[]>;
53
59
  getPost(slug: string): Promise<string>;
54
- getContentIndex(preparation?: ContentBuildPreparation): Promise<Map<string, string>>;
60
+ getContentIndex(plan?: ContentBuildPlan): Promise<Map<string, string>>;
55
61
  getProcessedContent(slug: string): Promise<PostContent>;
56
62
  private processContent;
57
63
  getManifest(options?: ContentBuildOptions): Promise<ContentManifest>;
@@ -59,6 +65,8 @@ export declare class ContentManager {
59
65
  build(options?: ContentBuildOptions): Promise<ContentManifest>;
60
66
  getProcessedContentCount(): number;
61
67
  getReusedContentCount(): number;
68
+ getManifestHookMetrics(): ManifestHookMetrics;
69
+ getNoChangeFastPathBlockReason(options?: ContentBuildOptions): Promise<string | undefined>;
62
70
  getOutputChangeSet(options?: ContentBuildOptions): Promise<OutputChangeSet>;
63
71
  getBacklinks(targetSlug: string): Promise<Backlink[]>;
64
72
  getContentGraph(): Promise<ContentGraph>;
@@ -75,7 +83,13 @@ export declare class ContentManager {
75
83
  private publishStrategy;
76
84
  private reuseManifestEntry;
77
85
  private getPipelineFingerprint;
86
+ private requestBuildStateCommit;
87
+ private commitBuildState;
88
+ private getBuildPlan;
89
+ private createBuildPlan;
90
+ private computePipelineFingerprint;
78
91
  private requiresFullContentRegeneration;
92
+ private getProcessedContentCacheFingerprint;
79
93
  private isRoutable;
80
94
  private ensureRoutableSlugs;
81
95
  private resolveRoutableSlugs;
@@ -87,5 +101,6 @@ declare module "../pipeline" {
87
101
  interface PipelineOptions {
88
102
  config?: ResolvedRiebeckiteConfig;
89
103
  publishingBuildTime?: Date | string;
104
+ commitBuildState?: boolean;
90
105
  }
91
106
  }
@@ -1,4 +1,5 @@
1
1
  import type { Logger, Tracer } from "../observability.js";
2
+ import type { ContentPublicLocation } from "../types/content_manifest.js";
2
3
  import type { PostContent } from "../types/post_content.js";
3
4
  import type { ResolvedRiebeckiteConfig } from "../types/resolved_riebeckite_config.js";
4
5
  import type { CachedContentDependency } from "./content_dependency_tracker.js";
@@ -16,8 +17,8 @@ export type PersistentContentCacheOptions = {
16
17
  logger?: Logger;
17
18
  tracer?: Tracer;
18
19
  };
19
- export declare const CONTENT_CACHE_SCHEMA_VERSION = 3;
20
- export declare const CONTENT_CACHE_COMPATIBILITY_VERSION = 1;
20
+ export declare const CONTENT_CACHE_SCHEMA_VERSION = 5;
21
+ export declare const CONTENT_CACHE_COMPATIBILITY_VERSION = 3;
21
22
  export type PersistentContentCacheMissReason = "disabled" | "no-entry" | "corrupted" | "unreadable" | "schema-mismatch" | "key-mismatch" | "invalid-entry";
22
23
  export type PersistentContentCacheLookup = {
23
24
  status: "hit";
@@ -40,9 +41,13 @@ export type CacheKeyInputs = {
40
41
  pipelineFingerprint: string;
41
42
  };
42
43
  export declare function computeContentCacheKey(inputs: CacheKeyInputs): string;
43
- export declare function computePipelineFingerprint(config: ResolvedRiebeckiteConfig): string;
44
- export declare function isPersistentlyCacheable(_slug: string, config: ResolvedRiebeckiteConfig): {
44
+ export declare function computePipelineFingerprint(config: ResolvedRiebeckiteConfig, contentScopeFingerprint?: string): string;
45
+ export declare function computeContentScopeFingerprint(locations: ReadonlyMap<string, ContentPublicLocation>): string;
46
+ export declare function isPersistentlyCacheable(slug: string, config: ResolvedRiebeckiteConfig, source?: string): {
45
47
  cacheable: boolean;
46
48
  reason?: string;
47
49
  };
50
+ export declare function computeContentCacheFingerprint(slug: string, source: string, config: ResolvedRiebeckiteConfig): string;
51
+ export declare function hasGlobalContentDependency(config: ResolvedRiebeckiteConfig): boolean;
52
+ export declare function requiresFullContentRegeneration(config: ResolvedRiebeckiteConfig): boolean;
48
53
  export declare function extractFrontmatter(markdown: string): PostContent["frontmatter"];
@@ -0,0 +1 @@
1
+ export declare function fingerprintExternalValue(value: unknown): string;
@@ -1,5 +1,5 @@
1
1
  import type { ContentSource, ContentSourceContent, ContentSourceEntry } from "./content_source.js";
2
- export declare const INTERNAL_CONTENT_IGNORE_PATTERNS: readonly [".git", ".git/**", ".github", ".github/**", ".obsidian", ".obsidian/**", "node_modules", "node_modules/**", "**/.DS_Store", "**/Thumbs.db", "**/desktop.ini"];
2
+ export declare const INTERNAL_CONTENT_IGNORE_PATTERNS: readonly [".riebeckite", ".riebeckite/**", ".git", ".git/**", ".github", ".github/**", ".obsidian", ".obsidian/**", "node_modules", "node_modules/**", "**/.DS_Store", "**/Thumbs.db", "**/desktop.ini"];
3
3
  export type ContentSourceExclusion = {
4
4
  readonly path: string;
5
5
  readonly pattern: string;
@@ -0,0 +1,7 @@
1
+ import type { ContentManifest } from "../types/content_manifest.js";
2
+ import type { ManifestHookCacheEntry, ManifestHookSnapshot } from "./content_build_state.js";
3
+ export declare function fingerprintManifestHookInput(manifest: ContentManifest): string;
4
+ export declare function createManifestHookCacheEntry(pluginName: string, cacheable: boolean, pipelineFingerprint: string | undefined, inputFingerprint: string, manifest: ContentManifest): ManifestHookCacheEntry;
5
+ export declare function fingerprintManifestHookSideEffects(manifest: ContentManifest): string;
6
+ export declare function fingerprintManifestHookSnapshot(snapshot: ManifestHookSnapshot, manifest: ContentManifest): string;
7
+ export declare function applyManifestHookSnapshot(manifest: ContentManifest, snapshot: ManifestHookSnapshot): boolean;
@@ -1,4 +1,5 @@
1
1
  import type { ContentManifest } from "../types/content_manifest.js";
2
+ import type { ExternalDependencyFailure, ExternalDependencySnapshot } from "../types/external_dependency.js";
2
3
  import type { OutputDependency } from "../types/output_dependency.js";
3
4
  import type { ContentBuildState } from "./content_build_state.js";
4
5
  import type { ContentChangeSet } from "./content_change_set.js";
@@ -29,6 +30,8 @@ export declare function determineOutputChanges(input: {
29
30
  };
30
31
  readonly pluginPageOutputs?: readonly OutputDescriptor[];
31
32
  readonly contentOutputDependencies?: readonly OutputDependency[];
33
+ readonly externalDependencies?: readonly ExternalDependencySnapshot[];
34
+ readonly externalDependencyFailures?: readonly ExternalDependencyFailure[];
32
35
  }): OutputChangeSet;
33
36
  export declare function buildOutputInventory(manifest: ContentManifest, pluginPageOutputs?: readonly OutputDescriptor[], contentOutputDependencies?: readonly OutputDependency[]): readonly OutputDescriptor[];
34
37
  export declare function htmlOutputPath(pathname: string): string;
@@ -0,0 +1,30 @@
1
+ import type { ContentLink, ContentManifest, ContentManifestEntry, ContentPublicLocation } from "../types/content_manifest.js";
2
+ import type { ResolvedPublishingState } from "./publishing.js";
3
+ type DeepReadonly<Value> = Value extends (...args: never[]) => unknown ? Value : Value extends readonly (infer Item)[] ? readonly DeepReadonly<Item>[] : Value extends object ? {
4
+ readonly [Key in keyof Value]: DeepReadonly<Value[Key]>;
5
+ } : Value;
6
+ export type PublicContentEntry = DeepReadonly<{
7
+ slug: string;
8
+ contentId?: string;
9
+ permalink: string;
10
+ publicLocation: ContentPublicLocation;
11
+ title: string;
12
+ aliases?: readonly string[];
13
+ publishing: ResolvedPublishingState;
14
+ tags: string[];
15
+ links: ContentLink[];
16
+ assets: ContentManifestEntry["assets"];
17
+ }>;
18
+ export type PublicContentView = {
19
+ readonly routableEntries: readonly PublicContentEntry[];
20
+ readonly discoverableEntries: readonly PublicContentEntry[];
21
+ findRoutableBySlug(slug: string): PublicContentEntry | undefined;
22
+ findDiscoverableBySlug(slug: string): PublicContentEntry | undefined;
23
+ findRoutableByAlias(alias: string): readonly PublicContentEntry[];
24
+ getDiscoverableEntriesByTag(tag: string): readonly PublicContentEntry[];
25
+ getDiscoverableOutgoing(slug: string): readonly PublicContentEntry[];
26
+ getDiscoverableIncoming(slug: string): readonly PublicContentEntry[];
27
+ getDiscoverableNeighbors(slug: string): readonly PublicContentEntry[];
28
+ };
29
+ export declare function createPublicContentView(manifest: ContentManifest): PublicContentView;
30
+ export {};
@@ -12,6 +12,7 @@ export interface PipelineOptions {
12
12
  isRoutable?: (slug: string) => boolean;
13
13
  onContentDependencies?(slug: string, dependencies: readonly CachedContentDependency[]): void;
14
14
  onPersistentContentCacheResult?(result: "hit" | "miss" | "bypass"): void;
15
+ contentScopeFingerprint?: string;
15
16
  }
16
17
  export declare class Pipeline {
17
18
  private contentIndex;
@@ -27,6 +28,7 @@ export declare class Pipeline {
27
28
  execute(markDownContent: string, context?: MarkdownExecutionContext): Promise<PostContent>;
28
29
  private executeWithEmbedState;
29
30
  private reportContentDependencies;
31
+ private contentPipelineFingerprint;
30
32
  private findInvalidDependency;
31
33
  private getPermalink;
32
34
  private resolveLinkDependency;
@@ -5,4 +5,5 @@ export declare class GeneratedOutputRegistry {
5
5
  private readonly owners;
6
6
  sinkFor(pluginName: string): GeneratedOutputSink;
7
7
  all(): GeneratedOutput[];
8
+ count(): number;
8
9
  }
@@ -1,7 +1,9 @@
1
+ import type { ManifestHookCacheEntry } from "../content/content_build_state.js";
1
2
  import type { OutputDescriptor } from "../content/output_dependency.js";
2
3
  import type { PipelineOptions } from "../pipeline.js";
3
4
  import type { ContentLocationInput, ContentManifest, ContentManifestEntry, ContentPublicLocation, PluginPageRoute } from "../types/content_manifest.js";
4
5
  import type { Diagnostic } from "../types/diagnostic.js";
6
+ import type { ExternalDependencyFailure, ExternalDependencySnapshot } from "../types/external_dependency.js";
5
7
  import type { GeneratedOutput, GeneratedOutputContent } from "../types/generated_output.js";
6
8
  import type { OutputDependency } from "../types/output_dependency.js";
7
9
  import { type PluginClientEntry } from "../types/plugin_asset.js";
@@ -13,6 +15,22 @@ import type { PostContent } from "../types/post_content.js";
13
15
  * Once `started` is reached the lifecycle never runs again.
14
16
  */
15
17
  export type PluginBuildStage = "idle" | "setup" | "buildStart" | "onConfigResolved" | "started";
18
+ export type ManifestHookMetrics = Readonly<{
19
+ manifestHooksExecuted: number;
20
+ manifestHooksReused: number;
21
+ manifestHookCacheHit: number;
22
+ manifestHookCacheMiss: number;
23
+ manifestHookDurationMs: number;
24
+ byPlugin: Readonly<Record<string, ManifestHookPluginMetrics>>;
25
+ }>;
26
+ export type ManifestHookPluginMetrics = Readonly<{
27
+ executed: number;
28
+ reused: number;
29
+ cacheHit: number;
30
+ cacheMiss: number;
31
+ durationMs: number;
32
+ reason: string;
33
+ }>;
16
34
  export declare class PluginRuntime {
17
35
  private pipelineOptions;
18
36
  private buildLifecycle;
@@ -24,9 +42,20 @@ export declare class PluginRuntime {
24
42
  private isBuildTime;
25
43
  private resolvedPlugins;
26
44
  private pageDeclarations;
45
+ private externalDependencies;
46
+ private externalDependencyFailures;
47
+ private externalDependenciesResolved;
48
+ private manifestHooksExecuted;
49
+ private manifestHookDurationMs;
50
+ private manifestHooksReused;
51
+ private manifestHookCache;
52
+ private manifestHookMetricsByPlugin;
27
53
  constructor(pipelineOptions?: PipelineOptions);
28
54
  getDiagnostics(): Diagnostic[];
29
55
  getBuildStage(): PluginBuildStage;
56
+ getManifestHookMetrics(): ManifestHookMetrics;
57
+ getNoChangeFastPathBlockReason(previousCache: readonly ManifestHookCacheEntry[], pipelineFingerprint: string | undefined): string | undefined;
58
+ getManifestHookCache(): readonly ManifestHookCacheEntry[];
30
59
  /**
31
60
  * Starts the plugin build lifecycle exactly once, regardless of which public
32
61
  * entry point (content processing or location resolution) reaches it first.
@@ -36,12 +65,18 @@ export declare class PluginRuntime {
36
65
  */
37
66
  startBuild(contentIndex: Map<string, string>): Promise<void>;
38
67
  runContentLoaded(slug: string, markdown: string, contentIndex: Map<string, string>): Promise<void>;
68
+ resolveExternalDependencies(contentIndex: Map<string, string>): Promise<{
69
+ readonly snapshots: readonly ExternalDependencySnapshot[];
70
+ readonly failures: readonly ExternalDependencyFailure[];
71
+ }>;
39
72
  resolveContentLocations(entries: readonly ContentLocationInput[], contentIndex: Map<string, string>): Promise<readonly ContentPublicLocation[]>;
40
73
  extendContentLocations(entries: readonly ContentLocationInput[], locations: Map<string, ContentPublicLocation>, contentIndex: Map<string, string>): Promise<void>;
41
74
  runPostHook(hookName: "onPostParsed" | "onPostProcessed", slug: string, markdown: string, content: PostContent, contentIndex: Map<string, string>): Promise<void>;
42
75
  runGraphHook(entries: ContentManifestEntry[], contentIndex: Map<string, string>): Promise<void>;
43
- runManifestCreated(manifest: ContentManifest, contentIndex: Map<string, string>): Promise<void>;
76
+ runManifestCreated(manifest: ContentManifest, contentIndex: Map<string, string>, previousCache: readonly ManifestHookCacheEntry[], pipelineFingerprint: string | undefined): Promise<void>;
77
+ private recordManifestHookMetric;
44
78
  runBuildEnd(manifest: ContentManifest, contentIndex: Map<string, string>): Promise<void>;
79
+ runGeneratedOutputPlans(manifest: ContentManifest): Promise<void>;
45
80
  enableBuildTime(): void;
46
81
  dispose(contentIndex: Map<string, string>): Promise<void>;
47
82
  collectAssets(): {
@@ -25,29 +25,6 @@ export type ContentManifestPluginAsset = PluginAsset & {
25
25
  * writing the slot string inline (for example `name="article.metadata"`) stays
26
26
  * the recommended, most readable form in TSX layouts.
27
27
  */
28
- export declare const ARTICLE_SLOT: {
29
- readonly header: "article.header";
30
- readonly metadata: "article.metadata";
31
- readonly aside: "article.aside";
32
- readonly beforeContent: "article.before-content";
33
- readonly afterContent: "article.after-content";
34
- readonly footer: "article.footer";
35
- };
36
- /**
37
- * A semantic position in a Site-owned article layout.
38
- *
39
- * The standard article consumer recognizes the `article.*` positions below.
40
- * Other names remain valid so a custom Site can define its own layout slots.
41
- */
42
- export type ContentBodySlot = (typeof ARTICLE_SLOT)[keyof typeof ARTICLE_SLOT] | (string & {});
43
- /**
44
- * A map of Plugin-provided HTML fragments keyed by slot name.
45
- *
46
- * The standard `article.*` names autocomplete while any custom slot name
47
- * remains valid, so custom Plugins keep working. A slot is considered empty
48
- * when its value is missing or whitespace-only.
49
- */
50
- export type ContentBodySlots = Partial<Record<(typeof ARTICLE_SLOT)[keyof typeof ARTICLE_SLOT], string>> & Readonly<Record<string, string | undefined>>;
51
28
  /** Client entries and their explicitly public configuration. */
52
29
  export type ContentManifestPluginClientEntry = PluginClientEntry;
53
30
  export type ContentManifestEntry = {
@@ -69,24 +46,11 @@ export type ContentManifestEntry = {
69
46
  * The Site owns the shell, so it decides whether to render them.
70
47
  */
71
48
  headTags?: readonly PluginHeadTag[];
72
- /**
73
- * Plugin-provided HTML fragments the Site renders in named body slots.
74
- * The Site owns its layout, so it decides where (and whether) each slot is
75
- * rendered; a plugin only supplies the fragment, keyed by slot name.
76
- */
77
- bodySlots?: Readonly<Record<string, string>>;
78
49
  tags: string[];
79
50
  links: ContentLink[];
80
51
  backlinks: string[];
81
52
  assets: ContentAsset[];
82
53
  };
83
- /**
84
- * Appends a Plugin-provided HTML fragment to a named Site layout slot.
85
- *
86
- * Plugins publish fragments; the Site chooses which slots to render and where.
87
- * Appending preserves contributions from earlier plugins in resolved order.
88
- */
89
- export declare function appendContentBodySlot(entry: ContentManifestEntry, slot: ContentBodySlot, html: string): void;
90
54
  export type ContentRedirect = {
91
55
  path: string;
92
56
  status: 301 | 302 | 307 | 308;
@@ -0,0 +1,31 @@
1
+ export type ExternalDependencyScope = {
2
+ readonly type: "content";
3
+ } | {
4
+ readonly type: "output";
5
+ readonly paths: readonly string[];
6
+ } | {
7
+ readonly type: "all";
8
+ };
9
+ export type ExternalDependencySnapshot<T = unknown> = {
10
+ readonly id: string;
11
+ readonly fingerprint: string;
12
+ readonly scope: ExternalDependencyScope;
13
+ readonly expiresAt?: string;
14
+ readonly value: T;
15
+ };
16
+ export type ExternalDependencyFailure = {
17
+ readonly id: string;
18
+ readonly reason: string;
19
+ };
20
+ export type ExternalDependencyResolution<T = unknown> = {
21
+ readonly kind: "resolved";
22
+ readonly snapshot: ExternalDependencySnapshot<T>;
23
+ } | {
24
+ readonly kind: "unavailable";
25
+ readonly failure: ExternalDependencyFailure;
26
+ };
27
+ export type ExternalDependencyContext = {
28
+ readonly config?: import("./resolved_riebeckite_config.js").ResolvedRiebeckiteConfig;
29
+ readonly contentIndex: Map<string, string>;
30
+ readonly cache: import("../plugin/plugin_cache.js").PluginCache;
31
+ };
@@ -10,6 +10,12 @@ export type OutputDependency = {
10
10
  } | {
11
11
  readonly type: "file";
12
12
  readonly path: string;
13
+ } | {
14
+ readonly type: "projection";
15
+ readonly projection: "navigation" | "recent-posts" | "taxonomy-tags" | "taxonomy-folders";
16
+ } | {
17
+ readonly type: "external";
18
+ readonly id: string;
13
19
  } | {
14
20
  readonly type: "global";
15
21
  } | {
@@ -1,5 +1,8 @@
1
1
  import type { PluginOptionsValidator } from "./config_validation.js";
2
+ import type { ContentManifestEntry } from "./content_manifest.js";
2
3
  import type { Diagnostic } from "./diagnostic.js";
4
+ import type { ExternalDependencyContext, ExternalDependencyResolution } from "./external_dependency.js";
5
+ import type { GeneratedOutputContent } from "./generated_output.js";
3
6
  import type { OutputDependency } from "./output_dependency.js";
4
7
  import type { PluginAsset, PluginClientEntry } from "./plugin_asset.js";
5
8
  import type { PluginContentContext, PluginContentLocationAugmentContext, PluginContentLocationResolver, PluginContentRenderer, PluginContext, PluginGeneratedHtmlInspector, PluginGraphContext, PluginLifecycleContext, PluginManifestContext, PluginPostContext } from "./plugin_context.js";
@@ -7,6 +10,7 @@ import type { PluginEndpoint } from "./plugin_endpoint.js";
7
10
  import type { PluginPageType } from "./plugin_page.js";
8
11
  import type { HtmlPipeline, MarkdownPipeline, MarkdownPipelineContext, PipelinePlugin } from "./plugin_pipeline.js";
9
12
  import type { PluginSeoExtension } from "./plugin_seo.js";
13
+ import type { ResolvedRiebeckiteConfig } from "./resolved_riebeckite_config.js";
10
14
  export type RiebeckitePlugin<TOptions = unknown> = {
11
15
  name: string;
12
16
  options?: TOptions;
@@ -39,18 +43,29 @@ export type RiebeckitePlugin<TOptions = unknown> = {
39
43
  * frontmatter, plugin options, and this contract version.
40
44
  * - `tracked`: external content/file inputs are read only through
41
45
  * framework-owned APIs that record dependency fingerprints.
46
+ * - `global`: processed HTML depends on the resolved content-location set.
42
47
  * - `unsafe`: persistent processed-content cache must bypass this plugin.
48
+ *
49
+ * `appliesToContent` may narrow an unsafe contract to raw Markdown sources
50
+ * it can affect. It must return `unknown` when it cannot prove the result;
51
+ * unknown falls back to bypassing the cache. Content and environment
52
+ * fingerprints must be stable whenever they are supplied.
43
53
  */
44
54
  processedContentCache?: {
45
55
  version: string;
46
- dependencyMode: "none" | "tracked" | "unsafe";
56
+ dependencyMode: "none" | "tracked" | "global" | "unsafe";
57
+ appliesToContent?: (context: ProcessedContentCacheContext) => ProcessedContentCacheApplicability;
58
+ contentDependencyFingerprint?: (context: ProcessedContentCacheContext) => string | undefined;
59
+ environmentFingerprint?: () => string | undefined;
47
60
  };
61
+ resolveExternalDependencies?(context: ExternalDependencyContext): Promise<readonly ExternalDependencyResolution[]>;
48
62
  validateOptions?: PluginOptionsValidator<TOptions>;
49
63
  remarkPlugins?: PipelinePlugin[];
50
64
  rehypePlugins?: PipelinePlugin[];
51
65
  setup?(context: PluginLifecycleContext): void | Promise<void>;
52
66
  buildStart?(context: PluginLifecycleContext): void | Promise<void>;
53
67
  buildEnd?(context: PluginManifestContext): void | Promise<void>;
68
+ planGeneratedOutputs?(context: PluginGeneratedOutputPlanContext): readonly PlannedGeneratedOutput[] | Promise<readonly PlannedGeneratedOutput[]>;
54
69
  dispose?(context: PluginLifecycleContext): void | Promise<void>;
55
70
  onConfigResolved?(context: PluginContext): void | Promise<void>;
56
71
  onContentLoaded?(context: PluginContentContext): void | Promise<void>;
@@ -74,10 +89,27 @@ export type RiebeckitePlugin<TOptions = unknown> = {
74
89
  seo?: PluginSeoExtension;
75
90
  extendContentGraph?(context: PluginGraphContext): void | Promise<void>;
76
91
  renderers?: PluginContentRenderer[];
77
- outputDependencies?: readonly OutputDependency[];
92
+ contentOutputDependencies?: readonly OutputDependency[];
78
93
  /** Framework-independent page types contributed by this plugin. */
79
94
  pageTypes?: PluginPageType[];
80
95
  };
96
+ export type PlannedGeneratedOutput = {
97
+ readonly path: string;
98
+ readonly content: GeneratedOutputContent;
99
+ readonly dependencies: readonly OutputDependency[];
100
+ };
101
+ export type PluginGeneratedOutputPlanContext = {
102
+ readonly manifest: Readonly<{
103
+ readonly discoverableEntries: readonly ContentManifestEntry[];
104
+ readonly routableEntries: readonly ContentManifestEntry[];
105
+ }>;
106
+ readonly site: Readonly<ResolvedRiebeckiteConfig["site"]> | undefined;
107
+ };
108
+ export type ProcessedContentCacheContext = {
109
+ readonly slug: string;
110
+ readonly source: string;
111
+ };
112
+ export type ProcessedContentCacheApplicability = "applies" | "does-not-apply" | "unknown";
81
113
  export type PluginInput = RiebeckitePlugin | false | null | undefined;
82
114
  export type ResolvedPluginMetadata = {
83
115
  readonly name: string;
@@ -18,8 +18,8 @@ export type PluginClientEntry<TPublicConfig extends JsonValue = JsonValue> = {
18
18
  */
19
19
  publicConfig?: TPublicConfig;
20
20
  };
21
- export declare function createStyleAsset(pluginName: string): PluginAsset;
22
- export declare function createClientEntry<TPublicConfig extends JsonValue>(pluginName: string, exportName?: string, publicConfig?: TPublicConfig): PluginClientEntry<TPublicConfig>;
21
+ export declare function createStyleAsset(pluginName: string, moduleSpecifier: string): PluginAsset;
22
+ export declare function createClientEntry<TPublicConfig extends JsonValue>(pluginName: string, moduleSpecifier: string, exportName?: string, publicConfig?: TPublicConfig): PluginClientEntry<TPublicConfig>;
23
23
  /**
24
24
  * Serializes explicitly registered client configuration for code generation.
25
25
  * Rejecting non-JSON values prevents generated client modules from silently
@@ -0,0 +1,2 @@
1
+ export type PluginDataHelper<Input, Model> = (input: Readonly<Input>) => Model | Promise<Model>;
2
+ export type PluginComponent<Props> = (props: Readonly<Props>) => unknown;
@@ -3,6 +3,7 @@ import type { Logger, Tracer } from "../observability.js";
3
3
  import type { PluginCache } from "../plugin/plugin_cache.js";
4
4
  import type { ContentLocationInput, ContentManifest, ContentManifestEntry, ContentPublicLocation } from "./content_manifest.js";
5
5
  import type { Diagnostic } from "./diagnostic.js";
6
+ import type { ExternalDependencySnapshot } from "./external_dependency.js";
6
7
  import type { GeneratedOutputSink } from "./generated_output.js";
7
8
  import type { PostContent } from "./post_content.js";
8
9
  import type { ResolvedRiebeckiteConfig } from "./resolved_riebeckite_config.js";
@@ -20,6 +21,7 @@ export type PluginContext = {
20
21
  logger: Logger;
21
22
  tracer: Tracer;
22
23
  contentSource?: ContentSource;
24
+ externalDependencies?: ReadonlyMap<string, ExternalDependencySnapshot>;
23
25
  };
24
26
  export type PluginLifecycleContext = PluginContext;
25
27
  export type PluginContentContext = PluginContext & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/core",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
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.22"
52
+ "@riebeckite/test": "0.0.24"
53
53
  },
54
54
  "repository": {
55
55
  "type": "git",