@moku-labs/web 1.1.0 → 1.3.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.cts CHANGED
@@ -1421,7 +1421,7 @@ type SpaApi = {
1421
1421
  current(): string;
1422
1422
  };
1423
1423
  declare namespace types_d_exports {
1424
- export { Api$3 as Api, BuildCacheEntry, BuildEvents, BuildResult, Config$3 as Config, ExtractApi, OgFont, OgImageConfig, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, RichOgInput, State$3 as State };
1424
+ export { Api$3 as Api, BuildCacheEntry, BuildEvents, BuildResult, BuildRunOverrides, Config$3 as Config, ExtractApi, OgFont, OgImageConfig, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, RenderCacheEntry, RichOgInput, RunOptions, State$3 as State };
1425
1425
  }
1426
1426
  /**
1427
1427
  * Structural extraction of a plugin instance's public API from its `_phantom`
@@ -1629,6 +1629,20 @@ type Config$3 = {
1629
1629
  * ```
1630
1630
  */
1631
1631
  type BuildCacheEntry = Record<string, string>;
1632
+ /**
1633
+ * One cached page render: the SSR body HTML keyed by a hash of the inputs that determine
1634
+ * it (the route's loaded data). Lets a dev incremental rebuild skip the synchronous,
1635
+ * dominant-cost `preact-render-to-string` for a page whose data is unchanged.
1636
+ *
1637
+ * @example
1638
+ * ```ts
1639
+ * const entry: RenderCacheEntry = { dataHash: "9f8e…", body: "<h1>Hi</h1>" };
1640
+ * ```
1641
+ */
1642
+ type RenderCacheEntry = {
1643
+ /** Hash of the page's render inputs (its loaded data). */dataHash: string; /** The SSR-rendered body HTML for those inputs. */
1644
+ body: string;
1645
+ };
1632
1646
  /**
1633
1647
  * Per-run closure state for the `build` plugin. Holds caches and config only —
1634
1648
  * no domain data is duplicated here (pulled fresh via `ctx.require` each run).
@@ -1653,6 +1667,13 @@ interface State$3 {
1653
1667
  * so unchanged articles are skipped on the next run.
1654
1668
  */
1655
1669
  ogImageHashCache: Map<string, string>;
1670
+ /**
1671
+ * Cross-run page-render cache: `name\0params\0locale` -> {@link RenderCacheEntry}.
1672
+ * Persists across dev rebuilds (never reset by a run) so an incremental rebuild reuses
1673
+ * the body of any page whose data is unchanged. Empty for a fresh process; cleared by a
1674
+ * full (non-incremental) render so stale entries for removed routes never linger.
1675
+ */
1676
+ renderCache: Map<string, RenderCacheEntry>;
1656
1677
  }
1657
1678
  /**
1658
1679
  * Ordered names of the build pipeline phases, in execution order.
@@ -1679,6 +1700,51 @@ interface BuildResult {
1679
1700
  /** Total wall-clock duration of the run, in milliseconds. */
1680
1701
  durationMs: number;
1681
1702
  }
