@pyreon/zero 0.14.0 → 0.16.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 (114) hide show
  1. package/lib/api-routes-Ci0kVmM4.js +146 -0
  2. package/lib/client.js +7 -2
  3. package/lib/csp.js +19 -9
  4. package/lib/env.js +6 -6
  5. package/lib/font.js +3 -3
  6. package/lib/{fs-router-CQ7Zxeca.js → fs-router-MewHc5SB.js} +56 -24
  7. package/lib/i18n-routing.js +112 -1
  8. package/lib/image-plugin.js +4 -0
  9. package/lib/image.js +141 -108
  10. package/lib/index.js +253 -132
  11. package/lib/link.js +1 -49
  12. package/lib/og-image.js +5 -5
  13. package/lib/rolldown-runtime-CjeV3_4I.js +18 -0
  14. package/lib/script.js +115 -74
  15. package/lib/seo.js +186 -15
  16. package/lib/server.js +275 -1247
  17. package/lib/theme.js +1 -50
  18. package/lib/types/config.d.ts +275 -3
  19. package/lib/types/env.d.ts +2 -2
  20. package/lib/types/i18n-routing.d.ts +197 -6
  21. package/lib/types/image.d.ts +105 -5
  22. package/lib/types/index.d.ts +640 -178
  23. package/lib/types/link.d.ts +3 -3
  24. package/lib/types/script.d.ts +78 -6
  25. package/lib/types/seo.d.ts +128 -4
  26. package/lib/types/server.d.ts +603 -77
  27. package/lib/types/theme.d.ts +2 -2
  28. package/lib/vite-plugin-xjWZwudX.js +2454 -0
  29. package/package.json +16 -13
  30. package/src/adapters/bun.ts +20 -1
  31. package/src/adapters/cloudflare.ts +78 -1
  32. package/src/adapters/index.ts +25 -3
  33. package/src/adapters/netlify.ts +63 -1
  34. package/src/adapters/node.ts +25 -1
  35. package/src/adapters/static.ts +26 -1
  36. package/src/adapters/validate.ts +8 -1
  37. package/src/adapters/vercel.ts +76 -1
  38. package/src/adapters/warn-missing-env.ts +49 -0
  39. package/src/app.ts +35 -1
  40. package/src/client.ts +18 -0
  41. package/src/csp.ts +28 -12
  42. package/src/entry-server.ts +55 -5
  43. package/src/env.ts +7 -7
  44. package/src/font.ts +3 -3
  45. package/src/fs-router.ts +123 -4
  46. package/src/i18n-routing.ts +246 -12
  47. package/src/image.tsx +242 -91
  48. package/src/index.ts +4 -4
  49. package/src/isr.ts +24 -6
  50. package/src/manifest.ts +675 -0
  51. package/src/og-image.ts +5 -5
  52. package/src/script.tsx +159 -36
  53. package/src/seo.ts +346 -15
  54. package/src/server.ts +10 -2
  55. package/src/ssg-plugin.ts +1523 -0
  56. package/src/types.ts +329 -19
  57. package/src/vercel-revalidate-handler.ts +204 -0
  58. package/src/vite-plugin.ts +326 -68
  59. package/lib/actions.js.map +0 -1
  60. package/lib/ai.js.map +0 -1
  61. package/lib/api-routes.js.map +0 -1
  62. package/lib/cache.js.map +0 -1
  63. package/lib/client.js.map +0 -1
  64. package/lib/compression.js.map +0 -1
  65. package/lib/config.js.map +0 -1
  66. package/lib/cors.js.map +0 -1
  67. package/lib/csp.js.map +0 -1
  68. package/lib/env.js.map +0 -1
  69. package/lib/favicon.js.map +0 -1
  70. package/lib/font.js.map +0 -1
  71. package/lib/fs-router-3xzp-4Wj.js.map +0 -1
  72. package/lib/fs-router-CQ7Zxeca.js.map +0 -1
  73. package/lib/i18n-routing.js.map +0 -1
  74. package/lib/image-plugin.js.map +0 -1
  75. package/lib/image.js.map +0 -1
  76. package/lib/index.js.map +0 -1
  77. package/lib/link.js.map +0 -1
  78. package/lib/logger.js.map +0 -1
  79. package/lib/meta.js.map +0 -1
  80. package/lib/middleware.js.map +0 -1
  81. package/lib/og-image.js.map +0 -1
  82. package/lib/rate-limit.js.map +0 -1
  83. package/lib/script.js.map +0 -1
  84. package/lib/seo.js.map +0 -1
  85. package/lib/server.js.map +0 -1
  86. package/lib/testing.js.map +0 -1
  87. package/lib/theme.js.map +0 -1
  88. package/lib/types/actions.d.ts.map +0 -1
  89. package/lib/types/ai.d.ts.map +0 -1
  90. package/lib/types/api-routes.d.ts.map +0 -1
  91. package/lib/types/cache.d.ts.map +0 -1
  92. package/lib/types/client.d.ts.map +0 -1
  93. package/lib/types/compression.d.ts.map +0 -1
  94. package/lib/types/config.d.ts.map +0 -1
  95. package/lib/types/cors.d.ts.map +0 -1
  96. package/lib/types/csp.d.ts.map +0 -1
  97. package/lib/types/env.d.ts.map +0 -1
  98. package/lib/types/favicon.d.ts.map +0 -1
  99. package/lib/types/font.d.ts.map +0 -1
  100. package/lib/types/i18n-routing.d.ts.map +0 -1
  101. package/lib/types/image-plugin.d.ts.map +0 -1
  102. package/lib/types/image.d.ts.map +0 -1
  103. package/lib/types/index.d.ts.map +0 -1
  104. package/lib/types/link.d.ts.map +0 -1
  105. package/lib/types/logger.d.ts.map +0 -1
  106. package/lib/types/meta.d.ts.map +0 -1
  107. package/lib/types/middleware.d.ts.map +0 -1
  108. package/lib/types/og-image.d.ts.map +0 -1
  109. package/lib/types/rate-limit.d.ts.map +0 -1
  110. package/lib/types/script.d.ts.map +0 -1
  111. package/lib/types/seo.d.ts.map +0 -1
  112. package/lib/types/server.d.ts.map +0 -1
  113. package/lib/types/testing.d.ts.map +0 -1
  114. package/lib/types/theme.d.ts.map +0 -1
@@ -1,9 +1,10 @@
1
- import * as _pyreon_core0 from "@pyreon/core";
1
+ import * as _$_pyreon_core0 from "@pyreon/core";
2
2
  import { ComponentFn } from "@pyreon/core";
