import { ReactNode } from 'react'; type ContentEventType = 'content.created' | 'content.updated' | 'content.published' | 'content.unpublished' | 'content.deleted'; type MediaEventType = 'media.uploaded' | 'media.deleted'; type SiteSettingsEventType = 'site.settings.updated'; /** * Emitted on every Post mutation (INSERT / MODIFY / REMOVE) with both * the previous and the next projection of the row. Drives index-style * derivation: the built-in trusted-processor handler uses it to keep * the `PostTag` denormalized index in sync without making every write * path (admin, MCP, future REST clients) remember to call a helper. * * Plugins that maintain their own indexes (custom search, sitemaps * with per-tag pages, etc.) can subscribe through the same hook * surface as the other event types — the diff payload is already in * the right shape for "compute add/remove/update". */ type PostIndexEventType = 'post.index.refresh'; type EventType = ContentEventType | MediaEventType | SiteSettingsEventType | PostIndexEventType; /** * Minimal projection of a Post item carried in events (no body, to keep * payloads small). `format` / `excerpt` are included so the denormalized * PostTag index can render tag-page summaries faithfully — without them the * `listPostsByTag` resolver can't know a post's real format and would * mislabel non-markdown posts. */ interface ContentEventPayload { postId: string; slug: string; title: string; status: 'draft' | 'published'; format?: ContentFormat; excerpt?: string; publishedAt?: string; tags?: string[]; } interface MediaEventPayload { mediaId: string; src: string; mimeType: string; } /** * Emitted whenever any setting under the `siteconfig:` PK in KvStore is * created, updated, or removed. Subscribers (built-in or user plugins) * can rebuild caches, theme assets, etc. */ interface SiteSettingsEventPayload { } /** * Diff payload for `post.index.refresh`. `previous` is null on INSERT; * `next` is null on REMOVE; both populated on MODIFY. Subscribers * compute the add / remove / update set from this — see the trusted * processor's `rebuildPostTags` handler for the canonical example. */ interface PostIndexEventPayload { previous: ContentEventPayload | null; next: ContentEventPayload | null; } type EventPayloadOf = T extends ContentEventType ? ContentEventPayload : T extends MediaEventType ? MediaEventPayload : T extends SiteSettingsEventType ? SiteSettingsEventPayload : T extends PostIndexEventType ? PostIndexEventPayload : never; interface AmplessEvent { type: T; payload: EventPayloadOf; /** ISO 8601 timestamp of when the source mutation happened. */ timestamp: string; } /** * Maps a single content mutation to the CMS-level events it represents. * Always emits `content.updated` for any MODIFY so plugins that subscribe * to "any change" reliably fire; status transitions add the matching * published / unpublished event on top. * * Used by the DynamoDB Stream dispatcher Lambda — kept here so the same * decision table is testable in plain Node without AWS deps. */ type StreamEventName = 'INSERT' | 'MODIFY' | 'REMOVE'; declare function detectContentEvents(input: { eventName: StreamEventName | string | undefined; oldStatus?: 'draft' | 'published'; newStatus?: 'draft' | 'published'; }): ContentEventType[]; type ThemeFieldType = 'color' | 'text' | 'select' | 'image' | 'length' | 'fontFamily' | 'linkList'; /** * Single entry in a `linkList` field. Stored as part of a JSON array * under the field's storage key. `url` may be: * - a relative path (`/about`) * - an absolute URL (`https://example.com`) * - a tag reference (`tag:guide`) — themes interpret this as * "expand to a list of posts with this tag" rather than rendering * a literal link. */ interface LinkListItem { label: string; url: string; } /** * A user-facing string in a manifest. Either a plain string (rendered * as-is, regardless of locale) or a per-locale map (the renderer picks * the active locale, falling back to `en`, then to any value). * * Themes that ship in a single language can keep these as plain * strings. The default themes use the map form so the same manifest * works for both built-in dictionaries. */ type LocalizedString = string | Record; interface ThemeFieldBase { /** Storage key. Persisted as `theme.{key}` in site settings. */ key: string; label: LocalizedString; description?: LocalizedString; /** Optional UI grouping (e.g. 'Colors', 'Typography', 'Branding'). */ group?: LocalizedString; /** Used when no override is set. Always a string for storage uniformity. */ default: string; /** * If set, the loader injects `${cssVar}: ${value}` into a `:root` * style block on every public page, so CSS rules using * `var(${cssVar})` pick up overrides at render time. * * Fields without `cssVar` (e.g. logo URL, header tagline) are exposed * to template code via `loadThemeConfig()` instead. */ cssVar?: string; } interface ThemeColorField extends ThemeFieldBase { type: 'color'; } interface ThemeTextField extends ThemeFieldBase { type: 'text'; maxLength?: number; } interface ThemeSelectField extends ThemeFieldBase { type: 'select'; options: ReadonlyArray<{ value: string; label: LocalizedString; }>; } interface ThemeImageField extends ThemeFieldBase { type: 'image'; } interface ThemeLengthField extends ThemeFieldBase { type: 'length'; } interface ThemeFontFamilyField extends ThemeFieldBase { type: 'fontFamily'; options: ReadonlyArray<{ value: string; label: LocalizedString; }>; } /** * A repeatable list of {label, url} entries — used for nav menus, * footer link sets, sidebar groups, etc. Stored in KvStore as a JSON * string so it fits the existing `string`-valued site-settings cache. * * `default` is declared as a plain array for ergonomics; the loader * stringifies it on the fly so manifest authors don't have to call * JSON.stringify by hand. */ interface ThemeLinkListField extends Omit { type: 'linkList'; default: ReadonlyArray; /** Cap admin-supplied list length. Default 50. */ maxItems?: number; } type ThemeField = ThemeColorField | ThemeTextField | ThemeSelectField | ThemeImageField | ThemeLengthField | ThemeFontFamilyField | ThemeLinkListField; interface ThemeManifest { /** Theme directory name (`themes//`). */ name: string; label: LocalizedString; description?: LocalizedString; fields: ReadonlyArray; } declare function defineTheme(m: ThemeManifest): ThemeManifest; /** * Resolve a `LocalizedString` to a plain string for display. * Strings pass through; maps pick `locale` → `fallback` → any value * → empty. The empty fallback keeps the renderer from crashing on a * malformed manifest while making the missing translation visible. */ declare function resolveLocalized(value: LocalizedString | undefined, locale: string, fallback?: string): string; interface ThemeRouteContext