1703
+ /**
1704
+ * Per-run {@link Config} field overrides for a single {@link Api.run} call. Lets a dev
1705
+ * rebuild disable expensive, preview-irrelevant outputs (feeds / sitemap / og-images /
1706
+ * locale-redirects) and minification WITHOUT mutating the persisted plugin config. Each
1707
+ * key maps to the same-named {@link Config} flag and is merged over the config snapshot
1708
+ * for that one run only.
1709
+ *
1710
+ * @example
1711
+ * ```ts
1712
+ * const dev: BuildRunOverrides = { minify: false, feeds: false, sitemap: false };
1713
+ * ```
1714
+ */
1715
+ type BuildRunOverrides = Readonly<Partial<Pick<Config$3, "minify" | "feeds" | "sitemap" | "ogImage" | "images" | "localeRedirects" | "notFound">>>;
1716
+ /**
1717
+ * Options for a single {@link Api.run} call. All fields are optional; an absent/empty
1718
+ * options object runs the full production build (clean + every configured phase). The
1719
+ * dev server (`serve()`) passes `skipClean`/`overrides`/`changed` to make a rebuild
1720
+ * fast without touching the production path.
1721
+ *
1722
+ * @example
1723
+ * ```ts
1724
+ * await app.build.run({ skipClean: true, overrides: { minify: false }, changed: ["/abs/content/intro/en.md"] });
1725
+ * ```
1726
+ */
1727
+ type RunOptions = {
1728
+ /** Override the configured output directory for this run. */outDir?: string;
1729
+ /**
1730
+ * Skip the destructive clean (`rm -rf outDir`) so caches + unchanged assets survive a dev
1731
+ * rebuild. TRADE-OFF: because nothing is pruned, output for a route DELETED or renamed
1732
+ * since the last build lingers on disk (and is still served by the dev server) until the
1733
+ * next full/clean build — restart `serve` or run a production `build` to clear it.
1734
+ */
1735
+ skipClean?: boolean; /** Per-run {@link Config} field overrides merged over the snapshot (e.g. disable feeds/sitemap/minify in dev). */
1736
+ overrides?: BuildRunOverrides;
1737
+ /**
1738
+ * Paths changed since the last build (dev incremental rebuild). When present, the pipeline
1739
+ * re-reads only changed Markdown and re-renders only pages whose loaded data changed; omit
1740
+ * it for a full build (initial build + every production build). CORRECTNESS NOTE: render
1741
+ * reuse is sound only when a route's render/layout output is a pure function of its
1742
+ * `.load()` data (+ params/locale/code) — a route whose `.render()` reads module-global or
1743
+ * other out-of-band state can show a stale body on a content-only rebuild until the next
1744
+ * code change or restart. Production builds omit `changed`, so they are never affected.
1745
+ */
1746
+ changed?: readonly string[];
1747
+ };
1682
1748
  /**
1683
1749
  * Public API surface mounted on `app.build`.
1684
1750
  *
@@ -1689,19 +1755,18 @@ interface BuildResult {
1689
1755
  */
1690
1756
  type Api$3 = {
1691
1757
  /**
1692
- * Run the full SSG pipeline and write the site to disk.
1758
+ * Run the full SSG pipeline and write the site to disk. With no options a full
1759
+ * production build runs (clean + every configured phase); dev callers pass
1760
+ * {@link RunOptions} (`skipClean`/`overrides`/`changed`) for a fast incremental rebuild.
1693
1761
  *
1694
- * @param options - Optional run overrides.
1695
- * @param options.outDir - Override the configured output directory for this run.
1762
+ * @param options - Optional per-run overrides ({@link RunOptions}).
1696
1763
  * @returns The build result (outDir, pageCount, durationMs).
1697
1764
  * @example
1698
1765
  * ```ts
1699
1766
  * const result = await app.build.run({ outDir: "./preview" });
1700
1767
  * ```
1701
1768
  */
1702
- run(options?: {
1703
- outDir?: string;
1704
- }): Promise<BuildResult>;
1769
+ run(options?: RunOptions): Promise<BuildResult>;
1705
1770
  /**
1706
1771
  * List the phases in execution order (introspection / tooling).
1707
1772
  *
@@ -1761,7 +1826,7 @@ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Con
1761
1826
  */
1762
1827
  type WorkflowTrigger = "auto" | "versioned-tag" | "dispatch";
1763
1828
  declare namespace types_d_exports$4 {
1764
- export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WorkflowTrigger, WranglerErrorKind };
1829
+ export { Api$2 as Api, Config$2 as Config, CreateProjectResult, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WorkflowTrigger, WranglerErrorKind };
1765
1830
  }
1766
1831
  /**
1767
1832
  * Options passed to the injected spawner — the subset of Bun.spawn's options the
@@ -1890,6 +1955,13 @@ type InitResult = {
1890
1955
  skipped: string[]; /** In check mode: paths whose on-disk content differs from what would be generated. */
1891
1956
  drifted: string[];
1892
1957
  };
1958
+ /**
1959
+ * Result of creating the remote Cloudflare Pages project.
1960
+ */
1961
+ type CreateProjectResult = {
1962
+ /** The created project name (the `toSlug(site.name())` slug). */name: string; /** The production branch the project was created with. */
1963
+ branch: string;
1964
+ };
1893
1965
  /**
1894
1966
  * Public API of the deploy plugin (returned from the api factory).
1895
1967
  */
