@moku-labs/web 0.5.5 → 0.6.0

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.mts CHANGED
@@ -4,7 +4,7 @@ import { ComponentChildren, VNode } from "preact";
4
4
  import { BundledTheme, ThemeRegistrationAny } from "shiki";
5
5
 
6
6
  //#region src/plugins/log/types.d.ts
7
- declare namespace types_d_exports$6 {
7
+ declare namespace types_d_exports$7 {
8
8
  export { ExpectChain, LogApi, LogConfig, LogEntry, LogLevel, LogSink, LogState };
9
9
  }
10
10
  /**
@@ -144,7 +144,7 @@ type LogApi = {
144
144
  addSink(sink: LogSink): void; /** Clear all recorded entries while keeping registered sinks. */
145
145
  reset(): void;
146
146
  };
147
- declare namespace types_d_exports$4 {
147
+ declare namespace types_d_exports$5 {
148
148
  export { EnvApi, EnvConfig, EnvProvider, EnvState, EnvVarSpec };
149
149
  }
150
150
  /**
@@ -340,7 +340,7 @@ declare const envPlugin: import("@moku-labs/core").CorePluginInstance<"env", Env
340
340
  * Global framework configuration. Minimal by design — per-plugin config is
341
341
  * resolved via `pluginConfigs`, not merged here.
342
342
  */
343
- type Config$7 = {
343
+ type Config$8 = {
344
344
  /** Runtime mode. Drives log sink defaults, content draft filtering, build minify. */mode: "production" | "development";
345
345
  };
346
346
  /**
@@ -375,7 +375,7 @@ type Events = {};
375
375
  * });
376
376
  * ```
377
377
  */
378
- type Config$6 = {
378
+ type Config$7 = {
379
379
  /** Human-readable site name. Used in feeds, og:site_name, and titles. MUST be non-empty. */name: string; /** Absolute base URL of the site, e.g. "https://blog.dev". MUST be a valid absolute URL (http/https). */
380
380
  url: string; /** Default author/byline for the site. Used in feeds and article author meta. */
381
381
  author: string; /** Short site description. Used in feeds, the default meta description, and og:description fallbacks. */
@@ -385,7 +385,7 @@ type Config$6 = {
385
385
  * Public API of the site plugin — read-only accessors over frozen global
386
386
  * site metadata, plus canonical URL construction.
387
387
  */
388
- type Api$6 = {
388
+ type Api$7 = {
389
389
  /**
390
390
  * Returns the configured site name.
391
391
  *
@@ -463,7 +463,7 @@ type Api$6 = {
463
463
  * }
464
464
  * ```
465
465
  */
466
- type Config$5 = {
466
+ type Config$6 = {
467
467
  readonly locales: readonly string[];
468
468
  readonly defaultLocale: string;
469
469
  readonly localeNames?: Record<string, string>;
@@ -474,7 +474,7 @@ type Config$5 = {
474
474
  * Public API of the i18n plugin. Injected as `app.i18n` and reachable from
475
475
  * other plugins via `ctx.require(i18nPlugin)`.
476
476
  */
477
- type Api$5 = {
477
+ type Api$6 = {
478
478
  /**
479
479
  * Returns the configured supported locales in declared order.
480
480
  *
@@ -542,8 +542,8 @@ type Api$5 = {
542
542
  */
543
543
  t(locale: string, key: string): string;
544
544
  };
545
- declare namespace types_d_exports$7 {
546
- export { Api$4 as Api, ClientRoute, CompileInput, CompiledRoute, Config$4 as Config, ExtractRouteParams, ExtractSegmentParameter, HeadConfig$1 as HeadConfig, LayoutContext, MatcherTable, Prettify, RouteBuilder, RouteContext, RouteDefinition, RouteHandlers, RouteMap, RouteState, RouterApi, RouterConfig, RouterState, State$4 as State, TypedRoute };
545
+ declare namespace types_d_exports$8 {
546
+ export { Api$5 as Api, ClientRoute, CompileInput, CompiledRoute, Config$5 as Config, ExtractRouteParams, ExtractSegmentParameter, HeadConfig$1 as HeadConfig, LayoutContext, MatcherTable, Prettify, RouteBuilder, RouteContext, RouteDefinition, RouteHandlers, RouteMap, RouteState, RouterApi, RouterConfig, RouterState, State$5 as State, TypedRoute };
547
547
  }
548
548
  /**
549
549
  * Param contribution of a single path segment. `{name:?}` / `:name?` → optional;
@@ -867,13 +867,13 @@ type RouterApi = {
867
867
  mode(): "ssg" | "spa" | "hybrid";
868
868
  };
869
869
  /** Re-export under the canonical `Config` name for the plugin-types barrel. */
870
- type Config$4 = RouterConfig;
870
+ type Config$5 = RouterConfig;
871
871
  /** Re-export under the canonical `State` name for the plugin-types barrel. */
872
- type State$4 = RouterState;
872
+ type State$5 = RouterState;
873
873
  /** Re-export under the canonical `Api` name for the plugin-types barrel. */
874
- type Api$4 = RouterApi;
875
- declare namespace types_d_exports$5 {
876
- export { Api$3 as Api, ArticleMeta, Config$3 as Config, HeadConfig, HeadDefaults, HeadElement, ResolvedRoute, State$3 as State };
874
+ type Api$5 = RouterApi;
875
+ declare namespace types_d_exports$6 {
876
+ export { Api$4 as Api, ArticleMeta, Config$4 as Config, HeadConfig, HeadDefaults, HeadElement, ResolvedRoute, State$4 as State };
877
877
  }
878
878
  /**
879
879
  * @file head plugin — type definitions skeleton
@@ -889,7 +889,7 @@ declare namespace types_d_exports$5 {
889
889
  * const config: Config = { titleTemplate: "%s — Moku" };
890
890
  * ```
891
891
  */
892
- type Config$3 = {
892
+ type Config$4 = {
893
893
  /** Title template applied to per-route titles. `%s` is replaced by the route title. */titleTemplate?: string; /** Default Open Graph image URL used when a route does not supply one. */
894
894
  defaultOgImage?: string; /** Default Twitter card type emitted when og/twitter content is present. */
895
895
  twitterCard?: "summary" | "summary_large_image"; /** Default Twitter site handle (e.g. `"@moku_labs"`) emitted as `twitter:site`. */
@@ -907,7 +907,7 @@ type Config$3 = {
907
907
  * const state: State = { defaults: null };
908
908
  * ```
909
909
  */
910
- type State$3 = {
910
+ type State$4 = {
911
911
  /** Normalized head defaults, assigned once in `onInit` (initially `null`). */defaults: HeadDefaults | null;
912
912
  };
913
913
  /**
@@ -1001,7 +1001,7 @@ type ResolvedRoute = {
1001
1001
  * const html: string = api.render(route, data);
1002
1002
  * ```
1003
1003
  */
1004
- type Api$3 = {
1004
+ type Api$4 = {
1005
1005
  /**
1006
1006
  * Compose the final `<head>` inner HTML for a route. Pulled synchronously by `build`.
1007
1007
  *
@@ -1015,7 +1015,7 @@ type Api$3 = {
1015
1015
  */
1016
1016
  render(route: ResolvedRoute, data: unknown): string;
1017
1017
  };
1018
- declare namespace types_d_exports$8 {
1018
+ declare namespace types_d_exports$9 {
1019
1019
  export { COMPONENT_HOOK_NAMES, ComponentContext, ComponentDef, ComponentHooks, ComponentInstance, ExtractApi$1 as ExtractApi, PageData, ResolvedSpaConfig, SpaApi, SpaConfig, SpaContext, SpaDataReader, SpaEmitFunction, SpaEvents, SpaKernel, SpaKernelDeps, SpaRequire, SpaState };
1020
1020
  }
1021
1021
  /** Payload map for the events `spa` emits, used to type the kernel's `emit` closure. */
@@ -1216,7 +1216,7 @@ interface SpaKernelDeps {
1216
1216
  /** Router plugin API — used for client-side route matching (`match`) + the resolved `mode`. */
1217
1217
  router: RouterApi;
1218
1218
  /** Head plugin API — its pure compose is reused for client head-sync. */
1219
- head: Api$3;
1219
+ head: Api$4;
1220
1220
  /**
1221
1221
  * The OPTIONAL `data` reader. Present only when the `data` plugin is composed.
1222
1222
  * When present (and `router.mode() !== "ssg"`), navigation first tries the client
@@ -1323,7 +1323,7 @@ type SpaApi = {
1323
1323
  current(): string;
1324
1324
  };
1325
1325
  declare namespace types_d_exports {
1326
- export { Api$2 as Api, BuildCacheEntry, BuildEvents, BuildResult, Config$2 as Config, ExtractApi, OgFont, OgImageConfig, OgPngRenderer, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, RichOgInput, State$2 as State };
1326
+ export { Api$3 as Api, BuildCacheEntry, BuildEvents, BuildResult, Config$3 as Config, ExtractApi, OgFont, OgImageConfig, OgPngRenderer, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, RichOgInput, State$3 as State };
1327
1327
  }
1328
1328
  /**
1329
1329
  * Structural extraction of a plugin instance's public API from its `_phantom`
@@ -1400,8 +1400,8 @@ type PhaseRequire = <PluginCandidate extends {
1400
1400
  * ```
1401
1401
  */
1402
1402
  type PhaseContext = {
1403
- /** Mutable per-run build state (caches + runId). */state: State$2; /** Resolved, frozen build config. */
1404
- readonly config: Readonly<Config$2>; /** Global framework config (mode, etc.). */
1403
+ /** Mutable per-run build state (caches + runId). */state: State$3; /** Resolved, frozen build config. */
1404
+ readonly config: Readonly<Config$3>; /** Global framework config (mode, etc.). */
1405
1405
  readonly global: Readonly<{
1406
1406
  mode: "production" | "development";
1407
1407
  }>; /** Resolve a depended-upon plugin instance to its public API. */
@@ -1510,7 +1510,7 @@ interface OgImageConfig {
1510
1510
  * const config: Config = { outDir: "./dist", minify: true, feeds: true, sitemap: true, images: true, ogImage: false };
1511
1511
  * ```
1512
1512
  */
1513
- type Config$2 = {
1513
+ type Config$3 = {
1514
1514
  /** Output directory for the built site. */outDir: string; /** Minify bundled CSS/JS. */
1515
1515
  minify: boolean; /** Generate RSS/Atom/JSON feeds. */
1516
1516
  feeds: boolean; /** Generate sitemap.xml + robots.txt. */
@@ -1551,9 +1551,9 @@ type BuildCacheEntry = Record<string, string>;
1551
1551
  * const state: State = { config, manifest: null, buildCache: new Map(), runId: null, ogImageHashCache: new Map() };
1552
1552
  * ```
1553
1553
  */
1554
- interface State$2 {
1554
+ interface State$3 {
1555
1555
  /** Resolved, frozen config snapshot. */
1556
- config: Config$2;
1556
+ config: Config$3;
1557
1557
  /** Cached route manifest for the current run (populated in Phase 3 from router). */
1558
1558
  manifest: RouteDefinition[] | null;
1559
1559
  /** Per-run build artifacts (e.g. hashed CSS/JS asset paths from Phase 1). */
@@ -1575,7 +1575,7 @@ interface State$2 {
1575
1575
  * const phase: PhaseName = "bundle";
1576
1576
  * ```
1577
1577
  */
1578
- type PhaseName = "bundle" | "content" | "images" | "pages" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
1578
+ type PhaseName = "bundle" | "content" | "images" | "pages" | "content-images" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
1579
1579
  /**
1580
1580
  * Result of a completed build run.
1581
1581
  *
@@ -1600,7 +1600,7 @@ interface BuildResult {
1600
1600
  * const result = await app.build.run();
1601
1601
  * ```
1602
1602
  */
1603
- type Api$2 = {
1603
+ type Api$3 = {
1604
1604
  /**
1605
1605
  * Run the full SSG pipeline and write the site to disk.
1606
1606
  *
@@ -1649,7 +1649,7 @@ type Api$2 = {
1649
1649
  * });
1650
1650
  * ```
1651
1651
  */
1652
- declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Config$2, State$2, Api$2, {
1652
+ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Config$3, State$3, Api$3, {
1653
1653
  "build:phase": {
1654
1654
  phase: PhaseName;
1655
1655
  status: "start" | "done";
@@ -1661,218 +1661,819 @@ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Con
1661
1661
  durationMs: number;
1662
1662
  };
1663
1663
  }> & Record<never, never>;
1664
- declare namespace types_d_exports$1 {
1665
- export { Api$1 as Api, Article, ArticleCard, ComputedFields, Config$1 as Config, ContentApiContext, ContentEvents, Frontmatter, State$1 as State };
1664
+ declare namespace types_d_exports$4 {
1665
+ export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WranglerErrorKind };
1666
1666
  }
1667
1667
  /**
1668
- * Configuration for the content plugin.
1669
- *
1670
- * @example
1671
- * ```ts
1672
- * { contentDir: "./src/content", trustedContent: false, shikiTheme: "github-dark" }
1673
- * ```
1674
- */
1675
- type Config$1 = {
1676
- /** Absolute or project-relative path to the content directory. Validated in onInit. */contentDir: string;
1677
- /**
1678
- * SECURITY GATE. When false (the default), rehype-sanitize runs as the final
1679
- * pipeline step. Set true ONLY for fully author-controlled Markdown — true
1680
- * disables sanitize and trusts all raw HTML.
1681
- */
1682
- trustedContent: boolean; /** Additional remark plugins, concatenated AFTER framework defaults. Defaults to []. */
1683
- extraRemarkPlugins?: readonly Pluggable[]; /** Additional rehype plugins, concatenated after custom transforms, before Shiki + sanitize. Defaults to []. */
1684
- extraRehypePlugins?: readonly Pluggable[];
1685
- /**
1686
- * Shiki theme for syntax highlighting: a bundled theme NAME — typed as Shiki's
1687
- * `BundledTheme` union so editors autocomplete the ~60 built-ins (default
1688
- * "github-dark") — or a custom `ThemeRegistration` object. Passed straight through
1689
- * to `@shikijs/rehype`'s `theme`. (Like Shiki's own theme type, an arbitrary string
1690
- * still compiles via the object arm, so this is autocomplete, not typo-rejection.)
1691
- */
1692
- shikiTheme?: BundledTheme | ThemeRegistrationAny; /** Author applied to articles whose frontmatter omits author. Defaults to undefined. */
1693
- defaultAuthor?: string;
1694
- };
1695
- /**
1696
- * Internal mutable state for the content plugin.
1697
- *
1698
- * @example
1699
- * ```ts
1700
- * { processor: null, articles: new Map(), slugs: null, dirtyPaths: new Set() }
1701
- * ```
1702
- */
1703
- type State$1 = {
1704
- /** Lazily-created unified processor singleton. null until first render()/loadAll(). */processor: Processor | null; /** Article cache keyed locale -> (slug -> Article). Starts empty. */
1705
- articles: Map<string, Map<string, Article>>; /** Discovered, sorted slug list cached after first disk scan. null until first discovery. */
1706
- slugs: string[] | null; /** Paths marked stale by invalidate(); next loadAll() re-reads only these. Starts empty. */
1707
- dirtyPaths: Set<string>;
1708
- };
1709
- /**
1710
- * YAML frontmatter parsed from each article file.
1711
- *
1712
- * @example
1713
- * ```ts
1714
- * { title: "Hello", date: "2026-01-15", description: "Intro", tags: [], language: "en" }
1715
- * ```
1668
+ * @file deploy plugin type definitions.
1716
1669
  */
1717
- type Frontmatter = {
1718
- /** Article title. Required. */title: string; /** ISO 8601 date string, e.g. "2026-01-15". Required. */
1719
- date: string; /** Short summary used in cards, feeds, and meta description. Required. */
1720
- description: string; /** Topic tags. Required (may be empty array). */
1721
- tags: string[]; /** Source language code of this file. Required. */
1722
- language: string; /** Draft flag. Excluded from output in production mode. Defaults to false. */
1723
- draft?: boolean; /** Author name. Falls back to config.defaultAuthor when omitted. */
1724
- author?: string;
1725
- };
1726
1670
  /**
1727
- * Fields computed by the pipeline (not authored in frontmatter).
1728
- *
1729
- * @example
1730
- * ```ts
1731
- * { slug: "hello", readingTime: 1, contentId: "hello", status: "published", wordCount: 42 }
1732
- * ```
1671
+ * Options passed to the injected spawner the subset of Bun.spawn's options the
1672
+ * plugin uses (piped stdout/stderr plus an env carrying the API token).
1733
1673
  */
1734
- type ComputedFields = {
1735
- /** Article directory name. */slug: string; /** Reading time in minutes (ceiling, minimum 1). */
1736
- readingTime: number; /** Stable content identifier (slug by default). */
1737
- contentId: string; /** Derived publication status. */
1738
- status: "published" | "draft"; /** Word count from the source body. */
1739
- wordCount: number;
1740
- };
1674
+ interface SpawnOptions {
1675
+ /** Capture stdout as a readable stream. */
1676
+ readonly stdout: "pipe";
1677
+ /** Capture stderr as a readable stream. */
1678
+ readonly stderr: "pipe";
1679
+ /** Subprocess environment — the API token is injected here, never via argv. */
1680
+ readonly env?: Record<string, string | undefined>;
1681
+ }
1741
1682
  /**
1742
- * A fully processed, render-ready article.
1743
- *
1744
- * @example
1745
- * ```ts
1746
- * { frontmatter, computed, html: "<p>…</p>", locale: "en", isFallback: false, url: "/en/hello/" }
1747
- * ```
1683
+ * The structural subprocess handle the plugin reads back: stdout/stderr streams
1684
+ * plus the exit-code promise. Streams are typed `unknown` and narrowed at the read
1685
+ * site so this carries no Bun namespace types.
1748
1686
  */
1749
- type Article = {
1750
- /** Parsed frontmatter. */frontmatter: Frontmatter; /** Pipeline-computed metadata. */
1751
- computed: ComputedFields; /** Sanitized rendered HTML body. */
1752
- html: string; /** Locale this Article instance represents (the requested locale, even on fallback). */
1753
- locale: string; /** True when the default-locale file was used because the requested locale was missing. */
1754
- isFallback: boolean; /** Canonical URL for this article in this locale. */
1755
- url: string;
1756
- };
1687
+ interface SpawnedProcess {
1688
+ /** Standard output stream (narrowed to a ReadableStream at the read site). */
1689
+ readonly stdout: unknown;
1690
+ /** Standard error stream (narrowed to a ReadableStream at the read site). */
1691
+ readonly stderr: unknown;
1692
+ /** Resolves with the subprocess exit code. */
1693
+ readonly exited: Promise<number>;
1694
+ }
1757
1695
  /**
1758
- * Lightweight projection of Article for cards/lists.
1759
- *
1760
- * @example
1761
- * ```ts
1762
- * { contentId: "hello", status: "published", title: "Hello", date: "2026-01-15", description: "Intro", tags: [], readingTime: 1, url: "/en/hello/" }
1763
- * ```
1696
+ * The subset of Bun.spawn's signature the plugin relies on (argv array + options).
1697
+ * Declared structurally — with NO `import("bun")` namespace types — so it survives
1698
+ * `.d.ts` bundling intact and tests can supply a fake spawn without importing Bun.
1764
1699
  */
1765
- type ArticleCard = {
1766
- /** Stable content identifier. */contentId: string; /** Derived publication status. */
1767
- status: "published" | "draft"; /** Article title. */
1768
- title: string; /** ISO 8601 date string. */
1769
- date: string; /** Short summary. */
1770
- description: string; /** Topic tags. */
1771
- tags: string[]; /** Reading time in minutes. */
1772
- readingTime: number; /** Canonical URL for this article in this locale. */
1773
- url: string;
1774
- };
1700
+ type SpawnFunction = (cmd: string[], options: SpawnOptions) => SpawnedProcess;
1775
1701
  /**
1776
- * Notification-only events emitted by the content plugin.
1777
- *
1778
- * @example
1779
- * ```ts
1780
- * emit("content:ready", { locales: ["en"], articleCount: 3 });
1781
- * ```
1702
+ * A deploy error `code` from the wrangler error taxonomy and preflight validators.
1782
1703
  */
1783
- type ContentEvents = {
1784
- /** All articles loaded across locales. */"content:ready": {
1785
- locales: readonly string[];
1786
- articleCount: number;
1787
- }; /** Article paths marked stale for dev rebuild. */
1788
- "content:invalidated": {
1789
- paths: readonly string[];
1790
- };
1791
- };
1704
+ type DeployErrorCode = "ERR_DEPLOY_NO_WRANGLER_CONFIG" | "ERR_DEPLOY_EMPTY_OUTDIR" | "ERR_DEPLOY_TOO_MANY_FILES" | "ERR_DEPLOY_FILE_TOO_LARGE" | "ERR_DEPLOY_PATH_TRAVERSAL" | "ERR_DEPLOY_INVALID_BRANCH" | "ERR_DEPLOY_NO_TOKEN" | "ERR_DEPLOY_PROJECT_NOT_FOUND" | "ERR_DEPLOY_AUTH_EXPIRED" | "ERR_DEPLOY_AUTH" | "ERR_DEPLOY_NETWORK" | "ERR_DEPLOY_WRANGLER_FAILED" | "ERR_DEPLOY_CONFIG";
1792
1705
  /**
1793
- * Kernel-free domain context handed to createContentApi by the wiring harness.
1794
- * Carries ctx.state (mutable escape hatch), config, global, emit, and the
1795
- * i18n-derived locale/url helpers — so api.ts stays free of createPlugin/ctx.
1796
- *
1797
- * @example
1798
- * ```ts
1799
- * const apiContext: ContentApiContext = { state, config, global, emit, locales, defaultLocale, articleToUrl };
1800
- * ```
1706
+ * The subset of wrangler error `code`s classifyWranglerError can produce from a
1707
+ * non-zero wrangler exit.
1801
1708
  */
1802
- type ContentApiContext = {
1803
- /** Mutable plugin state (article cache + lazy processor). */state: State$1; /** Resolved plugin configuration. */
1804
- config: Config$1; /** Global framework configuration (mode, etc.). */
1805
- global: {
1806
- mode: "production" | "development";
1807
- }; /** Emit a registered content event. */
1808
- emit: <K extends keyof ContentEvents>(event: K, payload: ContentEvents[K]) => void; /** Active locale codes from i18n. */
1809
- locales: () => readonly string[]; /** Default locale code from i18n (fallback source). */
1810
- defaultLocale: () => string; /** Build a canonical article URL for a locale + slug. */
1811
- articleToUrl: (locale: string, slug: string) => string;
1812
- };
1709
+ type WranglerErrorKind = Extract<DeployErrorCode, "ERR_DEPLOY_PROJECT_NOT_FOUND" | "ERR_DEPLOY_AUTH_EXPIRED" | "ERR_DEPLOY_AUTH" | "ERR_DEPLOY_NETWORK" | "ERR_DEPLOY_WRANGLER_FAILED">;
1813
1710
  /**
1814
- * Public API for the content plugin.
1815
- *
1816
- * @example
1817
- * ```ts
1818
- * const map = await app.content.loadAll();
1819
- * ```
1711
+ * Configuration for the deploy plugin.
1820
1712
  */
1821
- type Api$1 = {
1713
+ type Config$2 = {
1822
1714
  /**
1823
- * Load every article across every active locale, returning a locale-keyed
1824
- * map of date-descending Article arrays. Emits content:ready.
1715
+ * Deploy target. Only Cloudflare Pages is supported in this version.
1716
+ * Defaults to `cloudflare-pages`.
1825
1717
  */
1826
- loadAll(): Promise<Map<string, Article[]>>;
1718
+ target: "cloudflare-pages";
1827
1719
  /**
1828
- * Resolve and render a single article for one locale, with locale fallback.
1829
- *
1830
- * @param slug - Article directory name.
1831
- * @param locale - Requested locale code.
1720
+ * Directory (relative to project root) containing the built site to deploy.
1721
+ * Re-validated against cwd at run() time to block path traversal.
1722
+ * Defaults to `dist`.
1832
1723
  */
1833
- load(slug: string, locale: string): Promise<Article>;
1724
+ outDir: string;
1834
1725
  /**
1835
- * Render a raw Markdown string to HTML through the full pipeline.
1836
- *
1837
- * @param md - Raw Markdown source.
1726
+ * Branch treated as the Cloudflare Pages production branch.
1727
+ * Defaults to `main`.
1838
1728
  */
1839
- renderMarkdown(md: string): Promise<string>;
1729
+ productionBranch?: string;
1840
1730
  /**
1841
- * Mark file paths stale for incremental dev rebuilds. Emits content:invalidated.
1842
- *
1843
- * @param paths - File paths to invalidate.
1731
+ * Substrings exempt from entropy-gated secret scrubbing in logged output.
1732
+ * Defaults to `["CLOUDFLARE_ACCOUNT_ID"]`.
1844
1733
  */
1845
- invalidate(paths: readonly string[]): void;
1734
+ scrubAllowlist: string[];
1846
1735
  /**
1847
- * Project a full Article to a lightweight ArticleCard for list/grid rendering.
1848
- *
1849
- * @param article - The source article.
1736
+ * Cloudflare compatibility date written into generated wrangler.jsonc.
1737
+ * Defaults to `2024-01-01`.
1850
1738
  */
1851
- articleToCard(article: Article): ArticleCard;
1739
+ compatibilityDate?: string;
1740
+ /**
1741
+ * Whether init() also generates a GitHub Actions workflow.
1742
+ * Defaults to `false`.
1743
+ */
1744
+ ci?: boolean;
1852
1745
  };
1853
- //#endregion
1854
- //#region src/plugins/content/index.d.ts
1855
1746
  /**
1856
- * Content plugin Markdown pipeline: discovers files, parses frontmatter, renders
1857
- * to sanitized HTML (rehype-sanitize unless `trustedContent`), and exposes a
1858
- * locale-keyed Article model. Depends on i18n; emits `content:ready` and
1859
- * `content:invalidated`.
1860
- *
1861
- * @example Point at a content directory and pick a Shiki theme
1862
- * ```ts
1863
- * const app = createApp({
1864
- * pluginConfigs: {
1865
- * content: {
1866
- * contentDir: "./content",
1867
- * shikiTheme: "github-dark",
1868
- * defaultAuthor: "Ada Lovelace"
1869
- * // trustedContent: true // ONLY for fully author-controlled Markdown disables sanitize
1870
- * }
1871
- * }
1872
- * });
1873
- * ```
1747
+ * Result of a successful deploy.
1748
+ */
1749
+ type DeployResult = {
1750
+ /** The public deployment URL (e.g. https://my-site.pages.dev). */url: string; /** Cloudflare deployment ID parsed from wrangler output. */
1751
+ deploymentId: string; /** The branch that was deployed. */
1752
+ branch: string; /** Wall-clock duration of the deploy in milliseconds. */
1753
+ durationMs: number;
1754
+ };
1755
+ /**
1756
+ * Runtime state for the deploy plugin. Created in createState() and accessed via
1757
+ * ctx.state. deploy declares no onStop because nothing here is a long-lived resource.
1758
+ */
1759
+ type State$2 = {
1760
+ /** Result of the most recent successful deploy, or null before the first run. */lastDeployment: DeployResult | null;
1761
+ /**
1762
+ * Injectable subprocess spawner. Defaults to Bun.spawn. Swapped for a mock in
1763
+ * unit tests so wrangler is never actually invoked. Never reassigned at runtime.
1764
+ */
1765
+ spawn: SpawnFunction;
1766
+ };
1767
+ /**
1768
+ * Options for DeployApi.run.
1769
+ */
1770
+ type DeployRunOptions = {
1771
+ /**
1772
+ * Branch to deploy. Defaults to config.productionBranch (or "main"). Must match
1773
+ * /^[a-zA-Z0-9/_.-]+$/ — otherwise rejected with ERR_DEPLOY_INVALID_BRANCH.
1774
+ */
1775
+ branch?: string;
1776
+ /**
1777
+ * Whether to run the build before deploying. When false, deploys the existing outDir.
1778
+ * Defaults to `true`.
1779
+ */
1780
+ build?: boolean;
1781
+ };
1782
+ /**
1783
+ * Options for DeployApi.init.
1784
+ */
1785
+ type DeployInitOptions = {
1786
+ /** Also generate the GitHub Actions workflow. Defaults to config.ci. */ci?: boolean; /** Drift-only mode: report differences without writing any files. Defaults to `false`. */
1787
+ check?: boolean;
1788
+ };
1789
+ /**
1790
+ * Result of an init/scaffold operation.
1791
+ */
1792
+ type InitResult = {
1793
+ /** Paths written this invocation. */written: string[]; /** Paths skipped because they already exist. */
1794
+ skipped: string[]; /** In check mode: paths whose on-disk content differs from what would be generated. */
1795
+ drifted: string[];
1796
+ };
1797
+ /**
1798
+ * Public API of the deploy plugin (returned from the api factory).
1799
+ */
1800
+ type Api$2 = {
1801
+ /**
1802
+ * Deploy the built outDir to Cloudflare Pages via the wrangler subprocess.
1803
+ * Runs preflight validators, re-validates the resolved outdir against cwd, guards
1804
+ * the branch argument, spawns wrangler (no shell), scrubs all subprocess output
1805
+ * before logging, records lastDeployment, and emits deploy:complete.
1806
+ *
1807
+ * @param options - Optional branch override and build toggle.
1808
+ * @returns The deploy result (url, deploymentId, branch, durationMs).
1809
+ * @throws {Error} With a `code` from the deploy error taxonomy on any failure.
1810
+ * @example
1811
+ * const result = await app.deploy.run();
1812
+ * console.log(result.url); // https://my-site.pages.dev
1813
+ * @example
1814
+ * await app.deploy.run({ branch: "preview/landing", build: false });
1815
+ */
1816
+ run(options?: DeployRunOptions): Promise<DeployResult>;
1817
+ /**
1818
+ * Return the most recent successful deploy result, or null if none has occurred.
1819
+ * The returned object is read-only (a defensive snapshot).
1820
+ *
1821
+ * @returns The last DeployResult, or null.
1822
+ * @example
1823
+ * const last = app.deploy.getLastDeployment();
1824
+ * if (last) console.log(`Last deployed to ${last.url}`);
1825
+ */
1826
+ getLastDeployment(): Readonly<DeployResult> | null;
1827
+ /**
1828
+ * Generate deploy scaffolding: wrangler.jsonc (slug from site.name() + outDir +
1829
+ * compatibilityDate) and, when ci is enabled, .github/workflows/deploy.yml. Never
1830
+ * overwrites an existing wrangler.jsonc. In check mode, reports drift instead of writing.
1831
+ *
1832
+ * @param options - Optional ci toggle and check (drift-only) mode.
1833
+ * @returns Which files were written, skipped, or would drift.
1834
+ * @example
1835
+ * const out = await app.deploy.init({ ci: true });
1836
+ * // out.written -> ["wrangler.jsonc", ".github/workflows/deploy.yml"]
1837
+ * @example
1838
+ * const drift = await app.deploy.init({ check: true });
1839
+ * if (drift.drifted.length) process.exit(1);
1840
+ */
1841
+ init(options?: DeployInitOptions): Promise<InitResult>;
1842
+ };
1843
+ declare namespace types_d_exports$1 {
1844
+ export { Api$1 as Api, BuildOptions, BuildSummary, CliErrorCode, CliRenderer, Command, Config$1 as Config, DeployOptions, DeployOutcome, FileResponseFunction, PreviewOptions, ReloadInfo, ServeOptions, ServeStaticFunction, ServeStaticOptions, ServerHandle, ServerInfo, State$1 as State, WatchHandle };
1845
+ }
1846
+ /**
1847
+ * A cli error `code` from the config-validation and runtime taxonomy. Mirrors the
1848
+ * deploy plugin's coded-error pattern so every thrown value carries a stable `code`.
1849
+ *
1850
+ * @example
1851
+ * const code: CliErrorCode = "ERR_CLI_CONFIG";
1852
+ */
1853
+ type CliErrorCode = "ERR_CLI_CONFIG" | "ERR_CLI_NOT_FOUND";
1854
+ /**
1855
+ * The four commands a single cli process can run. Each maps to one consumer script
1856
+ * (`scripts/{build,serve,preview,deploy}.ts`) and is rendered as the Panel header.
1857
+ *
1858
+ * @example
1859
+ * const command: Command = "build";
1860
+ */
1861
+ type Command = "build" | "serve" | "preview" | "deploy";
1862
+ /**
1863
+ * Information rendered into the bordered server-ready panel by `serve()`/`preview()`.
1864
+ *
1865
+ * @example
1866
+ * const info: ServerInfo = { local: "http://localhost:4173", network: null };
1867
+ */
1868
+ type ServerInfo = {
1869
+ /** The loopback URL the server is reachable at (e.g. `http://localhost:4173`). */local: string; /** The LAN URL derived from the first non-internal IPv4, or `null` when offline. */
1870
+ network: string | null; /** Directories `serve()` is watching for changes (omitted by `preview()`). */
1871
+ watching?: string[];
1872
+ };
1873
+ /**
1874
+ * Information rendered after a single `serve()` rebuild: which file changed plus the
1875
+ * fresh build summary used to print the "rebuilt N pages" line.
1876
+ *
1877
+ * @example
1878
+ * const info: ReloadInfo = { file: "content/post.md", pageCount: 12, durationMs: 84 };
1879
+ */
1880
+ type ReloadInfo = {
1881
+ /** The changed path that triggered the rebuild. */file: string; /** Number of route pages rendered by the rebuild. */
1882
+ pageCount: number; /** Wall-clock duration of the rebuild in milliseconds. */
1883
+ durationMs: number;
1884
+ };
1885
+ /**
1886
+ * The Panel renderer surface — every line of terminal output flows through this so
1887
+ * tests can inject a line-capturing fake. Implemented by `createPanelRenderer` and
1888
+ * is TTY/`NO_COLOR`-aware (box-drawing + color on a TTY, plain lines otherwise).
1889
+ *
1890
+ * @example
1891
+ * const render: CliRenderer = createPanelRenderer();
1892
+ * render.header("build");
1893
+ */
1894
+ type CliRenderer = {
1895
+ /**
1896
+ * Render the boxed `MOKU WEB` logo + command label. Called once per command (one
1897
+ * command = one process), so it never repeats within a run.
1898
+ *
1899
+ * @param command - The command being run, shown beside the logo.
1900
+ * @returns Nothing.
1901
+ * @example
1902
+ * render.header("serve");
1903
+ */
1904
+ header(command: Command): void;
1905
+ /**
1906
+ * Render a live per-phase row from a `build:phase` event.
1907
+ *
1908
+ * @param phase - The `build:phase` payload (`{ phase, status, durationMs? }`).
1909
+ * @returns Nothing.
1910
+ * @example
1911
+ * render.phase({ phase: "pages", status: "done", durationMs: 12 });
1912
+ */
1913
+ phase(phase: BuildEvents["build:phase"]): void;
1914
+ /**
1915
+ * Render the BUILD summary block from a `build:complete` event.
1916
+ *
1917
+ * @param summary - The `build:complete` payload (`{ outDir, pageCount, durationMs }`).
1918
+ * @returns Nothing.
1919
+ * @example
1920
+ * render.built({ outDir: "dist", pageCount: 12, durationMs: 840 });
1921
+ */
1922
+ built(summary: BuildEvents["build:complete"]): void;
1923
+ /**
1924
+ * Render the bordered server-ready panel (Local / Network URLs + watched dirs).
1925
+ *
1926
+ * @param info - Local/Network URLs and optionally the watched directories.
1927
+ * @returns Nothing.
1928
+ * @example
1929
+ * render.serverReady({ local: "http://localhost:4173", network: null });
1930
+ */
1931
+ serverReady(info: ServerInfo): void;
1932
+ /**
1933
+ * Render the post-rebuild line ("~ file" + "✓ rebuilt N pages · Xms · reloaded").
1934
+ *
1935
+ * @param info - The changed file plus the rebuild's page count and duration.
1936
+ * @returns Nothing.
1937
+ * @example
1938
+ * render.reload({ file: "content/a.md", pageCount: 12, durationMs: 84 });
1939
+ */
1940
+ reload(info: ReloadInfo): void;
1941
+ /**
1942
+ * Render the deploy result panel from a `deploy:complete` event.
1943
+ *
1944
+ * @param result - The `deploy:complete` payload (`{ url, deploymentId, branch, durationMs }`).
1945
+ * @returns Nothing.
1946
+ * @example
1947
+ * render.deployed({ url: "https://x.pages.dev", deploymentId: "id", branch: "main", durationMs: 1200 });
1948
+ */
1949
+ deployed(result: DeployResult): void;
1950
+ /**
1951
+ * Render a neutral informational line (e.g. the non-interactive deploy note, watch notice).
1952
+ *
1953
+ * @param message - The line to print.
1954
+ * @returns Nothing.
1955
+ * @example
1956
+ * render.info("watching for changes…");
1957
+ */
1958
+ info(message: string): void;
1959
+ /**
1960
+ * Render a warning line (written to stderr).
1961
+ *
1962
+ * @param message - The warning to print.
1963
+ * @returns Nothing.
1964
+ * @example
1965
+ * render.warn("deploy skipped");
1966
+ */
1967
+ warn(message: string): void;
1968
+ /**
1969
+ * Render an error line (written to stderr), optionally with a cause.
1970
+ *
1971
+ * @param message - The error summary to print.
1972
+ * @param cause - Optional underlying error/value to print beneath the summary.
1973
+ * @returns Nothing.
1974
+ * @example
1975
+ * render.error("build failed", err);
1976
+ */
1977
+ error(message: string, cause?: unknown): void;
1978
+ };
1979
+ /**
1980
+ * A live directory watcher handle returned by the injectable `watch` seam. Closing
1981
+ * it detaches the underlying `node:fs.watch` listener.
1982
+ *
1983
+ * @example
1984
+ * const handle: WatchHandle = state.watch("content", onChange);
1985
+ * handle.close();
1986
+ */
1987
+ type WatchHandle = {
1988
+ /**
1989
+ * Stop watching and release the underlying listener.
1990
+ *
1991
+ * @returns Nothing.
1992
+ * @example
1993
+ * handle.close();
1994
+ */
1995
+ close(): void;
1996
+ };
1997
+ /**
1998
+ * A running static server handle the cli stops on teardown. Declared structurally
1999
+ * (no Bun namespace types) so it survives `.d.ts` bundling and tests can supply a
2000
+ * fake without importing Bun.
2001
+ *
2002
+ * @example
2003
+ * const handle: ServerHandle = state.serveStatic({ port, fetch });
2004
+ * handle.stop();
2005
+ */
2006
+ type ServerHandle = {
2007
+ /**
2008
+ * Stop the server and release its socket.
2009
+ *
2010
+ * @returns Nothing.
2011
+ * @example
2012
+ * handle.stop();
2013
+ */
2014
+ stop(): void;
2015
+ };
2016
+ /**
2017
+ * The subset of `Bun.serve`'s options the cli uses: a port plus a `fetch` handler.
2018
+ * Declared structurally so no Bun namespace type reaches the public surface.
2019
+ *
2020
+ * @example
2021
+ * const opts: ServeStaticOptions = { port: 4173, fetch: () => new Response("ok") };
2022
+ */
2023
+ type ServeStaticOptions = {
2024
+ /** Port to bind the server to. */port: number;
2025
+ /**
2026
+ * Per-request handler returning the response (sync or async).
2027
+ *
2028
+ * @param request - The incoming request.
2029
+ * @returns The response (or a promise of it).
2030
+ * @example
2031
+ * fetch(req) { return new Response("ok"); }
2032
+ */
2033
+ fetch(request: Request): Response | Promise<Response>;
2034
+ };
2035
+ /**
2036
+ * An injectable static-server factory (defaults to `Bun.serve`). Keeps the Bun
2037
+ * runtime dependency behind a structural seam so `serve()`/`preview()` never open a
2038
+ * real socket in tests.
2039
+ *
2040
+ * @example
2041
+ * const serveStatic: ServeStaticFunction = options => Bun.serve(options);
2042
+ */
2043
+ type ServeStaticFunction = (options: ServeStaticOptions) => ServerHandle;
2044
+ /**
2045
+ * An injectable file-response factory (defaults to `new Response(Bun.file(path))`).
2046
+ * Maps a resolved on-disk path + status to the response body the server returns.
2047
+ *
2048
+ * @example
2049
+ * const fileResponse: FileResponseFunction = (path, status) => new Response(Bun.file(path), { status });
2050
+ */
2051
+ type FileResponseFunction = (path: string, status: number) => Response;
2052
+ /**
2053
+ * Configuration for the cli plugin — the complete resolved `Config` (not a partial).
2054
+ * Consumers override individual fields via `pluginConfigs.cli`.
2055
+ *
2056
+ * @example
2057
+ * const config: Config = {
2058
+ * outDir: "dist", port: 4173, watchDirs: ["content", "src"],
2059
+ * debounceMs: 150, notFoundFile: "404.html", liveReload: true
2060
+ * };
2061
+ */
2062
+ type Config$1 = {
2063
+ /** Build output directory; served by preview, asserted by build, rebuilt by serve. Default `"dist"`. */outDir: string; /** Default port for serve()/preview() (overridable per-call via options.port). Default `4173`. */
2064
+ port: number; /** Directories serve() watches for changes (recursive). Default `["content", "src"]`. */
2065
+ watchDirs: string[]; /** Debounce window (ms) coalescing FS-event bursts into one rebuild. Default `150`. */
2066
+ debounceMs: number; /** Filename build() asserts exists at outDir root (CF Pages flips to SPA mode without it). Default `"404.html"`. */
2067
+ notFoundFile: string; /** Inject the live-reload SSE client into HTML during serve() (never during preview()). Default `true`. */
2068
+ liveReload: boolean;
2069
+ };
2070
+ /**
2071
+ * Runtime state for the cli plugin — injectable seams so every command is testable
2072
+ * without real sockets/FS-watch/TTY (mirrors deploy's injectable `spawn`).
2073
+ *
2074
+ * @example
2075
+ * const state: State = createState();
2076
+ */
2077
+ type State$1 = {
2078
+ /** Panel renderer — all terminal output flows through this. Tests inject a line-capturing fake. */render: CliRenderer;
2079
+ /**
2080
+ * Interactive y/N prompt used by deploy(). Default reads stdin (TTY); tests inject a canned answer.
2081
+ *
2082
+ * @param question - The yes/no question to display.
2083
+ * @returns Resolves `true` when the user answered yes.
2084
+ * @example
2085
+ * const ok = await state.confirm("Deploy dist/?");
2086
+ */
2087
+ confirm: (question: string) => Promise<boolean>;
2088
+ /**
2089
+ * Monotonic clock for durations. Default `Date.now`; tests inject for deterministic timing.
2090
+ *
2091
+ * @returns The current time in milliseconds.
2092
+ * @example
2093
+ * const t = state.clock();
2094
+ */
2095
+ clock: () => number;
2096
+ /**
2097
+ * Recursive directory watcher factory used by serve(). Default wraps `node:fs.watch`;
2098
+ * tests inject a fake emitter.
2099
+ *
2100
+ * @param dir - The directory to watch recursively.
2101
+ * @param onChange - Invoked on any change beneath `dir`.
2102
+ * @returns A handle whose `close()` detaches the watcher.
2103
+ * @example
2104
+ * const handle = state.watch("content", () => rebuild());
2105
+ */
2106
+ watch: (dir: string, onChange: () => void) => WatchHandle; /** Static-server factory used by serve()/preview(). Default `Bun.serve`; tests inject a fake. */
2107
+ serveStatic: ServeStaticFunction; /** File-response factory mapping a resolved path + status to a `Response`. Default `Bun.file`. */
2108
+ fileResponse: FileResponseFunction;
2109
+ /**
2110
+ * LAN network-URL deriver for the server-ready panel. Default reads `node:os`
2111
+ * interfaces; tests inject a deterministic value.
2112
+ *
2113
+ * @param port - The port the server is bound to.
2114
+ * @returns The `http://<ip>:<port>` URL, or `null` when offline.
2115
+ * @example
2116
+ * const url = state.networkUrl(4173);
2117
+ */
2118
+ networkUrl: (port: number) => string | null;
2119
+ };
2120
+ /**
2121
+ * Summary returned by `cli.build()` — the awaited `build.run()` result.
2122
+ *
2123
+ * @example
2124
+ * const summary: BuildSummary = { outDir: "dist", pageCount: 12, durationMs: 840 };
2125
+ */
2126
+ type BuildSummary = {
2127
+ /** Resolved output directory the site was written to. */outDir: string; /** Number of route pages rendered. */
2128
+ pageCount: number; /** Total wall-clock duration of the run, in milliseconds. */
2129
+ durationMs: number;
2130
+ };
2131
+ /**
2132
+ * Outcome returned by `cli.deploy()` — either a completed deploy (with details) or a
2133
+ * skipped one. A skip happens only when an interactive TTY user answers "no" at the
2134
+ * confirm prompt (`reason: "declined"`). Non-interactive runs (CI / non-TTY) never
2135
+ * prompt and always proceed, so they never skip — the scripts are CI-safe.
2136
+ *
2137
+ * @example
2138
+ * const outcome: DeployOutcome = { deployed: false, reason: "declined" };
2139
+ */
2140
+ type DeployOutcome = {
2141
+ deployed: true;
2142
+ url: string;
2143
+ deploymentId: string;
2144
+ branch: string;
2145
+ durationMs: number;
2146
+ } | {
2147
+ deployed: false;
2148
+ reason: "declined";
2149
+ };
2150
+ /**
2151
+ * Options for `cli.build()`.
2152
+ *
2153
+ * @example
2154
+ * await app.cli.build({ assertNotFound: false });
2155
+ */
2156
+ type BuildOptions = {
2157
+ /** Assert `outDir/notFoundFile` exists after the build. Defaults to `true`. */assertNotFound?: boolean;
2158
+ };
2159
+ /**
2160
+ * Options for `cli.serve()`.
2161
+ *
2162
+ * @example
2163
+ * await app.cli.serve({ port: 3000 });
2164
+ */
2165
+ type ServeOptions = {
2166
+ /** Port to bind the dev server to. Defaults to `config.port`. */port?: number; /** Reserved for opening the browser on start (not yet implemented). Defaults to `false`. */
2167
+ open?: boolean;
2168
+ };
2169
+ /**
2170
+ * Options for `cli.preview()`.
2171
+ *
2172
+ * @example
2173
+ * await app.cli.preview({ port: 8080 });
2174
+ */
2175
+ type PreviewOptions = {
2176
+ /** Port to bind the preview server to. Defaults to `config.port`. */port?: number;
2177
+ };
2178
+ /**
2179
+ * Options for `cli.deploy()`.
2180
+ *
2181
+ * @example
2182
+ * await app.cli.deploy({ branch: "preview/x", yes: true });
2183
+ */
2184
+ type DeployOptions = {
2185
+ /** Branch to deploy. Defaults to the deploy plugin's production branch. */branch?: string; /** Skip the y/N confirm and deploy immediately. Defaults to `false`. */
2186
+ yes?: boolean;
2187
+ };
2188
+ /**
2189
+ * Public API of the cli plugin (mounted at `app.cli`) — exactly four methods.
2190
+ *
2191
+ * @example
2192
+ * await app.cli.build();
2193
+ */
2194
+ type Api$1 = {
2195
+ /**
2196
+ * Run the SSG build and assert the not-found page exists.
2197
+ *
2198
+ * @param options - Optional `assertNotFound` toggle (default `true`).
2199
+ * @returns The build summary (`outDir`, `pageCount`, `durationMs`).
2200
+ * @throws {Error} `ERR_CLI_NOT_FOUND` when the not-found page is missing and asserted.
2201
+ * @example
2202
+ * const summary = await app.cli.build();
2203
+ */
2204
+ build(options?: BuildOptions): Promise<BuildSummary>;
2205
+ /**
2206
+ * Dev loop: build once, serve `dist/` in-process (live-reload injected), watch
2207
+ * `watchDirs`, debounced rebuild + reload. Resolves when SIGINT/SIGTERM tears down.
2208
+ *
2209
+ * @param options - Optional port override.
2210
+ * @returns Resolves once the server has been torn down.
2211
+ * @example
2212
+ * await app.cli.serve({ port: 3000 });
2213
+ */
2214
+ serve(options?: ServeOptions): Promise<void>;
2215
+ /**
2216
+ * Static preview of the built `dist/` with CF-Pages clean-URL resolution. No
2217
+ * reload injection (mirrors production). Resolves on SIGINT/SIGTERM.
2218
+ *
2219
+ * @param options - Optional port override.
2220
+ * @returns Resolves once the server has been torn down.
2221
+ * @example
2222
+ * await app.cli.preview();
2223
+ */
2224
+ preview(options?: PreviewOptions): Promise<void>;
2225
+ /**
2226
+ * Scaffold, then deploy. A y/N confirm is shown only on an interactive TTY;
2227
+ * non-interactive runs (CI, or any non-TTY) skip the prompt and deploy, so the
2228
+ * consumer scripts never block a pipeline. `options.yes` forces the skip anywhere.
2229
+ *
2230
+ * @param options - Optional branch override and `yes` flag.
2231
+ * @returns The deploy outcome (completed details, or `declined` if a TTY user says no).
2232
+ * @example
2233
+ * await app.cli.deploy({ branch: "preview/x", yes: true });
2234
+ */
2235
+ deploy(options?: DeployOptions): Promise<DeployOutcome>;
2236
+ };
2237
+ //#endregion
2238
+ //#region src/plugins/cli/index.d.ts
2239
+ /**
2240
+ * cli plugin — the node-only developer CLI for `@moku-labs/web`. Mounts exactly four
2241
+ * methods at `app.cli` (`build`/`serve`/`preview`/`deploy`), each rendering through
2242
+ * the boxed Panel UI. Live build/deploy progress rides on hooks over the `build` and
2243
+ * `deploy` plugins' events; there is no argv parser and no `run()` dispatcher — the
2244
+ * consumer drives it from one thin script per command.
2245
+ *
2246
+ * @example Compose the CLI in a consumer app (node-only)
2247
+ * ```ts
2248
+ * import { buildPlugin, cliPlugin, createApp, deployPlugin } from "@moku-labs/web";
2249
+ *
2250
+ * const app = createApp({
2251
+ * plugins: [buildPlugin, deployPlugin, cliPlugin],
2252
+ * pluginConfigs: { cli: { outDir: "dist", port: 4173, watchDirs: ["content", "src"] } }
2253
+ * });
2254
+ * await app.start();
2255
+ * await app.cli.build();
2256
+ * ```
2257
+ */
2258
+ declare const cliPlugin: import("@moku-labs/core").PluginInstance<"cli", Config$1, State$1, Api$1, {}> & Record<never, never>;
2259
+ declare namespace types_d_exports$2 {
2260
+ export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, Frontmatter, State };
2261
+ }
2262
+ /**
2263
+ * Configuration for the content plugin.
2264
+ *
2265
+ * @example
2266
+ * ```ts
2267
+ * { contentDir: "./src/content", trustedContent: false, shikiTheme: "github-dark" }
2268
+ * ```
2269
+ */
2270
+ type Config = {
2271
+ /** Absolute or project-relative path to the content directory. Validated in onInit. */contentDir: string;
2272
+ /**
2273
+ * SECURITY GATE. When false (the default), rehype-sanitize runs as the final
2274
+ * pipeline step. Set true ONLY for fully author-controlled Markdown — true
2275
+ * disables sanitize and trusts all raw HTML.
2276
+ */
2277
+ trustedContent: boolean; /** Additional remark plugins, concatenated AFTER framework defaults. Defaults to []. */
2278
+ extraRemarkPlugins?: readonly Pluggable[]; /** Additional rehype plugins, concatenated after custom transforms, before Shiki + sanitize. Defaults to []. */
2279
+ extraRehypePlugins?: readonly Pluggable[];
2280
+ /**
2281
+ * Shiki theme for syntax highlighting: a bundled theme NAME — typed as Shiki's
2282
+ * `BundledTheme` union so editors autocomplete the ~60 built-ins (default
2283
+ * "github-dark") — or a custom `ThemeRegistration` object. Passed straight through
2284
+ * to `@shikijs/rehype`'s `theme`. (Like Shiki's own theme type, an arbitrary string
2285
+ * still compiles via the object arm, so this is autocomplete, not typo-rejection.)
2286
+ */
2287
+ shikiTheme?: BundledTheme | ThemeRegistrationAny; /** Author applied to articles whose frontmatter omits author. Defaults to undefined. */
2288
+ defaultAuthor?: string;
2289
+ };
2290
+ /**
2291
+ * Internal mutable state for the content plugin.
2292
+ *
2293
+ * @example
2294
+ * ```ts
2295
+ * { processor: null, articles: new Map(), slugs: null, dirtyPaths: new Set() }
2296
+ * ```
2297
+ */
2298
+ type State = {
2299
+ /** Lazily-created unified processor singleton. null until first render()/loadAll(). */processor: Processor | null; /** Article cache keyed locale -> (slug -> Article). Starts empty. */
2300
+ articles: Map<string, Map<string, Article>>; /** Discovered, sorted slug list cached after first disk scan. null until first discovery. */
2301
+ slugs: string[] | null; /** Paths marked stale by invalidate(); next loadAll() re-reads only these. Starts empty. */
2302
+ dirtyPaths: Set<string>;
2303
+ };
2304
+ /**
2305
+ * YAML frontmatter parsed from each article file.
2306
+ *
2307
+ * @example
2308
+ * ```ts
2309
+ * { title: "Hello", date: "2026-01-15", description: "Intro", tags: [], language: "en" }
2310
+ * ```
2311
+ */
2312
+ type Frontmatter = {
2313
+ /** Article title. Required. */title: string; /** ISO 8601 date string, e.g. "2026-01-15". Required. */
2314
+ date: string; /** Short summary used in cards, feeds, and meta description. Required. */
2315
+ description: string; /** Topic tags. Required (may be empty array). */
2316
+ tags: string[]; /** Source language code of this file. Required. */
2317
+ language: string; /** Draft flag. Excluded from output in production mode. Defaults to false. */
2318
+ draft?: boolean; /** Author name. Falls back to config.defaultAuthor when omitted. */
2319
+ author?: string;
2320
+ };
2321
+ /**
2322
+ * Fields computed by the pipeline (not authored in frontmatter).
2323
+ *
2324
+ * @example
2325
+ * ```ts
2326
+ * { slug: "hello", readingTime: 1, contentId: "hello", status: "published", wordCount: 42 }
2327
+ * ```
2328
+ */
2329
+ type ComputedFields = {
2330
+ /** Article directory name. */slug: string; /** Reading time in minutes (ceiling, minimum 1). */
2331
+ readingTime: number; /** Stable content identifier (slug by default). */
2332
+ contentId: string; /** Derived publication status. */
2333
+ status: "published" | "draft"; /** Word count from the source body. */
2334
+ wordCount: number;
2335
+ };
2336
+ /**
2337
+ * A fully processed, render-ready article.
2338
+ *
2339
+ * @example
2340
+ * ```ts
2341
+ * { frontmatter, computed, html: "<p>…</p>", locale: "en", isFallback: false, url: "/en/hello/" }
2342
+ * ```
2343
+ */
2344
+ type Article = {
2345
+ /** Parsed frontmatter. */frontmatter: Frontmatter; /** Pipeline-computed metadata. */
2346
+ computed: ComputedFields; /** Sanitized rendered HTML body. */
2347
+ html: string; /** Locale this Article instance represents (the requested locale, even on fallback). */
2348
+ locale: string; /** True when the default-locale file was used because the requested locale was missing. */
2349
+ isFallback: boolean; /** Canonical URL for this article in this locale. */
2350
+ url: string;
2351
+ };
2352
+ /**
2353
+ * Lightweight projection of Article for cards/lists.
2354
+ *
2355
+ * @example
2356
+ * ```ts
2357
+ * { contentId: "hello", status: "published", title: "Hello", date: "2026-01-15", description: "Intro", tags: [], readingTime: 1, url: "/en/hello/" }
2358
+ * ```
2359
+ */
2360
+ type ArticleCard = {
2361
+ /** Stable content identifier. */contentId: string; /** Derived publication status. */
2362
+ status: "published" | "draft"; /** Article title. */
2363
+ title: string; /** ISO 8601 date string. */
2364
+ date: string; /** Short summary. */
2365
+ description: string; /** Topic tags. */
2366
+ tags: string[]; /** Reading time in minutes. */
2367
+ readingTime: number; /** Canonical URL for this article in this locale. */
2368
+ url: string;
2369
+ };
2370
+ /**
2371
+ * Notification-only events emitted by the content plugin.
2372
+ *
2373
+ * @example
2374
+ * ```ts
2375
+ * emit("content:ready", { locales: ["en"], articleCount: 3 });
2376
+ * ```
2377
+ */
2378
+ type ContentEvents = {
2379
+ /** All articles loaded across locales. */"content:ready": {
2380
+ locales: readonly string[];
2381
+ articleCount: number;
2382
+ }; /** Article paths marked stale for dev rebuild. */
2383
+ "content:invalidated": {
2384
+ paths: readonly string[];
2385
+ };
2386
+ };
2387
+ /**
2388
+ * Kernel-free domain context handed to createContentApi by the wiring harness.
2389
+ * Carries ctx.state (mutable escape hatch), config, global, emit, and the
2390
+ * i18n-derived locale/url helpers — so api.ts stays free of createPlugin/ctx.
2391
+ *
2392
+ * @example
2393
+ * ```ts
2394
+ * const apiContext: ContentApiContext = { state, config, global, emit, locales, defaultLocale, articleToUrl };
2395
+ * ```
2396
+ */
2397
+ type ContentApiContext = {
2398
+ /** Mutable plugin state (article cache + lazy processor). */state: State; /** Resolved plugin configuration. */
2399
+ config: Config; /** Global framework configuration (mode, etc.). */
2400
+ global: {
2401
+ mode: "production" | "development";
2402
+ }; /** Emit a registered content event. */
2403
+ emit: <K extends keyof ContentEvents>(event: K, payload: ContentEvents[K]) => void; /** Active locale codes from i18n. */
2404
+ locales: () => readonly string[]; /** Default locale code from i18n (fallback source). */
2405
+ defaultLocale: () => string; /** Build a canonical article URL for a locale + slug. */
2406
+ articleToUrl: (locale: string, slug: string) => string;
2407
+ };
2408
+ /**
2409
+ * Public API for the content plugin.
2410
+ *
2411
+ * @example
2412
+ * ```ts
2413
+ * const map = await app.content.loadAll();
2414
+ * ```
2415
+ */
2416
+ type Api = {
2417
+ /**
2418
+ * Load every article across every active locale, returning a locale-keyed
2419
+ * map of date-descending Article arrays. Emits content:ready.
2420
+ */
2421
+ loadAll(): Promise<Map<string, Article[]>>;
2422
+ /**
2423
+ * Resolve and render a single article for one locale, with locale fallback.
2424
+ *
2425
+ * @param slug - Article directory name.
2426
+ * @param locale - Requested locale code.
2427
+ */
2428
+ load(slug: string, locale: string): Promise<Article>;
2429
+ /**
2430
+ * Render a raw Markdown string to HTML through the full pipeline.
2431
+ *
2432
+ * @param md - Raw Markdown source.
2433
+ */
2434
+ renderMarkdown(md: string): Promise<string>;
2435
+ /**
2436
+ * Mark file paths stale for incremental dev rebuilds. Emits content:invalidated.
2437
+ *
2438
+ * @param paths - File paths to invalidate.
2439
+ */
2440
+ invalidate(paths: readonly string[]): void;
2441
+ /**
2442
+ * Project a full Article to a lightweight ArticleCard for list/grid rendering.
2443
+ *
2444
+ * @param article - The source article.
2445
+ */
2446
+ articleToCard(article: Article): ArticleCard;
2447
+ /**
2448
+ * The configured content source directory (e.g. `"./content"`). Lets the build copy each
2449
+ * article's co-located assets (`<contentDir>/<slug>/images/`) into the output so the absolute
2450
+ * image URLs the renderer emits resolve.
2451
+ */
2452
+ contentDir(): string;
2453
+ };
2454
+ //#endregion
2455
+ //#region src/plugins/content/index.d.ts
2456
+ /**
2457
+ * Content plugin — Markdown pipeline: discovers files, parses frontmatter, renders
2458
+ * to sanitized HTML (rehype-sanitize unless `trustedContent`), and exposes a
2459
+ * locale-keyed Article model. Depends on i18n; emits `content:ready` and
2460
+ * `content:invalidated`.
2461
+ *
2462
+ * @example Point at a content directory and pick a Shiki theme
2463
+ * ```ts
2464
+ * const app = createApp({
2465
+ * pluginConfigs: {
2466
+ * content: {
2467
+ * contentDir: "./content",
2468
+ * shikiTheme: "github-dark",
2469
+ * defaultAuthor: "Ada Lovelace"
2470
+ * // trustedContent: true // ONLY for fully author-controlled Markdown — disables sanitize
2471
+ * }
2472
+ * }
2473
+ * });
2474
+ * ```
1874
2475
  */
1875
- declare const contentPlugin: import("@moku-labs/core").PluginInstance<"content", Config$1, State$1, Api$1, {
2476
+ declare const contentPlugin: import("@moku-labs/core").PluginInstance<"content", Config, State, Api, {
1876
2477
  "content:ready": {
1877
2478
  locales: readonly string[];
1878
2479
  articleCount: number;
@@ -1881,7 +2482,7 @@ declare const contentPlugin: import("@moku-labs/core").PluginInstance<"content",
1881
2482
  paths: readonly string[];
1882
2483
  };
1883
2484
  }> & Record<never, never>;
1884
- declare namespace types_d_exports$2 {
2485
+ declare namespace types_d_exports$3 {
1885
2486
  export { DataConfig, DataEntry, DataProvider, DataState, DataWriteSummary };
1886
2487
  }
1887
2488
  /**
@@ -2027,185 +2628,6 @@ type DataProvider = {
2027
2628
  * ```
2028
2629
  */
2029
2630
  declare const dataPlugin: import("@moku-labs/core").PluginInstance<"data", DataConfig, DataState, DataProvider, {}> & Record<never, never>;
2030
- declare namespace types_d_exports$3 {
2031
- export { Api, Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State, WranglerErrorKind };
2032
- }
2033
- /**
2034
- * @file deploy plugin — type definitions.
2035
- */
2036
- /**
2037
- * Options passed to the injected spawner — the subset of Bun.spawn's options the
2038
- * plugin uses (piped stdout/stderr plus an env carrying the API token).
2039
- */
2040
- interface SpawnOptions {
2041
- /** Capture stdout as a readable stream. */
2042
- readonly stdout: "pipe";
2043
- /** Capture stderr as a readable stream. */
2044
- readonly stderr: "pipe";
2045
- /** Subprocess environment — the API token is injected here, never via argv. */
2046
- readonly env?: Record<string, string | undefined>;
2047
- }
2048
- /**
2049
- * The structural subprocess handle the plugin reads back: stdout/stderr streams
2050
- * plus the exit-code promise. Streams are typed `unknown` and narrowed at the read
2051
- * site so this carries no Bun namespace types.
2052
- */
2053
- interface SpawnedProcess {
2054
- /** Standard output stream (narrowed to a ReadableStream at the read site). */
2055
- readonly stdout: unknown;
2056
- /** Standard error stream (narrowed to a ReadableStream at the read site). */
2057
- readonly stderr: unknown;
2058
- /** Resolves with the subprocess exit code. */
2059
- readonly exited: Promise<number>;
2060
- }
2061
- /**
2062
- * The subset of Bun.spawn's signature the plugin relies on (argv array + options).
2063
- * Declared structurally — with NO `import("bun")` namespace types — so it survives
2064
- * `.d.ts` bundling intact and tests can supply a fake spawn without importing Bun.
2065
- */
2066
- type SpawnFunction = (cmd: string[], options: SpawnOptions) => SpawnedProcess;
2067
- /**
2068
- * A deploy error `code` from the wrangler error taxonomy and preflight validators.
2069
- */
2070
- type DeployErrorCode = "ERR_DEPLOY_NO_WRANGLER_CONFIG" | "ERR_DEPLOY_EMPTY_OUTDIR" | "ERR_DEPLOY_TOO_MANY_FILES" | "ERR_DEPLOY_FILE_TOO_LARGE" | "ERR_DEPLOY_PATH_TRAVERSAL" | "ERR_DEPLOY_INVALID_BRANCH" | "ERR_DEPLOY_NO_TOKEN" | "ERR_DEPLOY_PROJECT_NOT_FOUND" | "ERR_DEPLOY_AUTH_EXPIRED" | "ERR_DEPLOY_AUTH" | "ERR_DEPLOY_NETWORK" | "ERR_DEPLOY_WRANGLER_FAILED" | "ERR_DEPLOY_CONFIG";
2071
- /**
2072
- * The subset of wrangler error `code`s classifyWranglerError can produce from a
2073
- * non-zero wrangler exit.
2074
- */
2075
- type WranglerErrorKind = Extract<DeployErrorCode, "ERR_DEPLOY_PROJECT_NOT_FOUND" | "ERR_DEPLOY_AUTH_EXPIRED" | "ERR_DEPLOY_AUTH" | "ERR_DEPLOY_NETWORK" | "ERR_DEPLOY_WRANGLER_FAILED">;
2076
- /**
2077
- * Configuration for the deploy plugin.
2078
- */
2079
- type Config = {
2080
- /**
2081
- * Deploy target. Only Cloudflare Pages is supported in this version.
2082
- * Defaults to `cloudflare-pages`.
2083
- */
2084
- target: "cloudflare-pages";
2085
- /**
2086
- * Directory (relative to project root) containing the built site to deploy.
2087
- * Re-validated against cwd at run() time to block path traversal.
2088
- * Defaults to `dist`.
2089
- */
2090
- outDir: string;
2091
- /**
2092
- * Branch treated as the Cloudflare Pages production branch.
2093
- * Defaults to `main`.
2094
- */
2095
- productionBranch?: string;
2096
- /**
2097
- * Substrings exempt from entropy-gated secret scrubbing in logged output.
2098
- * Defaults to `["CLOUDFLARE_ACCOUNT_ID"]`.
2099
- */
2100
- scrubAllowlist: string[];
2101
- /**
2102
- * Cloudflare compatibility date written into generated wrangler.jsonc.
2103
- * Defaults to `2024-01-01`.
2104
- */
2105
- compatibilityDate?: string;
2106
- /**
2107
- * Whether init() also generates a GitHub Actions workflow.
2108
- * Defaults to `false`.
2109
- */
2110
- ci?: boolean;
2111
- };
2112
- /**
2113
- * Result of a successful deploy.
2114
- */
2115
- type DeployResult = {
2116
- /** The public deployment URL (e.g. https://my-site.pages.dev). */url: string; /** Cloudflare deployment ID parsed from wrangler output. */
2117
- deploymentId: string; /** The branch that was deployed. */
2118
- branch: string; /** Wall-clock duration of the deploy in milliseconds. */
2119
- durationMs: number;
2120
- };
2121
- /**
2122
- * Runtime state for the deploy plugin. Created in createState() and accessed via
2123
- * ctx.state. deploy declares no onStop because nothing here is a long-lived resource.
2124
- */
2125
- type State = {
2126
- /** Result of the most recent successful deploy, or null before the first run. */lastDeployment: DeployResult | null;
2127
- /**
2128
- * Injectable subprocess spawner. Defaults to Bun.spawn. Swapped for a mock in
2129
- * unit tests so wrangler is never actually invoked. Never reassigned at runtime.
2130
- */
2131
- spawn: SpawnFunction;
2132
- };
2133
- /**
2134
- * Options for DeployApi.run.
2135
- */
2136
- type DeployRunOptions = {
2137
- /**
2138
- * Branch to deploy. Defaults to config.productionBranch (or "main"). Must match
2139
- * /^[a-zA-Z0-9/_.-]+$/ — otherwise rejected with ERR_DEPLOY_INVALID_BRANCH.
2140
- */
2141
- branch?: string;
2142
- /**
2143
- * Whether to run the build before deploying. When false, deploys the existing outDir.
2144
- * Defaults to `true`.
2145
- */
2146
- build?: boolean;
2147
- };
2148
- /**
2149
- * Options for DeployApi.init.
2150
- */
2151
- type DeployInitOptions = {
2152
- /** Also generate the GitHub Actions workflow. Defaults to config.ci. */ci?: boolean; /** Drift-only mode: report differences without writing any files. Defaults to `false`. */
2153
- check?: boolean;
2154
- };
2155
- /**
2156
- * Result of an init/scaffold operation.
2157
- */
2158
- type InitResult = {
2159
- /** Paths written this invocation. */written: string[]; /** Paths skipped because they already exist. */
2160
- skipped: string[]; /** In check mode: paths whose on-disk content differs from what would be generated. */
2161
- drifted: string[];
2162
- };
2163
- /**
2164
- * Public API of the deploy plugin (returned from the api factory).
2165
- */
2166
- type Api = {
2167
- /**
2168
- * Deploy the built outDir to Cloudflare Pages via the wrangler subprocess.
2169
- * Runs preflight validators, re-validates the resolved outdir against cwd, guards
2170
- * the branch argument, spawns wrangler (no shell), scrubs all subprocess output
2171
- * before logging, records lastDeployment, and emits deploy:complete.
2172
- *
2173
- * @param options - Optional branch override and build toggle.
2174
- * @returns The deploy result (url, deploymentId, branch, durationMs).
2175
- * @throws {Error} With a `code` from the deploy error taxonomy on any failure.
2176
- * @example
2177
- * const result = await app.deploy.run();
2178
- * console.log(result.url); // https://my-site.pages.dev
2179
- * @example
2180
- * await app.deploy.run({ branch: "preview/landing", build: false });
2181
- */
2182
- run(options?: DeployRunOptions): Promise<DeployResult>;
2183
- /**
2184
- * Return the most recent successful deploy result, or null if none has occurred.
2185
- * The returned object is read-only (a defensive snapshot).
2186
- *
2187
- * @returns The last DeployResult, or null.
2188
- * @example
2189
- * const last = app.deploy.getLastDeployment();
2190
- * if (last) console.log(`Last deployed to ${last.url}`);
2191
- */
2192
- getLastDeployment(): Readonly<DeployResult> | null;
2193
- /**
2194
- * Generate deploy scaffolding: wrangler.jsonc (slug from site.name() + outDir +
2195
- * compatibilityDate) and, when ci is enabled, .github/workflows/deploy.yml. Never
2196
- * overwrites an existing wrangler.jsonc. In check mode, reports drift instead of writing.
2197
- *
2198
- * @param options - Optional ci toggle and check (drift-only) mode.
2199
- * @returns Which files were written, skipped, or would drift.
2200
- * @example
2201
- * const out = await app.deploy.init({ ci: true });
2202
- * // out.written -> ["wrangler.jsonc", ".github/workflows/deploy.yml"]
2203
- * @example
2204
- * const drift = await app.deploy.init({ check: true });
2205
- * if (drift.drifted.length) process.exit(1);
2206
- */
2207
- init(options?: DeployInitOptions): Promise<InitResult>;
2208
- };
2209
2631
  //#endregion
2210
2632
  //#region src/plugins/deploy/index.d.ts
2211
2633
  /**
@@ -2233,7 +2655,7 @@ type Api = {
2233
2655
  * });
2234
2656
  * ```
2235
2657
  */
2236
- declare const deployPlugin: import("@moku-labs/core").PluginInstance<"deploy", Config, State, Api, {
2658
+ declare const deployPlugin: import("@moku-labs/core").PluginInstance<"deploy", Config$2, State$2, Api$2, {
2237
2659
  "deploy:complete": {
2238
2660
  url: string;
2239
2661
  deploymentId: string;
@@ -2340,7 +2762,7 @@ declare function buildArticleHead(articleMeta: ArticleMeta, canonicalUrl: string
2340
2762
  * });
2341
2763
  * ```
2342
2764
  */
2343
- declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Config$3, State$3, Api$3, {}> & {
2765
+ declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Config$4, State$4, Api$4, {}> & {
2344
2766
  meta: typeof meta;
2345
2767
  og: typeof og;
2346
2768
  twitter: typeof twitter;
@@ -2371,7 +2793,7 @@ declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Confi
2371
2793
  * });
2372
2794
  * ```
2373
2795
  */
2374
- declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Config$5, Record<string, never>, Api$5, {}> & Record<never, never>;
2796
+ declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>;
2375
2797
  //#endregion
2376
2798
  //#region src/plugins/log/index.d.ts
2377
2799
  /**
@@ -2461,7 +2883,7 @@ declare const routerPlugin: import("@moku-labs/core").PluginInstance<"router", R
2461
2883
  * });
2462
2884
  * ```
2463
2885
  */
2464
- declare const sitePlugin: import("@moku-labs/core").PluginInstance<"site", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>;
2886
+ declare const sitePlugin: import("@moku-labs/core").PluginInstance<"site", Config$7, Record<string, never>, Api$7, {}> & Record<never, never>;
2465
2887
  //#endregion
2466
2888
  //#region src/plugins/spa/components.d.ts
2467
2889
  /**
@@ -2592,10 +3014,10 @@ declare function cloudflareBindings(): EnvProvider;
2592
3014
  * await app.build.run();
2593
3015
  * ```
2594
3016
  */
2595
- declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs/core").AnyPluginInstance[] = readonly []>(options?: import("@moku-labs/core").CreateAppOptions<Config$7, Events, (import("@moku-labs/core").PluginInstance<"site", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"i18n", Config$5, Record<string, never>, Api$5, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"router", RouterConfig, RouterState, RouterApi, {}> & {
3017
+ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs/core").AnyPluginInstance[] = readonly []>(options?: import("@moku-labs/core").CreateAppOptions<Config$8, Events, (import("@moku-labs/core").PluginInstance<"site", Config$7, Record<string, never>, Api$7, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"i18n", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"router", RouterConfig, RouterState, RouterApi, {}> & {
2596
3018
  route: typeof route;
2597
3019
  defineRoutes: typeof defineRoutes;
2598
- }) | (import("@moku-labs/core").PluginInstance<"head", Config$3, State$3, Api$3, {}> & {
3020
+ }) | (import("@moku-labs/core").PluginInstance<"head", Config$4, State$4, Api$4, {}> & {
2599
3021
  meta: typeof meta;
2600
3022
  og: typeof og;
2601
3023
  twitter: typeof twitter;
@@ -2620,10 +3042,10 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
2620
3042
  name: string;
2621
3043
  el: Element;
2622
3044
  };
2623
- }> & Record<never, never>) | ExtraPlugins[number], [...ExtraPlugins], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>, import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>]>> | undefined) => import("@moku-labs/core").App<Config$7, Events, (import("@moku-labs/core").PluginInstance<"site", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"i18n", Config$5, Record<string, never>, Api$5, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"router", RouterConfig, RouterState, RouterApi, {}> & {
3045
+ }> & Record<never, never>) | ExtraPlugins[number], [...ExtraPlugins], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>, import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>]>> | undefined) => import("@moku-labs/core").App<Config$8, Events, (import("@moku-labs/core").PluginInstance<"site", Config$7, Record<string, never>, Api$7, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"i18n", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"router", RouterConfig, RouterState, RouterApi, {}> & {
2624
3046
  route: typeof route;
2625
3047
  defineRoutes: typeof defineRoutes;
2626
- }) | (import("@moku-labs/core").PluginInstance<"head", Config$3, State$3, Api$3, {}> & {
3048
+ }) | (import("@moku-labs/core").PluginInstance<"head", Config$4, State$4, Api$4, {}> & {
2627
3049
  meta: typeof meta;
2628
3050
  og: typeof og;
2629
3051
  twitter: typeof twitter;
@@ -2664,6 +3086,6 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
2664
3086
  * const app = createApp({ plugins: [analytics] });
2665
3087
  * ```
2666
3088
  */
2667
- declare const createPlugin: import("@moku-labs/core").BoundCreatePluginFunction<Config$7, Events, import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>, import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>]>>;
3089
+ declare const createPlugin: import("@moku-labs/core").BoundCreatePluginFunction<Config$8, Events, import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>, import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>]>>;
2668
3090
  //#endregion
2669
- export { types_d_exports as Build, types_d_exports$1 as Content, types_d_exports$2 as Data, types_d_exports$3 as Deploy, types_d_exports$4 as Env, types_d_exports$5 as Head, types_d_exports$6 as Log, types_d_exports$7 as Router, types_d_exports$8 as Spa, browserEnv, buildArticleHead, buildPlugin, canonical, cloudflareBindings, contentPlugin, createApp, createComponent, createPlugin, dataPlugin, defineRoutes, deployPlugin, dotenv, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, processEnv, route, routerPlugin, sitePlugin, spaPlugin, twitter };
3091
+ export { types_d_exports as Build, types_d_exports$1 as Cli, types_d_exports$2 as Content, types_d_exports$3 as Data, types_d_exports$4 as Deploy, types_d_exports$5 as Env, types_d_exports$6 as Head, types_d_exports$7 as Log, types_d_exports$8 as Router, types_d_exports$9 as Spa, browserEnv, buildArticleHead, buildPlugin, canonical, cliPlugin, cloudflareBindings, contentPlugin, createApp, createComponent, createPlugin, dataPlugin, defineRoutes, deployPlugin, dotenv, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, processEnv, route, routerPlugin, sitePlugin, spaPlugin, twitter };