> { params: Promise

; } interface ThemeModule { /** Stable identifier — must match the directory name and the value * stored as `theme.active`. */ name: string; manifest: ThemeManifest; /** * Server components rendered by the dispatcher routes * (`app/page.tsx`, `app/[slug]/page.tsx`, `app/tag/[tag]/page.tsx`). * Each theme MUST provide Home; Post / Tag are recommended but * optional (dispatcher 404s when missing). */ components: { Home: (ctx: ThemeRouteContext) => Promise | unknown; Post?: (ctx: ThemeRouteContext<{ slug: string; }>) => Promise | unknown; Tag?: (ctx: ThemeRouteContext<{ tag: string; }>) => Promise | unknown; }; /** * Optional `generateMetadata` hooks called by the dispatcher. Same * signature as the matching component's params. */ metadata?: { Home?: (ctx: ThemeRouteContext) => Promise | unknown; Post?: (ctx: ThemeRouteContext<{ slug: string; }>) => Promise | unknown; Tag?: (ctx: ThemeRouteContext<{ tag: string; }>) => Promise | unknown; }; /** * Optional route handlers for /feed.xml and /sitemap.xml. The * dispatcher returns 404 if a theme doesn't provide them. */ routes?: { feed?: (ctx: { request: Request; }) => Promise; sitemap?: (ctx: { request: Request; }) => Promise; }; } declare function defineThemeModule(m: ThemeModule): ThemeModule; /** * Storage key used in KvStore. Prefix `theme.` keeps the namespace * separate from `site.*` / `media.*` so unrelated tools can scan * settings without colliding. */ declare function themeSettingKey(fieldKey: string): string; /** * Reject malformed or potentially-injectable values before they reach * the KvStore. Admin/editor are trusted but typos and copy-paste * mistakes shouldn't be able to break a site's CSS or sneak `` * into the inline tag the loader emits. * * Returns the normalized value, or null if the input is rejected. */ /** * Split a stored color value into its light / dark components. * * Accepts two storage forms: * - Single value: `oklch(...)` / `#abcdef` / etc. → `{ light: value, dark: null }` * - Pair: `light-dark(L, D)` → `{ light: L, dark: D }` * * Splits on the top-level comma (depth-aware) so nested commas inside * `rgb(...)` / `hsl(...)` don't trip the parser. */ declare function parseColorPair(value: string): { light: string; dark: string | null; }; /** * Build the storage string for a color field. When `dark` is non-empty * and differs from `light`, returns `light-dark(light, dark)`; otherwise * returns the bare `light` value. The runtime emits the result verbatim * into the inline `:root { --foo: }` override; `light-dark()` * is a Baseline-2024 CSS function so the browser picks per mode. */ declare function formatColorPair(light: string, dark?: string | null): string; declare function validateThemeValue(field: ThemeField, raw: unknown): string | null; /** Parse a stored linkList JSON value into typed items. Tolerant: bad * shapes resolve to []. Throwing here would cascade into rendering. */ declare function parseLinkList(raw: string | undefined | null): LinkListItem[]; declare function stringifyLinkList(items: ReadonlyArray): string; /** * Detect a `tag:` URL form. Themes use this to render a list of * posts under a heading instead of a literal link — useful for docs * sidebars and category-style nav. */ declare function isTagListUrl(url: string): { tag: string; } | null; /** * Resolve effective values for every manifest field, merging stored * overrides on top of defaults. `stored` is the flat settings map keyed * by `theme.{key}` — typically the output of `listSiteSettings()` * filtered to theme entries. */ declare function resolveThemeValues(manifest: ThemeManifest, stored: Record): Record; type TrustLevel = 'untrusted' | 'trusted' | 'privileged'; /** * Plugin capability declarations. The runtime uses this list for * declaration-vs-implementation reconciliation warnings and (in later * phases) for `allowCapabilities` gating in `cms.config.ts`. * * Active capabilities: * - `publicHead` / `publicBody`: descriptor-based head/body injection. * - `metadata` / `eventHooks`: name-only declaration for existing surfaces. * - `adminSettings`: admin-managed public settings manifest. * - `writePublicAsset`: trusted hook context can write namespaced public assets. * - `schema`: per-post body injection via `publicBodyForPost`, * scoped to JSON-LD `