@@ -1935,6 +2007,28 @@ type Api$2 = {
1935
2007
  * if (drift.drifted.length) process.exit(1);
1936
2008
  */
1937
2009
  init(options?: DeployInitOptions): Promise<InitResult>;
2010
+ /**
2011
+ * The Cloudflare Pages project name this app deploys to — `toSlug(site.name())`, the
2012
+ * same slug `run()` passes as `--project-name` and `createProject()` creates. Lets a
2013
+ * caller (e.g. the guided deploy wizard) name the project before it exists.
2014
+ *
2015
+ * @returns The project-name slug.
2016
+ * @example
2017
+ * app.deploy.projectName(); // "my-site"
2018
+ */
2019
+ projectName(): string;
2020
+ /**
2021
+ * Create the remote Cloudflare Pages project (`wrangler pages project create`) so a
2022
+ * first deploy has a target. The slug is derived from `site.name()` and the production
2023
+ * branch from `config.productionBranch` (or `"main"`). Wrangler errors if the project
2024
+ * already exists; the guided wizard calls this only after a project-not-found failure.
2025
+ *
2026
+ * @returns The created project name + production branch.
2027
+ * @throws {Error} With a `code` from the deploy error taxonomy on a non-zero exit.
2028
+ * @example
2029
+ * const created = await app.deploy.createProject(); // { name: "my-site", branch: "main" }
2030
+ */
2031
+ createProject(): Promise<CreateProjectResult>;
1938
2032
  };
1939
2033
  declare namespace types_d_exports$1 {
1940
2034
  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 };
@@ -2109,6 +2203,17 @@ type CliRenderer = {
2109
2203
  * render.check(false, "CLOUDFLARE_API_TOKEN is set", "Create one at …");
2110
2204
  */
2111
2205
  check(ok: boolean, label: string, detail?: string): void;
2206
+ /**
2207
+ * Stop any running animation (the live `serve()` idle pulse, a phase/rebuild spinner)
2208
+ * and release the renderer's interval timer. Called by `serve()`'s SIGINT/SIGTERM
2209
+ * teardown so the persistent idle-pulse ticker never outlives the dev server. A no-op
2210
+ * when nothing is animating; safe to call more than once.
2211
+ *
2212
+ * @returns Nothing.
2213
+ * @example
2214
+ * render.dispose();
2215
+ */
2216
+ dispose(): void;
2112
2217
  };
2113
2218
  /**
2114
2219
  * A live directory watcher handle returned by the injectable `watch` seam. Closing
@@ -2284,6 +2389,18 @@ type State$1 = {
2284
2389
  * const mtime = state.fileMtime("/abs/content/a.md");
2285
2390
  */
2286
2391
  fileMtime: (path: string) => number | null;
2392
+ /**
2393
+ * Resolve a file's content hash, or `null` when it does not exist. serve()'s change gate
2394
+ * uses it to drop a no-op save whose bytes are identical to the last successful build (the
2395
+ * double Ctrl-S habit). Default reads the file + hashes it (`node:fs` + `node:crypto`);
2396
+ * tests inject deterministic values.
2397
+ *
2398
+ * @param path - The absolute path to hash.
2399
+ * @returns The content hash, or `null` when the file is missing.
2400
+ * @example
2401
+ * const hash = state.fileHash("/abs/content/a.md");
2402
+ */
2403
+ fileHash: (path: string) => string | null;
2287
2404
  };
2288
2405
  /**
2289
2406
  * Summary returned by `cli.build()` — the awaited `build.run()` result.
@@ -2299,9 +2416,11 @@ type BuildSummary = {
2299
2416
  /**
2300
2417
  * Outcome returned by `cli.deploy()` — either a completed deploy (with details) or a
2301
2418
  * skipped one. A skip is `"declined"` when a user answers "no" at the confirm prompt,
2302
- * or `"blocked"` when the guided wizard found unmet prerequisites and stopped before
2303
- * deploying. Non-interactive direct runs (CI / non-TTY) never prompt and always proceed,
2304
- * so they never skip the scripts are CI-safe.
2419
+ * `"blocked"` when the guided wizard found unmet prerequisites and stopped before
2420
+ * deploying, or `"failed"` when the deploy itself errored (e.g. the Pages project does
2421
+ * not exist) and the wizard surfaced it as a styled error + fix hint instead of a raw
2422
+ * throw. Non-interactive direct runs (CI / non-TTY) never prompt and always proceed, so
2423
+ * they never `declined`-skip — the scripts are CI-safe.
2305
2424
  *
2306
2425
  * @example
2307
2426
  * const outcome: DeployOutcome = { deployed: false, reason: "declined" };
@@ -2314,7 +2433,7 @@ type DeployOutcome = {
2314
2433
  durationMs: number;
2315
2434
  } | {
2316
2435
  deployed: false;
2317
- reason: "declined" | "blocked";
2436
+ reason: "declined" | "blocked" | "failed";
2318
2437
  };
2319
2438
  /**
2320
2439
  * Options for `cli.build()`.
@@ -2334,6 +2453,15 @@ type BuildOptions = {
2334
2453
  type ServeOptions = {
2335
2454
  /** 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`. */
2336
2455
  open?: boolean;
2456
+ /**
2457
+ * Re-enable OG-image generation for this dev session. `serve()` disables expensive,
2458
+ * preview-irrelevant outputs by default for a fast rebuild; a thin consumer script
2459
+ * maps `--og` to this. Defaults to `false`.
2460
+ */
2461
+ og?: boolean; /** Re-enable `sitemap.xml` + `robots.txt` for this dev session (maps to `--sitemap`). Defaults to `false`. */
2462
+ sitemap?: boolean; /** Re-enable RSS/Atom/JSON feeds for this dev session (maps to `--feeds`). Defaults to `false`. */
2463
+ feeds?: boolean; /** Re-enable i18n locale-redirect pages for this dev session (maps to `--locale-redirects`). Defaults to `false`. */
2464
+ localeRedirects?: boolean;
2337
2465
  };
2338
2466
  /**
2339
2467
  * Options for `cli.preview()`.
@@ -2433,7 +2561,7 @@ type Api$1 = {
2433
2561
  */
2434
2562
  declare const cliPlugin: import("@moku-labs/core").PluginInstance<"cli", Config$1, State$1, Api$1, {}> & Record<never, never>;
2435
2563
  declare namespace types_d_exports$2 {
2436
- export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, ContentProvider, ContentProviderState, FileSystemContentOptions, Frontmatter, State };
2564
+ export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, ContentProvider, ContentProviderState, FileSystemContentOptions, Frontmatter, LoadAllOptions, State };
2437
2565
  }
2438
2566
  /**
2439
2567
  * YAML frontmatter parsed from each article file.
@@ -2652,6 +2780,24 @@ type ContentApiContext = {
2652
2780
  defaultLocale: () => string; /** The resolved content source (merged from `config.providers`). */
2653
2781
  provider: ContentProvider;
2654
2782
  };
2783
+ /**
2784
+ * Options for {@link Api.loadAll}.
2785
+ *
2786
+ * @example
2787
+ * ```ts
2788
+ * await app.content.loadAll({ reuse: true });
2789
+ * ```
2790
+ */
2791
+ type LoadAllOptions = {
2792
+ /**
2793
+ * Reuse already-cached articles for slugs NOT dropped by a preceding `invalidate()`,
2794
+ * re-reading + re-rendering (Shiki) ONLY the invalidated (dirty) articles. The
2795
+ * post-sort `contentId` ordinals are always recomputed across the full set, so order +
2796
+ * ids match a full load. Default `false` (a full load that re-reads every article).
2797
+ * Used by dev incremental rebuilds; a fresh process / production build never reuses.
2798
+ */
2799
+ reuse?: boolean;
2800
+ };
2655
2801
  /**
2656
2802
  * Public API for the content plugin.
2657
2803
  *
@@ -2664,8 +2810,10 @@ type Api = {
2664
2810
  /**
2665
2811
  * Load every article across every active locale, returning a locale-keyed
2666
2812
  * map of date-descending Article arrays. Emits content:ready.
2813
+ *
2814
+ * @param options - Optional load behaviour ({@link LoadAllOptions}); omit for a full load.
2667
2815
  */
