@moku-labs/web 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -12
- package/dist/convention-Dr8jxG70.cjs +81 -0
- package/dist/convention-X3zLTlJ8.mjs +33 -0
- package/dist/index.cjs +1319 -321
- package/dist/index.d.cts +966 -626
- package/dist/index.d.mts +965 -625
- package/dist/index.mjs +1290 -271
- package/dist/render-BL9Fv6G6.mjs +20 -0
- package/dist/render-BSTM0Akv.cjs +20 -0
- package/dist/writer-BcWqa_7I.mjs +90 -0
- package/dist/writer-DAF0pM25.cjs +92 -0
- package/package.json +3 -2
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { Pluggable, Processor } from "unified";
|
|
|
3
3
|
import { ComponentChildren, VNode } from "preact";
|
|
4
4
|
|
|
5
5
|
//#region src/plugins/log/types.d.ts
|
|
6
|
-
declare namespace types_d_exports$
|
|
6
|
+
declare namespace types_d_exports$6 {
|
|
7
7
|
export { ExpectChain, LogApi, LogConfig, LogEntry, LogLevel, LogSink, LogState };
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
@@ -143,7 +143,7 @@ type LogApi = {
|
|
|
143
143
|
addSink(sink: LogSink): void; /** Clear all recorded entries while keeping registered sinks. */
|
|
144
144
|
reset(): void;
|
|
145
145
|
};
|
|
146
|
-
declare namespace types_d_exports$
|
|
146
|
+
declare namespace types_d_exports$4 {
|
|
147
147
|
export { EnvApi, EnvConfig, EnvProvider, EnvState, EnvVarSpec };
|
|
148
148
|
}
|
|
149
149
|
/**
|
|
@@ -302,6 +302,26 @@ type EnvApi = {
|
|
|
302
302
|
getPublicMap(): ReadonlyMap<string, string>;
|
|
303
303
|
};
|
|
304
304
|
//#endregion
|
|
305
|
+
//#region src/plugins/env/providers.browser.d.ts
|
|
306
|
+
/**
|
|
307
|
+
* A browser-safe {@link EnvProvider} that reads `import.meta.env` and an optional
|
|
308
|
+
* `globalThis[globalKey]` snapshot, merging them with the runtime global winning.
|
|
309
|
+
* Contains zero `node:*` imports, so it is safe to include in the client bundle.
|
|
310
|
+
* Never throws on missing sources — each absent source resolves to `{}`.
|
|
311
|
+
*
|
|
312
|
+
* @param options - Optional settings.
|
|
313
|
+
* @param options.globalKey - `globalThis` key to read a public-env snapshot from. Defaults to `"__ENV__"`.
|
|
314
|
+
* @returns An {@link EnvProvider} named `browser-env`.
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* const provider = browserEnv();
|
|
318
|
+
* provider.load(); // { PUBLIC_API_URL: "/api", ... }
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
declare function browserEnv(options?: {
|
|
322
|
+
globalKey?: string;
|
|
323
|
+
}): EnvProvider;
|
|
324
|
+
//#endregion
|
|
305
325
|
//#region src/plugins/env/index.d.ts
|
|
306
326
|
/**
|
|
307
327
|
* Core plugin that resolves, validates, and freezes the environment at `onInit`,
|
|
@@ -521,8 +541,8 @@ type Api$5 = {
|
|
|
521
541
|
*/
|
|
522
542
|
t(locale: string, key: string): string;
|
|
523
543
|
};
|
|
524
|
-
declare namespace types_d_exports$
|
|
525
|
-
export { Api$4 as Api, CompileInput, CompiledRoute, Config$4 as Config, ExtractRouteParams, ExtractSegmentParameter, HeadConfig$1 as HeadConfig, MatcherTable, Prettify, RouteBuilder, RouteContext, RouteDefinition, RouteHandlers, RouteMap, RouteState, RouterApi, RouterConfig, RouterState, State$4 as State, TypedRoute };
|
|
544
|
+
declare namespace types_d_exports$7 {
|
|
545
|
+
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 };
|
|
526
546
|
}
|
|
527
547
|
/**
|
|
528
548
|
* Param contribution of a single path segment. `{name:?}` / `:name?` → optional;
|
|
@@ -562,6 +582,22 @@ interface RouteContext<S extends RouteState> {
|
|
|
562
582
|
/** Active locale for this render. */
|
|
563
583
|
readonly locale: string;
|
|
564
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* Context handed to a route's `.layout()` wrapper: the render-time
|
|
587
|
+
* {@link RouteContext} plus the route's `.meta()` bag, so persistent chrome (e.g. a
|
|
588
|
+
* TopBar/TabNav) can read `locale` and `meta.activeTab`. Distinct from
|
|
589
|
+
* `RouteContext` because the layout is the only handler that needs `meta`; keeping
|
|
590
|
+
* it on its own type leaves `.render()`/`.head()` contexts unchanged.
|
|
591
|
+
*
|
|
592
|
+
* @remarks
|
|
593
|
+
* The layout is applied in the SSG render path ONLY. On client (SPA) navigation the
|
|
594
|
+
* chrome is persistent and the layout is intentionally NOT re-applied — only the
|
|
595
|
+
* inner swap region is replaced. See `build`'s pages phase and `spa`'s kernel.
|
|
596
|
+
*/
|
|
597
|
+
interface LayoutContext<S extends RouteState> extends RouteContext<S> {
|
|
598
|
+
/** The route's `.meta()` bag (e.g. `{ activeTab: "home" }`). */
|
|
599
|
+
readonly meta: Record<string, unknown>;
|
|
600
|
+
}
|
|
565
601
|
/** Head metadata produced by a route's `.head()` handler. */
|
|
566
602
|
interface HeadConfig$1 {
|
|
567
603
|
/** Document title. */
|
|
@@ -584,15 +620,33 @@ interface RouteBuilder<S extends RouteState> extends RouteDefinition {
|
|
|
584
620
|
readonly params: S["params"];
|
|
585
621
|
readonly data: Awaited<D>;
|
|
586
622
|
}>;
|
|
587
|
-
/**
|
|
588
|
-
|
|
623
|
+
/**
|
|
624
|
+
* Attach a ctx-aware layout wrapper that frames this route's rendered page in
|
|
625
|
+
* persistent chrome. Receives the route's {@link LayoutContext} (render context +
|
|
626
|
+
* `meta`) and the page `children`. Applied in the SSG render path ONLY — on client
|
|
627
|
+
* navigation the chrome persists and only the inner swap region is replaced, so the
|
|
628
|
+
* layout is not re-run.
|
|
629
|
+
*/
|
|
630
|
+
layout(component: (ctx: LayoutContext<S>, children: ComponentChildren) => VNode): RouteBuilder<S>;
|
|
589
631
|
/** Attach the page render handler. */
|
|
590
632
|
render(handler: (ctx: RouteContext<S>) => VNode): RouteBuilder<S>;
|
|
633
|
+
/**
|
|
634
|
+
* Attach the client-side validation gate: parse the raw `unknown` fetched from
|
|
635
|
+
* the persisted data file back into this route's data type `S["data"]`. Runs at
|
|
636
|
+
* the trust boundary before `render` on the client (and MUST return `S["data"]`,
|
|
637
|
+
* so a mismatched schema is a compile error). Throw inside it to reject malformed
|
|
638
|
+
* data — `spa` then falls back to HTML-over-fetch. Use a hand guard or any
|
|
639
|
+
* Standard-Schema validator (zod/valibot/arktype).
|
|
640
|
+
*/
|
|
641
|
+
parse(handler: (raw: unknown) => S["data"]): RouteBuilder<S>;
|
|
591
642
|
/** Attach the head/SEO handler. */
|
|
592
643
|
head(handler: (ctx: RouteContext<S>) => HeadConfig$1): RouteBuilder<S>;
|
|
593
644
|
/** Attach a static-generation param producer. */
|
|
594
645
|
generate(handler: (locale: string) => S["params"][] | Promise<S["params"][]>): RouteBuilder<S>;
|
|
595
|
-
/**
|
|
646
|
+
/**
|
|
647
|
+
* Attach an arbitrary metadata bag. The bag MUST be JSON-serializable: it is
|
|
648
|
+
* projected verbatim into `clientManifest()` and shipped to the browser.
|
|
649
|
+
*/
|
|
596
650
|
meta(meta: Record<string, unknown>): RouteBuilder<S>;
|
|
597
651
|
/** Attach a JSON serializer for the route's data. */
|
|
598
652
|
toJson(handler: (ctx: RouteContext<S>) => unknown): RouteBuilder<S>;
|
|
@@ -603,10 +657,12 @@ interface RouteBuilder<S extends RouteState> extends RouteDefinition {
|
|
|
603
657
|
interface RouteHandlers {
|
|
604
658
|
/** Data loader. */
|
|
605
659
|
readonly load?: (params: Record<string, string>, locale: string) => unknown;
|
|
606
|
-
/** Layout wrapper. */
|
|
607
|
-
readonly layout?: (children: ComponentChildren) => VNode;
|
|
660
|
+
/** Layout wrapper (ctx-aware): frames the page in persistent chrome. SSG-only. */
|
|
661
|
+
readonly layout?: (ctx: LayoutContext<RouteState>, children: ComponentChildren) => VNode;
|
|
608
662
|
/** Page renderer. */
|
|
609
663
|
readonly render?: (ctx: RouteContext<RouteState>) => VNode;
|
|
664
|
+
/** Client-side validation gate: `unknown` (fetched JSON) → the route's data type, or throw. */
|
|
665
|
+
readonly parse?: (raw: unknown) => unknown;
|
|
610
666
|
/** Head/SEO producer. */
|
|
611
667
|
readonly head?: (ctx: RouteContext<RouteState>) => HeadConfig$1;
|
|
612
668
|
/** Static-generation param producer. */
|
|
@@ -711,6 +767,8 @@ interface MatcherTable {
|
|
|
711
767
|
interface RouterState {
|
|
712
768
|
/** Compiled matcher table; `null` until `onInit` assigns it. */
|
|
713
769
|
table: MatcherTable | null;
|
|
770
|
+
/** Resolved render mode (single source of truth; set in `onInit`). Defaults `"hybrid"`. */
|
|
771
|
+
mode: "ssg" | "spa" | "hybrid";
|
|
714
772
|
}
|
|
715
773
|
/** Plain-data input to `compileRoutes` — resolved DATA only, never the plugin ctx. */
|
|
716
774
|
interface CompileInput {
|
|
@@ -725,6 +783,24 @@ interface CompileInput {
|
|
|
725
783
|
/** Default locale used for bare-pattern fallback. */
|
|
726
784
|
readonly defaultLocale: string;
|
|
727
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* Serializable route entry for the client route-index — a projection of the
|
|
788
|
+
* compiled route table with NO `_handlers` closures, safe to ship to the browser
|
|
789
|
+
* (the SPA recompiles matchers lazily from `pattern`).
|
|
790
|
+
*
|
|
791
|
+
* @remarks
|
|
792
|
+
* `meta` MUST be JSON-serializable: `clientManifest()` is intended to survive a
|
|
793
|
+
* `JSON.stringify`/`JSON.parse` round-trip, so a route's `.meta()` bag must contain
|
|
794
|
+
* only JSON-safe values (no functions, symbols, or class instances).
|
|
795
|
+
*/
|
|
796
|
+
interface ClientRoute {
|
|
797
|
+
/** URL pattern string, e.g. `/{lang:?}/{slug}/`. */
|
|
798
|
+
readonly pattern: string;
|
|
799
|
+
/** Route name key from the route map. */
|
|
800
|
+
readonly name: string;
|
|
801
|
+
/** Route metadata bag from `.meta()`. MUST be JSON-serializable. */
|
|
802
|
+
readonly meta: Record<string, unknown>;
|
|
803
|
+
}
|
|
728
804
|
/** Public API exposed via `ctx.require(routerPlugin)`. */
|
|
729
805
|
type RouterApi = {
|
|
730
806
|
/**
|
|
@@ -767,197 +843,36 @@ type RouterApi = {
|
|
|
767
843
|
* for (const def of ctx.require(routerPlugin).manifest()) { def._handlers.load?.({}, "en"); }
|
|
768
844
|
*/
|
|
769
845
|
manifest(): readonly RouteDefinition[];
|
|
770
|
-
};
|
|
771
|
-
/** Re-export under the canonical `Config` name for the plugin-types barrel. */
|
|
772
|
-
type Config$4 = RouterConfig;
|
|
773
|
-
/** Re-export under the canonical `State` name for the plugin-types barrel. */
|
|
774
|
-
type State$4 = RouterState;
|
|
775
|
-
/** Re-export under the canonical `Api` name for the plugin-types barrel. */
|
|
776
|
-
type Api$4 = RouterApi;
|
|
777
|
-
declare namespace types_d_exports$1 {
|
|
778
|
-
export { Api$3 as Api, Article, ArticleCard, ComputedFields, Config$3 as Config, ContentApiContext, ContentEvents, Frontmatter, State$3 as State };
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Configuration for the content plugin.
|
|
782
|
-
*
|
|
783
|
-
* @example
|
|
784
|
-
* ```ts
|
|
785
|
-
* { contentDir: "./src/content", trustedContent: false, shikiTheme: "github-dark" }
|
|
786
|
-
* ```
|
|
787
|
-
*/
|
|
788
|
-
type Config$3 = {
|
|
789
|
-
/** Absolute or project-relative path to the content directory. Validated in onInit. */contentDir: string;
|
|
790
|
-
/**
|
|
791
|
-
* SECURITY GATE. When false (the default), rehype-sanitize runs as the final
|
|
792
|
-
* pipeline step. Set true ONLY for fully author-controlled Markdown — true
|
|
793
|
-
* disables sanitize and trusts all raw HTML.
|
|
794
|
-
*/
|
|
795
|
-
trustedContent: boolean; /** Additional remark plugins, concatenated AFTER framework defaults. Defaults to []. */
|
|
796
|
-
extraRemarkPlugins?: readonly Pluggable[]; /** Additional rehype plugins, concatenated after custom transforms, before Shiki + sanitize. Defaults to []. */
|
|
797
|
-
extraRehypePlugins?: readonly Pluggable[]; /** Shiki theme name for syntax highlighting. Defaults to "github-dark". */
|
|
798
|
-
shikiTheme?: string; /** Author applied to articles whose frontmatter omits author. Defaults to undefined. */
|
|
799
|
-
defaultAuthor?: string;
|
|
800
|
-
};
|
|
801
|
-
/**
|
|
802
|
-
* Internal mutable state for the content plugin.
|
|
803
|
-
*
|
|
804
|
-
* @example
|
|
805
|
-
* ```ts
|
|
806
|
-
* { processor: null, articles: new Map(), slugs: null, dirtyPaths: new Set() }
|
|
807
|
-
* ```
|
|
808
|
-
*/
|
|
809
|
-
type State$3 = {
|
|
810
|
-
/** Lazily-created unified processor singleton. null until first render()/loadAll(). */processor: Processor | null; /** Article cache keyed locale -> (slug -> Article). Starts empty. */
|
|
811
|
-
articles: Map<string, Map<string, Article>>; /** Discovered, sorted slug list cached after first disk scan. null until first discovery. */
|
|
812
|
-
slugs: string[] | null; /** Paths marked stale by invalidate(); next loadAll() re-reads only these. Starts empty. */
|
|
813
|
-
dirtyPaths: Set<string>;
|
|
814
|
-
};
|
|
815
|
-
/**
|
|
816
|
-
* YAML frontmatter parsed from each article file.
|
|
817
|
-
*
|
|
818
|
-
* @example
|
|
819
|
-
* ```ts
|
|
820
|
-
* { title: "Hello", date: "2026-01-15", description: "Intro", tags: [], language: "en" }
|
|
821
|
-
* ```
|
|
822
|
-
*/
|
|
823
|
-
type Frontmatter = {
|
|
824
|
-
/** Article title. Required. */title: string; /** ISO 8601 date string, e.g. "2026-01-15". Required. */
|
|
825
|
-
date: string; /** Short summary used in cards, feeds, and meta description. Required. */
|
|
826
|
-
description: string; /** Topic tags. Required (may be empty array). */
|
|
827
|
-
tags: string[]; /** Source language code of this file. Required. */
|
|
828
|
-
language: string; /** Draft flag. Excluded from output in production mode. Defaults to false. */
|
|
829
|
-
draft?: boolean; /** Author name. Falls back to config.defaultAuthor when omitted. */
|
|
830
|
-
author?: string;
|
|
831
|
-
};
|
|
832
|
-
/**
|
|
833
|
-
* Fields computed by the pipeline (not authored in frontmatter).
|
|
834
|
-
*
|
|
835
|
-
* @example
|
|
836
|
-
* ```ts
|
|
837
|
-
* { slug: "hello", readingTime: 1, contentId: "hello", status: "published", wordCount: 42 }
|
|
838
|
-
* ```
|
|
839
|
-
*/
|
|
840
|
-
type ComputedFields = {
|
|
841
|
-
/** Article directory name. */slug: string; /** Reading time in minutes (ceiling, minimum 1). */
|
|
842
|
-
readingTime: number; /** Stable content identifier (slug by default). */
|
|
843
|
-
contentId: string; /** Derived publication status. */
|
|
844
|
-
status: "published" | "draft"; /** Word count from the source body. */
|
|
845
|
-
wordCount: number;
|
|
846
|
-
};
|
|
847
|
-
/**
|
|
848
|
-
* A fully processed, render-ready article.
|
|
849
|
-
*
|
|
850
|
-
* @example
|
|
851
|
-
* ```ts
|
|
852
|
-
* { frontmatter, computed, html: "<p>…</p>", locale: "en", isFallback: false, url: "/en/hello/" }
|
|
853
|
-
* ```
|
|
854
|
-
*/
|
|
855
|
-
type Article = {
|
|
856
|
-
/** Parsed frontmatter. */frontmatter: Frontmatter; /** Pipeline-computed metadata. */
|
|
857
|
-
computed: ComputedFields; /** Sanitized rendered HTML body. */
|
|
858
|
-
html: string; /** Locale this Article instance represents (the requested locale, even on fallback). */
|
|
859
|
-
locale: string; /** True when the default-locale file was used because the requested locale was missing. */
|
|
860
|
-
isFallback: boolean; /** Canonical URL for this article in this locale. */
|
|
861
|
-
url: string;
|
|
862
|
-
};
|
|
863
|
-
/**
|
|
864
|
-
* Lightweight projection of Article for cards/lists.
|
|
865
|
-
*
|
|
866
|
-
* @example
|
|
867
|
-
* ```ts
|
|
868
|
-
* { contentId: "hello", status: "published", title: "Hello", date: "2026-01-15", description: "Intro", tags: [], readingTime: 1, url: "/en/hello/" }
|
|
869
|
-
* ```
|
|
870
|
-
*/
|
|
871
|
-
type ArticleCard = {
|
|
872
|
-
/** Stable content identifier. */contentId: string; /** Derived publication status. */
|
|
873
|
-
status: "published" | "draft"; /** Article title. */
|
|
874
|
-
title: string; /** ISO 8601 date string. */
|
|
875
|
-
date: string; /** Short summary. */
|
|
876
|
-
description: string; /** Topic tags. */
|
|
877
|
-
tags: string[]; /** Reading time in minutes. */
|
|
878
|
-
readingTime: number; /** Canonical URL for this article in this locale. */
|
|
879
|
-
url: string;
|
|
880
|
-
};
|
|
881
|
-
/**
|
|
882
|
-
* Notification-only events emitted by the content plugin.
|
|
883
|
-
*
|
|
884
|
-
* @example
|
|
885
|
-
* ```ts
|
|
886
|
-
* emit("content:ready", { locales: ["en"], articleCount: 3 });
|
|
887
|
-
* ```
|
|
888
|
-
*/
|
|
889
|
-
type ContentEvents = {
|
|
890
|
-
/** All articles loaded across locales. */"content:ready": {
|
|
891
|
-
locales: readonly string[];
|
|
892
|
-
articleCount: number;
|
|
893
|
-
}; /** Article paths marked stale for dev rebuild. */
|
|
894
|
-
"content:invalidated": {
|
|
895
|
-
paths: readonly string[];
|
|
896
|
-
};
|
|
897
|
-
};
|
|
898
|
-
/**
|
|
899
|
-
* Kernel-free domain context handed to createContentApi by the wiring harness.
|
|
900
|
-
* Carries ctx.state (mutable escape hatch), config, global, emit, and the
|
|
901
|
-
* i18n-derived locale/url helpers — so api.ts stays free of createPlugin/ctx.
|
|
902
|
-
*
|
|
903
|
-
* @example
|
|
904
|
-
* ```ts
|
|
905
|
-
* const apiContext: ContentApiContext = { state, config, global, emit, locales, defaultLocale, articleToUrl };
|
|
906
|
-
* ```
|
|
907
|
-
*/
|
|
908
|
-
type ContentApiContext = {
|
|
909
|
-
/** Mutable plugin state (article cache + lazy processor). */state: State$3; /** Resolved plugin configuration. */
|
|
910
|
-
config: Config$3; /** Global framework configuration (mode, etc.). */
|
|
911
|
-
global: {
|
|
912
|
-
mode: "production" | "development";
|
|
913
|
-
}; /** Emit a registered content event. */
|
|
914
|
-
emit: <K extends keyof ContentEvents>(event: K, payload: ContentEvents[K]) => void; /** Active locale codes from i18n. */
|
|
915
|
-
locales: () => readonly string[]; /** Default locale code from i18n (fallback source). */
|
|
916
|
-
defaultLocale: () => string; /** Build a canonical article URL for a locale + slug. */
|
|
917
|
-
articleToUrl: (locale: string, slug: string) => string;
|
|
918
|
-
};
|
|
919
|
-
/**
|
|
920
|
-
* Public API for the content plugin.
|
|
921
|
-
*
|
|
922
|
-
* @example
|
|
923
|
-
* ```ts
|
|
924
|
-
* const map = await app.content.loadAll();
|
|
925
|
-
* ```
|
|
926
|
-
*/
|
|
927
|
-
type Api$3 = {
|
|
928
|
-
/**
|
|
929
|
-
* Load every article across every active locale, returning a locale-keyed
|
|
930
|
-
* map of date-descending Article arrays. Emits content:ready.
|
|
931
|
-
*/
|
|
932
|
-
loadAll(): Promise<Map<string, Article[]>>;
|
|
933
|
-
/**
|
|
934
|
-
* Resolve and render a single article for one locale, with locale fallback.
|
|
935
|
-
*
|
|
936
|
-
* @param slug - Article directory name.
|
|
937
|
-
* @param locale - Requested locale code.
|
|
938
|
-
*/
|
|
939
|
-
load(slug: string, locale: string): Promise<Article>;
|
|
940
|
-
/**
|
|
941
|
-
* Render a raw Markdown string to HTML through the full pipeline.
|
|
942
|
-
*
|
|
943
|
-
* @param md - Raw Markdown source.
|
|
944
|
-
*/
|
|
945
|
-
renderMarkdown(md: string): Promise<string>;
|
|
946
846
|
/**
|
|
947
|
-
*
|
|
847
|
+
* Serializable, specificity-sorted projection of the route table for client
|
|
848
|
+
* shipping. Maps the compiled table to `{ pattern, name, meta }` entries with NO
|
|
849
|
+
* `_handlers` closures, returned as a fresh frozen array. JSON-serializable so the
|
|
850
|
+
* SPA can embed it and recompile matchers lazily in the browser.
|
|
948
851
|
*
|
|
949
|
-
* @
|
|
852
|
+
* @returns A fresh, frozen, specificity-sorted read-only array of {@link ClientRoute}.
|
|
853
|
+
* @example
|
|
854
|
+
* const json = JSON.stringify(ctx.require(routerPlugin).clientManifest());
|
|
950
855
|
*/
|
|
951
|
-
|
|
856
|
+
clientManifest(): readonly ClientRoute[];
|
|
952
857
|
/**
|
|
953
|
-
*
|
|
858
|
+
* The resolved render mode — the single source of truth for static/hybrid/spa
|
|
859
|
+
* behavior. `build` reads it to decide whether to emit client data sidecars;
|
|
860
|
+
* `spa` reads it to decide whether to attempt client DATA navigation.
|
|
954
861
|
*
|
|
955
|
-
* @
|
|
862
|
+
* @returns `"ssg" | "spa" | "hybrid"`.
|
|
863
|
+
* @example
|
|
864
|
+
* if (ctx.require(routerPlugin).mode() !== "ssg") { ... }
|
|
956
865
|
*/
|
|
957
|
-
|
|
866
|
+
mode(): "ssg" | "spa" | "hybrid";
|
|
958
867
|
};
|
|
959
|
-
|
|
960
|
-
|
|
868
|
+
/** Re-export under the canonical `Config` name for the plugin-types barrel. */
|
|
869
|
+
type Config$4 = RouterConfig;
|
|
870
|
+
/** Re-export under the canonical `State` name for the plugin-types barrel. */
|
|
871
|
+
type State$4 = RouterState;
|
|
872
|
+
/** Re-export under the canonical `Api` name for the plugin-types barrel. */
|
|
873
|
+
type Api$4 = RouterApi;
|
|
874
|
+
declare namespace types_d_exports$5 {
|
|
875
|
+
export { Api$3 as Api, ArticleMeta, Config$3 as Config, HeadConfig, HeadDefaults, HeadElement, ResolvedRoute, State$3 as State };
|
|
961
876
|
}
|
|
962
877
|
/**
|
|
963
878
|
* @file head plugin — type definitions skeleton
|
|
@@ -973,7 +888,7 @@ declare namespace types_d_exports$4 {
|
|
|
973
888
|
* const config: Config = { titleTemplate: "%s — Moku" };
|
|
974
889
|
* ```
|
|
975
890
|
*/
|
|
976
|
-
type Config$
|
|
891
|
+
type Config$3 = {
|
|
977
892
|
/** 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. */
|
|
978
893
|
defaultOgImage?: string; /** Default Twitter card type emitted when og/twitter content is present. */
|
|
979
894
|
twitterCard?: "summary" | "summary_large_image"; /** Default Twitter site handle (e.g. `"@moku_labs"`) emitted as `twitter:site`. */
|
|
@@ -991,7 +906,7 @@ type Config$2 = {
|
|
|
991
906
|
* const state: State = { defaults: null };
|
|
992
907
|
* ```
|
|
993
908
|
*/
|
|
994
|
-
type State$
|
|
909
|
+
type State$3 = {
|
|
995
910
|
/** Normalized head defaults, assigned once in `onInit` (initially `null`). */defaults: HeadDefaults | null;
|
|
996
911
|
};
|
|
997
912
|
/**
|
|
@@ -1085,7 +1000,7 @@ type ResolvedRoute = {
|
|
|
1085
1000
|
* const html: string = api.render(route, data);
|
|
1086
1001
|
* ```
|
|
1087
1002
|
*/
|
|
1088
|
-
type Api$
|
|
1003
|
+
type Api$3 = {
|
|
1089
1004
|
/**
|
|
1090
1005
|
* Compose the final `<head>` inner HTML for a route. Pulled synchronously by `build`.
|
|
1091
1006
|
*
|
|
@@ -1099,259 +1014,37 @@ type Api$2 = {
|
|
|
1099
1014
|
*/
|
|
1100
1015
|
render(route: ResolvedRoute, data: unknown): string;
|
|
1101
1016
|
};
|
|
1102
|
-
declare namespace types_d_exports {
|
|
1103
|
-
export {
|
|
1017
|
+
declare namespace types_d_exports$8 {
|
|
1018
|
+
export { COMPONENT_HOOK_NAMES, ComponentContext, ComponentDef, ComponentHooks, ComponentInstance, ExtractApi$1 as ExtractApi, PageData, ResolvedSpaConfig, SpaApi, SpaConfig, SpaContext, SpaDataReader, SpaEmitFunction, SpaEvents, SpaKernel, SpaKernelDeps, SpaRequire, SpaState };
|
|
1104
1019
|
}
|
|
1020
|
+
/** Payload map for the events `spa` emits, used to type the kernel's `emit` closure. */
|
|
1021
|
+
type SpaEvents = {
|
|
1022
|
+
/** A navigation has been intercepted and is starting. */"spa:navigate": {
|
|
1023
|
+
from: string;
|
|
1024
|
+
to: string;
|
|
1025
|
+
}; /** The swap completed and the new URL is active. */
|
|
1026
|
+
"spa:navigated": {
|
|
1027
|
+
url: string;
|
|
1028
|
+
}; /** A component instance attached to an element. */
|
|
1029
|
+
"spa:component-mount": {
|
|
1030
|
+
name: string;
|
|
1031
|
+
el: Element;
|
|
1032
|
+
}; /** A component instance detached from an element. */
|
|
1033
|
+
"spa:component-unmount": {
|
|
1034
|
+
name: string;
|
|
1035
|
+
el: Element;
|
|
1036
|
+
};
|
|
1037
|
+
};
|
|
1038
|
+
/** Strictly-typed emit closure for the spa events (kernel overload form). */
|
|
1039
|
+
type SpaEmitFunction = EmitFn<SpaEvents>;
|
|
1105
1040
|
/**
|
|
1106
1041
|
* Structural extraction of a plugin instance's public API from its `_phantom`
|
|
1107
|
-
* carrier (mirrors the kernel's non-exported `ExtractPluginApi`)
|
|
1108
|
-
* framework's generic `require` is assignable to {@link PhaseContext.require}.
|
|
1109
|
-
*
|
|
1110
|
-
* @example
|
|
1111
|
-
* ```ts
|
|
1112
|
-
* type ContentApi = ExtractApi<typeof contentPlugin>;
|
|
1113
|
-
* ```
|
|
1114
|
-
*/
|
|
1115
|
-
type ExtractApi$1<PluginCandidate> = PluginCandidate extends {
|
|
1116
|
-
readonly _phantom: {
|
|
1117
|
-
readonly api: infer PluginApi;
|
|
1118
|
-
};
|
|
1119
|
-
} ? PluginApi : never;
|
|
1120
|
-
/**
|
|
1121
|
-
* Minimal logger slice used by the pipeline and phases (the core `log` API).
|
|
1122
|
-
*
|
|
1123
|
-
* @example
|
|
1124
|
-
* ```ts
|
|
1125
|
-
* const log: PhaseLog = { info: () => {}, debug: () => {}, warn: () => {}, error: () => {} };
|
|
1126
|
-
* ```
|
|
1127
|
-
*/
|
|
1128
|
-
type PhaseLog = {
|
|
1129
|
-
/** Record an informational event. */info(event: string, data?: unknown): void; /** Record a debug event. */
|
|
1130
|
-
debug(event: string, data?: unknown): void; /** Record a warning event. */
|
|
1131
|
-
warn(event: string, data?: unknown): void; /** Record an error event. */
|
|
1132
|
-
error(event: string, data?: unknown): void;
|
|
1133
|
-
};
|
|
1134
|
-
/**
|
|
1135
|
-
* Payload map for the events `build` emits, used to type the `emit` closure
|
|
1136
|
-
* handed to the pipeline driver and phases.
|
|
1137
|
-
*
|
|
1138
|
-
* @example
|
|
1139
|
-
* ```ts
|
|
1140
|
-
* const emit: PhaseEmit = (name, payload) => kernel.emit(name, payload);
|
|
1141
|
-
* ```
|
|
1142
|
-
*/
|
|
1143
|
-
type BuildEvents = {
|
|
1144
|
-
/** Phase boundary marker (start, then done with durationMs). */"build:phase": {
|
|
1145
|
-
phase: PhaseName;
|
|
1146
|
-
status: "start" | "done";
|
|
1147
|
-
durationMs?: number;
|
|
1148
|
-
}; /** One successful-run summary. */
|
|
1149
|
-
"build:complete": {
|
|
1150
|
-
outDir: string;
|
|
1151
|
-
pageCount: number;
|
|
1152
|
-
durationMs: number;
|
|
1153
|
-
};
|
|
1154
|
-
};
|
|
1155
|
-
/** Strictly-typed emit closure for the build events (kernel overload form). */
|
|
1156
|
-
type PhaseEmit = EmitFn<BuildEvents>;
|
|
1157
|
-
/** Generic `require` closure for pulling dependency plugin APIs at run time. */
|
|
1158
|
-
type PhaseRequire = <PluginCandidate extends {
|
|
1159
|
-
readonly name: string;
|
|
1160
|
-
readonly spec: unknown;
|
|
1161
|
-
readonly _phantom: {
|
|
1162
|
-
readonly config: unknown;
|
|
1163
|
-
readonly state: unknown;
|
|
1164
|
-
readonly api: unknown;
|
|
1165
|
-
readonly events: Record<string, unknown>;
|
|
1166
|
-
};
|
|
1167
|
-
}>(plugin: PluginCandidate) => ExtractApi$1<PluginCandidate>;
|
|
1168
|
-
/**
|
|
1169
|
-
* The plugin-context slice the pipeline driver and every phase consume: the
|
|
1170
|
-
* mutable `state`, the resolved `config`/`global`, plus `require`/`emit`/`log`.
|
|
1171
|
-
* Typed to match the kernel's generic context so the framework execution
|
|
1172
|
-
* context is structurally assignable.
|
|
1173
|
-
*
|
|
1174
|
-
* @example
|
|
1175
|
-
* ```ts
|
|
1176
|
-
* const ctx: PhaseContext = { state, config, global, require, emit, log };
|
|
1177
|
-
* ```
|
|
1178
|
-
*/
|
|
1179
|
-
type PhaseContext = {
|
|
1180
|
-
/** Mutable per-run build state (caches + runId). */state: State$1; /** Resolved, frozen build config. */
|
|
1181
|
-
readonly config: Readonly<Config$1>; /** Global framework config (mode, etc.). */
|
|
1182
|
-
readonly global: Readonly<{
|
|
1183
|
-
mode: "production" | "development";
|
|
1184
|
-
}>; /** Resolve a depended-upon plugin instance to its public API. */
|
|
1185
|
-
require: PhaseRequire; /** Emit a build event (notification-only). */
|
|
1186
|
-
emit: PhaseEmit; /** Structured logger (core `log` API). */
|
|
1187
|
-
readonly log: PhaseLog;
|
|
1188
|
-
};
|
|
1189
|
-
/**
|
|
1190
|
-
* Injectable PNG renderer for the og-images phase. Defaults to the real
|
|
1191
|
-
* Satori → resvg pipeline; unit tests inject a fake to assert hash-cache skip
|
|
1192
|
-
* and the `p-limit` bound without rasterizing real images.
|
|
1193
|
-
*
|
|
1194
|
-
* @example
|
|
1195
|
-
* ```ts
|
|
1196
|
-
* const render: OgPngRenderer = async () => new Uint8Array();
|
|
1197
|
-
* ```
|
|
1198
|
-
*/
|
|
1199
|
-
type OgPngRenderer = (input: {
|
|
1200
|
-
/** Article title rendered into the card. */title: string; /** Output width in pixels. */
|
|
1201
|
-
width: number; /** Output height in pixels. */
|
|
1202
|
-
height: number;
|
|
1203
|
-
}) => Promise<Uint8Array>;
|
|
1204
|
-
/**
|
|
1205
|
-
* Optional OG-image generation config. Omit the field (or set `false`) to disable.
|
|
1206
|
-
*
|
|
1207
|
-
* @example
|
|
1208
|
-
* ```ts
|
|
1209
|
-
* const og: OgImageConfig = { fontDir: "./fonts" };
|
|
1210
|
-
* ```
|
|
1211
|
-
*/
|
|
1212
|
-
interface OgImageConfig {
|
|
1213
|
-
/** Directory containing at least one .ttf/.otf/.woff font. Validated in onInit (void — config check only). */
|
|
1214
|
-
fontDir: string;
|
|
1215
|
-
/** Optional path to a custom OG template module. Falls back to the built-in template. */
|
|
1216
|
-
template?: string;
|
|
1217
|
-
/** Output dimensions. Defaults to 1200x630. */
|
|
1218
|
-
size?: {
|
|
1219
|
-
width: number;
|
|
1220
|
-
height: number;
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
/**
|
|
1224
|
-
* Public configuration for the `build` plugin. Flags give opt-in granularity over
|
|
1225
|
-
* individual outputs without rewriting the pipeline.
|
|
1226
|
-
*
|
|
1227
|
-
* @example
|
|
1228
|
-
* ```ts
|
|
1229
|
-
* const config: Config = { outDir: "./dist", minify: true, feeds: true, sitemap: true, images: true, ogImage: false };
|
|
1230
|
-
* ```
|
|
1231
|
-
*/
|
|
1232
|
-
type Config$1 = {
|
|
1233
|
-
/** Output directory for the built site. */outDir: string; /** Minify bundled CSS/JS. */
|
|
1234
|
-
minify: boolean; /** Generate RSS/Atom/JSON feeds. */
|
|
1235
|
-
feeds: boolean; /** Generate sitemap.xml + robots.txt. */
|
|
1236
|
-
sitemap: boolean; /** Optimize + copy content images. */
|
|
1237
|
-
images: boolean; /** OG-image generation. `false` (or omitted) disables it; an object enables and configures it. */
|
|
1238
|
-
ogImage: OgImageConfig | false;
|
|
1239
|
-
};
|
|
1240
|
-
/**
|
|
1241
|
-
* Per-run closure state for the `build` plugin. Holds caches and config only —
|
|
1242
|
-
* no domain data is duplicated here (pulled fresh via `ctx.require` each run).
|
|
1243
|
-
*
|
|
1244
|
-
* @example
|
|
1245
|
-
* ```ts
|
|
1246
|
-
* const state: State = { config, manifest: null, buildCache: new Map(), runId: null, ogImageHashCache: new Map() };
|
|
1247
|
-
* ```
|
|
1248
|
-
*/
|
|
1249
|
-
interface State$1 {
|
|
1250
|
-
/** Resolved, frozen config snapshot. */
|
|
1251
|
-
config: Config$1;
|
|
1252
|
-
/** Cached route manifest for the current run (populated in Phase 3 from router). */
|
|
1253
|
-
manifest: RouteDefinition[] | null;
|
|
1254
|
-
/** Per-run build artifacts (e.g. hashed CSS/JS asset paths from Phase 1). */
|
|
1255
|
-
buildCache: Map<string, unknown>;
|
|
1256
|
-
/** Unique id for the current run (timestamp/uuid) — injected as build-id meta. */
|
|
1257
|
-
runId: string | null;
|
|
1258
|
-
/**
|
|
1259
|
-
* Content-hash cache for OG images: slug -> sha256(title + template + size).
|
|
1260
|
-
* Loaded from `<outDir>/.cache/og-images.json` at the OG phase and written back,
|
|
1261
|
-
* so unchanged articles are skipped on the next run.
|
|
1262
|
-
*/
|
|
1263
|
-
ogImageHashCache: Map<string, string>;
|
|
1264
|
-
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Ordered names of the build pipeline phases, in execution order.
|
|
1267
|
-
*
|
|
1268
|
-
* @example
|
|
1269
|
-
* ```ts
|
|
1270
|
-
* const phase: PhaseName = "bundle";
|
|
1271
|
-
* ```
|
|
1272
|
-
*/
|
|
1273
|
-
type PhaseName = "bundle" | "content" | "images" | "pages" | "feeds" | "sitemap" | "og-images" | "root-index";
|
|
1274
|
-
/**
|
|
1275
|
-
* Result of a completed build run.
|
|
1276
|
-
*
|
|
1277
|
-
* @example
|
|
1278
|
-
* ```ts
|
|
1279
|
-
* const result: BuildResult = { outDir: "./dist", pageCount: 12, durationMs: 840 };
|
|
1280
|
-
* ```
|
|
1281
|
-
*/
|
|
1282
|
-
interface BuildResult {
|
|
1283
|
-
/** Resolved output directory the site was written to. */
|
|
1284
|
-
outDir: string;
|
|
1285
|
-
/** Number of route pages rendered. */
|
|
1286
|
-
pageCount: number;
|
|
1287
|
-
/** Total wall-clock duration of the run, in milliseconds. */
|
|
1288
|
-
durationMs: number;
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Public API surface mounted on `app.build`.
|
|
1292
|
-
*
|
|
1293
|
-
* @example
|
|
1294
|
-
* ```ts
|
|
1295
|
-
* const result = await app.build.run();
|
|
1296
|
-
* ```
|
|
1297
|
-
*/
|
|
1298
|
-
type Api$1 = {
|
|
1299
|
-
/**
|
|
1300
|
-
* Run the full SSG pipeline and write the site to disk.
|
|
1301
|
-
*
|
|
1302
|
-
* @param options - Optional run overrides.
|
|
1303
|
-
* @param options.outDir - Override the configured output directory for this run.
|
|
1304
|
-
* @returns The build result (outDir, pageCount, durationMs).
|
|
1305
|
-
* @example
|
|
1306
|
-
* ```ts
|
|
1307
|
-
* const result = await app.build.run({ outDir: "./preview" });
|
|
1308
|
-
* ```
|
|
1309
|
-
*/
|
|
1310
|
-
run(options?: {
|
|
1311
|
-
outDir?: string;
|
|
1312
|
-
}): Promise<BuildResult>;
|
|
1313
|
-
/**
|
|
1314
|
-
* List the phases in execution order (introspection / tooling).
|
|
1315
|
-
*
|
|
1316
|
-
* @returns The static ordered phase names.
|
|
1317
|
-
* @example
|
|
1318
|
-
* ```ts
|
|
1319
|
-
* const order = app.build.phases();
|
|
1320
|
-
* ```
|
|
1321
|
-
*/
|
|
1322
|
-
phases(): PhaseName[];
|
|
1323
|
-
};
|
|
1324
|
-
declare namespace types_d_exports$7 {
|
|
1325
|
-
export { COMPONENT_HOOK_NAMES, ComponentContext, ComponentDef, ComponentHooks, ComponentInstance, ExtractApi, PageData, ResolvedSpaConfig, SpaApi, SpaConfig, SpaContext, SpaEmitFunction, SpaEvents, SpaKernel, SpaKernelDeps, SpaRequire, SpaState };
|
|
1326
|
-
}
|
|
1327
|
-
/** Payload map for the events `spa` emits, used to type the kernel's `emit` closure. */
|
|
1328
|
-
type SpaEvents = {
|
|
1329
|
-
/** A navigation has been intercepted and is starting. */"spa:navigate": {
|
|
1330
|
-
from: string;
|
|
1331
|
-
to: string;
|
|
1332
|
-
}; /** The swap completed and the new URL is active. */
|
|
1333
|
-
"spa:navigated": {
|
|
1334
|
-
url: string;
|
|
1335
|
-
}; /** A component instance attached to an element. */
|
|
1336
|
-
"spa:component-mount": {
|
|
1337
|
-
name: string;
|
|
1338
|
-
el: Element;
|
|
1339
|
-
}; /** A component instance detached from an element. */
|
|
1340
|
-
"spa:component-unmount": {
|
|
1341
|
-
name: string;
|
|
1342
|
-
el: Element;
|
|
1343
|
-
};
|
|
1344
|
-
};
|
|
1345
|
-
/** Strictly-typed emit closure for the spa events (kernel overload form). */
|
|
1346
|
-
type SpaEmitFunction = EmitFn<SpaEvents>;
|
|
1347
|
-
/**
|
|
1348
|
-
* Structural extraction of a plugin instance's public API from its `_phantom`
|
|
1349
|
-
* carrier (mirrors the kernel's non-exported `ExtractPluginApi`).
|
|
1042
|
+
* carrier (mirrors the kernel's non-exported `ExtractPluginApi`).
|
|
1350
1043
|
*
|
|
1351
1044
|
* @example
|
|
1352
1045
|
* type RApi = ExtractApi<typeof routerPlugin>;
|
|
1353
1046
|
*/
|
|
1354
|
-
type ExtractApi<PluginCandidate> = PluginCandidate extends {
|
|
1047
|
+
type ExtractApi$1<PluginCandidate> = PluginCandidate extends {
|
|
1355
1048
|
readonly _phantom: {
|
|
1356
1049
|
readonly api: infer PluginApi;
|
|
1357
1050
|
};
|
|
@@ -1366,7 +1059,7 @@ type SpaRequire = <PluginCandidate extends {
|
|
|
1366
1059
|
readonly api: unknown;
|
|
1367
1060
|
readonly events: Record<string, unknown>;
|
|
1368
1061
|
};
|
|
1369
|
-
}>(plugin: PluginCandidate) => ExtractApi<PluginCandidate>;
|
|
1062
|
+
}>(plugin: PluginCandidate) => ExtractApi$1<PluginCandidate>;
|
|
1370
1063
|
/**
|
|
1371
1064
|
* The plugin-context slice the spa wiring consumes in `onInit`/`onStart`:
|
|
1372
1065
|
* mutable `state`, resolved `config`, `require`/`emit`/`log`. Structurally
|
|
@@ -1379,6 +1072,11 @@ interface SpaContext {
|
|
|
1379
1072
|
readonly config: Readonly<SpaConfig>;
|
|
1380
1073
|
/** Resolve a depended-upon plugin instance to its public API. */
|
|
1381
1074
|
require: SpaRequire;
|
|
1075
|
+
/**
|
|
1076
|
+
* Whether a plugin is registered (by name). Used to detect the OPTIONAL `data`
|
|
1077
|
+
* plugin at init — `spa` enables client DATA navigation only when `has("data")`.
|
|
1078
|
+
*/
|
|
1079
|
+
has: (name: string) => boolean;
|
|
1382
1080
|
/** Emit a spa lifecycle event (notification-only). */
|
|
1383
1081
|
emit: SpaEmitFunction;
|
|
1384
1082
|
/** Structured logger (core `log` API). */
|
|
@@ -1505,12 +1203,26 @@ interface ComponentInstance {
|
|
|
1505
1203
|
}
|
|
1506
1204
|
/** Page data payload parsed from the inline `script#__DATA__` element. */
|
|
1507
1205
|
type PageData = Record<string, unknown>;
|
|
1508
|
-
/**
|
|
1206
|
+
/**
|
|
1207
|
+
* The OPTIONAL `data` provider reader the kernel uses for client DATA navigation —
|
|
1208
|
+
* a structural slice of the `data` plugin's API (fetch the persisted JSON for a
|
|
1209
|
+
* page path). Captured at init via `ctx.has("data")`/`ctx.require` so `spa` never
|
|
1210
|
+
* imports the `data` plugin or its types.
|
|
1211
|
+
*/
|
|
1212
|
+
type SpaDataReader = (path: string) => Promise<unknown | null>;
|
|
1213
|
+
/** Resolved dependency APIs the kernel reuses (router match/mode, head compose, optional data). */
|
|
1509
1214
|
interface SpaKernelDeps {
|
|
1510
|
-
/** Router plugin API — used for client-side route
|
|
1215
|
+
/** Router plugin API — used for client-side route matching (`match`) + the resolved `mode`. */
|
|
1511
1216
|
router: RouterApi;
|
|
1512
1217
|
/** Head plugin API — its pure compose is reused for client head-sync. */
|
|
1513
|
-
head: Api$
|
|
1218
|
+
head: Api$3;
|
|
1219
|
+
/**
|
|
1220
|
+
* The OPTIONAL `data` reader. Present only when the `data` plugin is composed.
|
|
1221
|
+
* When present (and `router.mode() !== "ssg"`), navigation first tries the client
|
|
1222
|
+
* DATA path (match → `dataAt(path)` → `route.parse` → `route.render`); otherwise
|
|
1223
|
+
* it always uses HTML-over-fetch.
|
|
1224
|
+
*/
|
|
1225
|
+
dataAt?: SpaDataReader;
|
|
1514
1226
|
}
|
|
1515
1227
|
/** The single shared SPA kernel — pure factory over state/config/emit/deps. */
|
|
1516
1228
|
interface SpaKernel {
|
|
@@ -1540,76 +1252,774 @@ interface SpaKernel {
|
|
|
1540
1252
|
*/
|
|
1541
1253
|
register(component: ComponentDef): void;
|
|
1542
1254
|
/**
|
|
1543
|
-
* Process a navigation to `path`: fetch then swap then head-sync then emit.
|
|
1255
|
+
* Process a navigation to `path`: fetch then swap then head-sync then emit.
|
|
1256
|
+
*
|
|
1257
|
+
* @param path - The target path to navigate to.
|
|
1258
|
+
* @returns void
|
|
1259
|
+
* @example
|
|
1260
|
+
* kernel.processNav("/about");
|
|
1261
|
+
*/
|
|
1262
|
+
processNav(path: string): void;
|
|
1263
|
+
/**
|
|
1264
|
+
* Query the swap region and mount components for matching elements.
|
|
1265
|
+
*
|
|
1266
|
+
* @returns void
|
|
1267
|
+
* @example
|
|
1268
|
+
* kernel.scan();
|
|
1269
|
+
*/
|
|
1270
|
+
scan(): void;
|
|
1271
|
+
/**
|
|
1272
|
+
* Tear down router listeners, run unmount/destroy, clear instances.
|
|
1273
|
+
*
|
|
1274
|
+
* @returns void
|
|
1275
|
+
* @example
|
|
1276
|
+
* kernel.dispose();
|
|
1277
|
+
*/
|
|
1278
|
+
dispose(): void;
|
|
1279
|
+
}
|
|
1280
|
+
/** Internal mutable state for the spa plugin (all kernel data lives here). */
|
|
1281
|
+
interface SpaState {
|
|
1282
|
+
/** Components registered by name (last-registered-wins). */
|
|
1283
|
+
registeredComponents: Map<string, ComponentDef>;
|
|
1284
|
+
/** Live component instances keyed by their bound element. */
|
|
1285
|
+
instances: Map<Element, ComponentInstance>;
|
|
1286
|
+
/** The current resolved URL (pathname + search). */
|
|
1287
|
+
currentUrl: string;
|
|
1288
|
+
/** Teardown handle for the attached router listeners (null when detached). */
|
|
1289
|
+
destroyRouter: (() => void) | null;
|
|
1290
|
+
/** Whether the browser runtime has been booted. */
|
|
1291
|
+
started: boolean;
|
|
1292
|
+
/** The single shared SPA kernel instance (null until onInit builds it). */
|
|
1293
|
+
kernel: SpaKernel | null;
|
|
1294
|
+
}
|
|
1295
|
+
/** Public API of the spa plugin (registration / control surface). */
|
|
1296
|
+
type SpaApi = {
|
|
1297
|
+
/**
|
|
1298
|
+
* Register a component definition for client mounting.
|
|
1299
|
+
*
|
|
1300
|
+
* @param component - The component definition created via `createComponent`.
|
|
1301
|
+
* @returns void
|
|
1302
|
+
* @example
|
|
1303
|
+
* app.spa.register(counter);
|
|
1304
|
+
*/
|
|
1305
|
+
register(component: ComponentDef): void;
|
|
1306
|
+
/**
|
|
1307
|
+
* Programmatically navigate to a path (client runtime; no-op without a DOM).
|
|
1308
|
+
*
|
|
1309
|
+
* @param path - Target path (pathname, optionally with search/hash).
|
|
1310
|
+
* @returns void
|
|
1311
|
+
* @example
|
|
1312
|
+
* app.spa.navigate("/about");
|
|
1313
|
+
*/
|
|
1314
|
+
navigate(path: string): void;
|
|
1315
|
+
/**
|
|
1316
|
+
* Read the current resolved URL.
|
|
1317
|
+
*
|
|
1318
|
+
* @returns The current pathname + search.
|
|
1319
|
+
* @example
|
|
1320
|
+
* const url = app.spa.current(); // "/about"
|
|
1321
|
+
*/
|
|
1322
|
+
current(): string;
|
|
1323
|
+
};
|
|
1324
|
+
declare namespace types_d_exports {
|
|
1325
|
+
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
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Structural extraction of a plugin instance's public API from its `_phantom`
|
|
1329
|
+
* carrier (mirrors the kernel's non-exported `ExtractPluginApi`) so the
|
|
1330
|
+
* framework's generic `require` is assignable to {@link PhaseContext.require}.
|
|
1331
|
+
*
|
|
1332
|
+
* @example
|
|
1333
|
+
* ```ts
|
|
1334
|
+
* type ContentApi = ExtractApi<typeof contentPlugin>;
|
|
1335
|
+
* ```
|
|
1336
|
+
*/
|
|
1337
|
+
type ExtractApi<PluginCandidate> = PluginCandidate extends {
|
|
1338
|
+
readonly _phantom: {
|
|
1339
|
+
readonly api: infer PluginApi;
|
|
1340
|
+
};
|
|
1341
|
+
} ? PluginApi : never;
|
|
1342
|
+
/**
|
|
1343
|
+
* Minimal logger slice used by the pipeline and phases (the core `log` API).
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* ```ts
|
|
1347
|
+
* const log: PhaseLog = { info: () => {}, debug: () => {}, warn: () => {}, error: () => {} };
|
|
1348
|
+
* ```
|
|
1349
|
+
*/
|
|
1350
|
+
type PhaseLog = {
|
|
1351
|
+
/** Record an informational event. */info(event: string, data?: unknown): void; /** Record a debug event. */
|
|
1352
|
+
debug(event: string, data?: unknown): void; /** Record a warning event. */
|
|
1353
|
+
warn(event: string, data?: unknown): void; /** Record an error event. */
|
|
1354
|
+
error(event: string, data?: unknown): void;
|
|
1355
|
+
};
|
|
1356
|
+
/**
|
|
1357
|
+
* Payload map for the events `build` emits, used to type the `emit` closure
|
|
1358
|
+
* handed to the pipeline driver and phases.
|
|
1359
|
+
*
|
|
1360
|
+
* @example
|
|
1361
|
+
* ```ts
|
|
1362
|
+
* const emit: PhaseEmit = (name, payload) => kernel.emit(name, payload);
|
|
1363
|
+
* ```
|
|
1364
|
+
*/
|
|
1365
|
+
type BuildEvents = {
|
|
1366
|
+
/** Phase boundary marker (start, then done with durationMs). */"build:phase": {
|
|
1367
|
+
phase: PhaseName;
|
|
1368
|
+
status: "start" | "done";
|
|
1369
|
+
durationMs?: number;
|
|
1370
|
+
}; /** One successful-run summary. */
|
|
1371
|
+
"build:complete": {
|
|
1372
|
+
outDir: string;
|
|
1373
|
+
pageCount: number;
|
|
1374
|
+
durationMs: number;
|
|
1375
|
+
};
|
|
1376
|
+
};
|
|
1377
|
+
/** Strictly-typed emit closure for the build events (kernel overload form). */
|
|
1378
|
+
type PhaseEmit = EmitFn<BuildEvents>;
|
|
1379
|
+
/** Generic `require` closure for pulling dependency plugin APIs at run time. */
|
|
1380
|
+
type PhaseRequire = <PluginCandidate extends {
|
|
1381
|
+
readonly name: string;
|
|
1382
|
+
readonly spec: unknown;
|
|
1383
|
+
readonly _phantom: {
|
|
1384
|
+
readonly config: unknown;
|
|
1385
|
+
readonly state: unknown;
|
|
1386
|
+
readonly api: unknown;
|
|
1387
|
+
readonly events: Record<string, unknown>;
|
|
1388
|
+
};
|
|
1389
|
+
}>(plugin: PluginCandidate) => ExtractApi<PluginCandidate>;
|
|
1390
|
+
/**
|
|
1391
|
+
* The plugin-context slice the pipeline driver and every phase consume: the
|
|
1392
|
+
* mutable `state`, the resolved `config`/`global`, plus `require`/`emit`/`log`.
|
|
1393
|
+
* Typed to match the kernel's generic context so the framework execution
|
|
1394
|
+
* context is structurally assignable.
|
|
1395
|
+
*
|
|
1396
|
+
* @example
|
|
1397
|
+
* ```ts
|
|
1398
|
+
* const ctx: PhaseContext = { state, config, global, require, emit, log };
|
|
1399
|
+
* ```
|
|
1400
|
+
*/
|
|
1401
|
+
type PhaseContext = {
|
|
1402
|
+
/** Mutable per-run build state (caches + runId). */state: State$2; /** Resolved, frozen build config. */
|
|
1403
|
+
readonly config: Readonly<Config$2>; /** Global framework config (mode, etc.). */
|
|
1404
|
+
readonly global: Readonly<{
|
|
1405
|
+
mode: "production" | "development";
|
|
1406
|
+
}>; /** Resolve a depended-upon plugin instance to its public API. */
|
|
1407
|
+
require: PhaseRequire; /** Whether a plugin is registered (by name) — used to detect the OPTIONAL `data` plugin. */
|
|
1408
|
+
has: (name: string) => boolean; /** Emit a build event (notification-only). */
|
|
1409
|
+
emit: PhaseEmit; /** Structured logger (core `log` API). */
|
|
1410
|
+
readonly log: PhaseLog;
|
|
1411
|
+
};
|
|
1412
|
+
/**
|
|
1413
|
+
* Injectable PNG renderer for the og-images phase. Defaults to the real
|
|
1414
|
+
* Satori → resvg pipeline; unit tests inject a fake to assert hash-cache skip
|
|
1415
|
+
* and the `p-limit` bound without rasterizing real images.
|
|
1416
|
+
*
|
|
1417
|
+
* @example
|
|
1418
|
+
* ```ts
|
|
1419
|
+
* const render: OgPngRenderer = async () => new Uint8Array();
|
|
1420
|
+
* ```
|
|
1421
|
+
*/
|
|
1422
|
+
type OgPngRenderer = (input: RichOgInput) => Promise<Uint8Array>;
|
|
1423
|
+
/**
|
|
1424
|
+
* Rich input handed to a custom OG `render` hook for a single article card. Carries
|
|
1425
|
+
* the full article + site metadata so a consumer can compose any layout. Returned by
|
|
1426
|
+
* the framework, not authored by consumers directly.
|
|
1427
|
+
*
|
|
1428
|
+
* @example
|
|
1429
|
+
* ```ts
|
|
1430
|
+
* const input: RichOgInput = {
|
|
1431
|
+
* title: "Hello", description: "Intro", date: "2026-01-15", tags: ["a"],
|
|
1432
|
+
* locale: "en", siteName: "Blog", size: { width: 1200, height: 630 }
|
|
1433
|
+
* };
|
|
1434
|
+
* ```
|
|
1435
|
+
*/
|
|
1436
|
+
interface RichOgInput {
|
|
1437
|
+
/** Article title rendered into the card. */
|
|
1438
|
+
title: string;
|
|
1439
|
+
/** Article description / summary. */
|
|
1440
|
+
description: string;
|
|
1441
|
+
/** Publication date (ISO string from frontmatter). */
|
|
1442
|
+
date: string;
|
|
1443
|
+
/** Article tags. */
|
|
1444
|
+
tags: string[];
|
|
1445
|
+
/** Optional author name. */
|
|
1446
|
+
author?: string;
|
|
1447
|
+
/** Active locale for the card. */
|
|
1448
|
+
locale: string;
|
|
1449
|
+
/** Site name (from the site plugin / config). */
|
|
1450
|
+
siteName: string;
|
|
1451
|
+
/** Output dimensions for the card. */
|
|
1452
|
+
size: {
|
|
1453
|
+
width: number;
|
|
1454
|
+
height: number;
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* A single custom OG font entry. Each `path` is read to a Buffer ONCE per build and
|
|
1459
|
+
* handed to Satori. `weight`/`style` default to `400`/`"normal"` when omitted.
|
|
1460
|
+
*
|
|
1461
|
+
* @example
|
|
1462
|
+
* ```ts
|
|
1463
|
+
* const font: OgFont = { name: "Inter", path: "./fonts/Inter.ttf", weight: 600 };
|
|
1464
|
+
* ```
|
|
1465
|
+
*/
|
|
1466
|
+
interface OgFont {
|
|
1467
|
+
/** Font family name referenced by the rendered card. */
|
|
1468
|
+
name: string;
|
|
1469
|
+
/** Path to the .ttf/.otf/.woff file. */
|
|
1470
|
+
path: string;
|
|
1471
|
+
/** Numeric weight (defaults to 400). */
|
|
1472
|
+
weight?: number;
|
|
1473
|
+
/** Font style (defaults to "normal"). */
|
|
1474
|
+
style?: "normal" | "italic";
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Optional OG-image generation config. Omit the field (or set `false`) to disable.
|
|
1478
|
+
*
|
|
1479
|
+
* The optional `render` hook (`@jsxImportSource preact`) lets a consumer return a
|
|
1480
|
+
* Preact `VNode` for the card; the framework casts it to Satori's input at the single
|
|
1481
|
+
* render boundary. `fonts` supplies multiple named fonts loaded once per build.
|
|
1482
|
+
*
|
|
1483
|
+
* @example
|
|
1484
|
+
* ```ts
|
|
1485
|
+
* const og: OgImageConfig = { fontDir: "./fonts" };
|
|
1486
|
+
* ```
|
|
1487
|
+
*/
|
|
1488
|
+
interface OgImageConfig {
|
|
1489
|
+
/** Directory containing at least one .ttf/.otf/.woff font. Validated in onInit (void — config check only). */
|
|
1490
|
+
fontDir: string;
|
|
1491
|
+
/** Optional path to a custom OG template module. Falls back to the built-in template. */
|
|
1492
|
+
template?: string;
|
|
1493
|
+
/** Output dimensions. Defaults to 1200x630. */
|
|
1494
|
+
size?: {
|
|
1495
|
+
width: number;
|
|
1496
|
+
height: number;
|
|
1497
|
+
};
|
|
1498
|
+
/** Custom card renderer; returns a Preact `VNode` from the {@link RichOgInput}. */
|
|
1499
|
+
render?(input: RichOgInput): import("preact").VNode;
|
|
1500
|
+
/** Explicit named fonts loaded once per build (overrides the first-file scan). */
|
|
1501
|
+
fonts?: OgFont[];
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Public configuration for the `build` plugin. Flags give opt-in granularity over
|
|
1505
|
+
* individual outputs without rewriting the pipeline.
|
|
1506
|
+
*
|
|
1507
|
+
* @example
|
|
1508
|
+
* ```ts
|
|
1509
|
+
* const config: Config = { outDir: "./dist", minify: true, feeds: true, sitemap: true, images: true, ogImage: false };
|
|
1510
|
+
* ```
|
|
1511
|
+
*/
|
|
1512
|
+
type Config$2 = {
|
|
1513
|
+
/** Output directory for the built site. */outDir: string; /** Minify bundled CSS/JS. */
|
|
1514
|
+
minify: boolean; /** Generate RSS/Atom/JSON feeds. */
|
|
1515
|
+
feeds: boolean; /** Generate sitemap.xml + robots.txt. */
|
|
1516
|
+
sitemap: boolean; /** Optimize + copy content images. */
|
|
1517
|
+
images: boolean; /** OG-image generation. `false` (or omitted) disables it; an object enables and configures it. */
|
|
1518
|
+
ogImage: OgImageConfig | false; /** Auto-inject bundled `main.{css,js}` into rendered pages. Default `true`. */
|
|
1519
|
+
injectAssets?: boolean; /** Directory copied verbatim into `outDir` (skipped silently if absent). Default `"public"`. */
|
|
1520
|
+
publicDir?: string;
|
|
1521
|
+
/**
|
|
1522
|
+
* Emit `outDir/404.html`. `true` for the built-in default page, or
|
|
1523
|
+
* `{ route }` to supply the page's literal HTML body content (NOT a route
|
|
1524
|
+
* path/slug — the string is written into the 404 page verbatim). Default `false`.
|
|
1525
|
+
*/
|
|
1526
|
+
notFound?: boolean | {
|
|
1527
|
+
route?: string;
|
|
1528
|
+
}; /** Emit per-path i18n bare-path redirect HTML pages. Default `false`. */
|
|
1529
|
+
localeRedirects?: boolean; /** Authoritative client bundle entry path (overrides the conventional scan). */
|
|
1530
|
+
clientEntry?: string; /** HTML shell template with `<!--moku:head-->`/`<!--moku:body-->`/`<!--moku:assets-->` placeholders. */
|
|
1531
|
+
template?: string;
|
|
1532
|
+
};
|
|
1533
|
+
/**
|
|
1534
|
+
* A typed asset-manifest entry for one bundled asset kind (CSS or JS): a map of the
|
|
1535
|
+
* original entry basename to its hashed on-disk output path. Replaces the untyped
|
|
1536
|
+
* `Map<string, unknown>` reads when emitting `<link>`/`<script>` tags in pages.tsx.
|
|
1537
|
+
*
|
|
1538
|
+
* @example
|
|
1539
|
+
* ```ts
|
|
1540
|
+
* const entry: BuildCacheEntry = { "main.css": "assets/main-abc123.css" };
|
|
1541
|
+
* ```
|
|
1542
|
+
*/
|
|
1543
|
+
type BuildCacheEntry = Record<string, string>;
|
|
1544
|
+
/**
|
|
1545
|
+
* Per-run closure state for the `build` plugin. Holds caches and config only —
|
|
1546
|
+
* no domain data is duplicated here (pulled fresh via `ctx.require` each run).
|
|
1547
|
+
*
|
|
1548
|
+
* @example
|
|
1549
|
+
* ```ts
|
|
1550
|
+
* const state: State = { config, manifest: null, buildCache: new Map(), runId: null, ogImageHashCache: new Map() };
|
|
1551
|
+
* ```
|
|
1552
|
+
*/
|
|
1553
|
+
interface State$2 {
|
|
1554
|
+
/** Resolved, frozen config snapshot. */
|
|
1555
|
+
config: Config$2;
|
|
1556
|
+
/** Cached route manifest for the current run (populated in Phase 3 from router). */
|
|
1557
|
+
manifest: RouteDefinition[] | null;
|
|
1558
|
+
/** Per-run build artifacts (e.g. hashed CSS/JS asset paths from Phase 1). */
|
|
1559
|
+
buildCache: Map<string, unknown>;
|
|
1560
|
+
/** Unique id for the current run (timestamp/uuid) — injected as build-id meta. */
|
|
1561
|
+
runId: string | null;
|
|
1562
|
+
/**
|
|
1563
|
+
* Content-hash cache for OG images: slug -> sha256(title + template + size).
|
|
1564
|
+
* Loaded from `<outDir>/.cache/og-images.json` at the OG phase and written back,
|
|
1565
|
+
* so unchanged articles are skipped on the next run.
|
|
1566
|
+
*/
|
|
1567
|
+
ogImageHashCache: Map<string, string>;
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Ordered names of the build pipeline phases, in execution order.
|
|
1571
|
+
*
|
|
1572
|
+
* @example
|
|
1573
|
+
* ```ts
|
|
1574
|
+
* const phase: PhaseName = "bundle";
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1577
|
+
type PhaseName = "bundle" | "content" | "images" | "pages" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
|
|
1578
|
+
/**
|
|
1579
|
+
* Result of a completed build run.
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```ts
|
|
1583
|
+
* const result: BuildResult = { outDir: "./dist", pageCount: 12, durationMs: 840 };
|
|
1584
|
+
* ```
|
|
1585
|
+
*/
|
|
1586
|
+
interface BuildResult {
|
|
1587
|
+
/** Resolved output directory the site was written to. */
|
|
1588
|
+
outDir: string;
|
|
1589
|
+
/** Number of route pages rendered. */
|
|
1590
|
+
pageCount: number;
|
|
1591
|
+
/** Total wall-clock duration of the run, in milliseconds. */
|
|
1592
|
+
durationMs: number;
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Public API surface mounted on `app.build`.
|
|
1596
|
+
*
|
|
1597
|
+
* @example
|
|
1598
|
+
* ```ts
|
|
1599
|
+
* const result = await app.build.run();
|
|
1600
|
+
* ```
|
|
1601
|
+
*/
|
|
1602
|
+
type Api$2 = {
|
|
1603
|
+
/**
|
|
1604
|
+
* Run the full SSG pipeline and write the site to disk.
|
|
1605
|
+
*
|
|
1606
|
+
* @param options - Optional run overrides.
|
|
1607
|
+
* @param options.outDir - Override the configured output directory for this run.
|
|
1608
|
+
* @returns The build result (outDir, pageCount, durationMs).
|
|
1609
|
+
* @example
|
|
1610
|
+
* ```ts
|
|
1611
|
+
* const result = await app.build.run({ outDir: "./preview" });
|
|
1612
|
+
* ```
|
|
1613
|
+
*/
|
|
1614
|
+
run(options?: {
|
|
1615
|
+
outDir?: string;
|
|
1616
|
+
}): Promise<BuildResult>;
|
|
1617
|
+
/**
|
|
1618
|
+
* List the phases in execution order (introspection / tooling).
|
|
1619
|
+
*
|
|
1620
|
+
* @returns The static ordered phase names.
|
|
1621
|
+
* @example
|
|
1622
|
+
* ```ts
|
|
1623
|
+
* const order = app.build.phases();
|
|
1624
|
+
* ```
|
|
1625
|
+
*/
|
|
1626
|
+
phases(): PhaseName[];
|
|
1627
|
+
};
|
|
1628
|
+
//#endregion
|
|
1629
|
+
//#region src/plugins/build/index.d.ts
|
|
1630
|
+
/**
|
|
1631
|
+
* Build plugin — the static-site-generation orchestrator. Renders every route to
|
|
1632
|
+
* `outDir`, and optionally emits feeds, a sitemap, optimized images, and OG
|
|
1633
|
+
* images. Depends on site, i18n, content, router, and head; emits `build:phase`.
|
|
1634
|
+
*
|
|
1635
|
+
* @example Configure the production build
|
|
1636
|
+
* ```ts
|
|
1637
|
+
* const app = createApp({
|
|
1638
|
+
* pluginConfigs: {
|
|
1639
|
+
* build: {
|
|
1640
|
+
* outDir: "dist",
|
|
1641
|
+
* minify: true,
|
|
1642
|
+
* feeds: true,
|
|
1643
|
+
* sitemap: true,
|
|
1644
|
+
* images: true,
|
|
1645
|
+
* ogImage: false // or an object to enable + configure OG-image generation
|
|
1646
|
+
* }
|
|
1647
|
+
* }
|
|
1648
|
+
* });
|
|
1649
|
+
* ```
|
|
1650
|
+
*/
|
|
1651
|
+
declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Config$2, State$2, Api$2, {
|
|
1652
|
+
"build:phase": {
|
|
1653
|
+
phase: PhaseName;
|
|
1654
|
+
status: "start" | "done";
|
|
1655
|
+
durationMs?: number;
|
|
1656
|
+
};
|
|
1657
|
+
"build:complete": {
|
|
1658
|
+
outDir: string;
|
|
1659
|
+
pageCount: number;
|
|
1660
|
+
durationMs: number;
|
|
1661
|
+
};
|
|
1662
|
+
}> & Record<never, never>;
|
|
1663
|
+
declare namespace types_d_exports$1 {
|
|
1664
|
+
export { Api$1 as Api, Article, ArticleCard, ComputedFields, Config$1 as Config, ContentApiContext, ContentEvents, Frontmatter, State$1 as State };
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Configuration for the content plugin.
|
|
1668
|
+
*
|
|
1669
|
+
* @example
|
|
1670
|
+
* ```ts
|
|
1671
|
+
* { contentDir: "./src/content", trustedContent: false, shikiTheme: "github-dark" }
|
|
1672
|
+
* ```
|
|
1673
|
+
*/
|
|
1674
|
+
type Config$1 = {
|
|
1675
|
+
/** Absolute or project-relative path to the content directory. Validated in onInit. */contentDir: string;
|
|
1676
|
+
/**
|
|
1677
|
+
* SECURITY GATE. When false (the default), rehype-sanitize runs as the final
|
|
1678
|
+
* pipeline step. Set true ONLY for fully author-controlled Markdown — true
|
|
1679
|
+
* disables sanitize and trusts all raw HTML.
|
|
1680
|
+
*/
|
|
1681
|
+
trustedContent: boolean; /** Additional remark plugins, concatenated AFTER framework defaults. Defaults to []. */
|
|
1682
|
+
extraRemarkPlugins?: readonly Pluggable[]; /** Additional rehype plugins, concatenated after custom transforms, before Shiki + sanitize. Defaults to []. */
|
|
1683
|
+
extraRehypePlugins?: readonly Pluggable[]; /** Shiki theme name for syntax highlighting. Defaults to "github-dark". */
|
|
1684
|
+
shikiTheme?: string; /** Author applied to articles whose frontmatter omits author. Defaults to undefined. */
|
|
1685
|
+
defaultAuthor?: string;
|
|
1686
|
+
};
|
|
1687
|
+
/**
|
|
1688
|
+
* Internal mutable state for the content plugin.
|
|
1689
|
+
*
|
|
1690
|
+
* @example
|
|
1691
|
+
* ```ts
|
|
1692
|
+
* { processor: null, articles: new Map(), slugs: null, dirtyPaths: new Set() }
|
|
1693
|
+
* ```
|
|
1694
|
+
*/
|
|
1695
|
+
type State$1 = {
|
|
1696
|
+
/** Lazily-created unified processor singleton. null until first render()/loadAll(). */processor: Processor | null; /** Article cache keyed locale -> (slug -> Article). Starts empty. */
|
|
1697
|
+
articles: Map<string, Map<string, Article>>; /** Discovered, sorted slug list cached after first disk scan. null until first discovery. */
|
|
1698
|
+
slugs: string[] | null; /** Paths marked stale by invalidate(); next loadAll() re-reads only these. Starts empty. */
|
|
1699
|
+
dirtyPaths: Set<string>;
|
|
1700
|
+
};
|
|
1701
|
+
/**
|
|
1702
|
+
* YAML frontmatter parsed from each article file.
|
|
1703
|
+
*
|
|
1704
|
+
* @example
|
|
1705
|
+
* ```ts
|
|
1706
|
+
* { title: "Hello", date: "2026-01-15", description: "Intro", tags: [], language: "en" }
|
|
1707
|
+
* ```
|
|
1708
|
+
*/
|
|
1709
|
+
type Frontmatter = {
|
|
1710
|
+
/** Article title. Required. */title: string; /** ISO 8601 date string, e.g. "2026-01-15". Required. */
|
|
1711
|
+
date: string; /** Short summary used in cards, feeds, and meta description. Required. */
|
|
1712
|
+
description: string; /** Topic tags. Required (may be empty array). */
|
|
1713
|
+
tags: string[]; /** Source language code of this file. Required. */
|
|
1714
|
+
language: string; /** Draft flag. Excluded from output in production mode. Defaults to false. */
|
|
1715
|
+
draft?: boolean; /** Author name. Falls back to config.defaultAuthor when omitted. */
|
|
1716
|
+
author?: string;
|
|
1717
|
+
};
|
|
1718
|
+
/**
|
|
1719
|
+
* Fields computed by the pipeline (not authored in frontmatter).
|
|
1720
|
+
*
|
|
1721
|
+
* @example
|
|
1722
|
+
* ```ts
|
|
1723
|
+
* { slug: "hello", readingTime: 1, contentId: "hello", status: "published", wordCount: 42 }
|
|
1724
|
+
* ```
|
|
1725
|
+
*/
|
|
1726
|
+
type ComputedFields = {
|
|
1727
|
+
/** Article directory name. */slug: string; /** Reading time in minutes (ceiling, minimum 1). */
|
|
1728
|
+
readingTime: number; /** Stable content identifier (slug by default). */
|
|
1729
|
+
contentId: string; /** Derived publication status. */
|
|
1730
|
+
status: "published" | "draft"; /** Word count from the source body. */
|
|
1731
|
+
wordCount: number;
|
|
1732
|
+
};
|
|
1733
|
+
/**
|
|
1734
|
+
* A fully processed, render-ready article.
|
|
1735
|
+
*
|
|
1736
|
+
* @example
|
|
1737
|
+
* ```ts
|
|
1738
|
+
* { frontmatter, computed, html: "<p>…</p>", locale: "en", isFallback: false, url: "/en/hello/" }
|
|
1739
|
+
* ```
|
|
1740
|
+
*/
|
|
1741
|
+
type Article = {
|
|
1742
|
+
/** Parsed frontmatter. */frontmatter: Frontmatter; /** Pipeline-computed metadata. */
|
|
1743
|
+
computed: ComputedFields; /** Sanitized rendered HTML body. */
|
|
1744
|
+
html: string; /** Locale this Article instance represents (the requested locale, even on fallback). */
|
|
1745
|
+
locale: string; /** True when the default-locale file was used because the requested locale was missing. */
|
|
1746
|
+
isFallback: boolean; /** Canonical URL for this article in this locale. */
|
|
1747
|
+
url: string;
|
|
1748
|
+
};
|
|
1749
|
+
/**
|
|
1750
|
+
* Lightweight projection of Article for cards/lists.
|
|
1751
|
+
*
|
|
1752
|
+
* @example
|
|
1753
|
+
* ```ts
|
|
1754
|
+
* { contentId: "hello", status: "published", title: "Hello", date: "2026-01-15", description: "Intro", tags: [], readingTime: 1, url: "/en/hello/" }
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
type ArticleCard = {
|
|
1758
|
+
/** Stable content identifier. */contentId: string; /** Derived publication status. */
|
|
1759
|
+
status: "published" | "draft"; /** Article title. */
|
|
1760
|
+
title: string; /** ISO 8601 date string. */
|
|
1761
|
+
date: string; /** Short summary. */
|
|
1762
|
+
description: string; /** Topic tags. */
|
|
1763
|
+
tags: string[]; /** Reading time in minutes. */
|
|
1764
|
+
readingTime: number; /** Canonical URL for this article in this locale. */
|
|
1765
|
+
url: string;
|
|
1766
|
+
};
|
|
1767
|
+
/**
|
|
1768
|
+
* Notification-only events emitted by the content plugin.
|
|
1769
|
+
*
|
|
1770
|
+
* @example
|
|
1771
|
+
* ```ts
|
|
1772
|
+
* emit("content:ready", { locales: ["en"], articleCount: 3 });
|
|
1773
|
+
* ```
|
|
1774
|
+
*/
|
|
1775
|
+
type ContentEvents = {
|
|
1776
|
+
/** All articles loaded across locales. */"content:ready": {
|
|
1777
|
+
locales: readonly string[];
|
|
1778
|
+
articleCount: number;
|
|
1779
|
+
}; /** Article paths marked stale for dev rebuild. */
|
|
1780
|
+
"content:invalidated": {
|
|
1781
|
+
paths: readonly string[];
|
|
1782
|
+
};
|
|
1783
|
+
};
|
|
1784
|
+
/**
|
|
1785
|
+
* Kernel-free domain context handed to createContentApi by the wiring harness.
|
|
1786
|
+
* Carries ctx.state (mutable escape hatch), config, global, emit, and the
|
|
1787
|
+
* i18n-derived locale/url helpers — so api.ts stays free of createPlugin/ctx.
|
|
1788
|
+
*
|
|
1789
|
+
* @example
|
|
1790
|
+
* ```ts
|
|
1791
|
+
* const apiContext: ContentApiContext = { state, config, global, emit, locales, defaultLocale, articleToUrl };
|
|
1792
|
+
* ```
|
|
1793
|
+
*/
|
|
1794
|
+
type ContentApiContext = {
|
|
1795
|
+
/** Mutable plugin state (article cache + lazy processor). */state: State$1; /** Resolved plugin configuration. */
|
|
1796
|
+
config: Config$1; /** Global framework configuration (mode, etc.). */
|
|
1797
|
+
global: {
|
|
1798
|
+
mode: "production" | "development";
|
|
1799
|
+
}; /** Emit a registered content event. */
|
|
1800
|
+
emit: <K extends keyof ContentEvents>(event: K, payload: ContentEvents[K]) => void; /** Active locale codes from i18n. */
|
|
1801
|
+
locales: () => readonly string[]; /** Default locale code from i18n (fallback source). */
|
|
1802
|
+
defaultLocale: () => string; /** Build a canonical article URL for a locale + slug. */
|
|
1803
|
+
articleToUrl: (locale: string, slug: string) => string;
|
|
1804
|
+
};
|
|
1805
|
+
/**
|
|
1806
|
+
* Public API for the content plugin.
|
|
1807
|
+
*
|
|
1808
|
+
* @example
|
|
1809
|
+
* ```ts
|
|
1810
|
+
* const map = await app.content.loadAll();
|
|
1811
|
+
* ```
|
|
1812
|
+
*/
|
|
1813
|
+
type Api$1 = {
|
|
1814
|
+
/**
|
|
1815
|
+
* Load every article across every active locale, returning a locale-keyed
|
|
1816
|
+
* map of date-descending Article arrays. Emits content:ready.
|
|
1817
|
+
*/
|
|
1818
|
+
loadAll(): Promise<Map<string, Article[]>>;
|
|
1819
|
+
/**
|
|
1820
|
+
* Resolve and render a single article for one locale, with locale fallback.
|
|
1821
|
+
*
|
|
1822
|
+
* @param slug - Article directory name.
|
|
1823
|
+
* @param locale - Requested locale code.
|
|
1824
|
+
*/
|
|
1825
|
+
load(slug: string, locale: string): Promise<Article>;
|
|
1826
|
+
/**
|
|
1827
|
+
* Render a raw Markdown string to HTML through the full pipeline.
|
|
1828
|
+
*
|
|
1829
|
+
* @param md - Raw Markdown source.
|
|
1830
|
+
*/
|
|
1831
|
+
renderMarkdown(md: string): Promise<string>;
|
|
1832
|
+
/**
|
|
1833
|
+
* Mark file paths stale for incremental dev rebuilds. Emits content:invalidated.
|
|
1834
|
+
*
|
|
1835
|
+
* @param paths - File paths to invalidate.
|
|
1836
|
+
*/
|
|
1837
|
+
invalidate(paths: readonly string[]): void;
|
|
1838
|
+
/**
|
|
1839
|
+
* Project a full Article to a lightweight ArticleCard for list/grid rendering.
|
|
1544
1840
|
*
|
|
1545
|
-
* @param
|
|
1546
|
-
* @returns void
|
|
1547
|
-
* @example
|
|
1548
|
-
* kernel.processNav("/about");
|
|
1841
|
+
* @param article - The source article.
|
|
1549
1842
|
*/
|
|
1550
|
-
|
|
1843
|
+
articleToCard(article: Article): ArticleCard;
|
|
1844
|
+
};
|
|
1845
|
+
//#endregion
|
|
1846
|
+
//#region src/plugins/content/index.d.ts
|
|
1847
|
+
/**
|
|
1848
|
+
* Content plugin — Markdown pipeline: discovers files, parses frontmatter, renders
|
|
1849
|
+
* to sanitized HTML (rehype-sanitize unless `trustedContent`), and exposes a
|
|
1850
|
+
* locale-keyed Article model. Depends on i18n; emits `content:ready` and
|
|
1851
|
+
* `content:invalidated`.
|
|
1852
|
+
*
|
|
1853
|
+
* @example Point at a content directory and pick a Shiki theme
|
|
1854
|
+
* ```ts
|
|
1855
|
+
* const app = createApp({
|
|
1856
|
+
* pluginConfigs: {
|
|
1857
|
+
* content: {
|
|
1858
|
+
* contentDir: "./content",
|
|
1859
|
+
* shikiTheme: "github-dark",
|
|
1860
|
+
* defaultAuthor: "Ada Lovelace"
|
|
1861
|
+
* // trustedContent: true // ONLY for fully author-controlled Markdown — disables sanitize
|
|
1862
|
+
* }
|
|
1863
|
+
* }
|
|
1864
|
+
* });
|
|
1865
|
+
* ```
|
|
1866
|
+
*/
|
|
1867
|
+
declare const contentPlugin: import("@moku-labs/core").PluginInstance<"content", Config$1, State$1, Api$1, {
|
|
1868
|
+
"content:ready": {
|
|
1869
|
+
locales: readonly string[];
|
|
1870
|
+
articleCount: number;
|
|
1871
|
+
};
|
|
1872
|
+
"content:invalidated": {
|
|
1873
|
+
paths: readonly string[];
|
|
1874
|
+
};
|
|
1875
|
+
}> & Record<never, never>;
|
|
1876
|
+
declare namespace types_d_exports$2 {
|
|
1877
|
+
export { DataConfig, DataEntry, DataProvider, DataState, DataWriteSummary };
|
|
1878
|
+
}
|
|
1879
|
+
/**
|
|
1880
|
+
* @file data plugin — type definitions (Standard tier).
|
|
1881
|
+
*
|
|
1882
|
+
* The `data` plugin is the **agnostic data provider** for the SSG→DATA→SPA pattern.
|
|
1883
|
+
* It owns ONE thing: the contract `page path → persisted JSON file`. It knows
|
|
1884
|
+
* NOTHING about what the data *is* — no domain types appear here. A route decides
|
|
1885
|
+
* its own data shape (`load`'s return) and its own validation (`route.parse`).
|
|
1886
|
+
*
|
|
1887
|
+
* - **Node (build):** `write(entries)` persists one JSON file per page, keyed by
|
|
1888
|
+
* the page's URL via {@link DataProvider.fileFor}. `build` supplies the entries
|
|
1889
|
+
* (it already expanded the routes), so there is no duplicate expansion here.
|
|
1890
|
+
* - **Browser (runtime):** `at(path)` fetches + caches that file as `unknown`; the
|
|
1891
|
+
* route's `parse` validates it into the route's data type before `render`.
|
|
1892
|
+
*
|
|
1893
|
+
* The Node-only file-writing code (`node:fs`) is isolated behind a lazy `import()`
|
|
1894
|
+
* inside `write()`, so composing `data` in a browser app keeps the bundle free of
|
|
1895
|
+
* `node:*`.
|
|
1896
|
+
*/
|
|
1897
|
+
/**
|
|
1898
|
+
* Configuration for {@link dataPlugin}. All fields have defaults (see `./config`).
|
|
1899
|
+
*
|
|
1900
|
+
* @example
|
|
1901
|
+
* ```ts
|
|
1902
|
+
* const cfg: DataConfig = { outputDir: "_data", baseUrl: "/_data/" };
|
|
1903
|
+
* ```
|
|
1904
|
+
*/
|
|
1905
|
+
type DataConfig = {
|
|
1551
1906
|
/**
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
* @returns void
|
|
1555
|
-
* @example
|
|
1556
|
-
* kernel.scan();
|
|
1907
|
+
* WRITE side (Node): output subdir relative to the build `outDir`, a filesystem
|
|
1908
|
+
* path where `write()` persists the per-page JSON. Default `"_data"`.
|
|
1557
1909
|
*/
|
|
1558
|
-
|
|
1910
|
+
outputDir: string;
|
|
1559
1911
|
/**
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1563
|
-
* @example
|
|
1564
|
-
* kernel.dispose();
|
|
1912
|
+
* READ side (browser): site-root-relative URL the client fetches the per-page
|
|
1913
|
+
* JSON from. A different domain from {@link DataConfig.outputDir} (a filesystem
|
|
1914
|
+
* path); keep consistent (`"/" + trim(outputDir) + "/"`). Default `"/_data/"`.
|
|
1565
1915
|
*/
|
|
1566
|
-
|
|
1916
|
+
baseUrl: string;
|
|
1917
|
+
};
|
|
1918
|
+
/** One page's data to persist — `build` produces these from its route expansion. */
|
|
1919
|
+
interface DataEntry {
|
|
1920
|
+
/** The page's URL path (e.g. `/en/hello/`); maps to a file via {@link DataProvider.fileFor}. */
|
|
1921
|
+
path: string;
|
|
1922
|
+
/** The serializable data for this page (the route's `load`/projection output). */
|
|
1923
|
+
data: unknown;
|
|
1567
1924
|
}
|
|
1568
|
-
/**
|
|
1569
|
-
interface
|
|
1570
|
-
/**
|
|
1571
|
-
|
|
1572
|
-
/**
|
|
1573
|
-
|
|
1574
|
-
/** The
|
|
1575
|
-
|
|
1576
|
-
/** Teardown handle for the attached router listeners (null when detached). */
|
|
1577
|
-
destroyRouter: (() => void) | null;
|
|
1578
|
-
/** Whether the browser runtime has been booted. */
|
|
1579
|
-
started: boolean;
|
|
1580
|
-
/** The single shared SPA kernel instance (null until onInit builds it). */
|
|
1581
|
-
kernel: SpaKernel | null;
|
|
1925
|
+
/** Summary returned by {@link DataProvider.write} and cached in state. */
|
|
1926
|
+
interface DataWriteSummary {
|
|
1927
|
+
/** Number of per-page JSON files written. */
|
|
1928
|
+
fileCount: number;
|
|
1929
|
+
/** Total bytes written across all files. */
|
|
1930
|
+
bytes: number;
|
|
1931
|
+
/** The written file paths, relative to the build `outDir`. */
|
|
1932
|
+
files: string[];
|
|
1582
1933
|
}
|
|
1583
|
-
/**
|
|
1584
|
-
|
|
1934
|
+
/**
|
|
1935
|
+
* Internal data state. `lastWrite` records the most recent `write()` (Node);
|
|
1936
|
+
* `cache` memoizes fetched per-path data (browser, lazy). Both empty until their
|
|
1937
|
+
* respective side first runs.
|
|
1938
|
+
*/
|
|
1939
|
+
interface DataState {
|
|
1940
|
+
/** Result of the last `write()`, or `null` if it has not run yet (Node). */
|
|
1941
|
+
lastWrite: DataWriteSummary | null;
|
|
1942
|
+
/** Per-path fetched data, cached after the first `at(path)` (browser). */
|
|
1943
|
+
cache: Map<string, unknown>;
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Public API mounted at `app.data` — the agnostic data provider. `write()` is the
|
|
1947
|
+
* Node persist side; `at()` is the browser read side; `urlFor`/`fileFor` are the
|
|
1948
|
+
* pure URL convention shared by both so the written file and fetched URL can never
|
|
1949
|
+
* drift.
|
|
1950
|
+
*
|
|
1951
|
+
* @example
|
|
1952
|
+
* ```ts
|
|
1953
|
+
* // Node build (build supplies the entries it already expanded):
|
|
1954
|
+
* await app.data.write([{ path: "/en/hello/", data: article }]);
|
|
1955
|
+
*
|
|
1956
|
+
* // Browser (inside spa nav): fetch the page's data, then route.parse validates it:
|
|
1957
|
+
* const raw = await app.data.at("/en/hello/"); // unknown | null
|
|
1958
|
+
* ```
|
|
1959
|
+
*/
|
|
1960
|
+
type DataProvider = {
|
|
1585
1961
|
/**
|
|
1586
|
-
*
|
|
1962
|
+
* READ (browser) — fetch (and cache) the persisted data for a page path from
|
|
1963
|
+
* `config.baseUrl`. Returns the raw parsed JSON as `unknown` (the caller's
|
|
1964
|
+
* `route.parse` validates it), or `null` if the fetch/parse fails.
|
|
1587
1965
|
*
|
|
1588
|
-
* @param
|
|
1589
|
-
* @returns
|
|
1590
|
-
* @example
|
|
1591
|
-
* app.spa.register(counter);
|
|
1966
|
+
* @param path - The page URL path (e.g. `/en/hello/`).
|
|
1967
|
+
* @returns The page's raw data, or `null` on failure.
|
|
1592
1968
|
*/
|
|
1593
|
-
|
|
1969
|
+
at(path: string): Promise<unknown | null>;
|
|
1594
1970
|
/**
|
|
1595
|
-
*
|
|
1971
|
+
* WRITE (Node) — persist one JSON file per entry, keyed by page path via
|
|
1972
|
+
* {@link DataProvider.fileFor}. Called by `build` after it expands routes (no
|
|
1973
|
+
* duplicate expansion). Lazily loads its `node:fs` writer, so it never
|
|
1974
|
+
* contaminates a browser bundle.
|
|
1596
1975
|
*
|
|
1597
|
-
* @param
|
|
1598
|
-
* @
|
|
1599
|
-
* @
|
|
1600
|
-
*
|
|
1976
|
+
* @param entries - The per-page data to persist.
|
|
1977
|
+
* @param options - Optional overrides.
|
|
1978
|
+
* @param options.outDir - Build output directory to write under (default `./dist`).
|
|
1979
|
+
* @returns A summary of the written files.
|
|
1601
1980
|
*/
|
|
1602
|
-
|
|
1981
|
+
write(entries: readonly DataEntry[], options?: {
|
|
1982
|
+
outDir?: string;
|
|
1983
|
+
}): Promise<DataWriteSummary>;
|
|
1603
1984
|
/**
|
|
1604
|
-
*
|
|
1985
|
+
* PURE — the browser fetch URL for a page path (e.g. `/en/hello/` →
|
|
1986
|
+
* `/_data/en/hello/index.json`). Shared with {@link DataProvider.fileFor}.
|
|
1605
1987
|
*
|
|
1606
|
-
* @
|
|
1607
|
-
* @
|
|
1608
|
-
* const url = app.spa.current(); // "/about"
|
|
1988
|
+
* @param path - The page URL path.
|
|
1989
|
+
* @returns The site-root-relative data URL.
|
|
1609
1990
|
*/
|
|
1610
|
-
|
|
1991
|
+
urlFor(path: string): string;
|
|
1992
|
+
/**
|
|
1993
|
+
* PURE — the `outDir`-relative file path for a page path (e.g. `/en/hello/` →
|
|
1994
|
+
* `_data/en/hello/index.json`). Shared with {@link DataProvider.urlFor}.
|
|
1995
|
+
*
|
|
1996
|
+
* @param path - The page URL path.
|
|
1997
|
+
* @returns The output-relative file path.
|
|
1998
|
+
*/
|
|
1999
|
+
fileFor(path: string): string;
|
|
1611
2000
|
};
|
|
1612
|
-
|
|
2001
|
+
//#endregion
|
|
2002
|
+
//#region src/plugins/data/index.d.ts
|
|
2003
|
+
/**
|
|
2004
|
+
* Data plugin — the agnostic data provider. Mounts `write(entries)` (Node persist),
|
|
2005
|
+
* `at(path)` (browser read), and the pure `urlFor`/`fileFor` convention at `app.data`.
|
|
2006
|
+
*
|
|
2007
|
+
* @example
|
|
2008
|
+
* ```ts
|
|
2009
|
+
* // Node build: `build` calls app.data.write(...) during its pages phase when
|
|
2010
|
+
* // router.mode !== "ssg". Just compose the plugin:
|
|
2011
|
+
* const app = createApp({
|
|
2012
|
+
* plugins: [dataPlugin, contentPlugin, buildPlugin],
|
|
2013
|
+
* pluginConfigs: { content: { contentDir: "./content" }, router: { routes, mode: "hybrid" } }
|
|
2014
|
+
* });
|
|
2015
|
+
* await app.start();
|
|
2016
|
+
* await app.build.run(); // writes HTML + per-page data sidecars
|
|
2017
|
+
*
|
|
2018
|
+
* // Browser app: compose `dataPlugin` too; spa fetches via app.data.at(path) on nav.
|
|
2019
|
+
* ```
|
|
2020
|
+
*/
|
|
2021
|
+
declare const dataPlugin: import("@moku-labs/core").PluginInstance<"data", DataConfig, DataState, DataProvider, {}> & Record<never, never>;
|
|
2022
|
+
declare namespace types_d_exports$3 {
|
|
1613
2023
|
export { Api, Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State, WranglerErrorKind };
|
|
1614
2024
|
}
|
|
1615
2025
|
/**
|
|
@@ -1824,72 +2234,6 @@ declare const deployPlugin: import("@moku-labs/core").PluginInstance<"deploy", C
|
|
|
1824
2234
|
};
|
|
1825
2235
|
}> & Record<never, never>;
|
|
1826
2236
|
//#endregion
|
|
1827
|
-
//#region src/plugins/build/index.d.ts
|
|
1828
|
-
/**
|
|
1829
|
-
* Build plugin — the static-site-generation orchestrator. Renders every route to
|
|
1830
|
-
* `outDir`, and optionally emits feeds, a sitemap, optimized images, and OG
|
|
1831
|
-
* images. Depends on site, i18n, content, router, and head; emits `build:phase`.
|
|
1832
|
-
*
|
|
1833
|
-
* @example Configure the production build
|
|
1834
|
-
* ```ts
|
|
1835
|
-
* const app = createApp({
|
|
1836
|
-
* pluginConfigs: {
|
|
1837
|
-
* build: {
|
|
1838
|
-
* outDir: "dist",
|
|
1839
|
-
* minify: true,
|
|
1840
|
-
* feeds: true,
|
|
1841
|
-
* sitemap: true,
|
|
1842
|
-
* images: true,
|
|
1843
|
-
* ogImage: false // or an object to enable + configure OG-image generation
|
|
1844
|
-
* }
|
|
1845
|
-
* }
|
|
1846
|
-
* });
|
|
1847
|
-
* ```
|
|
1848
|
-
*/
|
|
1849
|
-
declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Config$1, State$1, Api$1, {
|
|
1850
|
-
"build:phase": {
|
|
1851
|
-
phase: PhaseName;
|
|
1852
|
-
status: "start" | "done";
|
|
1853
|
-
durationMs?: number;
|
|
1854
|
-
};
|
|
1855
|
-
"build:complete": {
|
|
1856
|
-
outDir: string;
|
|
1857
|
-
pageCount: number;
|
|
1858
|
-
durationMs: number;
|
|
1859
|
-
};
|
|
1860
|
-
}> & Record<never, never>;
|
|
1861
|
-
//#endregion
|
|
1862
|
-
//#region src/plugins/content/index.d.ts
|
|
1863
|
-
/**
|
|
1864
|
-
* Content plugin — Markdown pipeline: discovers files, parses frontmatter, renders
|
|
1865
|
-
* to sanitized HTML (rehype-sanitize unless `trustedContent`), and exposes a
|
|
1866
|
-
* locale-keyed Article model. Depends on i18n; emits `content:ready` and
|
|
1867
|
-
* `content:invalidated`.
|
|
1868
|
-
*
|
|
1869
|
-
* @example Point at a content directory and pick a Shiki theme
|
|
1870
|
-
* ```ts
|
|
1871
|
-
* const app = createApp({
|
|
1872
|
-
* pluginConfigs: {
|
|
1873
|
-
* content: {
|
|
1874
|
-
* contentDir: "./content",
|
|
1875
|
-
* shikiTheme: "github-dark",
|
|
1876
|
-
* defaultAuthor: "Ada Lovelace"
|
|
1877
|
-
* // trustedContent: true // ONLY for fully author-controlled Markdown — disables sanitize
|
|
1878
|
-
* }
|
|
1879
|
-
* }
|
|
1880
|
-
* });
|
|
1881
|
-
* ```
|
|
1882
|
-
*/
|
|
1883
|
-
declare const contentPlugin: import("@moku-labs/core").PluginInstance<"content", Config$3, State$3, Api$3, {
|
|
1884
|
-
"content:ready": {
|
|
1885
|
-
locales: readonly string[];
|
|
1886
|
-
articleCount: number;
|
|
1887
|
-
};
|
|
1888
|
-
"content:invalidated": {
|
|
1889
|
-
paths: readonly string[];
|
|
1890
|
-
};
|
|
1891
|
-
}> & Record<never, never>;
|
|
1892
|
-
//#endregion
|
|
1893
2237
|
//#region src/plugins/head/primitives.d.ts
|
|
1894
2238
|
/**
|
|
1895
2239
|
* Build a `<meta name=… content=…>` descriptor.
|
|
@@ -1988,7 +2332,7 @@ declare function buildArticleHead(articleMeta: ArticleMeta, canonicalUrl: string
|
|
|
1988
2332
|
* });
|
|
1989
2333
|
* ```
|
|
1990
2334
|
*/
|
|
1991
|
-
declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Config$
|
|
2335
|
+
declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Config$3, State$3, Api$3, {}> & {
|
|
1992
2336
|
meta: typeof meta;
|
|
1993
2337
|
og: typeof og;
|
|
1994
2338
|
twitter: typeof twitter;
|
|
@@ -2150,42 +2494,82 @@ declare const spaPlugin: import("@moku-labs/core").PluginInstance<"spa", SpaConf
|
|
|
2150
2494
|
};
|
|
2151
2495
|
}> & Record<never, never>;
|
|
2152
2496
|
//#endregion
|
|
2497
|
+
//#region src/plugins/env/providers.d.ts
|
|
2498
|
+
/**
|
|
2499
|
+
* A zero-dependency `.env`-style provider that re-reads and re-parses the file
|
|
2500
|
+
* from disk on every `load()`. Missing file resolves to `{}` (optional
|
|
2501
|
+
* overrides). Strips a single outer quote pair; does not strip trailing inline
|
|
2502
|
+
* comments on unquoted values.
|
|
2503
|
+
*
|
|
2504
|
+
* @param path - Path to the dotenv file. Defaults to `.env.local`.
|
|
2505
|
+
* @returns An {@link EnvProvider} named `dotenv:<path>` that reads fresh per call.
|
|
2506
|
+
* @example
|
|
2507
|
+
* ```ts
|
|
2508
|
+
* const provider = dotenv(".env.local");
|
|
2509
|
+
* provider.load(); // { PUBLIC_API_URL: "/api", ... }
|
|
2510
|
+
* ```
|
|
2511
|
+
*/
|
|
2512
|
+
declare function dotenv(path?: string): EnvProvider;
|
|
2513
|
+
/**
|
|
2514
|
+
* A provider that returns a shallow copy of `process.env` at `load()` time.
|
|
2515
|
+
*
|
|
2516
|
+
* @returns An {@link EnvProvider} named `process-env`.
|
|
2517
|
+
* @example
|
|
2518
|
+
* ```ts
|
|
2519
|
+
* const provider = processEnv();
|
|
2520
|
+
* provider.load().HOME; // current process value
|
|
2521
|
+
* ```
|
|
2522
|
+
*/
|
|
2523
|
+
declare function processEnv(): EnvProvider;
|
|
2524
|
+
/**
|
|
2525
|
+
* A provider that reads live, per-request Cloudflare bindings from
|
|
2526
|
+
* `globalThis.__CLOUDFLARE_ENV__` at `load()` time (`?? {}` when absent). Never
|
|
2527
|
+
* caches the binding object; the consumer owns the global's request lifecycle.
|
|
2528
|
+
*
|
|
2529
|
+
* @returns An {@link EnvProvider} named `cloudflare`.
|
|
2530
|
+
* @example
|
|
2531
|
+
* ```ts
|
|
2532
|
+
* globalThis.__CLOUDFLARE_ENV__ = env; // set by the request handler
|
|
2533
|
+
* const provider = cloudflareBindings();
|
|
2534
|
+
* provider.load(); // reads the current request's bindings
|
|
2535
|
+
* ```
|
|
2536
|
+
*/
|
|
2537
|
+
declare function cloudflareBindings(): EnvProvider;
|
|
2538
|
+
//#endregion
|
|
2153
2539
|
//#region src/index.d.ts
|
|
2154
2540
|
/**
|
|
2155
2541
|
* Create and initialize a `@moku-labs/web` application — the Layer-3 entry point.
|
|
2156
2542
|
* Your overrides are merged over the framework defaults through the 4-level config
|
|
2157
2543
|
* cascade, every plugin's lifecycle runs, and a fully-typed, frozen app is returned.
|
|
2158
2544
|
*
|
|
2545
|
+
* The defaults are the isomorphic plugin set (`site`, `i18n`, `router`, `head`,
|
|
2546
|
+
* `spa` + `log`/`env` core). Add the Node-only plugins for an SSG build:
|
|
2547
|
+
* `createApp({ plugins: [contentPlugin, buildPlugin, deployPlugin] })`.
|
|
2548
|
+
*
|
|
2159
2549
|
* @param options - Optional configuration:
|
|
2160
|
-
* - `pluginConfigs` — per-plugin overrides, keyed by plugin name
|
|
2161
|
-
* (`site`, `i18n`, `router`, `content`, `head`, `build`, `spa`, `deploy`, `env`).
|
|
2550
|
+
* - `pluginConfigs` — per-plugin overrides, keyed by plugin name.
|
|
2162
2551
|
* - `config` — global framework config (e.g. `{ mode: "development" }`).
|
|
2163
|
-
* - `plugins` — extra
|
|
2552
|
+
* - `plugins` — extra plugins (Node-only built-ins or your own) merged into the app and its type.
|
|
2164
2553
|
* - `onReady` / `onError` / `onStart` / `onStop` — lifecycle callbacks.
|
|
2165
2554
|
* @returns The initialized app: `start()`, `stop()`, every plugin's API, and `log`.
|
|
2166
2555
|
* @example
|
|
2167
2556
|
* ```ts
|
|
2557
|
+
* // Node SSG build — add the node-only plugins:
|
|
2168
2558
|
* const app = createApp({
|
|
2559
|
+
* plugins: [contentPlugin, buildPlugin, deployPlugin],
|
|
2169
2560
|
* pluginConfigs: {
|
|
2170
2561
|
* site: { name: "My Blog", url: "https://blog.dev", author: "Ada", description: "Notes" },
|
|
2171
2562
|
* router: { routes: defineRoutes({ home: route("/"), post: route("/blog/{slug}/") }) }
|
|
2172
2563
|
* }
|
|
2173
2564
|
* });
|
|
2174
2565
|
* await app.start();
|
|
2566
|
+
* await app.build.run();
|
|
2175
2567
|
* ```
|
|
2176
2568
|
*/
|
|
2177
2569
|
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, {}> & {
|
|
2178
2570
|
route: typeof route;
|
|
2179
2571
|
defineRoutes: typeof defineRoutes;
|
|
2180
|
-
}) | (import("@moku-labs/core").PluginInstance<"
|
|
2181
|
-
"content:ready": {
|
|
2182
|
-
locales: readonly string[];
|
|
2183
|
-
articleCount: number;
|
|
2184
|
-
};
|
|
2185
|
-
"content:invalidated": {
|
|
2186
|
-
paths: readonly string[];
|
|
2187
|
-
};
|
|
2188
|
-
}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"head", Config$2, State$2, Api$2, {}> & {
|
|
2572
|
+
}) | (import("@moku-labs/core").PluginInstance<"head", Config$3, State$3, Api$3, {}> & {
|
|
2189
2573
|
meta: typeof meta;
|
|
2190
2574
|
og: typeof og;
|
|
2191
2575
|
twitter: typeof twitter;
|
|
@@ -2194,18 +2578,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2194
2578
|
hreflang: typeof hreflang;
|
|
2195
2579
|
feedLink: typeof feedLink;
|
|
2196
2580
|
buildArticleHead: typeof buildArticleHead;
|
|
2197
|
-
}) | (import("@moku-labs/core").PluginInstance<"
|
|
2198
|
-
"build:phase": {
|
|
2199
|
-
phase: PhaseName;
|
|
2200
|
-
status: "start" | "done";
|
|
2201
|
-
durationMs?: number;
|
|
2202
|
-
};
|
|
2203
|
-
"build:complete": {
|
|
2204
|
-
outDir: string;
|
|
2205
|
-
pageCount: number;
|
|
2206
|
-
durationMs: number;
|
|
2207
|
-
};
|
|
2208
|
-
}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
|
|
2581
|
+
}) | (import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
|
|
2209
2582
|
"spa:navigate": {
|
|
2210
2583
|
from: string;
|
|
2211
2584
|
to: string;
|
|
@@ -2221,25 +2594,10 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2221
2594
|
name: string;
|
|
2222
2595
|
el: Element;
|
|
2223
2596
|
};
|
|
2224
|
-
}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"deploy", Config, State, Api, {
|
|
2225
|
-
"deploy:complete": {
|
|
2226
|
-
url: string;
|
|
2227
|
-
deploymentId: string;
|
|
2228
|
-
branch: string;
|
|
2229
|
-
durationMs: number;
|
|
2230
|
-
};
|
|
2231
2597
|
}> & 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, {}> & {
|
|
2232
2598
|
route: typeof route;
|
|
2233
2599
|
defineRoutes: typeof defineRoutes;
|
|
2234
|
-
}) | (import("@moku-labs/core").PluginInstance<"
|
|
2235
|
-
"content:ready": {
|
|
2236
|
-
locales: readonly string[];
|
|
2237
|
-
articleCount: number;
|
|
2238
|
-
};
|
|
2239
|
-
"content:invalidated": {
|
|
2240
|
-
paths: readonly string[];
|
|
2241
|
-
};
|
|
2242
|
-
}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"head", Config$2, State$2, Api$2, {}> & {
|
|
2600
|
+
}) | (import("@moku-labs/core").PluginInstance<"head", Config$3, State$3, Api$3, {}> & {
|
|
2243
2601
|
meta: typeof meta;
|
|
2244
2602
|
og: typeof og;
|
|
2245
2603
|
twitter: typeof twitter;
|
|
@@ -2248,18 +2606,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2248
2606
|
hreflang: typeof hreflang;
|
|
2249
2607
|
feedLink: typeof feedLink;
|
|
2250
2608
|
buildArticleHead: typeof buildArticleHead;
|
|
2251
|
-
}) | (import("@moku-labs/core").PluginInstance<"
|
|
2252
|
-
"build:phase": {
|
|
2253
|
-
phase: PhaseName;
|
|
2254
|
-
status: "start" | "done";
|
|
2255
|
-
durationMs?: number;
|
|
2256
|
-
};
|
|
2257
|
-
"build:complete": {
|
|
2258
|
-
outDir: string;
|
|
2259
|
-
pageCount: number;
|
|
2260
|
-
durationMs: number;
|
|
2261
|
-
};
|
|
2262
|
-
}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
|
|
2609
|
+
}) | (import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
|
|
2263
2610
|
"spa:navigate": {
|
|
2264
2611
|
from: string;
|
|
2265
2612
|
to: string;
|
|
@@ -2275,13 +2622,6 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2275
2622
|
name: string;
|
|
2276
2623
|
el: Element;
|
|
2277
2624
|
};
|
|
2278
|
-
}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"deploy", Config, State, Api, {
|
|
2279
|
-
"deploy:complete": {
|
|
2280
|
-
url: string;
|
|
2281
|
-
deploymentId: string;
|
|
2282
|
-
branch: string;
|
|
2283
|
-
durationMs: number;
|
|
2284
|
-
};
|
|
2285
2625
|
}> & Record<never, never>) | ExtraPlugins[number], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>, import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>]>>;
|
|
2286
2626
|
/**
|
|
2287
2627
|
* Create a custom plugin bound to this framework's `Config`/`Events` and core
|
|
@@ -2300,4 +2640,4 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2300
2640
|
*/
|
|
2301
2641
|
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>]>>;
|
|
2302
2642
|
//#endregion
|
|
2303
|
-
export { types_d_exports as Build, types_d_exports$1 as Content, types_d_exports$2 as
|
|
2643
|
+
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, createPlugin, dataPlugin, defineRoutes, deployPlugin, dotenv, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, processEnv, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|