@moku-labs/web 0.1.0-alpha.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +64 -51
  3. package/dist/chunk-D7D4PA-g.mjs +13 -0
  4. package/dist/index.cjs +5972 -113
  5. package/dist/index.d.cts +2078 -106
  6. package/dist/index.d.mts +2078 -106
  7. package/dist/index.mjs +5859 -33
  8. package/package.json +65 -65
  9. package/dist/bin/moku.cjs +0 -1383
  10. package/dist/bin/moku.d.cts +0 -1
  11. package/dist/bin/moku.d.mts +0 -1
  12. package/dist/bin/moku.mjs +0 -1383
  13. package/dist/chunk-DQk6qfdC.mjs +0 -18
  14. package/dist/factory-CMOo4n6a.cjs +0 -1722
  15. package/dist/factory-DRFGSslp.d.mts +0 -114
  16. package/dist/factory-DiKypQqs.mjs +0 -1602
  17. package/dist/factory-k-YoScgB.d.cts +0 -114
  18. package/dist/index-DH3jlpNi.d.mts +0 -503
  19. package/dist/index-DaY7vTuo.d.cts +0 -503
  20. package/dist/plugins/head/build.cjs +0 -35
  21. package/dist/plugins/head/build.d.cts +0 -17
  22. package/dist/plugins/head/build.d.mts +0 -17
  23. package/dist/plugins/head/build.mjs +0 -27
  24. package/dist/plugins/spa/index.cjs +0 -26
  25. package/dist/plugins/spa/index.d.cts +0 -30
  26. package/dist/plugins/spa/index.d.mts +0 -30
  27. package/dist/plugins/spa/index.mjs +0 -24
  28. package/dist/primitives-BYUp6kae.cjs +0 -100
  29. package/dist/primitives-DKgZfRAO.d.mts +0 -71
  30. package/dist/primitives-Dhko-oLM.mjs +0 -58
  31. package/dist/primitives-yZqQkOVR.d.cts +0 -71
  32. package/dist/project-B8z4jeMC.cjs +0 -1383
  33. package/dist/project-guCYpUeD.mjs +0 -1244
  34. package/dist/test.cjs +0 -82
  35. package/dist/test.d.cts +0 -61
  36. package/dist/test.d.mts +0 -61
  37. package/dist/test.mjs +0 -79
  38. package/dist/wrangler-BlZWVmX9.mjs +0 -369
  39. package/dist/wrangler-Bomk9mU-.cjs +0 -423
package/dist/index.d.cts CHANGED
@@ -1,136 +1,2108 @@
1
- import { A as ContentState, B as Events, C as RouterApi, D as Article, E as types_d_exports$7, F as types_d_exports$5, G as EnvState, H as EnvApi, I as SiteApi, J as LogApi, L as SiteState, M as I18nApi, N as I18nConfig, O as ContentApi, P as I18nState, R as types_d_exports$8, S as RouteSpec, T as RouterState, V as createPlugin, X as types_d_exports$6, Y as LogState, _ as BuildApi, a as i18n, b as types_d_exports, c as deploy, d as RouteSpecMap, f as WebAppConfig, g as types_d_exports$2, h as DeployState, i as log, j as types_d_exports$1, k as ContentConfig, l as content, m as DeployConfig, n as router, o as head, p as DeployApi, q as types_d_exports$3, r as route, s as env, t as site, u as build, v as BuildConfig, w as RouterConfig, x as RouteBuilder, y as BuildState, z as Config } from "./index-DaY7vTuo.cjs";
2
- import { a as meta, d as HeadPluginConfig, f as HeadState, i as jsonLd, l as HeadApi, m as types_d_exports$4, n as feedLink, o as og, r as hreflang, s as twitter, t as canonical } from "./primitives-yZqQkOVR.cjs";
3
- import { a as SpaConfig, i as SpaApi, n as ComponentDef, o as SpaState, r as ComponentHooks, s as types_d_exports$9, t as createComponent } from "./factory-k-YoScgB.cjs";
4
- import { spa } from "./plugins/spa/index.cjs";
5
- import * as _moku_labs_core0 from "@moku-labs/core";
1
+ import { ComponentChildren, VNode } from "preact";
2
+ import { Pluggable, Processor } from "unified";
3
+ import { EmitFn } from "@moku-labs/core";
6
4
 