3
- import * as _pyreon_router0 from "@pyreon/router";
3
+ import * as _$_pyreon_router0 from "@pyreon/router";
4
4
  import { RouteRecord } from "@pyreon/router";
5
5
  import { Middleware, MiddlewareContext } from "@pyreon/server";
6
6
  import { Plugin } from "vite";
7
+
7
8
  //#region src/app.d.ts
8
9
  interface CreateAppOptions {
9
10
  /** Route definitions (from file-based routing or manual). */
@@ -16,6 +17,18 @@ interface CreateAppOptions {
16
17
  layout?: ComponentFn;
17
18
  /** Global error component. */
18
19
  errorComponent?: ComponentFn;
20
+ /**
21
+ * Base URL prefix for the deployed app (e.g. `/blog/`). Forwarded to
22
+ * `createRouter({ base })` so RouterLinks render correctly prefixed
23
+ * hrefs (`<a href="/blog/about">` instead of `<a href="/about">`) and
24
+ * the router strips the prefix from incoming URLs before matching.
25
+ *
26
+ * Default: `'/'`. Pre-fix this was disconnected from `zero({ base })`
27
+ * — RouterLinks rendered un-prefixed hrefs even when Vite's asset URL
28
+ * rewriting was correctly using the prefix, causing client-side
29
+ * navigation to break against subpath deploys.
30
+ */
31
+ base?: string;
19
32
  }
20
33
  /**
21
34
  * Create a full Zero app — assembles router, head provider, and root layout.
@@ -23,8 +36,8 @@ interface CreateAppOptions {
23
36
  * Used internally by entry-server and entry-client.
24
37
  */
25
38
  declare function createApp(options: CreateAppOptions): {
26
- App: () => _pyreon_core0.VNode;
27
- router: _pyreon_router0.Router<string>;
39
+ App: () => _$_pyreon_core0.VNode;
40
+ router: _$_pyreon_router0.Router<string>;
28
41
  };
29
42
  //#endregion
30
43
  //#region src/api-routes.d.ts
@@ -61,6 +74,66 @@ interface ApiRouteEntry {
61
74
  module: ApiRouteModule;
62
75
  }
63
76
  //#endregion
77
+ //#region src/i18n-routing.d.ts
78
+ interface I18nRoutingConfig {
79
+ /** Supported locales. e.g. ["en", "de", "cs"] */
80
+ locales: string[];
81
+ /** Default locale — served without prefix (/ instead of /en/). */
82
+ defaultLocale: string;
83
+ /** Redirect root to detected locale. Default: true */
84
+ detectLocale?: boolean;
85
+ /** Cookie name to persist locale preference. Default: "locale" */
86
+ cookieName?: string;
87
+ /** URL strategy. Default: "prefix-except-default" */
88
+ strategy?: 'prefix' | 'prefix-except-default';
89
+ }
90
+ interface LocaleContext {
91
+ /** Current locale code. e.g. "en", "de" */
92
+ locale: string;
93
+ /** All supported locales. */
94
+ locales: string[];
95
+ /** Default locale. */
96
+ defaultLocale: string;
97
+ /** Build a localized path. e.g. localePath("/about", "de") → "/de/about" */
98
+ localePath: (path: string, locale?: string) => string;
99
+ /** Get hreflang alternates for the current path. */
100
+ alternates: () => Array<{
101
+ locale: string;
102
+ url: string;
103
+ }>;
104
+ }
105
+ /**
106
+ * Detect preferred locale from Accept-Language header.
107
+ */
108
+ declare function detectLocaleFromHeader(acceptLanguage: string | null | undefined, locales: string[], defaultLocale: string): string;
109
+ /**
110
+ * Create a LocaleContext for use in components and loaders.
111
+ */
112
+ declare function createLocaleContext(locale: string, path: string, config: I18nRoutingConfig): LocaleContext;
113
+ /**
114
+ * I18n routing middleware for Zero's server.
115
+ *
116
+ * - Detects locale from URL prefix or Accept-Language header
117
+ * - Redirects root to preferred locale (when detectLocale is true)
118
+ * - Sets locale context for loaders and components
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * // zero.config.ts
123
+ * import { i18nRouting } from "@pyreon/zero"
124
+ *
125
+ * export default defineConfig({
126
+ * plugins: [
127
+ * i18nRouting({
128
+ * locales: ["en", "de", "cs"],
129
+ * defaultLocale: "en",
130
+ * }),
131
+ * ],
132
+ * })
133
+ * ```
134
+ */
135
+ declare function i18nRouting(config: I18nRoutingConfig): Plugin;
136
+ //#endregion
64
137
  //#region src/types.d.ts
65
138
  type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
66
139
  interface ISRConfig {
@@ -73,6 +146,42 @@ interface ISRConfig {
73
146
  * space (e.g. `/user/:id` where `:id` is free-form).
74
147
  */
75
148
  maxEntries?: number;
149
+ /**
150
+ * Cache-key derivation function. The default keys cache entries by
151
+ * `url.pathname` ONLY — query strings, cookies, and headers are
152
+ * stripped.
153
+ *
154
+ * **⚠️ Auth-gated incompatibility.** The default behavior is
155
+ * unsafe for request-dependent loaders. A loader that reads
156
+ * `request.headers.get('cookie')` to gate auth will render ONCE
157
+ * with the first user's cookie, then serve that HTML to every
158
+ * subsequent user. To use ISR with personalized / auth-gated
159
+ * pages, supply a `cacheKey` that varies on the auth identifier
160
+ * (session cookie, user-id header, etc.), OR don't use ISR for
161
+ * such routes — use SSR instead.
162
+ *
163
+ * @example
164
+ * // Vary cache by session cookie:
165
+ * isr: {
166
+ * revalidate: 60,
167
+ * cacheKey: (req) => {
168
+ * const url = new URL(req.url)
169
+ * const session = req.headers.get('cookie')?.match(/session=([^;]+)/)?.[1] ?? 'anon'
170
+ * return `${url.pathname}::${session}`
171
+ * },
172
+ * }
173
+ *
174
+ * @example
175
+ * // Vary by a query parameter:
176
+ * isr: {
177
+ * revalidate: 60,
178
+ * cacheKey: (req) => {
179
+ * const url = new URL(req.url)
180
+ * return `${url.pathname}?sort=${url.searchParams.get('sort') ?? ''}`
181
+ * },
182
+ * }
183
+ */
184
+ cacheKey?: (req: Request) => string;
76
185
  }
77
186
  interface ZeroConfig {
78
187
  /** Default rendering mode. Default: "ssr" */
@@ -86,13 +195,167 @@ interface ZeroConfig {
86
195
  /** SSG options — only used when mode is "ssg". */
87
196
  ssg?: {
88
197
  /** Paths to prerender (or function returning paths). */paths?: string[] | (() => string[] | Promise<string[]>);
198
+ /**
199
+ * Auto-emit `dist/404.html` from the route tree's `_404.tsx` /
200
+ * `_not-found.tsx` convention. fs-router already wires `_404.tsx` as
201
+ * `notFoundComponent` on its parent layout route; the SSG plugin walks
202
+ * the tree, picks up the first one, renders it through the same SSR
203
+ * pipeline as regular paths (so styler CSS / @pyreon/head metadata land
204
+ * correctly), and writes the result to `dist/404.html`. Static hosts
205
+ * (Netlify, Cloudflare Pages, GitHub Pages, S3+CloudFront) serve this
206
+ * file automatically for unmatched URLs. Default: `true`. Set to
207
+ * `false` to opt out — the route tree is left alone.
208
+ */
209
+ emit404?: boolean;
210
+ /**
211
+ * When a route loader throws `redirect('/target')` during SSG, write
212
+ * a `dist/_redirects` file (Netlify / Cloudflare Pages convention)
213
+ * AND a `dist/_redirects.json` (Vercel convention) listing every
214
+ * redirected source path → target. Static hosts pick whichever
215
+ * format their platform supports automatically. The redirected
216
+ * path's HTML file is NOT emitted — the redirect is the response.
217
+ *
218
+ * Without this option, redirect-throwing loaders land in
219
+ * `errors[]` and the path silently disappears from the build —
220
+ * the user sees no output for `/old` AND no warning that the
221
+ * loader ran a redirect. Default: `true`. Set to `false` to
222
+ * restore the pre-PR-B behaviour (redirects treated as errors).
223
+ */
224
+ emitRedirects?: boolean;
225
+ /**
226
+ * Additionally emit a static HTML file at the source path with a
227
+ * `<meta http-equiv="refresh">` redirect — for adapters / hosts
228
+ * that don't read `_redirects` (plain S3, GitHub Pages, simple
229
+ * file servers). The meta-refresh fallback works on any HTTP
230
+ * server that serves static files.
231
+ *
232
+ * - `'none'` (default): only `_redirects` / `_redirects.json` are
233
+ * emitted; no per-redirect HTML file.
234
+ * - `'meta-refresh'`: emit `dist/<source>/index.html` containing
235
+ * `<meta http-equiv="refresh" content="0; url=<target>">` plus
236
+ * a canonical link tag for SEO. Status code information is
237
+ * lost (meta-refresh has no status equivalent), so 301/302/307/
238
+ * 308 all collapse to "client-side refresh".
239
+ */
240
+ redirectsAsHtml?: 'none' | 'meta-refresh';
241
+ /**
242
+ * Callback invoked when a path's render throws (loader-throw that
243
+ * isn't a `redirect()`, render exception, anything that lands in the
244
+ * `errors[]` collection). Returns either:
245
+ * - `string` → written as the path's HTML in place of the failed
246
+ * render. Use this to emit a per-path fallback page (e.g. a generic
247
+ * "this content is temporarily unavailable" template) so static
248
+ * hosts have something to serve at that URL instead of 404'ing.
249
+ * - `null` → skip; the path produces no HTML output. The error
250
+ * stays in `errors[]` for the post-build summary.
251
+ *
252
+ * The callback runs ONCE per failed path. Async callbacks are
253
+ * awaited. If the callback itself throws, the throw is captured as
254
+ * a separate error entry and the path is skipped (no fallback HTML).
255
+ * Default: `undefined` — failed paths just land in `errors[]`.
256
+ *
257
+ * @example
258
+ * ssg: {
259
+ * onPathError: async (path, error) => {
260
+ * console.error(\`SSG render failed for \${path}:\`, error)
261
+ * return \`<!DOCTYPE html><html><body><h1>Page unavailable</h1></body></html>\`
262
+ * },
263
+ * }
264
+ */
265
+ onPathError?: (path: string, error: unknown) => string | null | Promise<string | null>;
266
+ /**
267
+ * When `'json'` (default), write `dist/_pyreon-ssg-errors.json` after
268
+ * the render loop summarising every error encountered (path traversal,
269
+ * timeout, render exception, getStaticPaths throw, fallback callback
270
+ * throw). Each entry has `{ path, message, name, stack }`. The file
271
+ * is ONLY written when `errors.length > 0` — successful builds don't
272
+ * leak an empty manifest. Reading it lets CI gate on render failures
273
+ * without parsing console output (e.g.
274
+ * `cat dist/_pyreon-ssg-errors.json | jq '.errors | length' | grep -q 0`).
275
+ *
276
+ * Set to `'none'` to opt out entirely — errors stay in console-only,
277
+ * matching pre-PR-G behaviour.
278
+ */
279
+ errorArtifact?: 'json' | 'none';
280
+ /**
281
+ * Maximum number of paths rendered in parallel during the SSG closeBundle
282
+ * loop. Default: `4` — a sensible balance between speedup and the risk
283
+ * of exhausting downstream resources (DB connection pools, fetch
284
+ * rate-limits) inside loaders. Set to `1` to render fully sequentially
285
+ * (the pre-PR-D behaviour). Set to a higher value for faster builds
286
+ * on CI / multi-core hosts; the practical ceiling is the number of
287
+ * loader-side concurrent connections your app's data layer tolerates.
288
+ *
289
+ * The render-error pipeline (`onPathError` callback, `errors[]`
290
+ * collection, `_pyreon-ssg-errors.json` artifact) is unchanged —
291
+ * concurrency only affects how many paths are in flight at once,
292
+ * not how their successes / failures are recorded.
293
+ *
294
+ * @example
295
+ * ssg: {
296
+ * concurrency: 8, // Faster builds for static-content sites
297
+ * }
298
+ */
299
+ concurrency?: number;
300
+ /**
301
+ * Per-path progress callback. Invoked once per path AFTER its render
302
+ * settles (success, redirect, OR failure) — never during in-flight
303
+ * renders. Receives `{ completed, total, currentPath, elapsed }`
304
+ * where:
305
+ * - `completed` is the count of paths whose render has settled (1-indexed)
306
+ * - `total` is the full path count from `resolvePaths()`
307
+ * - `currentPath` is the path that just settled
308
+ * - `elapsed` is wall-clock ms since the loop started
309
+ *
310
+ * Use cases: build-tool progress bars (Vite picks up stdout), CI
311
+ * heartbeat lines on long builds (10k-path sites take minutes —
312
+ * silent stretches look hung), build-time perf instrumentation.
313
+ *
314
+ * Async callbacks are awaited before the next path's progress fires,
315
+ * so a slow callback can serialize progress reporting (it does NOT
316
+ * gate the worker pool — paths keep rendering in parallel; only
317
+ * the progress callbacks themselves are serialized). Throws are
318
+ * captured into `errors[]` with the path suffix `(onProgress)` so
319
+ * a buggy callback can't take down the build.
320
+ *
321
+ * @example
322
+ * ssg: {
323
+ * onProgress: ({ completed, total, currentPath, elapsed }) => {
324
+ * console.log(`[${completed}/${total}] ${currentPath} (${elapsed}ms)`)
325
+ * },
326
+ * }
327
+ */
328
+ onProgress?: (info: {
329
+ completed: number;
330
+ total: number;
331
+ currentPath: string;
332
+ elapsed: number;
333
+ }) => void | Promise<void>;
89
334
  };
90
335
  /** ISR config — only used when mode is "isr". */
91
336
  isr?: ISRConfig;
92
- /** Deploy adapter. Default: "node" */
93
- adapter?: 'node' | 'bun' | 'static' | 'vercel' | 'cloudflare' | 'netlify';
337
+ /**
338
+ * Deploy adapter. Default: `"node"`.
339
+ *
340
+ * Accepts either a built-in adapter name (string) OR a constructed
341
+ * `Adapter` instance (e.g. `vercelAdapter()`). The scaffolded templates
342
+ * emit the instance form (`adapter: vercelAdapter()`) by convention.
343
+ * `resolveAdapter` (see `adapters/index.ts`) accepts both shapes —
344
+ * strings go through a switch lookup, instances pass through
345
+ * unchanged.
346
+ */
347
+ adapter?: 'node' | 'bun' | 'static' | 'vercel' | 'cloudflare' | 'netlify' | Adapter;
94
348
  /** Base URL path. Default: "/" */
95
349
  base?: string;
350
+ /**
351
+ * i18n routing — locale-prefixed route variants generated at build time
352
+ * (PR H of the SSG roadmap). When set, every `FileRoute` is fanned into
353
+ * per-locale duplicates by `expandRoutesForLocales` from
354
+ * `@pyreon/zero`. Independent from the `i18nRouting()` Vite plugin
355
+ * (which only handles request-time locale detection); both can be used
356
+ * together. See `expandRoutesForLocales` JSDoc for strategy semantics.
357
+ */
358
+ i18n?: I18nRoutingConfig;
96
359
  /** App-level middleware applied to all routes. */
97
360
  middleware?: Middleware[];
98
361
  /** Server port for dev/preview. Default: 3000 */
@@ -118,6 +381,42 @@ interface RouteFileExports {
118
381
  hasError: boolean;
119
382
  /** Has `export const middleware` */
120
383
  hasMiddleware: boolean;
384
+ /**
385
+ * Has `export const loaderKey` or `export function loaderKey`. When present,
386
+ * the route generator wires it as the `loaderKey` field on the route record,
387
+ * which controls cache identity for `_loaderCache`. Useful for auth-gate
388
+ * loaders that should invalidate when the session cookie changes — read
389
+ * `document.cookie` (CSR) or `ctx.request.headers.get('cookie')` (SSR) and
390
+ * derive a key from session identity. Default cache key is `path + params`,
391
+ * which doesn't see cookie changes.
392
+ */
393
+ hasLoaderKey: boolean;
394
+ /**
395
+ * Has `export const gcTime` (number, in ms). When present, the route generator
396
+ * inlines it on the route record. `gcTime: 0` disables caching entirely —
397
+ * the loader runs on every navigation. Useful for auth-gate loaders that
398
+ * must validate session on every navigation rather than serve stale data.
399
+ */
400
+ hasGcTime: boolean;
401
+ /**
402
+ * Has `export function getStaticPaths` or `export const getStaticPaths`.
403
+ * Used at SSG build time to enumerate concrete values for dynamic routes
404
+ * (`/posts/[id].tsx` → `[/posts/1, /posts/2, …]`). The function returns
405
+ * `Array<{ params: Record<string, string> }>`. Mirrors Astro's per-route
406
+ * convention. Without it, dynamic routes are silently skipped during SSG
407
+ * auto-detect — the user must hand-list every value in `ssg.paths`.
408
+ */
409
+ hasGetStaticPaths: boolean;
410
+ /**
411
+ * Has `export const revalidate` (number, in seconds, or `false` for
412
+ * never-revalidate). PR I — build-time ISR. The SSG plugin emits a
413
+ * `dist/_pyreon-revalidate.json` manifest mapping `{ path: revalidate }`
414
+ * which the deploy adapter (Vercel / Cloudflare / Netlify) consumes
415
+ * to wire platform-specific ISR rebuild-on-stale. The route generator
416
+ * does NOT inline `revalidate` onto the route record — it's a
417
+ * build-time-only concern that never reaches the runtime router.
418
+ */
419
+ hasRevalidate: boolean;
121
420
  /**
122
421
  * Raw text of the `export const meta = …` initializer, captured as a
123
422
  * literal expression. When present, the route generator inlines this
@@ -137,6 +436,22 @@ interface RouteFileExports {
137
436
  * as a literal expression. Same inlining strategy as `metaLiteral`.
138
437
  */
139
438
  renderModeLiteral?: string;
439
+ /**
440
+ * Raw text of the `export const revalidate = …` initializer (e.g.
441
+ * `'60'`, `'false'`, `'3600'`). Captured at scan time so the SSG
442
+ * plugin can read the value to emit the build-time ISR manifest
443
+ * WITHOUT loading the route module — which is critical because the
444
+ * manifest is emitted from the synthetic SSR build's outer plugin
445
+ * context, where evaluating route modules would re-trigger the
446
+ * recursive sub-build env-flag guard.
447
+ *
448
+ * Only set when the revalidate export is a top-level
449
+ * `export const revalidate = <numeric|boolean literal>` that passes
450
+ * `isPureLiteral`. Anything else (function calls, references to
451
+ * other declarations) leaves this undefined and the manifest falls
452
+ * back to omitting the entry.
453
+ */
454
+ revalidateLiteral?: string;
140
455
  }
141
456
  /** Internal representation of a file-system route before conversion to RouteRecord. */
142
457
  interface FileRoute {
@@ -179,16 +494,71 @@ interface Adapter {
179
494
  name: string;
180
495
  /** Build the production server/output for this adapter. */
181
496
  build(options: AdapterBuildOptions): Promise<void>;
497
+ /**
498
+ * Revalidate a prerendered path on the deploy platform's ISR layer
499
+ * (PR I — build-time ISR). Called by user code (webhook handlers,
500
+ * cron jobs, CMS triggers, etc.) to trigger a rebuild-on-stale for
501
+ * the named path. Optional — adapters without platform ISR support
502
+ * (static, node, bun) implement a no-op. Returns `{ regenerated:
503
+ * boolean }` so user code can branch on whether the platform actually
504
+ * accepted the revalidation request.
505
+ *
506
+ * Distinct from runtime ISR (`mode: 'isr'`, on-demand LRU caching in
507
+ * `@pyreon/zero/server`'s `createISRHandler`). Build-time ISR is
508
+ * static prerender + platform-driven rebuild-on-stale; runtime ISR is
509
+ * SSR-cached-with-TTL. They can coexist.
510
+ *
511
+ * Per-route `revalidate` metadata flows from `export const revalidate
512
+ * = 60` in route files into a `dist/_pyreon-revalidate.json` manifest
513
+ * the adapter reads at deploy time. Adapters use that manifest to
514
+ * configure platform ISR (Vercel `output/config.json`, Cloudflare
515
+ * Cache API rules, Netlify revalidation headers).
516
+ */
517
+ revalidate?(path: string): Promise<AdapterRevalidateResult>;
182
518
  }
183
- interface AdapterBuildOptions {
184
- /** Path to the built server entry. */
185
- serverEntry: string;
186
- /** Path to the client build output. */
187
- clientOutDir: string;
188
- /** Final output directory. */
519
+ /**
520
+ * Result of `Adapter.revalidate(path)`. `regenerated: false` means the
521
+ * adapter does not support platform ISR (no-op fallback) OR the
522
+ * platform rejected the request. Adapters that throw on platform-API
523
+ * failure should let it propagate so user code can handle the rejection.
524
+ */
525
+ interface AdapterRevalidateResult {
526
+ regenerated: boolean;
527
+ }
528
+ /**
529
+ * Inputs the build pipeline passes to an adapter's `build()` method.
530
+ *
531
+ * The `kind` field discriminates the two shapes. **SSR mode** (`'ssr'`)
532
+ * carries `serverEntry` + `clientOutDir` so adapters can wrap the user's
533
+ * server bundle as a serverless function. **SSG mode** (`'ssg'`) carries
534
+ * only `outDir` (which IS the rendered dist/) — no serverEntry exists
535
+ * because every page is already prerendered. SSG-mode adapters write
536
+ * platform-specific routing config so the host knows the deploy is
537
+ * fully-static (no function invocation per request).
538
+ *
539
+ * Pre-PR-J this was a single SSR-shaped struct; the SSG path had no way
540
+ * to invoke `adapter.build()` because it couldn't supply `serverEntry`.
541
+ * Adding `kind` (with TS-narrowing per branch) lets `ssgPlugin`
542
+ * `closeBundle` call `adapter.build({ kind: 'ssg', outDir, config })`
543
+ * cleanly, AND keeps the SSR-mode adapter implementations unchanged.
544
+ */
545
+ type AdapterBuildOptions = {
546
+ kind: 'ssr'; /** Path to the built server entry. */
547
+ serverEntry: string; /** Path to the client build output. */
548
+ clientOutDir: string; /** Final output directory. */
189
549
  outDir: string;
190
550
  config: ZeroConfig;
191
- }
551
+ } | {
552
+ kind: 'ssg';
553
+ /**
554
+ * The rendered dist directory. For SSG, this directory IS the
555
+ * publishable output — adapters write platform-specific routing
556
+ * config alongside (e.g. `.vercel/output/config.json`,
557
+ * `_routes.json`, `netlify.toml`) but generally don't move files.
558
+ */
559
+ outDir: string;
560
+ config: ZeroConfig;
561
+ };
192
562
  //#endregion
193
563
  //#region src/entry-server.d.ts
194
564
  interface CreateServerOptions {
@@ -240,6 +610,33 @@ declare function defineConfig(config: ZeroConfig): ZeroConfig;
240
610
  declare function resolveConfig(userConfig?: ZeroConfig): Required<Pick<ZeroConfig, 'mode' | 'base' | 'port' | 'adapter'>> & ZeroConfig;
241
611
  //#endregion
242
612
  //#region src/fs-router.d.ts
613
+ /**
614
+ * Return type of a route file's `getStaticPaths()` export. Each entry
615
+ * supplies one set of concrete values for the route's dynamic segments;
616
+ * the SSG plugin expands the route's URL pattern with these params and
617
+ * renders one HTML file per entry.
618
+ *
619
+ * @example
620
+ * ```tsx
621
+ * // src/routes/posts/[id].tsx
622
+ * import type { GetStaticPaths } from '@pyreon/zero/server'
623
+ *
624
+ * export const getStaticPaths: GetStaticPaths<{ id: string }> = async () => {
625
+ * const posts = await fetch('https://api.example.com/posts').then(r => r.json())
626
+ * return posts.map((p) => ({ params: { id: p.slug } }))
627
+ * }
628
+ *
629
+ * export default function Post() { ... }
630
+ * ```
631
+ *
632
+ * For catch-all routes (`/blog/[...slug].tsx`), pass the full path through
633
+ * the catch-all param: `{ params: { slug: 'a/b' } }` → `/blog/a/b`.
634
+ */
635
+ type GetStaticPaths<TParams extends Record<string, string> = Record<string, string>> = () => Array<{
636
+ params: TParams;
637
+ }> | Promise<Array<{
638
+ params: TParams;
639
+ }>>;
243
640
  /**
244
641
  * Parse a set of file paths (relative to routes dir) into FileRoute objects.
245
642
  *
@@ -281,6 +678,12 @@ declare function generateRouteModule(files: string[], routesDir: string, options
281
678
  /**
282
679
  * Generate a virtual module that maps URL patterns to their middleware exports.
283
680
  * Used by the server entry to dispatch per-route middleware.
681
+ *
682
+ * Detects whether each route file actually exports `middleware` (via
683
+ * `detectRouteExports` source scanning) and only emits an import for files
684
+ * that do. The `lazy()` import path tolerates missing exports, but the SSG
685
+ * static-import path fails Rolldown's missing-export check at build time —
686
+ * skipping no-middleware files keeps both paths working.
284
687
  */
285
688
  declare function generateMiddlewareModule(files: string[], routesDir: string): string;
286
689
  /**
@@ -305,9 +708,97 @@ declare function scanRouteFiles(routesDir: string): Promise<string[]>;
305
708
  */
306
709
  declare function createISRHandler(handler: (req: Request) => Promise<Response>, config: ISRConfig): (req: Request) => Promise<Response>;
307
710
  //#endregion
711
+ //#region src/vercel-revalidate-handler.d.ts
712
+ /**
713
+ * M3.1 — Drop-in Vercel revalidate webhook handler.
714
+ *
715
+ * Pre-M3.1 the `vercelAdapter.revalidate(path)` (PR I) POSTed to
716
+ * `/api/_pyreon-revalidate?path=...&secret=...` — a CONVENTION that users
717
+ * had to implement themselves. This helper scaffolds the convention:
718
+ *
719
+ * // src/routes/api/_pyreon-revalidate.ts (or `pages/api/...` in
720
+ * // Next-style apps deployed to Vercel)
721
+ * export { vercelRevalidateHandler as default } from '@pyreon/zero/server'
722
+ *
723
+ * The handler validates the secret query param against
724
+ * `VERCEL_REVALIDATE_TOKEN`, validates the path is in the build-time
725
+ * revalidate manifest, and calls Vercel's `res.revalidate(path)` API.
726
+ *
727
+ * Returns a standard `(req: Request) => Response` Web API handler — works
728
+ * with Vercel Edge functions, Node serverless functions (via Vercel's
729
+ * `@vercel/node` adapter that bridges Node `req`/`res` to Web standard
730
+ * fetch shapes), and the in-process `mode: 'ssr'` runtime.
731
+ *
732
+ * @example
733
+ * // src/routes/api/_pyreon-revalidate.ts
734
+ * import { vercelRevalidateHandler } from '@pyreon/zero/server'
735
+ *
736
+ * export const POST = vercelRevalidateHandler({
737
+ * // Optional — defaults to reading `_pyreon-revalidate.json` from cwd.
738
+ * manifestPath: './dist/_pyreon-revalidate.json',
739
+ * })
740
+ *
741
+ * @example
742
+ * // Custom revalidate impl (e.g. for a self-hosted Pyreon SSR runtime
743
+ * // that wants build-time revalidate behavior without Vercel's
744
+ * // `res.revalidate()` API):
745
+ * export const POST = vercelRevalidateHandler({
746
+ * onRevalidate: async (path) => {
747
+ * // Clear your in-process ISR cache, emit a metrics event, etc.
748
+ * await myCache.invalidate(path)
749
+ * },
750
+ * })
751
+ */
752
+ interface VercelRevalidateHandlerOptions {
753
+ /**
754
+ * Absolute or cwd-relative path to the `_pyreon-revalidate.json` manifest.
755
+ * Defaults to `./dist/_pyreon-revalidate.json` (the standard SSG output).
756
+ *
757
+ * The handler refuses to revalidate paths NOT in this manifest — protects
758
+ * against arbitrary-path revalidation attacks even when the secret leaks.
759
+ */
760
+ manifestPath?: string;
761
+ /**
762
+ * Custom revalidation impl. Defaults to calling Vercel's `res.revalidate()`
763
+ * API via the dynamic `@vercel/node`-bridged response object on globalThis
764
+ * (Vercel injects it for serverless functions).
765
+ *
766
+ * Supply this when running OUTSIDE Vercel (self-hosted SSR with a custom
767
+ * in-process ISR cache, edge runtimes that have their own purge API, etc.).
768
+ * Receives the validated path; throw to signal failure (handler returns 500).
769
+ */
770
+ onRevalidate?: (path: string) => void | Promise<void>;
771
+ /**
772
+ * Override the env-var name the handler reads the secret from. Default
773
+ * `VERCEL_REVALIDATE_TOKEN` matches the adapter's `revalidate()` write.
774
+ * Useful when adopting the helper outside Vercel and the production
775
+ * webhook uses a different secret name.
776
+ */
777
+ secretEnvVar?: string;
778
+ }
779
+ /**
780
+ * Create the Web-standard request handler. Reads the manifest once on first
781
+ * invocation (cached in-process) so repeated revalidations don't re-read the
782
+ * file. Manifest read failures cache the failure too — until next process
783
+ * restart, all requests get the same 500 response (signals deploy-time misconfig).
784
+ */
785
+ declare function vercelRevalidateHandler(options?: VercelRevalidateHandlerOptions): (req: Request) => Promise<Response>;
786
+ /**
787
+ * Reset the in-process manifest cache. Test-only — production code never
788
+ * reaches this. Used by unit tests to exercise the "manifest changed
789
+ * between requests" path without spinning up a new handler.
790
+ * @internal
791
+ */
792
+ declare function _resetVercelRevalidateHandlerCache(handler: (req: Request) => Promise<Response>): void;
793
+ //#endregion
308
794
  //#region src/adapters/bun.d.ts
309
795
  /**
310
796
  * Bun adapter — generates a standalone Bun.serve() entry.
797
+ *
798
+ * **SSG mode (PR J)**: no-op. Bun adapter exists for serving the SSR
799
+ * runtime; SSG output is already complete static HTML — serve it with
800
+ * any static-file server (`bun preview` / `bunx serve` / nginx / Caddy).
801
+ * Use `staticAdapter()` if you want explicit SSG semantics.
311
802
  */
312
803
  declare function bunAdapter(): Adapter;
313
804
  //#endregion
@@ -361,6 +852,11 @@ declare function netlifyAdapter(): Adapter;
361
852
  //#region src/adapters/node.d.ts
362
853
  /**
363
854
  * Node.js adapter — generates a standalone server entry using node:http.
855
+ *
856
+ * **SSG mode (PR J)**: no-op. Node adapter exists for serving the SSR
857
+ * runtime; SSG output is already complete static HTML — serve it with
858
+ * any static-file server (`bun preview` / nginx / Caddy / `npx serve`).
859
+ * Use `staticAdapter()` if you want explicit SSG semantics.
364
860
  */
365
861
  declare function nodeAdapter(): Adapter;
366
862
  //#endregion
@@ -368,6 +864,15 @@ declare function nodeAdapter(): Adapter;
368
864
  /**
369
865
  * Static adapter — just copies the client build output.
370
866
  * Used with SSG mode where all pages are pre-rendered at build time.
867
+ *
868
+ * **SSG mode (PR J)**: no-op — `outDir` already IS the dist directory
869
+ * the SSG plugin produced. Copying it onto itself would only fail. The
870
+ * static adapter is the canonical zero-overhead deploy target for
871
+ * pure-static sites.
872
+ *
873
+ * **SSR mode**: copies clientOutDir → outDir. Calling `static` with SSR
874
+ * mode is unusual — the static adapter doesn't support server-side
875
+ * execution — but preserved as a "client-only output packager".
371
876
  */
372
877
  declare function staticAdapter(): Adapter;
373
878
  //#endregion
@@ -396,6 +901,16 @@ declare function vercelAdapter(): Adapter;
396
901
  /**
397
902
  * Resolve the adapter from config.
398
903
  * Returns a built-in adapter or throws if unknown.
904
+ *
905
+ * Accepts BOTH forms — the `ZeroConfig.adapter` type advertises string
906
+ * names (`'vercel'` / `'cloudflare'` / …) but the scaffolded templates
907
+ * historically emit `adapter: vercelAdapter()` (an Adapter instance via
908
+ * the named factory). Both work: a string goes through the switch lookup;
909
+ * an Adapter object (duck-typed via `name` + `build` fields) passes
910
+ * through. Pre-PR-J `resolveAdapter` was never called from production
911
+ * code so the string-vs-object mismatch was invisible; PR J wires the
912
+ * call into `ssgPlugin.closeBundle`, surfacing the contract divergence.
913
+ * The passthrough preserves both shapes without a breaking type change.
399
914
  */
400
915
  declare function resolveAdapter(config: ZeroConfig): Adapter;
401
916
  //#endregion
@@ -442,6 +957,12 @@ declare function compose(...middlewares: Middleware[]): Middleware;
442
957
  declare function getContext(ctx: MiddlewareContext): Record<string, unknown>;
443
958
  //#endregion
444
959
  //#region src/vite-plugin.d.ts
960
+ /**
961
+ * Retrieve the `ZeroConfig` that was passed to `zeroPlugin(userConfig)`
962
+ * when the plugin was created. Returns `undefined` if the argument
963
+ * isn't a recognized pyreon-zero main plugin instance.
964
+ */
965
+ declare function getZeroPluginConfig(plugin: Plugin): ZeroConfig | undefined;
445
966
  /**
446
967
  * Zero Vite plugin — adds file-based routing and zero-config conventions
447
968
  * on top of @pyreon/vite-plugin.
@@ -455,7 +976,7 @@ declare function getContext(ctx: MiddlewareContext): Record<string, unknown>;
455
976
  * plugins: [pyreon(), zero()],
456
977
  * }
457
978
  */
458
- declare function zeroPlugin(userConfig?: ZeroConfig): Plugin;
979
+ declare function zeroPlugin(userConfig?: ZeroConfig): Plugin[];
459
980
  //#endregion
460
981
  //#region src/favicon.d.ts
461
982
  interface FaviconLocaleConfig {
@@ -565,6 +1086,62 @@ interface SitemapConfig {
565
1086
  priority?: number;
566
1087
  /** Additional URLs to include (for dynamic routes). */
567
1088
  additionalPaths?: SitemapEntry[];
1089
+ /**
1090
+ * When `true` AND the build is running in SSG mode, the sitemap reads
1091
+ * the resolved-paths manifest emitted by the SSG plugin
1092
+ * (`dist/_pyreon-ssg-paths.json`) and includes EVERY prerendered URL —
1093
+ * including dynamic routes enumerated via `getStaticPaths` (PR A) and
1094
+ * per-locale variants (PR H, when shipped). Without this flag the
1095
+ * sitemap walks the file-system route tree directly and silently
1096
+ * skips dynamic routes (`[id]` / `[...slug]`) because their concrete
1097
+ * values aren't knowable without running each route's enumerator.
1098
+ *
1099
+ * Sequencing: when `true`, sitemap.xml emission moves from Vite's
1100
+ * `generateBundle` hook (where the SSG plugin's path enumeration
1101
+ * hasn't run yet) to `closeBundle` with `enforce: 'post'` so it
1102
+ * runs AFTER the SSG plugin. The user must ensure `seoPlugin()` is
1103
+ * placed AFTER `zero()` in the Vite plugin array (the canonical
1104
+ * ordering — `closeBundle` hooks fire in plugin-registration order).
1105
+ *
1106
+ * Falls back gracefully: when the manifest doesn't exist (mode is
1107
+ * not `ssg`, or the SSG step was skipped), the sitemap still walks
1108
+ * the file-system routes — same shape as without this flag.
1109
+ *
1110
+ * Default: `false` (preserves prior behaviour). Set `true` for SSG
1111
+ * sites that ship dynamic-route enumerations.
1112
+ */
1113
+ useSsgPaths?: boolean;
1114
+ /**
1115
+ * Emit `<xhtml:link rel="alternate" hreflang="...">` cross-references
1116
+ * inside each `<url>` entry, declaring the locale variants of every
1117
+ * page (PR K — i18n follow-up).
1118
+ *
1119
+ * Accepts:
1120
+ * - `true` — read the i18n config from the SSG paths manifest
1121
+ * (which `zero({ i18n: ... })` automatically embeds when SSG runs).
1122
+ * Zero-config win — declare i18n once, sitemap picks it up.
1123
+ * - `I18nRoutingConfig` — pass the i18n config explicitly. Use when
1124
+ * the project doesn't run SSG (file-scan sitemap path) but still
1125
+ * wants hreflang in the emitted sitemap.
1126
+ * - `false` / omitted — no hreflang, plain `<url>` entries.
1127
+ *
1128
+ * The emitted shape per page-cluster is the Google-recommended form:
1129
+ *
1130
+ * <url>
1131
+ * <loc>https://example.com/about</loc>
1132
+ * <xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/>
1133
+ * <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/about"/>
1134
+ * <xhtml:link rel="alternate" hreflang="cs" href="https://example.com/cs/about"/>
1135
+ * <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about"/>
1136
+ * </url>
1137
+ *
1138
+ * The `x-default` entry points at the default-locale URL so search
1139
+ * engines have a fallback when the user's language doesn't match any
1140
+ * of the configured locales. URLs are clustered by their un-prefixed
1141
+ * (default-locale) form — `/about`, `/de/about`, `/cs/about` collapse
1142
+ * into ONE `<url>` entry with three `xhtml:link` siblings.
1143
+ */
1144
+ hreflang?: boolean | I18nRoutingConfig;
568
1145
  }
569
1146
  interface SitemapEntry {
570
1147
  path: string;
@@ -575,8 +1152,14 @@ interface SitemapEntry {
575
1152
  type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
576
1153
  /**
577
1154
  * Generate a sitemap.xml string from route file paths.
1155
+ *
1156
+ * When `i18n` is set (PR K — passed by `seoPlugin` after reading the
1157
+ * i18n config from `zero({ i18n: ... })`), URLs are clustered by their
1158
+ * un-prefixed (default-locale) form and each `<url>` carries
1159
+ * `<xhtml:link rel="alternate" hreflang="...">` siblings for every
1160
+ * locale variant + an `x-default` entry pointing at the default locale.
578
1161
  */
579
- declare function generateSitemap(routeFiles: string[], config: SitemapConfig): string;
1162
+ declare function generateSitemap(routeFiles: string[], config: SitemapConfig, i18n?: I18nRoutingConfig): string;
580
1163
  interface RobotsConfig {
581
1164
  /** Rules per user-agent. */
582
1165
  rules?: RobotsRule[];
@@ -626,7 +1209,10 @@ interface SeoPluginConfig {
626
1209
  * pyreon(),
627
1210
  * zero(),
628
1211
  * seoPlugin({
629
- * sitemap: { origin: "https://example.com" },
1212
+ * sitemap: {
1213
+ * origin: "https://example.com",
1214
+ * useSsgPaths: true, // include dynamic-route enumerations
1215
+ * },
630
1216
  * robots: { sitemap: "https://example.com/sitemap.xml" },
631
1217
  * }),
632
1218
  * ],
@@ -881,65 +1467,5 @@ declare function inferJsonLd(options: InferJsonLdOptions): Record<string, unknow
881
1467
  */
882
1468
  declare function aiPlugin(config: AiPluginConfig): Plugin;
883
1469
  //#endregion
884
- //#region src/i18n-routing.d.ts
885
- interface I18nRoutingConfig {
886
- /** Supported locales. e.g. ["en", "de", "cs"] */
887
- locales: string[];
888
- /** Default locale — served without prefix (/ instead of /en/). */
889
- defaultLocale: string;
890
- /** Redirect root to detected locale. Default: true */
891
- detectLocale?: boolean;
892
- /** Cookie name to persist locale preference. Default: "locale" */
893
- cookieName?: string;
894
- /** URL strategy. Default: "prefix-except-default" */
895
- strategy?: 'prefix' | 'prefix-except-default';
896
- }
897
- interface LocaleContext {
898
- /** Current locale code. e.g. "en", "de" */
899
- locale: string;
900
- /** All supported locales. */
901
- locales: string[];
902
- /** Default locale. */
903
- defaultLocale: string;
904
- /** Build a localized path. e.g. localePath("/about", "de") → "/de/about" */
905
- localePath: (path: string, locale?: string) => string;
906
- /** Get hreflang alternates for the current path. */
907
- alternates: () => Array<{
908
- locale: string;
909
- url: string;
910
- }>;
911
- }
912
- /**
913
- * Detect preferred locale from Accept-Language header.
914
- */
915
- declare function detectLocaleFromHeader(acceptLanguage: string | null | undefined, locales: string[], defaultLocale: string): string;
916
- /**
917
- * Create a LocaleContext for use in components and loaders.
918
- */
919
- declare function createLocaleContext(locale: string, path: string, config: I18nRoutingConfig): LocaleContext;
920
- /**
921
- * I18n routing middleware for Zero's server.
922
- *
923
- * - Detects locale from URL prefix or Accept-Language header
924
- * - Redirects root to preferred locale (when detectLocale is true)
925
- * - Sets locale context for loaders and components
926
- *
927
- * @example
928
- * ```ts
929
- * // zero.config.ts
930
- * import { i18nRouting } from "@pyreon/zero"
931
- *
932
- * export default defineConfig({
933
- * plugins: [
934
- * i18nRouting({
935
- * locales: ["en", "de", "cs"],
936
- * defaultLocale: "en",
937
- * }),
938
- * ],
939
- * })
940
- * ```
941
- */
942
- declare function i18nRouting(config: I18nRoutingConfig): Plugin;
943
- //#endregion
944
- export { type AiPluginConfig, type CreateAppOptions, type CreateServerOptions, type FaviconLocaleConfig, type FaviconPluginConfig, type GenerateRouteModuleOptions, type InferJsonLdOptions, type OgImageLayer, type OgImagePluginConfig, type OgImageTemplate, type RobotsConfig, type SeoPluginConfig, type SitemapConfig, aiPlugin, bunAdapter, cloudflareAdapter, compose, createApp, createISRHandler, createLocaleContext, createServer, zeroPlugin as default, defineConfig, detectLocaleFromHeader, faviconLinks, faviconPlugin, filePathToUrlPath, generateLlmsFullTxt, generateLlmsTxt, generateMiddlewareModule, generateRobots, generateRouteModule, generateSitemap, getContext, i18nRouting, inferJsonLd, jsonLd, netlifyAdapter, nodeAdapter, ogImagePath, ogImagePlugin, parseFileRoutes, render404Page, resolveAdapter, resolveConfig, scanRouteFiles, seoMiddleware, seoPlugin, staticAdapter, vercelAdapter };
1470
+ export { type AiPluginConfig, type CreateAppOptions, type CreateServerOptions, type FaviconLocaleConfig, type FaviconPluginConfig, type GenerateRouteModuleOptions, type GetStaticPaths, type InferJsonLdOptions, type OgImageLayer, type OgImagePluginConfig, type OgImageTemplate, type RobotsConfig, type SeoPluginConfig, type SitemapConfig, type VercelRevalidateHandlerOptions, _resetVercelRevalidateHandlerCache, aiPlugin, bunAdapter, cloudflareAdapter, compose, createApp, createISRHandler, createLocaleContext, createServer, zeroPlugin as default, defineConfig, detectLocaleFromHeader, faviconLinks, faviconPlugin, filePathToUrlPath, generateLlmsFullTxt, generateLlmsTxt, generateMiddlewareModule, generateRobots, generateRouteModule, generateSitemap, getContext, getZeroPluginConfig, i18nRouting, inferJsonLd, jsonLd, netlifyAdapter, nodeAdapter, ogImagePath, ogImagePlugin, parseFileRoutes, render404Page, resolveAdapter, resolveConfig, scanRouteFiles, seoMiddleware, seoPlugin, staticAdapter, vercelAdapter, vercelRevalidateHandler };
945
1471
  //# sourceMappingURL=server2.d.ts.map