2668
- loadAll(): Promise<Map<string, Article[]>>;
2816
+ loadAll(options?: LoadAllOptions): Promise<Map<string, Article[]>>;
2669
2817
  /**
2670
2818
  * Resolve and render a single article for one locale, with locale fallback.
2671
2819
  *
package/dist/index.d.mts CHANGED
@@ -1421,7 +1421,7 @@ type SpaApi = {
1421
1421
  current(): string;
1422
1422
  };
1423
1423
  declare namespace types_d_exports {
1424
- export { Api$3 as Api, BuildCacheEntry, BuildEvents, BuildResult, Config$3 as Config, ExtractApi, OgFont, OgImageConfig, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, RichOgInput, State$3 as State };
1424
+ export { Api$3 as Api, BuildCacheEntry, BuildEvents, BuildResult, BuildRunOverrides, Config$3 as Config, ExtractApi, OgFont, OgImageConfig, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, RenderCacheEntry, RichOgInput, RunOptions, State$3 as State };
1425
1425
  }
1426
1426
  /**
1427
1427
  * Structural extraction of a plugin instance's public API from its `_phantom`
@@ -1629,6 +1629,20 @@ type Config$3 = {
1629
1629
  * ```
1630
1630
  */
1631
1631
  type BuildCacheEntry = Record<string, string>;
1632
+ /**
1633
+ * One cached page render: the SSR body HTML keyed by a hash of the inputs that determine
1634
+ * it (the route's loaded data). Lets a dev incremental rebuild skip the synchronous,
1635
+ * dominant-cost `preact-render-to-string` for a page whose data is unchanged.
1636
+ *
1637
+ * @example
1638
+ * ```ts
1639
+ * const entry: RenderCacheEntry = { dataHash: "9f8e…", body: "<h1>Hi</h1>" };
1640
+ * ```
1641
+ */
1642
+ type RenderCacheEntry = {
1643
+ /** Hash of the page's render inputs (its loaded data). */dataHash: string; /** The SSR-rendered body HTML for those inputs. */
1644
+ body: string;
1645
+ };
1632
1646
  /**
1633
1647
  * Per-run closure state for the `build` plugin. Holds caches and config only —
1634
1648
  * no domain data is duplicated here (pulled fresh via `ctx.require` each run).
@@ -1653,6 +1667,13 @@ interface State$3 {
1653
1667
  * so unchanged articles are skipped on the next run.
1654
1668
  */
1655
1669
  ogImageHashCache: Map<string, string>;
1670
+ /**
1671
+ * Cross-run page-render cache: `name\0params\0locale` -> {@link RenderCacheEntry}.
1672
+ * Persists across dev rebuilds (never reset by a run) so an incremental rebuild reuses
1673
+ * the body of any page whose data is unchanged. Empty for a fresh process; cleared by a
1674
+ * full (non-incremental) render so stale entries for removed routes never linger.
1675
+ */
1676
+ renderCache: Map<string, RenderCacheEntry>;
1656
1677
  }
1657
1678
  /**
1658
1679
  * Ordered names of the build pipeline phases, in execution order.
@@ -1679,6 +1700,51 @@ interface BuildResult {
1679
1700
  /** Total wall-clock duration of the run, in milliseconds. */
1680
1701
  durationMs: number;
1681
1702
  }
1703
+ /**
1704
+ * Per-run {@link Config} field overrides for a single {@link Api.run} call. Lets a dev
1705
+ * rebuild disable expensive, preview-irrelevant outputs (feeds / sitemap / og-images /
1706
+ * locale-redirects) and minification WITHOUT mutating the persisted plugin config. Each
1707
+ * key maps to the same-named {@link Config} flag and is merged over the config snapshot
1708
+ * for that one run only.
1709
+ *
1710
+ * @example
1711
+ * ```ts
1712
+ * const dev: BuildRunOverrides = { minify: false, feeds: false, sitemap: false };
1713
+ * ```
1714
+ */
1715
+ type BuildRunOverrides = Readonly<Partial<Pick<Config$3, "minify" | "feeds" | "sitemap" | "ogImage" | "images" | "localeRedirects" | "notFound">>>;
1716
+ /**
1717
+ * Options for a single {@link Api.run} call. All fields are optional; an absent/empty
1718
+ * options object runs the full production build (clean + every configured phase). The
1719
+ * dev server (`serve()`) passes `skipClean`/`overrides`/`changed` to make a rebuild
1720
+ * fast without touching the production path.
1721
+ *
1722
+ * @example
1723
+ * ```ts
1724
+ * await app.build.run({ skipClean: true, overrides: { minify: false }, changed: ["/abs/content/intro/en.md"] });
1725
+ * ```
1726
+ */
1727
+ type RunOptions = {
1728
+ /** Override the configured output directory for this run. */outDir?: string;
1729
+ /**
1730
+ * Skip the destructive clean (`rm -rf outDir`) so caches + unchanged assets survive a dev
1731
+ * rebuild. TRADE-OFF: because nothing is pruned, output for a route DELETED or renamed
1732
+ * since the last build lingers on disk (and is still served by the dev server) until the
1733
+ * next full/clean build — restart `serve` or run a production `build` to clear it.
1734
+ */
1735
+ skipClean?: boolean; /** Per-run {@link Config} field overrides merged over the snapshot (e.g. disable feeds/sitemap/minify in dev). */
1736
+ overrides?: BuildRunOverrides;
1737
+ /**
1738
+ * Paths changed since the last build (dev incremental rebuild). When present, the pipeline
1739
+ * re-reads only changed Markdown and re-renders only pages whose loaded data changed; omit
1740
+ * it for a full build (initial build + every production build). CORRECTNESS NOTE: render
1741
+ * reuse is sound only when a route's render/layout output is a pure function of its
1742
+ * `.load()` data (+ params/locale/code) — a route whose `.render()` reads module-global or
1743
+ * other out-of-band state can show a stale body on a content-only rebuild until the next
1744
+ * code change or restart. Production builds omit `changed`, so they are never affected.
1745
+ */
1746
+ changed?: readonly string[];
1747
+ };
1682
1748
  /**
1683
1749
  * Public API surface mounted on `app.build`.
1684
1750
  *
@@ -1689,19 +1755,18 @@ interface BuildResult {
1689
1755
  */
1690
1756
  type Api$3 = {
1691
1757
  /**
1692
- * Run the full SSG pipeline and write the site to disk.
1758
+ * Run the full SSG pipeline and write the site to disk. With no options a full
1759
+ * production build runs (clean + every configured phase); dev callers pass
1760
+ * {@link RunOptions} (`skipClean`/`overrides`/`changed`) for a fast incremental rebuild.
1693
1761
  *
1694
- * @param options - Optional run overrides.
1695
- * @param options.outDir - Override the configured output directory for this run.
1762
+ * @param options - Optional per-run overrides ({@link RunOptions}).
1696
1763
  * @returns The build result (outDir, pageCount, durationMs).
1697
1764
  * @example
1698
1765
  * ```ts
1699
1766
  * const result = await app.build.run({ outDir: "./preview" });
1700
1767
  * ```
1701
1768
  */
1702
- run(options?: {
1703
- outDir?: string;
1704
- }): Promise<BuildResult>;
1769
+ run(options?: RunOptions): Promise<BuildResult>;
1705
1770
  /**
1706
1771
  * List the phases in execution order (introspection / tooling).
1707
1772
  *
@@ -1761,7 +1826,7 @@ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Con
1761
1826
  */
1762
1827
  type WorkflowTrigger = "auto" | "versioned-tag" | "dispatch";
1763
1828
  declare namespace types_d_exports$4 {
1764
- export { Api$2 as Api, Config$2 as Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WorkflowTrigger, WranglerErrorKind };
1829
+ export { Api$2 as Api, Config$2 as Config, CreateProjectResult, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State$2 as State, WorkflowTrigger, WranglerErrorKind };
1765
1830
  }
1766
1831
  /**
1767
1832
  * Options passed to the injected spawner — the subset of Bun.spawn's options the
@@ -1890,6 +1955,13 @@ type InitResult = {
1890
1955
  skipped: string[]; /** In check mode: paths whose on-disk content differs from what would be generated. */
1891
1956
  drifted: string[];
1892
1957
  };
1958
+ /**
1959
+ * Result of creating the remote Cloudflare Pages project.
1960
+ */
1961
+ type CreateProjectResult = {
1962
+ /** The created project name (the `toSlug(site.name())` slug). */name: string; /** The production branch the project was created with. */
1963
+ branch: string;
1964
+ };
1893
1965
  /**
1894
1966
  * Public API of the deploy plugin (returned from the api factory).
1895
1967
  */
@@ -1935,6 +2007,28 @@ type Api$2 = {
1935
2007
  * if (drift.drifted.length) process.exit(1);
1936
2008
  */
1937
2009
  init(options?: DeployInitOptions): Promise<InitResult>;
2010
+ /**
2011
+ * The Cloudflare Pages project name this app deploys to — `toSlug(site.name())`, the
2012
+ * same slug `run()` passes as `--project-name` and `createProject()` creates. Lets a
2013
+ * caller (e.g. the guided deploy wizard) name the project before it exists.
2014
+ *
2015
+ * @returns The project-name slug.
2016
+ * @example
2017
+ * app.deploy.projectName(); // "my-site"
2018
+ */
2019
+ projectName(): string;
2020
+ /**
2021
+ * Create the remote Cloudflare Pages project (`wrangler pages project create`) so a
2022
+ * first deploy has a target. The slug is derived from `site.name()` and the production
2023
+ * branch from `config.productionBranch` (or `"main"`). Wrangler errors if the project
2024
+ * already exists; the guided wizard calls this only after a project-not-found failure.
2025
+ *
2026
+ * @returns The created project name + production branch.
2027
+ * @throws {Error} With a `code` from the deploy error taxonomy on a non-zero exit.
2028
+ * @example
2029
+ * const created = await app.deploy.createProject(); // { name: "my-site", branch: "main" }
2030
+ */
2031
+ createProject(): Promise<CreateProjectResult>;
1938
2032
  };
1939
2033
  declare namespace types_d_exports$1 {
1940
2034
  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 };
@@ -2109,6 +2203,17 @@ type CliRenderer = {
2109
2203
  * render.check(false, "CLOUDFLARE_API_TOKEN is set", "Create one at …");
2110
2204
  */
2111
2205
  check(ok: boolean, label: string, detail?: string): void;
2206
+ /**
2207
+ * Stop any running animation (the live `serve()` idle pulse, a phase/rebuild spinner)
2208
+ * and release the renderer's interval timer. Called by `serve()`'s SIGINT/SIGTERM
2209
+ * teardown so the persistent idle-pulse ticker never outlives the dev server. A no-op
2210
+ * when nothing is animating; safe to call more than once.
2211
+ *
2212
+ * @returns Nothing.
2213
+ * @example
2214
+ * render.dispose();
2215
+ */
2216
+ dispose(): void;
2112
2217
  };
2113
2218
  /**
2114
2219
  * A live directory watcher handle returned by the injectable `watch` seam. Closing
@@ -2284,6 +2389,18 @@ type State$1 = {
2284
2389
  * const mtime = state.fileMtime("/abs/content/a.md");
2285
2390
  */
2286
2391
  fileMtime: (path: string) => number | null;
2392
+ /**
2393
+ * Resolve a file's content hash, or `null` when it does not exist. serve()'s change gate
2394
+ * uses it to drop a no-op save whose bytes are identical to the last successful build (the
2395
+ * double Ctrl-S habit). Default reads the file + hashes it (`node:fs` + `node:crypto`);
2396
+ * tests inject deterministic values.
2397
+ *
2398
+ * @param path - The absolute path to hash.
2399
+ * @returns The content hash, or `null` when the file is missing.
2400
+ * @example
2401
+ * const hash = state.fileHash("/abs/content/a.md");
2402
+ */
2403
+ fileHash: (path: string) => string | null;
2287
2404
  };
2288
2405
  /**
2289
2406
  * Summary returned by `cli.build()` — the awaited `build.run()` result.
@@ -2299,9 +2416,11 @@ type BuildSummary = {
2299
2416
  /**
2300
2417
  * Outcome returned by `cli.deploy()` — either a completed deploy (with details) or a
2301
2418
  * skipped one. A skip is `"declined"` when a user answers "no" at the confirm prompt,
2302
- * or `"blocked"` when the guided wizard found unmet prerequisites and stopped before
2303
- * deploying. Non-interactive direct runs (CI / non-TTY) never prompt and always proceed,
2304
- * so they never skip the scripts are CI-safe.
2419
+ * `"blocked"` when the guided wizard found unmet prerequisites and stopped before
2420
+ * deploying, or `"failed"` when the deploy itself errored (e.g. the Pages project does
2421
+ * not exist) and the wizard surfaced it as a styled error + fix hint instead of a raw
2422
+ * throw. Non-interactive direct runs (CI / non-TTY) never prompt and always proceed, so
2423
+ * they never `declined`-skip — the scripts are CI-safe.
2305
2424
  *
2306
2425
  * @example
2307
2426
  * const outcome: DeployOutcome = { deployed: false, reason: "declined" };
@@ -2314,7 +2433,7 @@ type DeployOutcome = {
2314
2433
  durationMs: number;
2315
2434
  } | {
2316
2435
  deployed: false;
2317
- reason: "declined" | "blocked";
2436
+ reason: "declined" | "blocked" | "failed";
2318
2437
  };
2319
2438
  /**
2320
2439
  * Options for `cli.build()`.
@@ -2334,6 +2453,15 @@ type BuildOptions = {
2334
2453
  type ServeOptions = {
2335
2454
  /** 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`. */
2336
2455
  open?: boolean;
2456
+ /**
2457
+ * Re-enable OG-image generation for this dev session. `serve()` disables expensive,
2458
+ * preview-irrelevant outputs by default for a fast rebuild; a thin consumer script
2459
+ * maps `--og` to this. Defaults to `false`.
2460
+ */
2461
+ og?: boolean; /** Re-enable `sitemap.xml` + `robots.txt` for this dev session (maps to `--sitemap`). Defaults to `false`. */
2462
+ sitemap?: boolean; /** Re-enable RSS/Atom/JSON feeds for this dev session (maps to `--feeds`). Defaults to `false`. */
2463
+ feeds?: boolean; /** Re-enable i18n locale-redirect pages for this dev session (maps to `--locale-redirects`). Defaults to `false`. */
2464
+ localeRedirects?: boolean;
2337
2465
  };
2338
2466
  /**
2339
2467
  * Options for `cli.preview()`.
@@ -2433,7 +2561,7 @@ type Api$1 = {
2433
2561
  */
2434
2562
  declare const cliPlugin: import("@moku-labs/core").PluginInstance<"cli", Config$1, State$1, Api$1, {}> & Record<never, never>;
2435
2563
  declare namespace types_d_exports$2 {
2436
- export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, ContentProvider, ContentProviderState, FileSystemContentOptions, Frontmatter, State };
2564
+ export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, ContentProvider, ContentProviderState, FileSystemContentOptions, Frontmatter, LoadAllOptions, State };
2437
2565
  }
2438
2566
  /**
2439
2567
  * YAML frontmatter parsed from each article file.
@@ -2652,6 +2780,24 @@ type ContentApiContext = {
2652
2780
  defaultLocale: () => string; /** The resolved content source (merged from `config.providers`). */
2653
2781
  provider: ContentProvider;
2654
2782
  };
2783
+ /**
2784
+ * Options for {@link Api.loadAll}.
2785
+ *
2786
+ * @example
2787
+ * ```ts
2788
+ * await app.content.loadAll({ reuse: true });
2789
+ * ```
2790
+ */
2791
+ type LoadAllOptions = {
2792
+ /**
2793
+ * Reuse already-cached articles for slugs NOT dropped by a preceding `invalidate()`,
2794
+ * re-reading + re-rendering (Shiki) ONLY the invalidated (dirty) articles. The
2795
+ * post-sort `contentId` ordinals are always recomputed across the full set, so order +
2796
+ * ids match a full load. Default `false` (a full load that re-reads every article).
2797
+ * Used by dev incremental rebuilds; a fresh process / production build never reuses.
2798
+ */
2799
+ reuse?: boolean;
2800
+ };
2655
2801
  /**
2656
2802
  * Public API for the content plugin.
2657
2803
  *
@@ -2664,8 +2810,10 @@ type Api = {
2664
2810
  /**
2665
2811
  * Load every article across every active locale, returning a locale-keyed
2666
2812
  * map of date-descending Article arrays. Emits content:ready.
2813
+ *
2814
+ * @param options - Optional load behaviour ({@link LoadAllOptions}); omit for a full load.
2667
2815
  */
2668
- loadAll(): Promise<Map<string, Article[]>>;
2816
+ loadAll(options?: LoadAllOptions): Promise<Map<string, Article[]>>;
2669
2817
  /**
2670
2818
  * Resolve and render a single article for one locale, with locale fallback.
2671
2819
  *