7
- //#region src/index.d.ts
8
- declare const kernelCreateApp: <const ExtraPlugins extends readonly _moku_labs_core0.AnyPluginInstance[] = readonly []>(options?: _moku_labs_core0.CreateAppOptions<Config, Events, (_moku_labs_core0.PluginInstance<"site", {
5
+ //#region \0rolldown/runtime.js
6
+ declare namespace types_d_exports$5 {
7
+ export { ExpectChain, LogApi, LogConfig, LogEntry, LogLevel, LogSink, LogState };
8
+ }
9
+ /**
10
+ * @file log plugin — type definitions skeleton.
11
+ *
12
+ * Core-plugin type surface: config, state, public API, and the supporting
13
+ * value/sink/assertion-chain types. These types are inferred onto the plugin
14
+ * via state.ts / api.ts; index.ts passes NO explicit generics.
15
+ */
16
+ /**
17
+ * Runtime mode for the log plugin. Selects which default sinks are installed at
18
+ * onInit. The in-memory trace sink is ALWAYS installed regardless of mode.
19
+ *
20
+ * - "test" — no console sink (keeps test output clean); trace only.
21
+ * - "silent" — no console sink (explicit quiet); trace only.
22
+ * - "dev" — console sink + trace.
23
+ * - "production" — console sink + trace.
24
+ */
25
+ type LogConfig = {
26
+ /** Sink-selection mode. Defaults to `production`. */mode: "test" | "dev" | "production" | "silent";
27
+ };
28
+ /** Severity level for a log entry. */
29
+ type LogLevel = "debug" | "info" | "warn" | "error";
30
+ /**
31
+ * A single recorded log entry.
32
+ */
33
+ type LogEntry = {
34
+ /** Severity level. */level: LogLevel; /** Event identifier (free-form string; convention: `domain:action`). */
35
+ event: string; /** Optional structured payload associated with the event. */
36
+ data?: unknown; /** Capture timestamp in epoch milliseconds (`Date.now()` at append time). */
37
+ ts: number; /** Optional originating plugin name. Reserved for future enrichment. */
38
+ plugin?: string;
39
+ };
40
+ /**
41
+ * Pluggable output target. Implement this to add console/file/JSON/etc. sinks
42
+ * WITHOUT changing the log API. Each logged entry is passed to `write` once,
43
+ * in registration order.
44
+ */
45
+ type LogSink = {
46
+ /**
47
+ * Write a single entry to this sink.
48
+ *
49
+ * @param entry - The entry to emit.
50
+ */
51
+ write(entry: LogEntry): void;
52
+ };
53
+ /**
54
+ * Fluent event-trace assertion chain. Reads the live entries array on each call,
55
+ * so assertions reflect the trace state at call time (not chain-creation time).
56
+ * Every method returns the same chain for fluent chaining; assertion failures throw.
57
+ */
58
+ type ExpectChain = {
59
+ /**
60
+ * Assert at least one entry has `event`, optionally matching `partial` (subset match).
61
+ *
62
+ * @param event - Event name to find.
63
+ * @param partial - Optional partial data shape (subset-matched against `entry.data`).
64
+ * @returns The same chain for chaining.
65
+ * @throws {Error} `LogExpectAssertionError` when no matching entry exists.
66
+ */
67
+ toHaveEvent(event: string, partial?: Record<string, unknown>): ExpectChain;
68
+ /**
69
+ * Assert all of `events` appear in the trace in the given relative order
70
+ * (gaps allowed; later events must occur after earlier ones).
71
+ *
72
+ * @param events - Ordered list of event names.
73
+ * @returns The same chain for chaining.
74
+ * @throws {Error} `LogExpectAssertionError` when the ordering cannot be satisfied.
75
+ */
76
+ toHaveEventInOrder(events: string[]): ExpectChain;
77
+ /**
78
+ * Assert NO entry has `event` (optionally narrowed by `partial`).
79
+ *
80
+ * @param event - Event name that must be absent.
81
+ * @param partial - Optional partial data shape; only matching entries violate the assertion.
82
+ * @returns The same chain for chaining.
83
+ * @throws {Error} `LogExpectAssertionError` when a matching entry exists.
84
+ */
85
+ toNotHaveEvent(event: string, partial?: Record<string, unknown>): ExpectChain;
86
+ };
87
+ /**
88
+ * Internal mutable state for the log plugin. Created fresh per createApp construction.
89
+ */
90
+ type LogState = {
91
+ /** Append-only ordered trace of every logged entry (the in-memory trace sink's backing store). */entries: LogEntry[]; /** Registered output sinks. Each entry is written to every sink in order. */
92
+ sinks: LogSink[];
93
+ };
94
+ /** Public log API injected as `ctx.log` on every regular plugin and exposed as `app.log`. */
95
+ type LogApi = {
96
+ /**
97
+ * Append an `info` entry and fan it out to every sink.
98
+ *
99
+ * @param event - Event identifier (convention: `domain:action`).
100
+ * @param data - Optional structured payload.
101
+ */
102
+ info(event: string, data?: unknown): void;
103
+ /**
104
+ * Append a `debug` entry and fan it out to every sink.
105
+ *
106
+ * @param event - Event identifier (convention: `domain:action`).
107
+ * @param data - Optional structured payload.
108
+ */
109
+ debug(event: string, data?: unknown): void;
110
+ /**
111
+ * Append a `warn` entry and fan it out to every sink.
112
+ *
113
+ * @param event - Event identifier (convention: `domain:action`).
114
+ * @param data - Optional structured payload.
115
+ */
116
+ warn(event: string, data?: unknown): void;
117
+ /**
118
+ * Append an `error` entry. When `error` is provided, its `message`/`stack` are
119
+ * merged into `data` under an `error` key; otherwise `data` is recorded as-is.
120
+ *
121
+ * @param event - Event identifier (convention: `domain:action`).
122
+ * @param data - Optional structured payload.
123
+ * @param error - Optional originating Error to merge into `data`.
124
+ */
125
+ error(event: string, data?: unknown, error?: Error): void;
126
+ /**
127
+ * Return a frozen snapshot of the entries recorded so far (a fresh copy).
128
+ *
129
+ * @returns A readonly, frozen copy of the recorded entries.
130
+ */
131
+ trace(): readonly LogEntry[];
132
+ /**
133
+ * Return a fluent assertion chain bound to the live entries array.
134
+ *
135
+ * @returns A fresh {@link ExpectChain}.
136
+ */
137
+ expect(): ExpectChain;
138
+ /**
139
+ * Register an additional output sink at runtime.
140
+ *
141
+ * @param sink - The sink to add to the fan-out list.
142
+ */
143
+ addSink(sink: LogSink): void; /** Clear all recorded entries while keeping registered sinks. */
144
+ reset(): void;
145
+ };
146
+ declare namespace types_d_exports$3 {
147
+ export { EnvApi, EnvConfig, EnvProvider, EnvState, EnvVarSpec };
148
+ }
149
+ /**
150
+ * @file env plugin — public + boundary type definitions.
151
+ */
152
+ /**
153
+ * A source of raw environment values.
154
+ *
155
+ * Providers are walked in array order during resolution; the first provider to
156
+ * return a non-`undefined` (and non-empty-string) value for a key wins. `load()`
157
+ * is called once per resolution at `onInit` time — except for live, per-request
158
+ * sources (e.g. {@link cloudflareBindings}) which read fresh on every call.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const custom: EnvProvider = {
163
+ * name: "vault",
164
+ * load: () => ({ DB_URL: readVaultSecret("db") })
165
+ * };
166
+ * ```
167
+ */
168
+ interface EnvProvider {
169
+ /** Human-readable provider name, used in diagnostics and error messages. */
9
170
  name: string;
10
- url: string;
11
- author: string;
171
+ /**
172
+ * Reads this provider's current view of the environment.
173
+ *
174
+ * @returns A flat record of variable names to string values. Keys the
175
+ * provider cannot supply must be omitted or set to `undefined`.
176
+ */
177
+ load(): Record<string, string | undefined>;
178
+ }
179
+ /**
180
+ * Declares how a single environment variable is validated and exposed.
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * const port: EnvVarSpec = { public: false, required: false, default: "3000" };
185
+ * const apiBase: EnvVarSpec = { public: true }; // key must start with PUBLIC_
186
+ * const token: EnvVarSpec = { public: false, required: true, secret: true };
187
+ * ```
188
+ */
189
+ interface EnvVarSpec {
190
+ /**
191
+ * Whether the variable is safe to ship to the browser. When `true`, the key
192
+ * **must** start with {@link EnvConfig.publicPrefix} (cross-checked at
193
+ * `onInit`), and the variable is included in {@link EnvApi.getPublicMap}.
194
+ */
195
+ public: boolean;
196
+ /** Whether resolution fails if the variable is still undefined after defaults. */
197
+ required?: boolean;
198
+ /** Value applied when no provider supplies the variable. */
199
+ default?: string;
200
+ /**
201
+ * Marks the variable as a secret for documentation / tooling. Has no runtime
202
+ * effect on resolution, but secrets are never permitted to be `public`.
203
+ */
204
+ secret?: boolean;
205
+ }
206
+ /**
207
+ * Configuration for the {@link envPlugin} core plugin.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * createCoreConfig("web", {
212
+ * plugins: [envPlugin],
213
+ * pluginConfigs: {
214
+ * env: {
215
+ * schema: {
216
+ * PUBLIC_API_URL: { public: true, default: "/api" },
217
+ * SESSION_SECRET: { public: false, required: true, secret: true }
218
+ * }
219
+ * }
220
+ * }
221
+ * });
222
+ * ```
223
+ */
224
+ type EnvConfig = {
225
+ /** Per-variable validation + exposure rules, keyed by variable name. */schema: Record<string, EnvVarSpec>;
226
+ /**
227
+ * Ordered list of value sources. The first provider yielding a non-`undefined`
228
+ * (and non-empty-string) value for a key wins. The plugin's own spec default is
229
+ * `[]`; the framework layer supplies the working default `[dotenv(), processEnv()]`.
230
+ */
231
+ providers: EnvProvider[];
232
+ /**
233
+ * Prefix that public variable names must carry. Bidirectionally enforced at
234
+ * `onInit`. Framework default is `"PUBLIC_"`.
235
+ */
236
+ publicPrefix: string;
237
+ };
238
+ /**
239
+ * Internal env plugin state: the resolved variable table and its public subset.
240
+ * Both maps are populated and frozen (via `freezeMap`) during `onInit`.
241
+ *
242
+ * Exported only to type the `createState` / `api` / `validate` boundary —
243
+ * consumers use {@link EnvApi}, never `EnvState`.
244
+ */
245
+ interface EnvState {
246
+ /** All validated variables that resolved to a defined value (incl. defaults). */
247
+ resolved: Map<string, string>;
248
+ /** Subset of `resolved` where `schema[key].public === true`. */
249
+ publicMap: Map<string, string>;
250
+ }
251
+ /**
252
+ * The resolved-environment accessor mounted at `ctx.env`. Built by the plugin's
253
+ * `api` factory over `ctx.state` ({@link EnvState}).
254
+ *
255
+ * Available after `onInit` (i.e. inside any plugin's lifecycle and in consumer
256
+ * code). All accessors read from the frozen `resolved` / `publicMap` maps;
257
+ * mutation is impossible.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * const url = ctx.env.get("PUBLIC_API_URL"); // string | undefined
262
+ * const token = ctx.env.require("DEPLOY_TOKEN"); // string, or throws
263
+ * ```
264
+ */
265
+ type EnvApi = {
266
+ /**
267
+ * Reads a resolved variable.
268
+ *
269
+ * @param key - Variable name.
270
+ * @returns The value, or `undefined` if not present / not in schema.
271
+ */
272
+ get(key: string): string | undefined;
273
+ /**
274
+ * Reads a variable that must exist.
275
+ *
276
+ * @param key - Variable name.
277
+ * @returns The value.
278
+ * @throws {Error} If the variable is undefined.
279
+ */
280
+ require(key: string): string;
281
+ /**
282
+ * Tests presence of a resolved variable.
283
+ *
284
+ * @param key - Variable name.
285
+ * @returns `true` if a value is present.
286
+ */
287
+ has(key: string): boolean;
288
+ /**
289
+ * Returns all public variables as a frozen plain object — convenient for
290
+ * spreading into a serializable payload.
291
+ *
292
+ * @returns A frozen `Record` of public variable names to values.
293
+ */
294
+ getPublic(): Readonly<Record<string, string>>;
295
+ /**
296
+ * Returns the frozen map of public variables. This is the **sole** intended
297
+ * input to a build-time `define` injection: every entry is safe to inline
298
+ * into the browser bundle.
299
+ *
300
+ * @returns The frozen public map.
301
+ */
302
+ getPublicMap(): ReadonlyMap<string, string>;
303
+ };
304
+ //#endregion
305
+ //#region src/plugins/env/index.d.ts
306
+ /**
307
+ * Core plugin that resolves, validates, and freezes the environment at `onInit`,
308
+ * exposing a read-only accessor at `ctx.env`. No `onStart`/`onStop` — holds no resource.
309
+ *
310
+ * @example
311
+ * ```ts
312
+ * createApp({ pluginConfigs: { env: { schema: { PUBLIC_API_URL: { public: true } } } } });
313
+ * ```
314
+ */
315
+ declare const envPlugin: import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>;
316
+ //#endregion
317
+ //#region src/config.d.ts
318
+ /**
319
+ * Global framework configuration. Minimal by design — per-plugin config is
320
+ * resolved via `pluginConfigs`, not merged here.
321
+ */
322
+ type Config$7 = {
323
+ /** Runtime mode. Drives log sink defaults, content draft filtering, build minify. */mode: "production" | "development";
324
+ };
325
+ /**
326
+ * Framework event contract. Empty base — each plugin declares its own events
327
+ * via the `events` register callback (spec/14 §2).
328
+ */
329
+ type Events = {};
330
+ //#endregion
331
+ //#region src/plugins/site/types.d.ts
332
+ /**
333
+ * @file site plugin — public type definitions (Config + Api).
334
+ */
335
+ /**
336
+ * Configuration for the site plugin — global, frozen site metadata.
337
+ *
338
+ * All four fields are required at runtime. The framework ships empty-string
339
+ * defaults and `onInit` fails fast (at `createApp`) if `name` is blank or
340
+ * `url` is not a valid absolute URL. Consumers MUST supply real values via
341
+ * `pluginConfigs.site`.
342
+ *
343
+ * @example
344
+ * ```ts
345
+ * createApp({
346
+ * pluginConfigs: {
347
+ * site: {
348
+ * name: "My Blog",
349
+ * url: "https://blog.dev",
350
+ * author: "Alex",
351
+ * description: "A personal blog about web frameworks."
352
+ * }
353
+ * }
354
+ * });
355
+ * ```
356
+ */
357
+ type Config$6 = {
358
+ /** Human-readable site name. Used in feeds, og:site_name, and titles. MUST be non-empty. */name: string; /** Absolute base URL of the site, e.g. "https://blog.dev". MUST be a valid absolute URL (http/https). */
359
+ url: string; /** Default author/byline for the site. Used in feeds and article author meta. */
360
+ author: string; /** Short site description. Used in feeds, the default meta description, and og:description fallbacks. */
12
361
  description: string;
13
- }, SiteState, SiteApi, {}> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"i18n", I18nConfig, I18nState<readonly string[]>, I18nApi<readonly string[]>, {}> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"content", ContentConfig, ContentState, ContentApi, {
14
- 'content:ready': {
15
- articles: Map<string, Article[]>;
16
- };
17
- 'content:invalidated': {
18
- paths: string[];
362
+ };
363
+ /**
364
+ * Public API of the site plugin — read-only accessors over frozen global
365
+ * site metadata, plus canonical URL construction.
366
+ */
367
+ type Api$6 = {
368
+ /**
369
+ * Returns the configured site name.
370
+ *
371
+ * @returns {string} The human-readable site name from `config.name`.
372
+ * @example
373
+ * ```ts
374
+ * app.site.name(); // "My Blog"
375
+ * ```
376
+ */
377
+ name: () => string;
378
+ /**
379
+ * Returns the configured absolute base URL of the site.
380
+ *
381
+ * @returns {string} The base URL from `config.url`, e.g. "https://blog.dev".
382
+ * @example
383
+ * ```ts
384
+ * app.site.url(); // "https://blog.dev"
385
+ * ```
386
+ */
387
+ url: () => string;
388
+ /**
389
+ * Returns the configured site author/byline.
390
+ *
391
+ * @returns {string} The author from `config.author`.
392
+ * @example
393
+ * ```ts
394
+ * app.site.author(); // "Alex"
395
+ * ```
396
+ */
397
+ author: () => string;
398
+ /**
399
+ * Returns the configured site description.
400
+ *
401
+ * @returns {string} The description from `config.description`.
402
+ * @example
403
+ * ```ts
404
+ * app.site.description(); // "A personal blog about web frameworks."
405
+ * ```
406
+ */
407
+ description: () => string;
408
+ /**
409
+ * Joins a path against the configured base `url` to produce an absolute
410
+ * canonical URL. An empty path (or "/") returns the base URL unchanged.
411
+ *
412
+ * @param {string} path - Relative path for the page, e.g. "/about/" or "blog/post/".
413
+ * @returns {string} The absolute canonical URL, e.g. "https://blog.dev/about/".
414
+ * @example
415
+ * ```ts
416
+ * app.site.canonical("/about/"); // "https://blog.dev/about/"
417
+ * app.site.canonical("/"); // "https://blog.dev"
418
+ * ```
419
+ */
420
+ canonical: (path: string) => string;
421
+ };
422
+ //#endregion
423
+ //#region src/plugins/i18n/types.d.ts
424
+ /**
425
+ * @file i18n plugin — public type definitions (Config + Api).
426
+ */
427
+ /**
428
+ * i18n plugin configuration. Mirrors the legacy `I18nConfig` shape.
429
+ *
430
+ * `locales` and `defaultLocale` are required and validated in `onInit`. The
431
+ * optional maps default to empty objects so every lookup method is total —
432
+ * lookups return `undefined` on a miss and `t()` falls back to the key.
433
+ *
434
+ * @example
435
+ * ```ts
436
+ * {
437
+ * locales: ["en", "uk"],
438
+ * defaultLocale: "en",
439
+ * localeNames: { en: "English", uk: "Українська" },
440
+ * ogLocaleMap: { en: "en_US", uk: "uk_UA" },
441
+ * translations: { en: { "nav.home": "Home" }, uk: { "nav.home": "Головна" } }
442
+ * }
443
+ * ```
444
+ */
445
+ type Config$5 = {
446
+ readonly locales: readonly string[];
447
+ readonly defaultLocale: string;
448
+ readonly localeNames?: Record<string, string>;
449
+ readonly ogLocaleMap?: Record<string, string>;
450
+ readonly translations?: Record<string, Record<string, string>>;
451
+ };
452
+ /**
453
+ * Public API of the i18n plugin. Injected as `app.i18n` and reachable from
454
+ * other plugins via `ctx.require(i18nPlugin)`.
455
+ */
456
+ type Api$5 = {
457
+ /**
458
+ * Returns the configured supported locales in declared order.
459
+ *
460
+ * @returns The configured `locales` list (priority/display order).
461
+ * @example
462
+ * ```ts
463
+ * app.i18n.locales(); // ["en", "uk"]
464
+ * ```
465
+ */
466
+ locales(): readonly string[];
467
+ /**
468
+ * Returns the fallback locale used when a requested locale is absent.
469
+ *
470
+ * @returns The configured `defaultLocale`.
471
+ * @example
472
+ * ```ts
473
+ * app.i18n.defaultLocale(); // "en"
474
+ * ```
475
+ */
476
+ defaultLocale(): string;
477
+ /**
478
+ * Membership guard: whether `x` is one of the supported locales.
479
+ *
480
+ * @param x - Candidate locale code.
481
+ * @returns `true` if `x ∈ locales`, else `false`.
482
+ * @example
483
+ * ```ts
484
+ * app.i18n.isLocale("uk"); // true
485
+ * ```
486
+ */
487
+ isLocale(x: string): boolean;
488
+ /**
489
+ * Human-readable display name for a locale.
490
+ *
491
+ * @param locale - Locale code to look up.
492
+ * @returns The display name, or `undefined` if unmapped.
493
+ * @example
494
+ * ```ts
495
+ * app.i18n.localeName("uk"); // "Українська"
496
+ * ```
497
+ */
498
+ localeName(locale: string): string | undefined;
499
+ /**
500
+ * Open Graph `og:locale` value for a locale.
501
+ *
502
+ * @param locale - Locale code to look up.
503
+ * @returns The `og:locale` value (e.g. `"en_US"`), or `undefined` if unmapped.
504
+ * @example
505
+ * ```ts
506
+ * app.i18n.ogLocale("en"); // "en_US"
507
+ * ```
508
+ */
509
+ ogLocale(locale: string): string | undefined;
510
+ /**
511
+ * Translate `key` for `locale` with a deterministic fallback chain
512
+ * (requested locale → default locale → the key itself).
513
+ *
514
+ * @param locale - Requested locale code.
515
+ * @param key - Translation key (e.g. `"nav.home"`).
516
+ * @returns The translated value, the default-locale value, or `key`.
517
+ * @example
518
+ * ```ts
519
+ * app.i18n.t("uk", "nav.home"); // "Головна"
520
+ * ```
521
+ */
522
+ t(locale: string, key: string): string;
523
+ };
524
+ declare namespace types_d_exports$6 {
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 };
526
+ }
527
+ /**
528
+ * Param contribution of a single path segment. `{name:?}` / `:name?` → optional;
529
+ * `{name}` / `:name` → required; static segments contribute nothing.
530
+ *
531
+ * @example
532
+ * type S = ExtractSegmentParameter<"{slug}">; // { slug: string }
533
+ */
534
+ type ExtractSegmentParameter<Segment extends string> = Segment extends `{${infer Name}:?}` ? { [K in Name]?: string } : Segment extends `{${infer Name}}` ? { [K in Name]: string } : Segment extends `:${infer Name}?` ? { [K in Name]?: string } : Segment extends `:${infer Name}` ? { [K in Name]: string } : Record<never, never>;
535
+ /**
536
+ * Template-literal type that extracts path params from a URL pattern by walking
537
+ * one `/`-delimited segment at a time (so mixed required/optional patterns infer
538
+ * correctly). `{name}` / `:name` become required; `{name:?}` becomes optional.
539
+ *
540
+ * @example
541
+ * type P = ExtractRouteParams<"/{lang:?}/{slug}/">; // { lang?: string; slug: string }
542
+ */
543
+ type ExtractRouteParams<P extends string> = P extends `${infer Head}/${infer Tail}` ? ExtractSegmentParameter<Head> & ExtractRouteParams<Tail> : ExtractSegmentParameter<P>;
544
+ /** Flattens an intersection type into a single object literal for readable IntelliSense. */
545
+ type Prettify<T> = { [K in keyof T]: T[K] };
546
+ /**
547
+ * Accumulating generic carried by `RouteBuilder` as the fluent chain grows.
548
+ * `P` is the pattern's extracted params; `D` is the data type produced by `.load()`.
549
+ */
550
+ interface RouteState<P extends string = string, D = unknown> {
551
+ /** Path params inferred from the pattern. */
552
+ readonly params: Prettify<ExtractRouteParams<P>>;
553
+ /** Loaded data type produced by `.load()` (widened only by `.load()`). */
554
+ readonly data: D;
555
+ }
556
+ /** Render-time context handed to `.render()` / `.head()`; `data` is `.load()`'s return. */
557
+ interface RouteContext<S extends RouteState> {
558
+ /** Resolved path params. */
559
+ readonly params: S["params"];
560
+ /** Loaded data (the return value of this route's `.load()`). */
561
+ readonly data: S["data"];
562
+ /** Active locale for this render. */
563
+ readonly locale: string;
564
+ }
565
+ /** Head metadata produced by a route's `.head()` handler. */
566
+ interface HeadConfig$1 {
567
+ /** Document title. */
568
+ readonly title?: string;
569
+ /** Meta description. */
570
+ readonly description?: string;
571
+ /** Arbitrary extra head fields. */
572
+ readonly [key: string]: unknown;
573
+ }
574
+ /**
575
+ * Fluent route builder. Each chain method returns the same builder with a
576
+ * (possibly widened) state generic. Only `.load()` widens the data type `D`.
577
+ */
578
+ interface RouteBuilder<S extends RouteState> extends RouteDefinition {
579
+ /**
580
+ * Attach a data loader; widens the data generic (and ONLY the data generic) so
581
+ * `.render()`/`.head()` see its return. Path params are preserved unchanged.
582
+ */
583
+ load<D>(loader: (params: S["params"], locale: string) => D | Promise<D>): RouteBuilder<{
584
+ readonly params: S["params"];
585
+ readonly data: Awaited<D>;
586
+ }>;
587
+ /** Attach a layout wrapper component. */
588
+ layout(component: (children: ComponentChildren) => VNode): RouteBuilder<S>;
589
+ /** Attach the page render handler. */
590
+ render(handler: (ctx: RouteContext<S>) => VNode): RouteBuilder<S>;
591
+ /** Attach the head/SEO handler. */
592
+ head(handler: (ctx: RouteContext<S>) => HeadConfig$1): RouteBuilder<S>;
593
+ /** Attach a static-generation param producer. */
594
+ generate(handler: (locale: string) => S["params"][] | Promise<S["params"][]>): RouteBuilder<S>;
595
+ /** Attach an arbitrary metadata bag. */
596
+ meta(meta: Record<string, unknown>): RouteBuilder<S>;
597
+ /** Attach a JSON serializer for the route's data. */
598
+ toJson(handler: (ctx: RouteContext<S>) => unknown): RouteBuilder<S>;
599
+ /** Override the output file-path producer. */
600
+ toFile(handler: (params: S["params"]) => string): RouteBuilder<S>;
601
+ }
602
+ /** Build-only handler bag captured by a `RouteBuilder` (consumed by `build` via `manifest()`). */
603
+ interface RouteHandlers {
604
+ /** Data loader. */
605
+ readonly load?: (params: Record<string, string>, locale: string) => unknown;
606
+ /** Layout wrapper. */
607
+ readonly layout?: (children: ComponentChildren) => VNode;
608
+ /** Page renderer. */
609
+ readonly render?: (ctx: RouteContext<RouteState>) => VNode;
610
+ /** Head/SEO producer. */
611
+ readonly head?: (ctx: RouteContext<RouteState>) => HeadConfig$1;
612
+ /** Static-generation param producer. */
613
+ readonly generate?: (locale: string) => unknown[] | Promise<unknown[]>;
614
+ /** JSON serializer. */
615
+ readonly toJson?: (ctx: RouteContext<RouteState>) => unknown;
616
+ /** Output file-path producer. */
617
+ readonly toFile?: (params: Record<string, string>) => string;
618
+ }
619
+ /**
620
+ * A single route definition: the (erased) carrier produced by `route(...)`.
621
+ * Build consumes `_handlers` via `manifest()`; per-route param/data integrity
622
+ * is a call-site property established before config erasure.
623
+ */
624
+ interface RouteDefinition {
625
+ /** URL pattern string, e.g. `/{lang:?}/{slug}/`. */
626
+ readonly pattern: string;
627
+ /** Metadata bag accumulated from `.meta()` (named `_meta` to avoid clashing with the `.meta()` builder method). */
628
+ readonly _meta: Record<string, unknown>;
629
+ /** Build-time handler bag (load/render/head/generate/toJson/toFile). */
630
+ readonly _handlers: RouteHandlers;
631
+ }
632
+ /**
633
+ * Map of route name → route definition. The element type is intentionally the
634
+ * base (erased) `RouteDefinition`; this is the documented generic-erasure boundary.
635
+ */
636
+ type RouteMap = Record<string, RouteDefinition>;
637
+ /**
638
+ * Configuration for the router plugin.
639
+ *
640
+ * @remarks
641
+ * `routes` is an OPAQUE carrier at the config boundary — the framework `Config`
642
+ * generic erases the per-route element types (spec/05 §8, spec/09 §4). Downstream
643
+ * plugins read the typed route set via `ctx.require(routerPlugin).manifest()`.
644
+ */
645
+ type RouterConfig = {
646
+ /**
647
+ * Named route definitions. Element type erases to the base `RouteDefinition`
648
+ * at this config boundary; per-route call-site types are preserved only through
649
+ * `defineRoutes()` + `route()` at the consumer and re-exposed via `manifest()`.
650
+ */
651
+ routes: RouteMap;
652
+ /**
653
+ * Render mode for URL/file resolution. Defaults to `"hybrid"`.
654
+ * - `"ssg"` static generation only (no client router emitted).
655
+ * - `"spa"` client-side routing only.
656
+ * - `"hybrid"` static HTML + client navigation overlay.
657
+ */
658
+ mode?: "ssg" | "spa" | "hybrid";
659
+ };
660
+ /** A resolved route exposing URL utilities with typed params (port of legacy TypedRoute). */
661
+ interface TypedRoute<TParams = Record<string, string>> {
662
+ /** URL pattern string, e.g. `/{lang:?}/{slug}/`. */
663
+ readonly pattern: string;
664
+ /** Route name key. */
665
+ readonly name: string;
666
+ /** Metadata bag from `.meta()`. */
667
+ readonly meta: Record<string, unknown>;
668
+ /** Build a URL from typed params. */
669
+ toUrl(params: TParams): string;
670
+ /** Build an output file path from typed params. */
671
+ toFile(params: TParams): string;
672
+ /** Match a pathname into typed params, or `null`. */
673
+ match(pathname: string): TParams | null;
674
+ }
675
+ /** A single compiled route entry: name, pattern, specificity, matchers, URL utilities. */
676
+ interface CompiledRoute {
677
+ /** Route name key from the route map. */
678
+ readonly name: string;
679
+ /** Original user pattern, e.g. `/{lang:?}/{slug}/`. */
680
+ readonly pattern: string;
681
+ /** Dynamic-segment count (lower = more specific = matched first). */
682
+ readonly dynamicSegmentCount: number;
683
+ /** Pre-built URLPattern matchers (lang-aware + bare fallback). */
684
+ readonly matchers: {
685
+ readonly withLang: URLPattern;
686
+ readonly bare: URLPattern;
19
687
  };
20
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"router", RouterConfig, RouterState, RouterApi, {
21
- 'router:registered': {
22
- routeCount: number;
688
+ /** Resolve pathname into params (withLang first, then bare with defaultLocale injected). */
689
+ readonly matchFn: (pathname: string) => Record<string, string> | null;
690
+ /** Build a URL from params. */
691
+ readonly toUrl: (params: Record<string, string>) => string;
692
+ /** Build an output file path from params. */
693
+ readonly toFile: (params: Record<string, string>) => string;
694
+ /** The original (opaque) RouteDefinition — preserved for `manifest()`. */
695
+ readonly definition: RouteDefinition;
696
+ /** Route metadata bag from `.meta()`. */
697
+ readonly meta: Record<string, unknown>;
698
+ }
699
+ /** The compiled matcher table (immutable once `onInit` assigns it). */
700
+ interface MatcherTable {
701
+ /** All compiled routes, sorted by specificity (fewest dynamic segments first). */
702
+ readonly compiled: readonly CompiledRoute[];
703
+ /** Name → CompiledRoute index for O(1) `toUrl(name, ...)` lookups. */
704
+ readonly byName: ReadonlyMap<string, CompiledRoute>;
705
+ }
706
+ /**
707
+ * Router plugin state. `createState` runs with minimal context and returns a
708
+ * mutable holder whose `table` is `null` until `onInit` (which has full context)
709
+ * compiles and assigns it. Keeps all mutable state in `ctx.state` (no singletons).
710
+ */
711
+ interface RouterState {
712
+ /** Compiled matcher table; `null` until `onInit` assigns it. */
713
+ table: MatcherTable | null;
714
+ }
715
+ /** Plain-data input to `compileRoutes` — resolved DATA only, never the plugin ctx. */
716
+ interface CompileInput {
717
+ /** The opaque route map from config. */
718
+ readonly routes: RouteMap;
719
+ /** Resolved render mode. */
720
+ readonly mode: "ssg" | "spa" | "hybrid";
721
+ /** Site base URL (from `ctx.require(sitePlugin).url()`). */
722
+ readonly baseUrl: string;
723
+ /** Available locales (from `ctx.require(i18nPlugin).locales()`). */
724
+ readonly locales: readonly string[];
725
+ /** Default locale used for bare-pattern fallback. */
726
+ readonly defaultLocale: string;
727
+ }
728
+ /** Public API exposed via `ctx.require(routerPlugin)`. */
729
+ type RouterApi = {
730
+ /**
731
+ * Match a pathname against the compiled route table (specificity-sorted).
732
+ *
733
+ * @param pathname - URL pathname, e.g. `/en/hello/`.
734
+ * @returns `{ params, route }` for the most specific match, or `null` if none.
735
+ * @example
736
+ * const hit = ctx.require(routerPlugin).match("/en/hello/");
737
+ */
738
+ match(pathname: string): {
739
+ params: Record<string, string>;
740
+ route: RouteDefinition;
741
+ } | null;
742
+ /**
743
+ * Build a URL for a named route from params.
744
+ *
745
+ * @param routeName - Route name key from the route map.
746
+ * @param params - Param values to substitute into the pattern.
747
+ * @returns The resolved URL string (e.g. `/en/hello/`).
748
+ * @throws {Error} If `routeName` is unknown.
749
+ * @example
750
+ * ctx.require(routerPlugin).toUrl("article", { lang: "en", slug: "hello" });
751
+ */
752
+ toUrl(routeName: string, params: Record<string, string>): string;
753
+ /**
754
+ * All resolved routes as typed URL utilities, in specificity order.
755
+ *
756
+ * @returns Read-only array of resolved typed routes.
757
+ * @example
758
+ * for (const r of ctx.require(routerPlugin).entries()) { r.toUrl({ slug: "x" }); }
759
+ */
760
+ entries(): readonly TypedRoute[];
761
+ /**
762
+ * The typed route set for build-time consumption (the KEY mechanism). An API
763
+ * return, NOT a config readback — preserves per-route types despite config erasure.
764
+ *
765
+ * @returns Read-only array of the typed route definitions, in declaration order.
766
+ * @example
767
+ * for (const def of ctx.require(routerPlugin).manifest()) { def._handlers.load?.({}, "en"); }
768
+ */
769
+ 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[];
23
896
  };
24
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"head", HeadPluginConfig, HeadState, HeadApi, {}> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"build", BuildConfig, BuildState, BuildApi, {
25
- 'build:phase': {
26
- phase: "bundle" | "content" | "pages" | "feeds" | "sitemap" | "og" | "images";
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
+ /**
947
+ * Mark file paths stale for incremental dev rebuilds. Emits content:invalidated.
948
+ *
949
+ * @param paths - File paths to invalidate.
950
+ */
951
+ invalidate(paths: readonly string[]): void;
952
+ /**
953
+ * Project a full Article to a lightweight ArticleCard for list/grid rendering.
954
+ *
955
+ * @param article - The source article.
956
+ */
957
+ articleToCard(article: Article): ArticleCard;
958
+ };
959
+ declare namespace types_d_exports$4 {
960
+ export { Api$2 as Api, ArticleMeta, Config$2 as Config, HeadConfig, HeadDefaults, HeadElement, ResolvedRoute, State$2 as State };
961
+ }
962
+ /**
963
+ * @file head plugin — type definitions skeleton
964
+ */
965
+ /**
966
+ * Configuration for the `head` plugin.
967
+ *
968
+ * All fields are optional; sensible empty/identity defaults apply. Site-level values
969
+ * (title, description, base URL) are owned by the `site` plugin and read at render time.
970
+ *
971
+ * @example
972
+ * ```ts
973
+ * const config: Config = { titleTemplate: "%s — Moku" };
974
+ * ```
975
+ */
976
+ type Config$2 = {
977
+ /** 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
+ defaultOgImage?: string; /** Default Twitter card type emitted when og/twitter content is present. */
979
+ twitterCard?: "summary" | "summary_large_image"; /** Default Twitter site handle (e.g. `"@moku_labs"`) emitted as `twitter:site`. */
980
+ twitterHandle?: string;
981
+ };
982
+ /**
983
+ * Internal head state: a single `defaults` slot holding the normalized head defaults.
984
+ *
985
+ * `createState` initializes `defaults` to `null`; `onInit` assigns the normalized snapshot
986
+ * exactly once. The field is mutable (assigned in `onInit`) and nullable (initial value
987
+ * before `onInit`).
988
+ *
989
+ * @example
990
+ * ```ts
991
+ * const state: State = { defaults: null };
992
+ * ```
993
+ */
994
+ type State$2 = {
995
+ /** Normalized head defaults, assigned once in `onInit` (initially `null`). */defaults: HeadDefaults | null;
996
+ };
997
+ /**
998
+ * The normalized, resolved head defaults snapshot built from `Config` in `onInit` and
999
+ * read by `render`.
1000
+ *
1001
+ * @example
1002
+ * ```ts
1003
+ * const defaults: HeadDefaults = { twitterCard: "summary_large_image" };
1004
+ * ```
1005
+ */
1006
+ type HeadDefaults = {
1007
+ /** Title template carried over from config (validated to contain `%s`). */readonly titleTemplate?: string; /** Default Open Graph image URL. */
1008
+ readonly defaultOgImage?: string; /** Resolved Twitter card type (defaulted to `"summary_large_image"`). */
1009
+ readonly twitterCard: "summary" | "summary_large_image"; /** Default Twitter site handle. */
1010
+ readonly twitterHandle?: string;
1011
+ };
1012
+ /**
1013
+ * A serializable descriptor for a single `<head>` tag.
1014
+ *
1015
+ * Deliberately a PLAIN serializable object (NOT a Preact `VNode`) so `head` stays
1016
+ * decoupled from any renderer and can be produced/consumed in build (string) and in the
1017
+ * SPA (DOM) without pulling in `preact`.
1018
+ *
1019
+ * @example
1020
+ * ```ts
1021
+ * const el: HeadElement = { tag: "meta", attrs: { name: "robots", content: "index" } };
1022
+ * ```
1023
+ */
1024
+ type HeadElement = {
1025
+ /** The tag name to emit. */tag: "meta" | "link" | "title" | "script"; /** Attribute map (already-unescaped values; escaping happens at serialization). */
1026
+ attrs?: Record<string, string>; /** Inner text content (for `<title>` and JSON-LD `<script>`). */
1027
+ children?: string; /** Stable identity used for de-duplication during `render` (e.g. `"meta:description"`). */
1028
+ key?: string;
1029
+ };
1030
+ /**
1031
+ * The shape returned by a route's `.head(data)` callback. All fields optional so routes
1032
+ * supply only what they override.
1033
+ *
1034
+ * @example
1035
+ * ```ts
1036
+ * const head: HeadConfig = { title: "Home", description: "Welcome" };
1037
+ * ```
1038
+ */
1039
+ type HeadConfig = {
1040
+ /** Page title (before `titleTemplate` is applied). */title?: string; /** Page description (`<meta name=description>` + og/twitter fallback). */
1041
+ description?: string; /** Canonical URL override (otherwise derived from `router.toUrl`). */
1042
+ canonical?: string; /** Open Graph image override for this page. */
1043
+ image?: string; /** Arbitrary extra head elements (use the SEO primitive helpers to build these). */
1044
+ elements?: HeadElement[];
1045
+ };
1046
+ /**
1047
+ * Metadata describing an article page, consumed by `buildArticleHead`.
1048
+ *
1049
+ * @example
1050
+ * ```ts
1051
+ * const meta: ArticleMeta = { title: "Hi", author: "A", published: "2026-01-01" };
1052
+ * ```
1053
+ */
1054
+ type ArticleMeta = {
1055
+ /** Article title. */title: string; /** Article description. */
1056
+ description?: string; /** Article author. */
1057
+ author?: string; /** ISO 8601 publish date. */
1058
+ published?: string; /** ISO 8601 last-modified date. */
1059
+ modified?: string; /** Section/category. */
1060
+ section?: string; /** Article tags. */
1061
+ tags?: string[]; /** Article image URL. */
1062
+ image?: string;
1063
+ };
1064
+ /**
1065
+ * A resolved route descriptor passed to `render` (path, params, locale, and its `.head()`
1066
+ * result as a `HeadConfig`).
1067
+ *
1068
+ * @example
1069
+ * ```ts
1070
+ * const route: ResolvedRoute = { path: "/about", params: {}, name: "about" };
1071
+ * ```
1072
+ */
1073
+ type ResolvedRoute = {
1074
+ /** The resolved path of the route. */path: string; /** The route name key (used by `router.toUrl` for canonical/alternate hrefs). */
1075
+ name: string; /** Resolved route params. */
1076
+ params: Record<string, string>; /** The active locale for this route render. */
1077
+ locale?: string; /** The route's `.head()` result. */
1078
+ head?: HeadConfig;
1079
+ };
1080
+ /**
1081
+ * The public API surface of the `head` plugin.
1082
+ *
1083
+ * @example
1084
+ * ```ts
1085
+ * const html: string = api.render(route, data);
1086
+ * ```
1087
+ */
1088
+ type Api$2 = {
1089
+ /**
1090
+ * Compose the final `<head>` inner HTML for a route. Pulled synchronously by `build`.
1091
+ *
1092
+ * @param route - The resolved route descriptor (incl. its `.head()` HeadConfig).
1093
+ * @param data - The page data object passed to the route's loader/render.
1094
+ * @returns The serialized inner HTML of `<head>` (no surrounding `<head>` tags).
1095
+ * @example
1096
+ * ```ts
1097
+ * api.render(route, data);
1098
+ * ```
1099
+ */
1100
+ render(route: ResolvedRoute, data: unknown): string;
1101
+ };
1102
+ declare namespace types_d_exports {
1103
+ export { Api$1 as Api, BuildEvents, BuildResult, Config$1 as Config, ExtractApi$1 as ExtractApi, OgImageConfig, OgPngRenderer, PhaseContext, PhaseEmit, PhaseLog, PhaseName, PhaseRequire, State$1 as State };
1104
+ }
1105
+ /**
1106
+ * Structural extraction of a plugin instance's public API from its `_phantom`
1107
+ * carrier (mirrors the kernel's non-exported `ExtractPluginApi`) so the
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;
27
1118
  };
28
- 'build:complete': {
29
- pages: number;
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;
30
1152
  durationMs: number;
31
1153
  };
32
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
33
- 'component:create': {
34
- name: string;
35
- element: Element;
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>;
36
1166
  };
37
- 'component:mount': {
38
- name: string;
39
- element: Element;
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;
40
1221
  };
41
- 'component:unmount': {
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": {
42
1337
  name: string;
43
- reason: "navigation" | "destroy";
44
- };
45
- 'component:destroy': {
1338
+ el: Element;
1339
+ }; /** A component instance detached from an element. */
1340
+ "spa:component-unmount": {
46
1341
  name: string;
1342
+ el: Element;
47
1343
  };
48
- 'nav:start': {
49
- url: string;
50
- fromUrl: string;
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`).
1350
+ *
1351
+ * @example
1352
+ * type RApi = ExtractApi<typeof routerPlugin>;
1353
+ */
1354
+ type ExtractApi<PluginCandidate> = PluginCandidate extends {
1355
+ readonly _phantom: {
1356
+ readonly api: infer PluginApi;
51
1357
  };
52
- 'nav:end': {
53
- url: string;
1358
+ } ? PluginApi : never;
1359
+ /** Generic `require` closure for pulling dependency plugin APIs at init time. */
1360
+ type SpaRequire = <PluginCandidate extends {
1361
+ readonly name: string;
1362
+ readonly spec: unknown;
1363
+ readonly _phantom: {
1364
+ readonly config: unknown;
1365
+ readonly state: unknown;
1366
+ readonly api: unknown;
1367
+ readonly events: Record<string, unknown>;
54
1368
  };
55
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"deploy", DeployConfig, DeployState, DeployApi, {}> & Record<never, never>) | ExtraPlugins[number], [...ExtraPlugins], _moku_labs_core0.CoreApisFromTuple<[_moku_labs_core0.CorePluginInstance<"log", {
56
- level: "info";
57
- mode: "auto";
58
- }, LogState, LogApi>, _moku_labs_core0.CorePluginInstance<"env", {
59
- schema: {};
60
- providers: never[];
61
- publicPrefix: string;
62
- }, EnvState, EnvApi>]>> | undefined) => _moku_labs_core0.App<Config, Events, (_moku_labs_core0.PluginInstance<"site", {
1369
+ }>(plugin: PluginCandidate) => ExtractApi<PluginCandidate>;
1370
+ /**
1371
+ * The plugin-context slice the spa wiring consumes in `onInit`/`onStart`:
1372
+ * mutable `state`, resolved `config`, `require`/`emit`/`log`. Structurally
1373
+ * assignable from the framework's generic execution context.
1374
+ */
1375
+ interface SpaContext {
1376
+ /** Mutable spa state (all kernel data lives here). */
1377
+ state: SpaState;
1378
+ /** Resolved, frozen spa config. */
1379
+ readonly config: Readonly<SpaConfig>;
1380
+ /** Resolve a depended-upon plugin instance to its public API. */
1381
+ require: SpaRequire;
1382
+ /** Emit a spa lifecycle event (notification-only). */
1383
+ emit: SpaEmitFunction;
1384
+ /** Structured logger (core `log` API). */
1385
+ readonly log: LogApi;
1386
+ }
1387
+ /** Configuration for the SPA runtime plugin. All fields optional; defaults applied in onInit. */
1388
+ type SpaConfig = {
1389
+ /**
1390
+ * CSS selector for the page region swapped on navigation. Defaults to
1391
+ * `"main > section"`.
1392
+ */
1393
+ swapSelector?: string;
1394
+ /**
1395
+ * Use the View Transitions API for cross-fade swaps when available.
1396
+ * Falls back to an instant swap when unsupported. Defaults to `false`.
1397
+ */
1398
+ viewTransitions?: boolean;
1399
+ /**
1400
+ * Show the in-house top progress bar during navigation. Defaults to `true`.
1401
+ */
1402
+ progressBar?: boolean;
1403
+ /**
1404
+ * Components to auto-register at init (in addition to runtime `register`).
1405
+ * Defaults to an empty array.
1406
+ */
1407
+ components?: ComponentDef[];
1408
+ };
1409
+ /** Resolved SPA config after defaults are applied. */
1410
+ interface ResolvedSpaConfig {
1411
+ /** CSS selector for the swapped page region. */
1412
+ swapSelector: string;
1413
+ /** Whether View Transitions are enabled. */
1414
+ viewTransitions: boolean;
1415
+ /** Whether the progress bar is enabled. */
1416
+ progressBar: boolean;
1417
+ /** Pre-registered components. */
1418
+ components: ComponentDef[];
1419
+ }
1420
+ /** Context handed to every component lifecycle hook. */
1421
+ interface ComponentContext {
1422
+ /** The element the component instance is bound to. */
1423
+ el: Element;
1424
+ /** Page data extracted from the `script#__DATA__` payload. */
1425
+ data: PageData;
1426
+ }
1427
+ /** Lifecycle hooks a component may implement. */
1428
+ interface ComponentHooks {
1429
+ /**
1430
+ * Called once when the instance is created (before DOM attach).
1431
+ *
1432
+ * @param ctx - The component context for this instance.
1433
+ * @returns void
1434
+ * @example
1435
+ * onCreate({ el }) { el.dataset.ready = "1"; }
1436
+ */
1437
+ onCreate?(ctx: ComponentContext): void;
1438
+ /**
1439
+ * Called after the instance is attached to its element.
1440
+ *
1441
+ * @param ctx - The component context for this instance.
1442
+ * @returns void
1443
+ * @example
1444
+ * onMount({ el }) { el.textContent = "0"; }
1445
+ */
1446
+ onMount?(ctx: ComponentContext): void;
1447
+ /**
1448
+ * Called when a navigation begins while this instance is mounted.
1449
+ *
1450
+ * @param ctx - The component context for this instance.
1451
+ * @returns void
1452
+ * @example
1453
+ * onNavStart({ el }) { el.dataset.loading = ""; }
1454
+ */
1455
+ onNavStart?(ctx: ComponentContext): void;
1456
+ /**
1457
+ * Called when a navigation completes while this instance is mounted.
1458
+ *
1459
+ * @param ctx - The component context for this instance.
1460
+ * @returns void
1461
+ * @example
1462
+ * onNavEnd({ el }) { delete el.dataset.loading; }
1463
+ */
1464
+ onNavEnd?(ctx: ComponentContext): void;
1465
+ /**
1466
+ * Called before the instance is detached from its element.
1467
+ *
1468
+ * @param ctx - The component context for this instance.
1469
+ * @returns void
1470
+ * @example
1471
+ * onUnMount({ el }) { el.replaceChildren(); }
1472
+ */
1473
+ onUnMount?(ctx: ComponentContext): void;
1474
+ /**
1475
+ * Called once when the instance is destroyed (after detach).
1476
+ *
1477
+ * @param ctx - The component context for this instance.
1478
+ * @returns void
1479
+ * @example
1480
+ * onDestroy({ el }) { delete el.dataset.ready; }
1481
+ */
1482
+ onDestroy?(ctx: ComponentContext): void;
1483
+ }
1484
+ /** Allowed hook names — single source of truth for fail-fast validation. */
1485
+ declare const COMPONENT_HOOK_NAMES: readonly ["onCreate", "onMount", "onNavStart", "onNavEnd", "onUnMount", "onDestroy"];
1486
+ /** A registered component definition. */
1487
+ interface ComponentDef {
1488
+ /** Unique component name (matched against `data-component`). */
63
1489
  name: string;
64
- url: string;
65
- author: string;
66
- description: string;
67
- }, SiteState, SiteApi, {}> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"i18n", I18nConfig, I18nState<readonly string[]>, I18nApi<readonly string[]>, {}> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"content", ContentConfig, ContentState, ContentApi, {
68
- 'content:ready': {
69
- articles: Map<string, Article[]>;
70
- };
71
- 'content:invalidated': {
72
- paths: string[];
73
- };
74
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"router", RouterConfig, RouterState, RouterApi, {
75
- 'router:registered': {
76
- routeCount: number;
77
- };
78
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"head", HeadPluginConfig, HeadState, HeadApi, {}> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"build", BuildConfig, BuildState, BuildApi, {
79
- 'build:phase': {
80
- phase: "bundle" | "content" | "pages" | "feeds" | "sitemap" | "og" | "images";
81
- };
82
- 'build:complete': {
83
- pages: number;
1490
+ /** Lifecycle hooks. */
1491
+ hooks: ComponentHooks;
1492
+ }
1493
+ /** A live, mounted component instance. */
1494
+ interface ComponentInstance {
1495
+ /** The definition this instance was created from. */
1496
+ def: ComponentDef;
1497
+ /** The element this instance is bound to. */
1498
+ el: Element;
1499
+ /**
1500
+ * True if the element is OUTSIDE the swap area — persists across navigations
1501
+ * and receives onNavStart/onNavEnd (never onUnMount on nav). False =
1502
+ * page-specific: full unmount/destroy on every navigation.
1503
+ */
1504
+ persistent: boolean;
1505
+ }
1506
+ /** Page data payload parsed from the inline `script#__DATA__` element. */
1507
+ type PageData = Record<string, unknown>;
1508
+ /** Resolved dependency APIs the kernel reuses (router match/manifest, head compose). */
1509
+ interface SpaKernelDeps {
1510
+ /** Router plugin API — used for client-side route classification/matching. */
1511
+ router: RouterApi;
1512
+ /** Head plugin API — its pure compose is reused for client head-sync. */
1513
+ head: Api$2;
1514
+ }
1515
+ /** The single shared SPA kernel — pure factory over state/config/emit/deps. */
1516
+ interface SpaKernel {
1517
+ /**
1518
+ * Validate config, register config.components, seed currentUrl.
1519
+ *
1520
+ * @returns void
1521
+ * @example
1522
+ * kernel.init();
1523
+ */
1524
+ init(): void;
1525
+ /**
1526
+ * Boot the browser runtime (router listeners + initial scan). Throws if started.
1527
+ *
1528
+ * @returns void
1529
+ * @example
1530
+ * kernel.boot();
1531
+ */
1532
+ boot(): void;
1533
+ /**
1534
+ * Register a component definition (last-registered-wins).
1535
+ *
1536
+ * @param component - The component definition to register.
1537
+ * @returns void
1538
+ * @example
1539
+ * kernel.register(counter);
1540
+ */
1541
+ register(component: ComponentDef): void;
1542
+ /**
1543
+ * Process a navigation to `path`: fetch then swap then head-sync then emit.
1544
+ *
1545
+ * @param path - The target path to navigate to.
1546
+ * @returns void
1547
+ * @example
1548
+ * kernel.processNav("/about");
1549
+ */
1550
+ processNav(path: string): void;
1551
+ /**
1552
+ * Query the swap region and mount components for matching elements.
1553
+ *
1554
+ * @returns void
1555
+ * @example
1556
+ * kernel.scan();
1557
+ */
1558
+ scan(): void;
1559
+ /**
1560
+ * Tear down router listeners, run unmount/destroy, clear instances.
1561
+ *
1562
+ * @returns void
1563
+ * @example
1564
+ * kernel.dispose();
1565
+ */
1566
+ dispose(): void;
1567
+ }
1568
+ /** Internal mutable state for the spa plugin (all kernel data lives here). */
1569
+ interface SpaState {
1570
+ /** Components registered by name (last-registered-wins). */
1571
+ registeredComponents: Map<string, ComponentDef>;
1572
+ /** Live component instances keyed by their bound element. */
1573
+ instances: Map<Element, ComponentInstance>;
1574
+ /** The current resolved URL (pathname + search). */
1575
+ currentUrl: string;
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;
1582
+ }
1583
+ /** Public API of the spa plugin (registration / control surface). */
1584
+ type SpaApi = {
1585
+ /**
1586
+ * Register a component definition for client mounting.
1587
+ *
1588
+ * @param component - The component definition created via `createComponent`.
1589
+ * @returns void
1590
+ * @example
1591
+ * app.spa.register(counter);
1592
+ */
1593
+ register(component: ComponentDef): void;
1594
+ /**
1595
+ * Programmatically navigate to a path (client runtime; no-op without a DOM).
1596
+ *
1597
+ * @param path - Target path (pathname, optionally with search/hash).
1598
+ * @returns void
1599
+ * @example
1600
+ * app.spa.navigate("/about");
1601
+ */
1602
+ navigate(path: string): void;
1603
+ /**
1604
+ * Read the current resolved URL.
1605
+ *
1606
+ * @returns The current pathname + search.
1607
+ * @example
1608
+ * const url = app.spa.current(); // "/about"
1609
+ */
1610
+ current(): string;
1611
+ };
1612
+ declare namespace types_d_exports$2 {
1613
+ export { Api, Config, DeployErrorCode, DeployInitOptions, DeployResult, DeployRunOptions, InitResult, SpawnFunction, SpawnOptions, SpawnedProcess, State, WranglerErrorKind };
1614
+ }
1615
+ /**
1616
+ * @file deploy plugin — type definitions.
1617
+ */
1618
+ /**
1619
+ * Options passed to the injected spawner — the subset of Bun.spawn's options the
1620
+ * plugin uses (piped stdout/stderr plus an env carrying the API token).
1621
+ */
1622
+ interface SpawnOptions {
1623
+ /** Capture stdout as a readable stream. */
1624
+ readonly stdout: "pipe";
1625
+ /** Capture stderr as a readable stream. */
1626
+ readonly stderr: "pipe";
1627
+ /** Subprocess environment — the API token is injected here, never via argv. */
1628
+ readonly env?: Record<string, string | undefined>;
1629
+ }
1630
+ /**
1631
+ * The structural subprocess handle the plugin reads back: stdout/stderr streams
1632
+ * plus the exit-code promise. Streams are typed `unknown` and narrowed at the read
1633
+ * site so this carries no Bun namespace types.
1634
+ */
1635
+ interface SpawnedProcess {
1636
+ /** Standard output stream (narrowed to a ReadableStream at the read site). */
1637
+ readonly stdout: unknown;
1638
+ /** Standard error stream (narrowed to a ReadableStream at the read site). */
1639
+ readonly stderr: unknown;
1640
+ /** Resolves with the subprocess exit code. */
1641
+ readonly exited: Promise<number>;
1642
+ }
1643
+ /**
1644
+ * The subset of Bun.spawn's signature the plugin relies on (argv array + options).
1645
+ * Declared structurally — with NO `import("bun")` namespace types — so it survives
1646
+ * `.d.ts` bundling intact and tests can supply a fake spawn without importing Bun.
1647
+ */
1648
+ type SpawnFunction = (cmd: string[], options: SpawnOptions) => SpawnedProcess;
1649
+ /**
1650
+ * A deploy error `code` from the wrangler error taxonomy and preflight validators.
1651
+ */
1652
+ type DeployErrorCode = "ERR_DEPLOY_NO_WRANGLER_CONFIG" | "ERR_DEPLOY_EMPTY_OUTDIR" | "ERR_DEPLOY_TOO_MANY_FILES" | "ERR_DEPLOY_FILE_TOO_LARGE" | "ERR_DEPLOY_PATH_TRAVERSAL" | "ERR_DEPLOY_INVALID_BRANCH" | "ERR_DEPLOY_NO_TOKEN" | "ERR_DEPLOY_PROJECT_NOT_FOUND" | "ERR_DEPLOY_AUTH_EXPIRED" | "ERR_DEPLOY_AUTH" | "ERR_DEPLOY_NETWORK" | "ERR_DEPLOY_WRANGLER_FAILED" | "ERR_DEPLOY_CONFIG";
1653
+ /**
1654
+ * The subset of wrangler error `code`s classifyWranglerError can produce from a
1655
+ * non-zero wrangler exit.
1656
+ */
1657
+ type WranglerErrorKind = Extract<DeployErrorCode, "ERR_DEPLOY_PROJECT_NOT_FOUND" | "ERR_DEPLOY_AUTH_EXPIRED" | "ERR_DEPLOY_AUTH" | "ERR_DEPLOY_NETWORK" | "ERR_DEPLOY_WRANGLER_FAILED">;
1658
+ /**
1659
+ * Configuration for the deploy plugin.
1660
+ */
1661
+ type Config = {
1662
+ /**
1663
+ * Deploy target. Only Cloudflare Pages is supported in this version.
1664
+ * Defaults to `cloudflare-pages`.
1665
+ */
1666
+ target: "cloudflare-pages";
1667
+ /**
1668
+ * Directory (relative to project root) containing the built site to deploy.
1669
+ * Re-validated against cwd at run() time to block path traversal.
1670
+ * Defaults to `dist`.
1671
+ */
1672
+ outDir: string;
1673
+ /**
1674
+ * Branch treated as the Cloudflare Pages production branch.
1675
+ * Defaults to `main`.
1676
+ */
1677
+ productionBranch?: string;
1678
+ /**
1679
+ * Substrings exempt from entropy-gated secret scrubbing in logged output.
1680
+ * Defaults to `["CLOUDFLARE_ACCOUNT_ID"]`.
1681
+ */
1682
+ scrubAllowlist: string[];
1683
+ /**
1684
+ * Cloudflare compatibility date written into generated wrangler.jsonc.
1685
+ * Defaults to `2024-01-01`.
1686
+ */
1687
+ compatibilityDate?: string;
1688
+ /**
1689
+ * Whether init() also generates a GitHub Actions workflow.
1690
+ * Defaults to `false`.
1691
+ */
1692
+ ci?: boolean;
1693
+ };
1694
+ /**
1695
+ * Result of a successful deploy.
1696
+ */
1697
+ type DeployResult = {
1698
+ /** The public deployment URL (e.g. https://my-site.pages.dev). */url: string; /** Cloudflare deployment ID parsed from wrangler output. */
1699
+ deploymentId: string; /** The branch that was deployed. */
1700
+ branch: string; /** Wall-clock duration of the deploy in milliseconds. */
1701
+ durationMs: number;
1702
+ };
1703
+ /**
1704
+ * Runtime state for the deploy plugin. Created in createState() and accessed via
1705
+ * ctx.state. deploy declares no onStop because nothing here is a long-lived resource.
1706
+ */
1707
+ type State = {
1708
+ /** Result of the most recent successful deploy, or null before the first run. */lastDeployment: DeployResult | null;
1709
+ /**
1710
+ * Injectable subprocess spawner. Defaults to Bun.spawn. Swapped for a mock in
1711
+ * unit tests so wrangler is never actually invoked. Never reassigned at runtime.
1712
+ */
1713
+ spawn: SpawnFunction;
1714
+ };
1715
+ /**
1716
+ * Options for DeployApi.run.
1717
+ */
1718
+ type DeployRunOptions = {
1719
+ /**
1720
+ * Branch to deploy. Defaults to config.productionBranch (or "main"). Must match
1721
+ * /^[a-zA-Z0-9/_.-]+$/ — otherwise rejected with ERR_DEPLOY_INVALID_BRANCH.
1722
+ */
1723
+ branch?: string;
1724
+ /**
1725
+ * Whether to run the build before deploying. When false, deploys the existing outDir.
1726
+ * Defaults to `true`.
1727
+ */
1728
+ build?: boolean;
1729
+ };
1730
+ /**
1731
+ * Options for DeployApi.init.
1732
+ */
1733
+ type DeployInitOptions = {
1734
+ /** Also generate the GitHub Actions workflow. Defaults to config.ci. */ci?: boolean; /** Drift-only mode: report differences without writing any files. Defaults to `false`. */
1735
+ check?: boolean;
1736
+ };
1737
+ /**
1738
+ * Result of an init/scaffold operation.
1739
+ */
1740
+ type InitResult = {
1741
+ /** Paths written this invocation. */written: string[]; /** Paths skipped because they already exist. */
1742
+ skipped: string[]; /** In check mode: paths whose on-disk content differs from what would be generated. */
1743
+ drifted: string[];
1744
+ };
1745
+ /**
1746
+ * Public API of the deploy plugin (returned from the api factory).
1747
+ */
1748
+ type Api = {
1749
+ /**
1750
+ * Deploy the built outDir to Cloudflare Pages via the wrangler subprocess.
1751
+ * Runs preflight validators, re-validates the resolved outdir against cwd, guards
1752
+ * the branch argument, spawns wrangler (no shell), scrubs all subprocess output
1753
+ * before logging, records lastDeployment, and emits deploy:complete.
1754
+ *
1755
+ * @param options - Optional branch override and build toggle.
1756
+ * @returns The deploy result (url, deploymentId, branch, durationMs).
1757
+ * @throws {Error} With a `code` from the deploy error taxonomy on any failure.
1758
+ * @example
1759
+ * const result = await app.deploy.run();
1760
+ * console.log(result.url); // https://my-site.pages.dev
1761
+ * @example
1762
+ * await app.deploy.run({ branch: "preview/landing", build: false });
1763
+ */
1764
+ run(options?: DeployRunOptions): Promise<DeployResult>;
1765
+ /**
1766
+ * Return the most recent successful deploy result, or null if none has occurred.
1767
+ * The returned object is read-only (a defensive snapshot).
1768
+ *
1769
+ * @returns The last DeployResult, or null.
1770
+ * @example
1771
+ * const last = app.deploy.getLastDeployment();
1772
+ * if (last) console.log(`Last deployed to ${last.url}`);
1773
+ */
1774
+ getLastDeployment(): Readonly<DeployResult> | null;
1775
+ /**
1776
+ * Generate deploy scaffolding: wrangler.jsonc (slug from site.name() + outDir +
1777
+ * compatibilityDate) and, when ci is enabled, .github/workflows/deploy.yml. Never
1778
+ * overwrites an existing wrangler.jsonc. In check mode, reports drift instead of writing.
1779
+ *
1780
+ * @param options - Optional ci toggle and check (drift-only) mode.
1781
+ * @returns Which files were written, skipped, or would drift.
1782
+ * @example
1783
+ * const out = await app.deploy.init({ ci: true });
1784
+ * // out.written -> ["wrangler.jsonc", ".github/workflows/deploy.yml"]
1785
+ * @example
1786
+ * const drift = await app.deploy.init({ check: true });
1787
+ * if (drift.drifted.length) process.exit(1);
1788
+ */
1789
+ init(options?: DeployInitOptions): Promise<InitResult>;
1790
+ };
1791
+ //#endregion
1792
+ //#region src/plugins/deploy/index.d.ts
1793
+ /**
1794
+ * @file deploy — Standard Plugin (wiring harness only). Deploys the built dist/
1795
+ * to Cloudflare Pages via the injectable wrangler subprocess.
1796
+ *
1797
+ * Depends: site. Emits: deploy:complete.
1798
+ * @see README.md
1799
+ */
1800
+ declare const deployPlugin: import("@moku-labs/core").PluginInstance<"deploy", Config, State, Api, {
1801
+ "deploy:complete": {
1802
+ url: string;
1803
+ deploymentId: string;
1804
+ branch: string;
84
1805
  durationMs: number;
85
1806
  };
86
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
87
- 'component:create': {
88
- name: string;
89
- element: Element;
90
- };
91
- 'component:mount': {
92
- name: string;
93
- element: Element;
94
- };
95
- 'component:unmount': {
96
- name: string;
97
- reason: "navigation" | "destroy";
1807
+ }> & Record<never, never>;
1808
+ //#endregion
1809
+ //#region src/plugins/build/index.d.ts
1810
+ declare const buildPlugin: import("@moku-labs/core").PluginInstance<"build", Config$1, State$1, Api$1, {
1811
+ "build:phase": {
1812
+ phase: PhaseName;
1813
+ status: "start" | "done";
1814
+ durationMs?: number;
98
1815
  };
99
- 'component:destroy': {
100
- name: string;
1816
+ "build:complete": {
1817
+ outDir: string;
1818
+ pageCount: number;
1819
+ durationMs: number;
101
1820
  };
102
- 'nav:start': {
103
- url: string;
104
- fromUrl: string;
1821
+ }> & Record<never, never>;
1822
+ //#endregion
1823
+ //#region src/plugins/content/index.d.ts
1824
+ declare const contentPlugin: import("@moku-labs/core").PluginInstance<"content", Config$3, State$3, Api$3, {
1825
+ "content:ready": {
1826
+ locales: readonly string[];
1827
+ articleCount: number;
105
1828
  };
106
- 'nav:end': {
107
- url: string;
1829
+ "content:invalidated": {
1830
+ paths: readonly string[];
108
1831
  };
109
- }> & Record<never, never>) | (_moku_labs_core0.PluginInstance<"deploy", DeployConfig, DeployState, DeployApi, {}> & Record<never, never>) | ExtraPlugins[number], _moku_labs_core0.CoreApisFromTuple<[_moku_labs_core0.CorePluginInstance<"log", {
110
- level: "info";
111
- mode: "auto";
112
- }, LogState, LogApi>, _moku_labs_core0.CorePluginInstance<"env", {
113
- schema: {};
114
- providers: never[];
115
- publicPrefix: string;
116
- }, EnvState, EnvApi>]>>;
1832
+ }> & Record<never, never>;
1833
+ //#endregion
1834
+ //#region src/plugins/head/primitives.d.ts
1835
+ /**
1836
+ * Build a `<meta name=… content=…>` descriptor.
1837
+ *
1838
+ * @param name - The meta `name` attribute (e.g. `"description"`, `"robots"`).
1839
+ * @param content - The meta `content` value.
1840
+ * @returns A serializable head element keyed `meta:<name>`.
1841
+ * @example meta("description", "A web framework built on @moku-labs/core")
1842
+ */
1843
+ declare function meta(name: string, content: string): HeadElement;
1844
+ /**
1845
+ * Build an Open Graph `<meta property=… content=…>` descriptor.
1846
+ *
1847
+ * @param property - The OG property, used verbatim (e.g. `"og:title"`, `"og:image"`).
1848
+ * @param content - The property value.
1849
+ * @returns A serializable head element keyed `meta:<property>`.
1850
+ * @example og("og:title", "Home")
1851
+ */
1852
+ declare function og(property: string, content: string): HeadElement;
117
1853
  /**
118
- * Kernel app augmented so `app.router.routes` carries the consumer's literal
119
- * route-name keys (with `RouteSpec` values — the shape the router stores).
1854
+ * Build a Twitter-card `<meta name=… content=…>` descriptor.
1855
+ *
1856
+ * @param name - The Twitter meta name, used verbatim (e.g. `"twitter:title"`).
1857
+ * @param content - The value.
1858
+ * @returns A serializable head element keyed `meta:<name>`.
1859
+ * @example twitter("twitter:card", "summary_large_image")
120
1860
  */
121
- type WebApp<Routes extends Record<string, RouteBuilder>> = Omit<ReturnType<typeof kernelCreateApp>, 'router'> & {
122
- router: RouterApi<RouteSpecMap<Routes>>;
1861
+ declare function twitter(name: string, content: string): HeadElement;
1862
+ /**
1863
+ * Build a JSON-LD `<script type="application/ld+json">` descriptor.
1864
+ *
1865
+ * XSS-SAFE: the serialized JSON has `<`, `>`, and `&` unicode-escaped (`<`,
1866
+ * `>`, `&`) so the payload can never break out of the `<script>` element
1867
+ * or inject markup, while still round-tripping via `JSON.parse`.
1868
+ *
1869
+ * @param data - Any JSON-serializable structured-data object.
1870
+ * @returns A serializable head element carrying the escaped JSON-LD script.
1871
+ * @example jsonLd({ "@context": "https://schema.org", "@type": "Article", headline: "Hi" })
1872
+ */
1873
+ declare function jsonLd(data: unknown): HeadElement;
1874
+ /**
1875
+ * Build a canonical `<link rel="canonical" href=…>` descriptor.
1876
+ *
1877
+ * @param url - The canonical absolute URL.
1878
+ * @returns A serializable head element keyed `link:canonical`.
1879
+ * @example canonical("https://example.com/post")
1880
+ */
1881
+ declare function canonical(url: string): HeadElement;
1882
+ /**
1883
+ * Build an alternate-language `<link rel="alternate" hreflang=… href=…>` descriptor.
1884
+ *
1885
+ * @param locale - The BCP-47 locale tag (e.g. `"en"`, `"uk"`, `"x-default"`).
1886
+ * @param url - The absolute URL of the localized page.
1887
+ * @returns A serializable head element keyed `link:alternate:<locale>`.
1888
+ * @example hreflang("uk", "https://example.com/uk/post")
1889
+ */
1890
+ declare function hreflang(locale: string, url: string): HeadElement;
1891
+ /**
1892
+ * Build a feed `<link rel="alternate" type=… title=… href=…>` descriptor.
1893
+ *
1894
+ * @param title - Human-readable feed title.
1895
+ * @param url - The feed URL.
1896
+ * @param type - The feed MIME type. Defaults to `"application/rss+xml"`.
1897
+ * @returns A serializable head element keyed `link:feed:<url>`.
1898
+ * @example feedLink("My Blog", "/feed.xml", "application/atom+xml")
1899
+ */
1900
+ declare function feedLink(title: string, url: string, type?: string): HeadElement;
1901
+ /**
1902
+ * Compose the full head element set for an article page: og:type=article, published/
1903
+ * modified times, author, section, tags, plus a JSON-LD `Article` block and canonical.
1904
+ *
1905
+ * @param articleMeta - Article metadata (title, description, author, dates, tags, image…).
1906
+ * @param canonicalUrl - The article's canonical absolute URL.
1907
+ * @returns An ordered array of serializable head elements.
1908
+ * @example buildArticleHead({ title: "Hi", author: "A", published: "2026-01-01" }, "https://x/p")
1909
+ */
1910
+ declare function buildArticleHead(articleMeta: ArticleMeta, canonicalUrl: string): HeadElement[];
1911
+ //#endregion
1912
+ //#region src/plugins/head/index.d.ts
1913
+ declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Config$2, State$2, Api$2, {}> & {
1914
+ meta: typeof meta;
1915
+ og: typeof og;
1916
+ twitter: typeof twitter;
1917
+ jsonLd: typeof jsonLd;
1918
+ canonical: typeof canonical;
1919
+ hreflang: typeof hreflang;
1920
+ feedLink: typeof feedLink;
1921
+ buildArticleHead: typeof buildArticleHead;
123
1922
  };
1923
+ //#endregion
1924
+ //#region src/plugins/i18n/index.d.ts
1925
+ declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Config$5, Record<string, never>, Api$5, {}> & Record<never, never>;
1926
+ //#endregion
1927
+ //#region src/plugins/log/index.d.ts
1928
+ /**
1929
+ * Core logging plugin — always-on in-memory trace + `expect()` event-trace DSL.
1930
+ * API injected as `ctx.log` on every regular plugin and surfaced as `app.log`.
1931
+ * No depends / events / hooks (core plugin per spec/03 §5).
1932
+ *
1933
+ * @see README.md
1934
+ */
1935
+ declare const logPlugin: import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>;
1936
+ //#endregion
1937
+ //#region src/plugins/router/builders/route-builder.d.ts
124
1938
  /**
125
- * Layer-2 createApp wrapper generic-preserving signature.
1939
+ * Create a fluent route builder from a URL pattern string. Captures the pattern
1940
+ * as a literal type for compile-time param inference; `.load()` is the only method
1941
+ * that widens the data generic, so `ctx.data` in `.render()`/`.head()` is typed by
1942
+ * `.load()`'s return at the CALL SITE. The returned object is itself the route
1943
+ * definition (`pattern` / `_meta` / `_handlers`), so it slots straight into a route map.
126
1944
  *
127
- * Projects the consumer's flat config into pluginConfigs before delegating to
128
- * the kernel-bound createApp. The Routes generic flows through to
129
- * `app.router.routes` so callers get typed access to their route names.
1945
+ * @param pattern - URL pattern with `{param}` / `{param:?}` placeholders.
1946
+ * @returns A `RouteBuilder<RouteState<P>>` carrying the typed fluent chain.
1947
+ * @example
1948
+ * ```ts
1949
+ * route("/{lang:?}/{slug}/")
1950
+ * .load(({ slug }) => loadArticle(slug))
1951
+ * .render((ctx) => <Article a={ctx.data} />)
1952
+ * .head((ctx) => ({ title: ctx.data.title }));
1953
+ * ```
1954
+ */
1955
+ declare function route<P extends string>(pattern: P): RouteBuilder<RouteState<P>>;
1956
+ /**
1957
+ * Typed identity helper for route maps. Preserves the precise literal type of the
1958
+ * route object for IntelliSense at the consumer call site (before config erasure).
130
1959
  *
131
- * @param input - Consumer-facing flat web-app config.
132
- * @returns The assembled app with typed router routes.
1960
+ * @param routes - The route map object.
1961
+ * @returns The same object, with its precise type preserved.
1962
+ * @example
1963
+ * ```ts
1964
+ * const routes = defineRoutes({ home: route("/"), article: route("/{slug}/") });
1965
+ * ```
133
1966
  */
134
- declare function createApp<Routes extends Record<string, RouteBuilder>>(input: WebAppConfig<Routes>): WebApp<Routes>;
1967
+ declare function defineRoutes<T extends RouteMap>(routes: T): T;
1968
+ //#endregion
1969
+ //#region src/plugins/router/index.d.ts
1970
+ declare const routerPlugin: import("@moku-labs/core").PluginInstance<"router", RouterConfig, RouterState, RouterApi, {}> & {
1971
+ route: typeof route;
1972
+ defineRoutes: typeof defineRoutes;
1973
+ };
1974
+ //#endregion
1975
+ //#region src/plugins/site/index.d.ts
1976
+ declare const sitePlugin: import("@moku-labs/core").PluginInstance<"site", Config$6, Record<string, never>, Api$6, {}> & Record<never, never>;
1977
+ //#endregion
1978
+ //#region src/plugins/spa/index.d.ts
1979
+ declare const spaPlugin: import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
1980
+ "spa:navigate": {
1981
+ from: string;
1982
+ to: string;
1983
+ };
1984
+ "spa:navigated": {
1985
+ url: string;
1986
+ };
1987
+ "spa:component-mount": {
1988
+ name: string;
1989
+ el: Element;
1990
+ };
1991
+ "spa:component-unmount": {
1992
+ name: string;
1993
+ el: Element;
1994
+ };
1995
+ }> & Record<never, never>;
1996
+ //#endregion
1997
+ //#region src/index.d.ts
1998
+ 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, {}> & {
1999
+ route: typeof route;
2000
+ defineRoutes: typeof defineRoutes;
2001
+ }) | (import("@moku-labs/core").PluginInstance<"content", Config$3, State$3, Api$3, {
2002
+ "content:ready": {
2003
+ locales: readonly string[];
2004
+ articleCount: number;
2005
+ };
2006
+ "content:invalidated": {
2007
+ paths: readonly string[];
2008
+ };
2009
+ }> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"head", Config$2, State$2, Api$2, {}> & {
2010
+ meta: typeof meta;
2011
+ og: typeof og;
2012
+ twitter: typeof twitter;
2013
+ jsonLd: typeof jsonLd;
2014
+ canonical: typeof canonical;
2015
+ hreflang: typeof hreflang;
2016
+ feedLink: typeof feedLink;
2017
+ buildArticleHead: typeof buildArticleHead;
2018
+ }) | (import("@moku-labs/core").PluginInstance<"build", Config$1, State$1, Api$1, {
2019
+ "build:phase": {
2020
+ phase: PhaseName;
2021
+ status: "start" | "done";
2022
+ durationMs?: number;
2023
+ };
2024
+ "build:complete": {
2025
+ outDir: string;
2026
+ pageCount: number;
2027
+ durationMs: number;
2028
+ };
2029
+ }> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
2030
+ "spa:navigate": {
2031
+ from: string;
2032
+ to: string;
2033
+ };
2034
+ "spa:navigated": {
2035
+ url: string;
2036
+ };
2037
+ "spa:component-mount": {
2038
+ name: string;
2039
+ el: Element;
2040
+ };
2041
+ "spa:component-unmount": {
2042
+ name: string;
2043
+ el: Element;
2044
+ };
2045
+ }> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"deploy", Config, State, Api, {
2046
+ "deploy:complete": {
2047
+ url: string;
2048
+ deploymentId: string;
2049
+ branch: string;
2050
+ durationMs: number;
2051
+ };
2052
+ }> & 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, {}> & {
2053
+ route: typeof route;
2054
+ defineRoutes: typeof defineRoutes;
2055
+ }) | (import("@moku-labs/core").PluginInstance<"content", Config$3, State$3, Api$3, {
2056
+ "content:ready": {
2057
+ locales: readonly string[];
2058
+ articleCount: number;
2059
+ };
2060
+ "content:invalidated": {
2061
+ paths: readonly string[];
2062
+ };
2063
+ }> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"head", Config$2, State$2, Api$2, {}> & {
2064
+ meta: typeof meta;
2065
+ og: typeof og;
2066
+ twitter: typeof twitter;
2067
+ jsonLd: typeof jsonLd;
2068
+ canonical: typeof canonical;
2069
+ hreflang: typeof hreflang;
2070
+ feedLink: typeof feedLink;
2071
+ buildArticleHead: typeof buildArticleHead;
2072
+ }) | (import("@moku-labs/core").PluginInstance<"build", Config$1, State$1, Api$1, {
2073
+ "build:phase": {
2074
+ phase: PhaseName;
2075
+ status: "start" | "done";
2076
+ durationMs?: number;
2077
+ };
2078
+ "build:complete": {
2079
+ outDir: string;
2080
+ pageCount: number;
2081
+ durationMs: number;
2082
+ };
2083
+ }> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"spa", SpaConfig, SpaState, SpaApi, {
2084
+ "spa:navigate": {
2085
+ from: string;
2086
+ to: string;
2087
+ };
2088
+ "spa:navigated": {
2089
+ url: string;
2090
+ };
2091
+ "spa:component-mount": {
2092
+ name: string;
2093
+ el: Element;
2094
+ };
2095
+ "spa:component-unmount": {
2096
+ name: string;
2097
+ el: Element;
2098
+ };
2099
+ }> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"deploy", Config, State, Api, {
2100
+ "deploy:complete": {
2101
+ url: string;
2102
+ deploymentId: string;
2103
+ branch: string;
2104
+ durationMs: number;
2105
+ };
2106
+ }> & 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>]>>, 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>]>>;
135
2107
  //#endregion
136
- export { types_d_exports as Build, type ComponentDef, type ComponentHooks, type Config, types_d_exports$1 as Content, types_d_exports$2 as Deploy, types_d_exports$3 as Env, type Events, types_d_exports$4 as Head, types_d_exports$5 as I18n, types_d_exports$6 as Log, type RouteBuilder, type RouteSpec, types_d_exports$7 as Router, types_d_exports$8 as Site, types_d_exports$9 as Spa, WebApp, type WebAppConfig, build, canonical, content, createApp, createComponent, createPlugin, deploy, env, feedLink, head, hreflang, i18n, jsonLd, log, meta, og, route, router, site, spa, twitter };
2108
+ export { types_d_exports as Build, types_d_exports$1 as Content, types_d_exports$2 as Deploy, types_d_exports$3 as Env, types_d_exports$4 as Head, types_d_exports$5 as Log, types_d_exports$6 as Router, types_d_exports$7 as Spa, buildArticleHead, buildPlugin, canonical, contentPlugin, createApp, createPlugin, defineRoutes, deployPlugin, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };