@pyreon/zero 0.12.1 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/actions.js +97 -0
- package/lib/actions.js.map +1 -0
- package/lib/ai.js +503 -0
- package/lib/ai.js.map +1 -0
- package/lib/api-routes.js +137 -0
- package/lib/api-routes.js.map +1 -0
- package/lib/compression.js +80 -0
- package/lib/compression.js.map +1 -0
- package/lib/cors.js +57 -0
- package/lib/cors.js.map +1 -0
- package/lib/csp.js +119 -0
- package/lib/csp.js.map +1 -0
- package/lib/env.js +217 -0
- package/lib/env.js.map +1 -0
- package/lib/favicon.js +424 -0
- package/lib/favicon.js.map +1 -0
- package/lib/i18n-routing.js +167 -0
- package/lib/i18n-routing.js.map +1 -0
- package/lib/index.js +1631 -179
- package/lib/index.js.map +1 -1
- package/lib/link.js +5 -0
- package/lib/link.js.map +1 -1
- package/lib/logger.js +78 -0
- package/lib/logger.js.map +1 -0
- package/lib/meta.js +336 -0
- package/lib/meta.js.map +1 -0
- package/lib/middleware.js +53 -0
- package/lib/middleware.js.map +1 -0
- package/lib/og-image.js +233 -0
- package/lib/og-image.js.map +1 -0
- package/lib/rate-limit.js +76 -0
- package/lib/rate-limit.js.map +1 -0
- package/lib/testing.js +179 -0
- package/lib/testing.js.map +1 -0
- package/lib/theme.js +11 -2
- package/lib/theme.js.map +1 -1
- package/lib/types/actions.d.ts +27 -24
- package/lib/types/actions.d.ts.map +1 -1
- package/lib/types/ai.d.ts +163 -0
- package/lib/types/ai.d.ts.map +1 -0
- package/lib/types/api-routes.d.ts +37 -33
- package/lib/types/api-routes.d.ts.map +1 -1
- package/lib/types/cache.d.ts +26 -22
- package/lib/types/cache.d.ts.map +1 -1
- package/lib/types/client.d.ts +13 -9
- package/lib/types/client.d.ts.map +1 -1
- package/lib/types/compression.d.ts +14 -10
- package/lib/types/compression.d.ts.map +1 -1
- package/lib/types/config.d.ts +39 -4
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/cors.d.ts +20 -16
- package/lib/types/cors.d.ts.map +1 -1
- package/lib/types/csp.d.ts +88 -0
- package/lib/types/csp.d.ts.map +1 -0
- package/lib/types/env.d.ts +118 -0
- package/lib/types/env.d.ts.map +1 -0
- package/lib/types/favicon.d.ts +70 -24
- package/lib/types/favicon.d.ts.map +1 -1
- package/lib/types/font.d.ts +68 -65
- package/lib/types/font.d.ts.map +1 -1
- package/lib/types/i18n-routing.d.ts +43 -37
- package/lib/types/i18n-routing.d.ts.map +1 -1
- package/lib/types/image-plugin.d.ts +49 -45
- package/lib/types/image-plugin.d.ts.map +1 -1
- package/lib/types/image.d.ts +47 -36
- package/lib/types/image.d.ts.map +1 -1
- package/lib/types/index.d.ts +1961 -46
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/link.d.ts +61 -56
- package/lib/types/link.d.ts.map +1 -1
- package/lib/types/logger.d.ts +57 -0
- package/lib/types/logger.d.ts.map +1 -0
- package/lib/types/meta.d.ts +180 -69
- package/lib/types/meta.d.ts.map +1 -1
- package/lib/types/middleware.d.ts +8 -4
- package/lib/types/middleware.d.ts.map +1 -1
- package/lib/types/og-image.d.ts +111 -0
- package/lib/types/og-image.d.ts.map +1 -0
- package/lib/types/rate-limit.d.ts +20 -16
- package/lib/types/rate-limit.d.ts.map +1 -1
- package/lib/types/script.d.ts +23 -19
- package/lib/types/script.d.ts.map +1 -1
- package/lib/types/seo.d.ts +47 -43
- package/lib/types/seo.d.ts.map +1 -1
- package/lib/types/testing.d.ts +64 -27
- package/lib/types/testing.d.ts.map +1 -1
- package/lib/types/theme.d.ts +22 -12
- package/lib/types/theme.d.ts.map +1 -1
- package/package.json +37 -12
- package/src/actions.ts +1 -3
- package/src/adapters/bun.ts +2 -0
- package/src/adapters/cloudflare.ts +84 -0
- package/src/adapters/index.ts +13 -1
- package/src/adapters/netlify.ts +86 -0
- package/src/adapters/node.ts +2 -0
- package/src/adapters/validate.ts +16 -0
- package/src/adapters/vercel.ts +86 -0
- package/src/ai.ts +623 -0
- package/src/compression.ts +19 -3
- package/src/csp.ts +207 -0
- package/src/entry-server.ts +28 -5
- package/src/env.ts +344 -0
- package/src/favicon.ts +221 -80
- package/src/index.ts +42 -2
- package/src/link.tsx +6 -0
- package/src/logger.ts +144 -0
- package/src/meta.tsx +124 -14
- package/src/og-image.ts +378 -0
- package/src/rate-limit.ts +11 -9
- package/src/theme.tsx +12 -1
- package/src/types.ts +1 -1
- package/src/vite-plugin.ts +5 -1
- package/lib/types/adapters/bun.d.ts +0 -6
- package/lib/types/adapters/bun.d.ts.map +0 -1
- package/lib/types/adapters/index.d.ts +0 -10
- package/lib/types/adapters/index.d.ts.map +0 -1
- package/lib/types/adapters/node.d.ts +0 -6
- package/lib/types/adapters/node.d.ts.map +0 -1
- package/lib/types/adapters/static.d.ts +0 -7
- package/lib/types/adapters/static.d.ts.map +0 -1
- package/lib/types/app.d.ts +0 -24
- package/lib/types/app.d.ts.map +0 -1
- package/lib/types/entry-server.d.ts +0 -37
- package/lib/types/entry-server.d.ts.map +0 -1
- package/lib/types/error-overlay.d.ts +0 -6
- package/lib/types/error-overlay.d.ts.map +0 -1
- package/lib/types/fs-router.d.ts +0 -47
- package/lib/types/fs-router.d.ts.map +0 -1
- package/lib/types/isr.d.ts +0 -9
- package/lib/types/isr.d.ts.map +0 -1
- package/lib/types/not-found.d.ts +0 -7
- package/lib/types/not-found.d.ts.map +0 -1
- package/lib/types/types.d.ts +0 -111
- package/lib/types/types.d.ts.map +0 -1
- package/lib/types/utils/use-intersection-observer.d.ts +0 -10
- package/lib/types/utils/use-intersection-observer.d.ts.map +0 -1
- package/lib/types/utils/with-headers.d.ts +0 -6
- package/lib/types/utils/with-headers.d.ts.map +0 -1
- package/lib/types/vite-plugin.d.ts +0 -17
- package/lib/types/vite-plugin.d.ts.map +0 -1
package/lib/types/index.d.ts
CHANGED
|
@@ -1,46 +1,1961 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
import * as _pyreon_core0 from "@pyreon/core";
|
|
2
|
+
import { ComponentFn, VNodeChild } from "@pyreon/core";
|
|
3
|
+
import * as _pyreon_router0 from "@pyreon/router";
|
|
4
|
+
import { NavigationGuard, RouteRecord } from "@pyreon/router";
|
|
5
|
+
import { Middleware, MiddlewareContext } from "@pyreon/server";
|
|
6
|
+
import { Plugin } from "vite";
|
|
7
|
+
import * as _pyreon_reactivity0 from "@pyreon/reactivity";
|
|
8
|
+
|
|
9
|
+
//#region src/app.d.ts
|
|
10
|
+
interface CreateAppOptions {
|
|
11
|
+
/** Route definitions (from file-based routing or manual). */
|
|
12
|
+
routes: RouteRecord[];
|
|
13
|
+
/** Router mode. Default: "history" for SSR, "hash" for SPA. */
|
|
14
|
+
routerMode?: 'hash' | 'history';
|
|
15
|
+
/** Initial URL for SSR. */
|
|
16
|
+
url?: string;
|
|
17
|
+
/** Root layout component wrapping all routes. */
|
|
18
|
+
layout?: ComponentFn;
|
|
19
|
+
/** Global error component. */
|
|
20
|
+
errorComponent?: ComponentFn;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a full Zero app — assembles router, head provider, and root layout.
|
|
24
|
+
*
|
|
25
|
+
* Used internally by entry-server and entry-client.
|
|
26
|
+
*/
|
|
27
|
+
declare function createApp(options: CreateAppOptions): {
|
|
28
|
+
App: () => _pyreon_core0.VNode;
|
|
29
|
+
router: _pyreon_router0.Router;
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/api-routes.d.ts
|
|
33
|
+
/** HTTP methods supported by API routes. */
|
|
34
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
35
|
+
/** Context passed to API route handlers. */
|
|
36
|
+
interface ApiContext {
|
|
37
|
+
/** The incoming request. */
|
|
38
|
+
request: Request;
|
|
39
|
+
/** Parsed URL. */
|
|
40
|
+
url: URL;
|
|
41
|
+
/** URL path. */
|
|
42
|
+
path: string;
|
|
43
|
+
/** Dynamic route parameters (e.g., { id: "123" }). */
|
|
44
|
+
params: Record<string, string>;
|
|
45
|
+
/** Request headers. */
|
|
46
|
+
headers: Headers;
|
|
47
|
+
}
|
|
48
|
+
/** An API route handler function. */
|
|
49
|
+
type ApiHandler = (ctx: ApiContext) => Response | Promise<Response>;
|
|
50
|
+
/** An API route module — exports named HTTP method handlers. */
|
|
51
|
+
interface ApiRouteModule {
|
|
52
|
+
GET?: ApiHandler;
|
|
53
|
+
POST?: ApiHandler;
|
|
54
|
+
PUT?: ApiHandler;
|
|
55
|
+
PATCH?: ApiHandler;
|
|
56
|
+
DELETE?: ApiHandler;
|
|
57
|
+
HEAD?: ApiHandler;
|
|
58
|
+
OPTIONS?: ApiHandler;
|
|
59
|
+
}
|
|
60
|
+
/** A registered API route entry. */
|
|
61
|
+
interface ApiRouteEntry {
|
|
62
|
+
/** URL pattern (e.g., "/api/posts/:id"). */
|
|
63
|
+
pattern: string;
|
|
64
|
+
/** The route module with method handlers. */
|
|
65
|
+
module: ApiRouteModule;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a middleware that dispatches API route requests.
|
|
69
|
+
* API routes are matched by URL pattern and HTTP method.
|
|
70
|
+
*/
|
|
71
|
+
declare function createApiMiddleware(routes: ApiRouteEntry[]): Middleware;
|
|
72
|
+
/**
|
|
73
|
+
* Generate a virtual module that exports API route entries.
|
|
74
|
+
* Each entry maps a URL pattern to a module with HTTP method handlers.
|
|
75
|
+
*/
|
|
76
|
+
declare function generateApiRouteModule(files: string[], routesDir: string): string;
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/types.d.ts
|
|
79
|
+
/** What a route file (e.g. `src/routes/index.tsx`) can export. */
|
|
80
|
+
interface RouteModule {
|
|
81
|
+
/** Default export is the page component. */
|
|
82
|
+
default?: ComponentFn;
|
|
83
|
+
/** Layout wrapper — wraps this route and all children. */
|
|
84
|
+
layout?: ComponentFn;
|
|
85
|
+
/** Loading component shown while lazy-loading or during Suspense. */
|
|
86
|
+
loading?: ComponentFn;
|
|
87
|
+
/** Error component shown when the route errors. */
|
|
88
|
+
error?: ComponentFn;
|
|
89
|
+
/** Server-side data loader. */
|
|
90
|
+
loader?: (ctx: LoaderContext) => Promise<unknown>;
|
|
91
|
+
/** Per-route middleware. */
|
|
92
|
+
middleware?: Middleware | Middleware[];
|
|
93
|
+
/** Navigation guard — can redirect or block navigation. */
|
|
94
|
+
guard?: NavigationGuard;
|
|
95
|
+
/** Route metadata. */
|
|
96
|
+
meta?: RouteMeta;
|
|
97
|
+
/** Rendering mode override for this route. */
|
|
98
|
+
renderMode?: RenderMode;
|
|
99
|
+
}
|
|
100
|
+
/** Context passed to route loaders. */
|
|
101
|
+
interface LoaderContext {
|
|
102
|
+
params: Record<string, string>;
|
|
103
|
+
query: Record<string, string>;
|
|
104
|
+
signal: AbortSignal;
|
|
105
|
+
request: Request;
|
|
106
|
+
}
|
|
107
|
+
/** Per-route metadata. */
|
|
108
|
+
interface RouteMeta {
|
|
109
|
+
title?: string;
|
|
110
|
+
description?: string;
|
|
111
|
+
[key: string]: unknown;
|
|
112
|
+
}
|
|
113
|
+
type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
|
|
114
|
+
interface ISRConfig {
|
|
115
|
+
/** Revalidation interval in seconds. */
|
|
116
|
+
revalidate: number;
|
|
117
|
+
}
|
|
118
|
+
interface ZeroConfig {
|
|
119
|
+
/** Default rendering mode. Default: "ssr" */
|
|
120
|
+
mode?: RenderMode;
|
|
121
|
+
/** Vite config overrides. */
|
|
122
|
+
vite?: Record<string, unknown>;
|
|
123
|
+
/** SSR options. */
|
|
124
|
+
ssr?: {
|
|
125
|
+
/** Streaming mode. Default: "string" */mode?: 'string' | 'stream';
|
|
126
|
+
};
|
|
127
|
+
/** SSG options — only used when mode is "ssg". */
|
|
128
|
+
ssg?: {
|
|
129
|
+
/** Paths to prerender (or function returning paths). */paths?: string[] | (() => string[] | Promise<string[]>);
|
|
130
|
+
};
|
|
131
|
+
/** ISR config — only used when mode is "isr". */
|
|
132
|
+
isr?: ISRConfig;
|
|
133
|
+
/** Deploy adapter. Default: "node" */
|
|
134
|
+
adapter?: 'node' | 'bun' | 'static' | 'vercel' | 'cloudflare' | 'netlify';
|
|
135
|
+
/** Base URL path. Default: "/" */
|
|
136
|
+
base?: string;
|
|
137
|
+
/** App-level middleware applied to all routes. */
|
|
138
|
+
middleware?: Middleware[];
|
|
139
|
+
/** Server port for dev/preview. Default: 3000 */
|
|
140
|
+
port?: number;
|
|
141
|
+
}
|
|
142
|
+
/** Internal representation of a file-system route before conversion to RouteRecord. */
|
|
143
|
+
interface FileRoute {
|
|
144
|
+
/** File path relative to routes dir (e.g. "users/[id].tsx") */
|
|
145
|
+
filePath: string;
|
|
146
|
+
/** Parsed URL path pattern (e.g. "/users/:id") */
|
|
147
|
+
urlPath: string;
|
|
148
|
+
/** Directory path for grouping (e.g. "users" or "" for root) */
|
|
149
|
+
dirPath: string;
|
|
150
|
+
/** Route segment depth for nesting. */
|
|
151
|
+
depth: number;
|
|
152
|
+
/** Whether this is a layout file. */
|
|
153
|
+
isLayout: boolean;
|
|
154
|
+
/** Whether this is an error boundary file. */
|
|
155
|
+
isError: boolean;
|
|
156
|
+
/** Whether this is a loading fallback file. */
|
|
157
|
+
isLoading: boolean;
|
|
158
|
+
/** Whether this is a not-found (404) file. */
|
|
159
|
+
isNotFound: boolean;
|
|
160
|
+
/** Whether this is a catch-all route. */
|
|
161
|
+
isCatchAll: boolean;
|
|
162
|
+
/** Resolved rendering mode. */
|
|
163
|
+
renderMode: RenderMode;
|
|
164
|
+
}
|
|
165
|
+
/** Entry mapping a URL pattern to its route-level middleware. */
|
|
166
|
+
interface RouteMiddlewareEntry {
|
|
167
|
+
pattern: string;
|
|
168
|
+
middleware: Middleware | Middleware[];
|
|
169
|
+
}
|
|
170
|
+
interface Adapter {
|
|
171
|
+
name: string;
|
|
172
|
+
/** Build the production server/output for this adapter. */
|
|
173
|
+
build(options: AdapterBuildOptions): Promise<void>;
|
|
174
|
+
}
|
|
175
|
+
interface AdapterBuildOptions {
|
|
176
|
+
/** Path to the built server entry. */
|
|
177
|
+
serverEntry: string;
|
|
178
|
+
/** Path to the client build output. */
|
|
179
|
+
clientOutDir: string;
|
|
180
|
+
/** Final output directory. */
|
|
181
|
+
outDir: string;
|
|
182
|
+
config: ZeroConfig;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/entry-server.d.ts
|
|
186
|
+
interface CreateServerOptions {
|
|
187
|
+
/** Route definitions. */
|
|
188
|
+
routes: RouteRecord[];
|
|
189
|
+
/** Zero config. */
|
|
190
|
+
config?: ZeroConfig;
|
|
191
|
+
/** Additional middleware. */
|
|
192
|
+
middleware?: Middleware[];
|
|
193
|
+
/** Per-route middleware from virtual:zero/route-middleware. */
|
|
194
|
+
routeMiddleware?: RouteMiddlewareEntry[];
|
|
195
|
+
/** API route entries from virtual:zero/api-routes. */
|
|
196
|
+
apiRoutes?: ApiRouteEntry[];
|
|
197
|
+
/** HTML template override. */
|
|
198
|
+
template?: string;
|
|
199
|
+
/** Client entry path. */
|
|
200
|
+
clientEntry?: string;
|
|
201
|
+
/** Component to render when no route matches (from _404.tsx). */
|
|
202
|
+
notFoundComponent?: ComponentFn;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Create the SSR request handler for production.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* import { routes } from "virtual:zero/routes"
|
|
209
|
+
* import { routeMiddleware } from "virtual:zero/route-middleware"
|
|
210
|
+
* import { createServer } from "@pyreon/zero"
|
|
211
|
+
*
|
|
212
|
+
* export default createServer({ routes, routeMiddleware, apiRoutes })
|
|
213
|
+
*/
|
|
214
|
+
declare function createServer(options: CreateServerOptions): (req: Request) => Promise<Response>;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/vite-plugin.d.ts
|
|
217
|
+
/**
|
|
218
|
+
* Zero Vite plugin — adds file-based routing and zero-config conventions
|
|
219
|
+
* on top of @pyreon/vite-plugin.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* // vite.config.ts
|
|
223
|
+
* import pyreon from "@pyreon/vite-plugin"
|
|
224
|
+
* import zero from "@pyreon/zero"
|
|
225
|
+
*
|
|
226
|
+
* export default {
|
|
227
|
+
* plugins: [pyreon(), zero()],
|
|
228
|
+
* }
|
|
229
|
+
*/
|
|
230
|
+
declare function zeroPlugin(userConfig?: ZeroConfig): Plugin;
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/fs-router.d.ts
|
|
233
|
+
/**
|
|
234
|
+
* Parse a set of file paths (relative to routes dir) into FileRoute objects.
|
|
235
|
+
*
|
|
236
|
+
* @param files Array of file paths like ["index.tsx", "users/[id].tsx"]
|
|
237
|
+
* @param defaultMode Default rendering mode from config
|
|
238
|
+
*/
|
|
239
|
+
declare function parseFileRoutes(files: string[], defaultMode?: RenderMode): FileRoute[];
|
|
240
|
+
/**
|
|
241
|
+
* Convert a file path (without extension) to a URL path pattern.
|
|
242
|
+
*
|
|
243
|
+
* Examples:
|
|
244
|
+
* "index" → "/"
|
|
245
|
+
* "about" → "/about"
|
|
246
|
+
* "users/index" → "/users"
|
|
247
|
+
* "users/[id]" → "/users/:id"
|
|
248
|
+
* "blog/[...slug]" → "/blog/:slug*"
|
|
249
|
+
* "(auth)/login" → "/login" (group stripped)
|
|
250
|
+
* "_layout" → "/" (layout marker)
|
|
251
|
+
*/
|
|
252
|
+
declare function filePathToUrlPath(filePath: string): string;
|
|
253
|
+
/**
|
|
254
|
+
* Generate a virtual module that exports a nested route tree.
|
|
255
|
+
* Wires up layouts as parent routes with children, loaders, guards,
|
|
256
|
+
* error/loading components, middleware, and meta from route module exports.
|
|
257
|
+
*/
|
|
258
|
+
interface GenerateRouteModuleOptions {
|
|
259
|
+
/**
|
|
260
|
+
* When true, skip lazy() for route components and use static imports.
|
|
261
|
+
* Use for SSG/prerender mode where all routes are rendered at build time
|
|
262
|
+
* and code splitting provides no benefit. Avoids Rolldown warnings about
|
|
263
|
+
* static + dynamic imports of the same module.
|
|
264
|
+
*/
|
|
265
|
+
staticImports?: boolean;
|
|
266
|
+
}
|
|
267
|
+
declare function generateRouteModule(files: string[], routesDir: string, options?: GenerateRouteModuleOptions): string;
|
|
268
|
+
/**
|
|
269
|
+
* Generate a virtual module that maps URL patterns to their middleware exports.
|
|
270
|
+
* Used by the server entry to dispatch per-route middleware.
|
|
271
|
+
*/
|
|
272
|
+
declare function generateMiddlewareModule(files: string[], routesDir: string): string;
|
|
273
|
+
/**
|
|
274
|
+
* Scan a directory for route files.
|
|
275
|
+
* Returns paths relative to the routes directory.
|
|
276
|
+
*/
|
|
277
|
+
declare function scanRouteFiles(routesDir: string): Promise<string[]>;
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/config.d.ts
|
|
280
|
+
/**
|
|
281
|
+
* Define a Zero configuration.
|
|
282
|
+
* Used in `zero.config.ts` at the project root.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* import { defineConfig } from "@pyreon/zero/config"
|
|
286
|
+
*
|
|
287
|
+
* export default defineConfig({
|
|
288
|
+
* mode: "ssr",
|
|
289
|
+
* ssr: { mode: "stream" },
|
|
290
|
+
* port: 3000,
|
|
291
|
+
* })
|
|
292
|
+
*/
|
|
293
|
+
declare function defineConfig(config: ZeroConfig): ZeroConfig;
|
|
294
|
+
/** Merge user config with defaults. */
|
|
295
|
+
declare function resolveConfig(userConfig?: ZeroConfig): Required<Pick<ZeroConfig, 'mode' | 'base' | 'port' | 'adapter'>> & ZeroConfig;
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/isr.d.ts
|
|
298
|
+
/**
|
|
299
|
+
* In-memory ISR cache with stale-while-revalidate semantics.
|
|
300
|
+
*
|
|
301
|
+
* Wraps an SSR handler and caches responses per URL path.
|
|
302
|
+
* Serves stale content immediately while revalidating in the background.
|
|
303
|
+
*/
|
|
304
|
+
declare function createISRHandler(handler: (req: Request) => Promise<Response>, config: ISRConfig): (req: Request) => Promise<Response>;
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/adapters/bun.d.ts
|
|
307
|
+
/**
|
|
308
|
+
* Bun adapter — generates a standalone Bun.serve() entry.
|
|
309
|
+
*/
|
|
310
|
+
declare function bunAdapter(): Adapter;
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/adapters/cloudflare.d.ts
|
|
313
|
+
/**
|
|
314
|
+
* Cloudflare Pages adapter — generates output for Cloudflare Pages with Functions.
|
|
315
|
+
*
|
|
316
|
+
* Produces:
|
|
317
|
+
* - Client assets in the output directory root (served as static)
|
|
318
|
+
* - `_worker.js` — Cloudflare Pages Function for SSR
|
|
319
|
+
*
|
|
320
|
+
* Note: Cloudflare Pages Functions have a ~1MB module size limit.
|
|
321
|
+
* For large apps, configure Vite's SSR build to bundle server code:
|
|
322
|
+
* `ssr: { noExternal: true }` in vite.config.ts.
|
|
323
|
+
*
|
|
324
|
+
* Deploy with: `npx wrangler pages deploy ./dist`
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```ts
|
|
328
|
+
* // zero.config.ts
|
|
329
|
+
* import { defineConfig } from "@pyreon/zero"
|
|
330
|
+
*
|
|
331
|
+
* export default defineConfig({
|
|
332
|
+
* adapter: "cloudflare",
|
|
333
|
+
* })
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
declare function cloudflareAdapter(): Adapter;
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/adapters/netlify.d.ts
|
|
339
|
+
/**
|
|
340
|
+
* Netlify adapter — generates output for Netlify Functions (v2).
|
|
341
|
+
*
|
|
342
|
+
* Produces:
|
|
343
|
+
* - Client assets in `publish/` directory
|
|
344
|
+
* - `netlify/functions/ssr.mjs` — Netlify Function for SSR
|
|
345
|
+
* - `netlify.toml` — routing configuration
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```ts
|
|
349
|
+
* // zero.config.ts
|
|
350
|
+
* import { defineConfig } from "@pyreon/zero"
|
|
351
|
+
*
|
|
352
|
+
* export default defineConfig({
|
|
353
|
+
* adapter: "netlify",
|
|
354
|
+
* })
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
declare function netlifyAdapter(): Adapter;
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/adapters/node.d.ts
|
|
360
|
+
/**
|
|
361
|
+
* Node.js adapter — generates a standalone server entry using node:http.
|
|
362
|
+
*/
|
|
363
|
+
declare function nodeAdapter(): Adapter;
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/adapters/static.d.ts
|
|
366
|
+
/**
|
|
367
|
+
* Static adapter — just copies the client build output.
|
|
368
|
+
* Used with SSG mode where all pages are pre-rendered at build time.
|
|
369
|
+
*/
|
|
370
|
+
declare function staticAdapter(): Adapter;
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/adapters/vercel.d.ts
|
|
373
|
+
/**
|
|
374
|
+
* Vercel adapter — generates output for Vercel's Build Output API v3.
|
|
375
|
+
*
|
|
376
|
+
* Produces a `.vercel/output` directory with:
|
|
377
|
+
* - `static/` — client-side assets (JS, CSS, images)
|
|
378
|
+
* - `functions/ssr.func/` — serverless function for SSR
|
|
379
|
+
* - `config.json` — routing configuration
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* // zero.config.ts
|
|
384
|
+
* import { defineConfig } from "@pyreon/zero"
|
|
385
|
+
*
|
|
386
|
+
* export default defineConfig({
|
|
387
|
+
* adapter: "vercel",
|
|
388
|
+
* })
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
declare function vercelAdapter(): Adapter;
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/adapters/index.d.ts
|
|
394
|
+
/**
|
|
395
|
+
* Resolve the adapter from config.
|
|
396
|
+
* Returns a built-in adapter or throws if unknown.
|
|
397
|
+
*/
|
|
398
|
+
declare function resolveAdapter(config: ZeroConfig): Adapter;
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/image-plugin.d.ts
|
|
401
|
+
interface ImagePluginConfig {
|
|
402
|
+
/** Output directory for processed images. Default: "assets/img" */
|
|
403
|
+
outDir?: string;
|
|
404
|
+
/** Default widths for responsive images. Default: [640, 1024, 1920] */
|
|
405
|
+
widths?: number[];
|
|
406
|
+
/** Output formats. Default: ["webp"] */
|
|
407
|
+
formats?: ImageFormat[];
|
|
408
|
+
/** Quality for lossy formats (1-100). Default: 80 */
|
|
409
|
+
quality?: number;
|
|
410
|
+
/** Blur placeholder size in px. Default: 16 */
|
|
411
|
+
placeholderSize?: number;
|
|
412
|
+
/** File patterns to process. Default: /\.(jpe?g|png|webp|avif)$/i */
|
|
413
|
+
include?: RegExp;
|
|
414
|
+
}
|
|
415
|
+
type ImageFormat = 'webp' | 'avif' | 'jpeg' | 'png';
|
|
416
|
+
/** Per-format source set for <picture> <source> elements. */
|
|
417
|
+
interface FormatSource {
|
|
418
|
+
/** MIME type. e.g. "image/webp", "image/avif" */
|
|
419
|
+
type: string;
|
|
420
|
+
/** srcset string for this format. e.g. "/img-640.webp 640w, /img-1920.webp 1920w" */
|
|
421
|
+
srcset: string;
|
|
422
|
+
}
|
|
423
|
+
interface ProcessedImage {
|
|
424
|
+
/** Fallback source path (last format, largest width). */
|
|
425
|
+
src: string;
|
|
426
|
+
/** Fallback srcset string (last format). */
|
|
427
|
+
srcset: string;
|
|
428
|
+
/** Intrinsic width. */
|
|
429
|
+
width: number;
|
|
430
|
+
/** Intrinsic height. */
|
|
431
|
+
height: number;
|
|
432
|
+
/** Base64 blur placeholder data URI. */
|
|
433
|
+
placeholder: string;
|
|
434
|
+
/** Per-format source sets for <picture> element. Ordered by priority (best format first). */
|
|
435
|
+
formats: FormatSource[];
|
|
436
|
+
/** Flat list of all sources. */
|
|
437
|
+
sources: Array<{
|
|
438
|
+
src: string;
|
|
439
|
+
width: number;
|
|
440
|
+
format: string;
|
|
441
|
+
}>;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Zero image processing Vite plugin.
|
|
445
|
+
*
|
|
446
|
+
* Transforms image imports with query params into optimized responsive images:
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* // vite.config.ts
|
|
450
|
+
* import { imagePlugin } from "@pyreon/zero/image-plugin"
|
|
451
|
+
*
|
|
452
|
+
* export default {
|
|
453
|
+
* plugins: [
|
|
454
|
+
* pyreon(),
|
|
455
|
+
* zero(),
|
|
456
|
+
* imagePlugin({ widths: [480, 960, 1440], quality: 85 }),
|
|
457
|
+
* ],
|
|
458
|
+
* }
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* // In a component — import with ?optimize
|
|
462
|
+
* import hero from "./images/hero.jpg?optimize"
|
|
463
|
+
* // hero = { src, srcset, width, height, placeholder }
|
|
464
|
+
*
|
|
465
|
+
* <Image {...hero} alt="Hero" priority />
|
|
466
|
+
*/
|
|
467
|
+
declare function imagePlugin(config?: ImagePluginConfig): Plugin;
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region src/image.d.ts
|
|
470
|
+
interface ImageProps {
|
|
471
|
+
/** Image source URL. */
|
|
472
|
+
src: string;
|
|
473
|
+
/** Alt text (required for accessibility). */
|
|
474
|
+
alt: string;
|
|
475
|
+
/** Intrinsic width of the image. */
|
|
476
|
+
width: number;
|
|
477
|
+
/** Intrinsic height of the image. */
|
|
478
|
+
height: number;
|
|
479
|
+
/** Responsive sizes attribute. Default: "100vw" */
|
|
480
|
+
sizes?: string;
|
|
481
|
+
/** Responsive srcset string or source array. */
|
|
482
|
+
srcset?: string | ImageSource[];
|
|
483
|
+
/** Per-format source sets for <picture>. Provided automatically by imagePlugin. */
|
|
484
|
+
formats?: FormatSource[];
|
|
485
|
+
/** Loading strategy. "lazy" uses IntersectionObserver, "eager" loads immediately. Default: "lazy" */
|
|
486
|
+
loading?: 'lazy' | 'eager';
|
|
487
|
+
/** Mark as priority (LCP image). Disables lazy loading, adds fetchPriority="high". */
|
|
488
|
+
priority?: boolean;
|
|
489
|
+
/** Low-quality placeholder image URL or base64 data URI for blur-up effect. */
|
|
490
|
+
placeholder?: string;
|
|
491
|
+
/** CSS class name. */
|
|
492
|
+
class?: string;
|
|
493
|
+
/** Inline styles. */
|
|
494
|
+
style?: string;
|
|
495
|
+
/** CSS object-fit. Default: "cover" */
|
|
496
|
+
fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
497
|
+
/** Decode async. Default: true */
|
|
498
|
+
decoding?: 'sync' | 'async' | 'auto';
|
|
499
|
+
}
|
|
500
|
+
interface ImageSource {
|
|
501
|
+
src: string;
|
|
502
|
+
width: number;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Optimized image component with lazy loading, responsive images,
|
|
506
|
+
* multi-format <picture> support, and blur-up placeholders.
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* // With imagePlugin — spread the import directly
|
|
510
|
+
* import hero from "./hero.jpg?optimize"
|
|
511
|
+
* <Image {...hero} alt="Hero" priority />
|
|
512
|
+
*
|
|
513
|
+
* @example
|
|
514
|
+
* // Manual usage
|
|
515
|
+
* <Image src="/hero.jpg" alt="Hero" width={1200} height={630} />
|
|
516
|
+
*/
|
|
517
|
+
declare function Image(props: ImageProps): VNodeChild;
|
|
518
|
+
//#endregion
|
|
519
|
+
//#region src/link.d.ts
|
|
520
|
+
interface LinkProps {
|
|
521
|
+
/** Target URL path. */
|
|
522
|
+
href: string;
|
|
523
|
+
/** Link content. */
|
|
524
|
+
children?: any;
|
|
525
|
+
/** CSS class name. */
|
|
526
|
+
class?: string;
|
|
527
|
+
/** Class applied when this link matches the current route. */
|
|
528
|
+
activeClass?: string;
|
|
529
|
+
/** Class applied when this link exactly matches the current route. */
|
|
530
|
+
exactActiveClass?: string;
|
|
531
|
+
/** Prefetch strategy. Default: "hover" */
|
|
532
|
+
prefetch?: 'hover' | 'viewport' | 'none';
|
|
533
|
+
/** Open in new tab. */
|
|
534
|
+
external?: boolean;
|
|
535
|
+
/** Inline styles. */
|
|
536
|
+
style?: string;
|
|
537
|
+
/** ARIA label. */
|
|
538
|
+
'aria-label'?: string;
|
|
539
|
+
/** Additional click handler — called before navigation. Call e.preventDefault() to cancel. */
|
|
540
|
+
onClick?: ((e: MouseEvent) => void) | undefined;
|
|
541
|
+
}
|
|
542
|
+
/** Props passed to a custom component via createLink. */
|
|
543
|
+
interface LinkRenderProps {
|
|
544
|
+
href: string;
|
|
545
|
+
ref: _pyreon_core0.Ref<HTMLAnchorElement>;
|
|
546
|
+
onClick: (e: MouseEvent) => void;
|
|
547
|
+
onMouseEnter: () => void;
|
|
548
|
+
onTouchStart: () => void;
|
|
549
|
+
isActive: () => boolean;
|
|
550
|
+
isExactActive: () => boolean;
|
|
551
|
+
/** Reactive class string — pass directly to element for auto-updates on route change. */
|
|
552
|
+
class: (() => string) | string | undefined;
|
|
553
|
+
style?: string;
|
|
554
|
+
target?: string;
|
|
555
|
+
rel?: string;
|
|
556
|
+
'aria-label'?: string;
|
|
557
|
+
children?: any;
|
|
558
|
+
}
|
|
559
|
+
/** Return type of useLink. */
|
|
560
|
+
interface UseLinkReturn {
|
|
561
|
+
/** Ref object — attach to the root element for viewport-based prefetch. */
|
|
562
|
+
ref: _pyreon_core0.Ref<HTMLAnchorElement>;
|
|
563
|
+
/** Click handler — performs client-side navigation. */
|
|
564
|
+
handleClick: (e: MouseEvent) => void;
|
|
565
|
+
/** Mouse enter handler — triggers hover prefetch. */
|
|
566
|
+
handleMouseEnter: () => void;
|
|
567
|
+
/** Touch start handler — triggers prefetch on mobile. */
|
|
568
|
+
handleTouchStart: () => void;
|
|
569
|
+
/** Whether the link partially matches the current route. */
|
|
570
|
+
isActive: () => boolean;
|
|
571
|
+
/** Whether the link exactly matches the current route. */
|
|
572
|
+
isExactActive: () => boolean;
|
|
573
|
+
/** Resolved class string including active classes. */
|
|
574
|
+
classes: () => string;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Prefetch a route's JS chunk by injecting `<link rel="prefetch">` into the
|
|
578
|
+
* document head. Deduplicates — calling with the same href twice is a no-op.
|
|
579
|
+
*
|
|
580
|
+
* @example
|
|
581
|
+
* prefetchRoute('/about')
|
|
582
|
+
* prefetchRoute('/dashboard')
|
|
583
|
+
*/
|
|
584
|
+
declare function prefetchRoute(href: string): void;
|
|
585
|
+
/**
|
|
586
|
+
* Composable that provides all link behavior — navigation, prefetching,
|
|
587
|
+
* active state, and viewport observation.
|
|
588
|
+
*
|
|
589
|
+
* Use this for full control when `createLink` is too opinionated.
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* function MyLink(props: LinkProps) {
|
|
593
|
+
* const link = useLink(props)
|
|
594
|
+
* return (
|
|
595
|
+
* <button ref={link.ref} class={link.classes()} onClick={link.handleClick}>
|
|
596
|
+
* {props.children}
|
|
597
|
+
* </button>
|
|
598
|
+
* )
|
|
599
|
+
* }
|
|
600
|
+
*/
|
|
601
|
+
declare function useLink(props: LinkProps): UseLinkReturn;
|
|
602
|
+
/**
|
|
603
|
+
* Higher-order component that wraps any component with link behavior.
|
|
604
|
+
*
|
|
605
|
+
* The wrapped component receives {@link LinkRenderProps} with all handlers,
|
|
606
|
+
* active state, and accessibility attributes pre-wired.
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* // Custom button link
|
|
610
|
+
* const ButtonLink = createLink((props) => (
|
|
611
|
+
* <button
|
|
612
|
+
* ref={props.ref}
|
|
613
|
+
* class={props.class}
|
|
614
|
+
* onClick={props.onClick}
|
|
615
|
+
* onMouseEnter={props.onMouseEnter}
|
|
616
|
+
* >
|
|
617
|
+
* {props.children}
|
|
618
|
+
* </button>
|
|
619
|
+
* ))
|
|
620
|
+
*
|
|
621
|
+
* // Custom styled component
|
|
622
|
+
* const CardLink = createLink((props) => (
|
|
623
|
+
* <div
|
|
624
|
+
* ref={props.ref}
|
|
625
|
+
* class={`card ${props.isActive() ? "card--active" : ""}`}
|
|
626
|
+
* onClick={props.onClick}
|
|
627
|
+
* onMouseEnter={props.onMouseEnter}
|
|
628
|
+
* >
|
|
629
|
+
* {props.children}
|
|
630
|
+
* </div>
|
|
631
|
+
* ))
|
|
632
|
+
*
|
|
633
|
+
* // Usage
|
|
634
|
+
* <ButtonLink href="/about">About</ButtonLink>
|
|
635
|
+
* <CardLink href="/posts" prefetch="viewport">Posts</CardLink>
|
|
636
|
+
*/
|
|
637
|
+
declare function createLink(Component: (props: LinkRenderProps) => any): (props: LinkProps) => any;
|
|
638
|
+
/**
|
|
639
|
+
* Default navigation link built on an `<a>` tag.
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* <Link href="/about" prefetch="viewport">About</Link>
|
|
643
|
+
* <Link href="/posts" activeClass="nav-active">Posts</Link>
|
|
644
|
+
*/
|
|
645
|
+
declare const Link: (props: LinkProps) => any;
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/script.d.ts
|
|
648
|
+
interface ScriptProps {
|
|
649
|
+
/** Script source URL. */
|
|
650
|
+
src: string;
|
|
651
|
+
/** Loading strategy. Default: "afterHydration" */
|
|
652
|
+
strategy?: ScriptStrategy;
|
|
653
|
+
/** Inline script content (alternative to src). */
|
|
654
|
+
children?: string;
|
|
655
|
+
/** Script id for deduplication. */
|
|
656
|
+
id?: string;
|
|
657
|
+
/** Async attribute. Default: true */
|
|
658
|
+
async?: boolean;
|
|
659
|
+
/** onLoad callback. */
|
|
660
|
+
onLoad?: () => void;
|
|
661
|
+
/** onError callback. */
|
|
662
|
+
onError?: (error: Error) => void;
|
|
663
|
+
}
|
|
664
|
+
type ScriptStrategy = 'beforeHydration' | 'afterHydration' | 'onIdle' | 'onInteraction' | 'onViewport';
|
|
665
|
+
/**
|
|
666
|
+
* Optimized script loading component.
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* // Load analytics after page is interactive
|
|
670
|
+
* <Script src="https://analytics.example.com/script.js" strategy="onIdle" />
|
|
671
|
+
*
|
|
672
|
+
* // Load chat widget when user scrolls
|
|
673
|
+
* <Script src="/chat-widget.js" strategy="onViewport" />
|
|
674
|
+
*
|
|
675
|
+
* // Inline script with deferred execution
|
|
676
|
+
* <Script strategy="afterHydration">
|
|
677
|
+
* {`console.log("App hydrated!")`}
|
|
678
|
+
* </Script>
|
|
679
|
+
*/
|
|
680
|
+
declare function Script(props: ScriptProps): VNodeChild;
|
|
681
|
+
//#endregion
|
|
682
|
+
//#region src/not-found.d.ts
|
|
683
|
+
/**
|
|
684
|
+
* Render a 404 component to a full HTML string.
|
|
685
|
+
* If no component is provided, returns a default 404 page.
|
|
686
|
+
*/
|
|
687
|
+
declare function render404Page(component: ComponentFn | undefined, template?: string): Promise<string>;
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region src/cache.d.ts
|
|
690
|
+
interface CacheConfig {
|
|
691
|
+
/** Cache duration for immutable hashed assets (seconds). Default: 31536000 (1 year) */
|
|
692
|
+
immutable?: number;
|
|
693
|
+
/** Cache duration for static assets like images/fonts (seconds). Default: 86400 (1 day) */
|
|
694
|
+
static?: number;
|
|
695
|
+
/** Cache duration for pages (seconds). Default: 0 (no cache) */
|
|
696
|
+
pages?: number;
|
|
697
|
+
/** Stale-while-revalidate window for pages (seconds). Default: 60 */
|
|
698
|
+
staleWhileRevalidate?: number;
|
|
699
|
+
/** Custom rules by URL pattern. */
|
|
700
|
+
rules?: CacheRule[];
|
|
701
|
+
}
|
|
702
|
+
interface CacheRule {
|
|
703
|
+
/** URL pattern to match (glob-style). e.g. "/api/*" */
|
|
704
|
+
match: string;
|
|
705
|
+
/** Cache-Control header value. */
|
|
706
|
+
control: string;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Cache control middleware for Zero.
|
|
710
|
+
* Sets Cache-Control headers on the response based on asset type.
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* import { cacheMiddleware } from "@pyreon/zero/cache"
|
|
714
|
+
*
|
|
715
|
+
* export default createHandler({
|
|
716
|
+
* routes,
|
|
717
|
+
* middleware: [
|
|
718
|
+
* cacheMiddleware({
|
|
719
|
+
* pages: 60,
|
|
720
|
+
* staleWhileRevalidate: 300,
|
|
721
|
+
* rules: [
|
|
722
|
+
* { match: "/api/*", control: "no-store" },
|
|
723
|
+
* ],
|
|
724
|
+
* }),
|
|
725
|
+
* ],
|
|
726
|
+
* })
|
|
727
|
+
*/
|
|
728
|
+
declare function cacheMiddleware(config?: CacheConfig): Middleware;
|
|
729
|
+
/**
|
|
730
|
+
* Security headers middleware.
|
|
731
|
+
* Adds common security headers to all responses.
|
|
732
|
+
*/
|
|
733
|
+
declare function securityHeaders(): Middleware;
|
|
734
|
+
/**
|
|
735
|
+
* Compression detection middleware.
|
|
736
|
+
* Sets Vary: Accept-Encoding header so caches can serve compressed variants.
|
|
737
|
+
* Actual compression is handled by the runtime (Bun/Node) or reverse proxy.
|
|
738
|
+
*/
|
|
739
|
+
declare function varyEncoding(): Middleware;
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/middleware.d.ts
|
|
742
|
+
/**
|
|
743
|
+
* Compose multiple middleware into a single middleware function.
|
|
744
|
+
* Middleware runs sequentially — if any returns a Response, the chain stops.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* import { compose } from "@pyreon/zero/middleware"
|
|
748
|
+
* import { corsMiddleware } from "@pyreon/zero/cors"
|
|
749
|
+
* import { rateLimitMiddleware } from "@pyreon/zero/rate-limit"
|
|
750
|
+
*
|
|
751
|
+
* const combined = compose(
|
|
752
|
+
* corsMiddleware({ origin: "*" }),
|
|
753
|
+
* rateLimitMiddleware({ max: 100 }),
|
|
754
|
+
* cacheMiddleware(),
|
|
755
|
+
* )
|
|
756
|
+
*/
|
|
757
|
+
declare function compose(...middlewares: Middleware[]): Middleware;
|
|
758
|
+
/**
|
|
759
|
+
* Get the shared Zero context from a middleware context.
|
|
760
|
+
* Creates one if it doesn't exist. Middleware can use this to
|
|
761
|
+
* pass data to downstream middleware without polluting `ctx.locals`.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* const authMiddleware: Middleware = (ctx) => {
|
|
765
|
+
* const zctx = getContext(ctx)
|
|
766
|
+
* zctx.userId = "user_123"
|
|
767
|
+
* }
|
|
768
|
+
*
|
|
769
|
+
* const loggingMiddleware: Middleware = (ctx) => {
|
|
770
|
+
* const zctx = getContext(ctx)
|
|
771
|
+
* console.log("User:", zctx.userId)
|
|
772
|
+
* }
|
|
773
|
+
*/
|
|
774
|
+
declare function getContext(ctx: MiddlewareContext): Record<string, unknown>;
|
|
775
|
+
//#endregion
|
|
776
|
+
//#region src/font.d.ts
|
|
777
|
+
interface FontConfig {
|
|
778
|
+
/**
|
|
779
|
+
* Google Fonts families.
|
|
780
|
+
*
|
|
781
|
+
* Accepts both string shorthand and structured objects:
|
|
782
|
+
* - String: "Inter:wght@400;500;700" or "Inter:wght@100..900"
|
|
783
|
+
* - Object: { family: "Inter", weights: [400, 500, 700] }
|
|
784
|
+
* - Variable: { family: "Inter", variable: true, weightRange: [100, 900] }
|
|
785
|
+
*/
|
|
786
|
+
google?: GoogleFontInput[];
|
|
787
|
+
/** Local font files. */
|
|
788
|
+
local?: LocalFont[];
|
|
789
|
+
/** Default font-display strategy. Default: "swap" */
|
|
790
|
+
display?: FontDisplay;
|
|
791
|
+
/** Preload critical fonts. Default: true */
|
|
792
|
+
preload?: boolean;
|
|
793
|
+
/** Self-host Google Fonts at build time. Default: true */
|
|
794
|
+
selfHost?: boolean;
|
|
795
|
+
/** Fallback font metrics for reducing CLS. */
|
|
796
|
+
fallbacks?: Record<string, FallbackMetrics>;
|
|
797
|
+
}
|
|
798
|
+
/** Static Google Font config. */
|
|
799
|
+
interface GoogleFontStatic {
|
|
800
|
+
family: string;
|
|
801
|
+
weights: number[];
|
|
802
|
+
italic?: boolean;
|
|
803
|
+
variable?: false;
|
|
804
|
+
}
|
|
805
|
+
/** Variable Google Font config. */
|
|
806
|
+
interface GoogleFontVariable {
|
|
807
|
+
family: string;
|
|
808
|
+
/** Weight range as [min, max] tuple. e.g. [100, 900] */
|
|
809
|
+
weightRange: [number, number];
|
|
810
|
+
italic?: boolean;
|
|
811
|
+
variable: true;
|
|
812
|
+
}
|
|
813
|
+
/** Google font input: structured object or string shorthand. */
|
|
814
|
+
type GoogleFontInput = GoogleFontStatic | GoogleFontVariable | string;
|
|
815
|
+
interface LocalFont {
|
|
816
|
+
family: string;
|
|
817
|
+
src: string;
|
|
818
|
+
/** Single weight (400) or variable range ("100 900"). */
|
|
819
|
+
weight?: number | `${number} ${number}`;
|
|
820
|
+
style?: 'normal' | 'italic';
|
|
821
|
+
display?: FontDisplay;
|
|
822
|
+
}
|
|
823
|
+
type FontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
|
|
824
|
+
/** Metrics for generating size-adjusted fallback fonts to reduce CLS. */
|
|
825
|
+
interface FallbackMetrics {
|
|
826
|
+
/** The fallback font to adjust. e.g. "Arial", "Georgia" */
|
|
827
|
+
fallback: string;
|
|
828
|
+
/** Size adjustment factor. e.g. 1.05 */
|
|
829
|
+
sizeAdjust?: number;
|
|
830
|
+
/** Ascent override percentage. e.g. 90 */
|
|
831
|
+
ascentOverride?: number;
|
|
832
|
+
/** Descent override percentage. e.g. 22 */
|
|
833
|
+
descentOverride?: number;
|
|
834
|
+
/** Line gap override percentage. e.g. 0 */
|
|
835
|
+
lineGapOverride?: number;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Zero font optimization Vite plugin.
|
|
839
|
+
*
|
|
840
|
+
* Dev mode: injects Google Fonts CDN link for fast startup.
|
|
841
|
+
* Build mode: downloads and self-hosts fonts for maximum performance + privacy.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* import { fontPlugin } from "@pyreon/zero/font"
|
|
845
|
+
*
|
|
846
|
+
* export default {
|
|
847
|
+
* plugins: [
|
|
848
|
+
* pyreon(),
|
|
849
|
+
* zero(),
|
|
850
|
+
* fontPlugin({
|
|
851
|
+
* google: ["Inter:wght@400;500;600;700", "JetBrains Mono:wght@400"],
|
|
852
|
+
* fallbacks: {
|
|
853
|
+
* "Inter": { fallback: "Arial", sizeAdjust: 1.07, ascentOverride: 90 },
|
|
854
|
+
* },
|
|
855
|
+
* }),
|
|
856
|
+
* ],
|
|
857
|
+
* }
|
|
858
|
+
*/
|
|
859
|
+
declare function fontPlugin(config?: FontConfig): Plugin;
|
|
860
|
+
/**
|
|
861
|
+
* Generate CSS variables for font families.
|
|
862
|
+
*/
|
|
863
|
+
declare function fontVariables(families: Record<string, string>): string;
|
|
864
|
+
//#endregion
|
|
865
|
+
//#region src/theme.d.ts
|
|
866
|
+
type Theme = 'light' | 'dark' | 'system';
|
|
867
|
+
/** Reactive theme signal. */
|
|
868
|
+
declare const theme: _pyreon_reactivity0.Signal<Theme>;
|
|
869
|
+
/**
|
|
870
|
+
* Set the default theme for SSR (when `matchMedia` is unavailable).
|
|
871
|
+
* Call once at server startup before rendering.
|
|
872
|
+
*/
|
|
873
|
+
declare function setSSRThemeDefault(value: 'light' | 'dark'): void;
|
|
874
|
+
/** Computed resolved theme (what's actually applied). */
|
|
875
|
+
declare function resolvedTheme(): 'light' | 'dark';
|
|
876
|
+
/** Toggle between light and dark. */
|
|
877
|
+
declare function toggleTheme(): void;
|
|
878
|
+
/** Set theme explicitly. */
|
|
879
|
+
declare function setTheme(t: Theme): void;
|
|
880
|
+
/**
|
|
881
|
+
* Initialize the theme system. Call once in your app entry or layout.
|
|
882
|
+
* Reads from localStorage, listens for system preference changes.
|
|
883
|
+
*/
|
|
884
|
+
declare function initTheme(): void;
|
|
885
|
+
/**
|
|
886
|
+
* Theme toggle button component.
|
|
887
|
+
*
|
|
888
|
+
* @example
|
|
889
|
+
* import { ThemeToggle } from "@pyreon/zero/theme"
|
|
890
|
+
* <ThemeToggle />
|
|
891
|
+
*/
|
|
892
|
+
declare function ThemeToggle(props: {
|
|
893
|
+
class?: string;
|
|
894
|
+
style?: string;
|
|
895
|
+
}): VNodeChild;
|
|
896
|
+
/**
|
|
897
|
+
* Inline script to prevent flash of wrong theme.
|
|
898
|
+
* Include this in your index.html <head> BEFORE any stylesheets.
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* // index.html
|
|
902
|
+
* <head>
|
|
903
|
+
* <script>{themeScript}</script>
|
|
904
|
+
* ...
|
|
905
|
+
* </head>
|
|
906
|
+
*/
|
|
907
|
+
declare const themeScript = "(function(){try{var t=localStorage.getItem(\"zero-theme\");var r=t===\"light\"?\"light\":t===\"dark\"?\"dark\":window.matchMedia(\"(prefers-color-scheme:dark)\").matches?\"dark\":\"light\";document.documentElement.dataset.theme=r}catch(e){}})()";
|
|
908
|
+
//#endregion
|
|
909
|
+
//#region src/seo.d.ts
|
|
910
|
+
interface SitemapConfig {
|
|
911
|
+
/** Base URL of the site (required). e.g. "https://example.com" */
|
|
912
|
+
origin: string;
|
|
913
|
+
/** Paths to exclude from the sitemap. */
|
|
914
|
+
exclude?: string[];
|
|
915
|
+
/** Default change frequency. Default: "weekly" */
|
|
916
|
+
changefreq?: ChangeFreq;
|
|
917
|
+
/** Default priority. Default: 0.7 */
|
|
918
|
+
priority?: number;
|
|
919
|
+
/** Additional URLs to include (for dynamic routes). */
|
|
920
|
+
additionalPaths?: SitemapEntry[];
|
|
921
|
+
}
|
|
922
|
+
interface SitemapEntry {
|
|
923
|
+
path: string;
|
|
924
|
+
changefreq?: ChangeFreq;
|
|
925
|
+
priority?: number;
|
|
926
|
+
lastmod?: string;
|
|
927
|
+
}
|
|
928
|
+
type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
929
|
+
/**
|
|
930
|
+
* Generate a sitemap.xml string from route file paths.
|
|
931
|
+
*/
|
|
932
|
+
declare function generateSitemap(routeFiles: string[], config: SitemapConfig): string;
|
|
933
|
+
interface RobotsConfig {
|
|
934
|
+
/** Rules per user-agent. */
|
|
935
|
+
rules?: RobotsRule[];
|
|
936
|
+
/** Sitemap URL. */
|
|
937
|
+
sitemap?: string;
|
|
938
|
+
/** Host directive. */
|
|
939
|
+
host?: string;
|
|
940
|
+
}
|
|
941
|
+
interface RobotsRule {
|
|
942
|
+
userAgent: string;
|
|
943
|
+
allow?: string[];
|
|
944
|
+
disallow?: string[];
|
|
945
|
+
crawlDelay?: number;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Generate a robots.txt string.
|
|
949
|
+
*/
|
|
950
|
+
declare function generateRobots(config?: RobotsConfig): string;
|
|
951
|
+
type JsonLdType = 'WebSite' | 'WebPage' | 'Article' | 'BlogPosting' | 'Product' | 'Organization' | 'Person' | 'BreadcrumbList' | 'FAQPage' | (string & {});
|
|
952
|
+
/**
|
|
953
|
+
* Generate a JSON-LD script tag string for structured data.
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* useHead({
|
|
957
|
+
* script: [jsonLd({
|
|
958
|
+
* "@type": "WebSite",
|
|
959
|
+
* name: "My Site",
|
|
960
|
+
* url: "https://example.com",
|
|
961
|
+
* })],
|
|
962
|
+
* })
|
|
963
|
+
*/
|
|
964
|
+
declare function jsonLd(data: Record<string, unknown>): string;
|
|
965
|
+
interface SeoPluginConfig {
|
|
966
|
+
/** Sitemap configuration. */
|
|
967
|
+
sitemap?: SitemapConfig;
|
|
968
|
+
/** Robots.txt configuration. */
|
|
969
|
+
robots?: RobotsConfig;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Zero SEO Vite plugin.
|
|
973
|
+
* Generates sitemap.xml and robots.txt at build time.
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* import { seoPlugin } from "@pyreon/zero/seo"
|
|
977
|
+
*
|
|
978
|
+
* export default {
|
|
979
|
+
* plugins: [
|
|
980
|
+
* pyreon(),
|
|
981
|
+
* zero(),
|
|
982
|
+
* seoPlugin({
|
|
983
|
+
* sitemap: { origin: "https://example.com" },
|
|
984
|
+
* robots: { sitemap: "https://example.com/sitemap.xml" },
|
|
985
|
+
* }),
|
|
986
|
+
* ],
|
|
987
|
+
* }
|
|
988
|
+
*/
|
|
989
|
+
declare function seoPlugin(config?: SeoPluginConfig): Plugin;
|
|
990
|
+
/**
|
|
991
|
+
* SEO middleware for dev server.
|
|
992
|
+
* Serves sitemap.xml and robots.txt dynamically during development.
|
|
993
|
+
*/
|
|
994
|
+
declare function seoMiddleware(config?: SeoPluginConfig): Middleware;
|
|
995
|
+
//#endregion
|
|
996
|
+
//#region src/cors.d.ts
|
|
997
|
+
interface CorsConfig {
|
|
998
|
+
/** Allowed origins. Use `"*"` for any origin. Default: `"*"` */
|
|
999
|
+
origin?: string | string[] | ((origin: string) => boolean);
|
|
1000
|
+
/** Allowed HTTP methods. Default: `["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]` */
|
|
1001
|
+
methods?: string[];
|
|
1002
|
+
/** Allowed request headers. Default: `["Content-Type", "Authorization"]` */
|
|
1003
|
+
allowedHeaders?: string[];
|
|
1004
|
+
/** Headers exposed to the client. Default: `[]` */
|
|
1005
|
+
exposedHeaders?: string[];
|
|
1006
|
+
/** Allow credentials (cookies, auth headers). Default: `false` */
|
|
1007
|
+
credentials?: boolean;
|
|
1008
|
+
/** Preflight cache duration in seconds. Default: `86400` (24 hours) */
|
|
1009
|
+
maxAge?: number;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* CORS middleware — handles preflight requests and sets appropriate
|
|
1013
|
+
* Access-Control headers on all responses.
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* import { corsMiddleware } from "@pyreon/zero/cors"
|
|
1017
|
+
*
|
|
1018
|
+
* corsMiddleware({ origin: "https://example.com", credentials: true })
|
|
1019
|
+
*
|
|
1020
|
+
* // Allow any origin
|
|
1021
|
+
* corsMiddleware({ origin: "*" })
|
|
1022
|
+
*
|
|
1023
|
+
* // Multiple origins
|
|
1024
|
+
* corsMiddleware({ origin: ["https://app.com", "https://admin.com"] })
|
|
1025
|
+
*/
|
|
1026
|
+
declare function corsMiddleware(config?: CorsConfig): Middleware;
|
|
1027
|
+
//#endregion
|
|
1028
|
+
//#region src/rate-limit.d.ts
|
|
1029
|
+
interface RateLimitConfig {
|
|
1030
|
+
/** Maximum requests per window. Default: `100` */
|
|
1031
|
+
max?: number;
|
|
1032
|
+
/** Time window in seconds. Default: `60` */
|
|
1033
|
+
window?: number;
|
|
1034
|
+
/** Function to extract the client identifier. Default: IP from headers. */
|
|
1035
|
+
keyFn?: (ctx: MiddlewareContext) => string;
|
|
1036
|
+
/** Custom response when rate limited. */
|
|
1037
|
+
onLimit?: (ctx: MiddlewareContext) => Response;
|
|
1038
|
+
/** URL patterns to rate limit (glob-style). Default: all paths. */
|
|
1039
|
+
include?: string[];
|
|
1040
|
+
/** URL patterns to exclude from rate limiting. */
|
|
1041
|
+
exclude?: string[];
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Rate limiting middleware — limits requests per client within a time window.
|
|
1045
|
+
* Uses an in-memory store (suitable for single-instance deployments).
|
|
1046
|
+
*
|
|
1047
|
+
* @example
|
|
1048
|
+
* import { rateLimitMiddleware } from "@pyreon/zero/rate-limit"
|
|
1049
|
+
*
|
|
1050
|
+
* // 100 requests per minute (default)
|
|
1051
|
+
* rateLimitMiddleware()
|
|
1052
|
+
*
|
|
1053
|
+
* // Strict API rate limiting
|
|
1054
|
+
* rateLimitMiddleware({
|
|
1055
|
+
* max: 20,
|
|
1056
|
+
* window: 60,
|
|
1057
|
+
* include: ["/api/*"],
|
|
1058
|
+
* })
|
|
1059
|
+
*/
|
|
1060
|
+
declare function rateLimitMiddleware(config?: RateLimitConfig): Middleware;
|
|
1061
|
+
//#endregion
|
|
1062
|
+
//#region src/compression.d.ts
|
|
1063
|
+
interface CompressionConfig {
|
|
1064
|
+
/** Minimum response size in bytes to compress. Default: `1024` (1KB) */
|
|
1065
|
+
threshold?: number;
|
|
1066
|
+
/** Encoding preference order. Default: `["gzip", "deflate"]` */
|
|
1067
|
+
encodings?: ('gzip' | 'deflate')[];
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Compression middleware — compresses responses using gzip or deflate
|
|
1071
|
+
* based on the client's Accept-Encoding header.
|
|
1072
|
+
*
|
|
1073
|
+
* Only compresses text-based content types (HTML, JSON, JS, CSS, XML, SVG).
|
|
1074
|
+
* Skips responses below the size threshold and already-encoded responses.
|
|
1075
|
+
*
|
|
1076
|
+
* @example
|
|
1077
|
+
* import { compressionMiddleware } from "@pyreon/zero/compression"
|
|
1078
|
+
*
|
|
1079
|
+
* compressionMiddleware() // gzip with 1KB threshold
|
|
1080
|
+
* compressionMiddleware({ threshold: 512, encodings: ["gzip"] })
|
|
1081
|
+
*/
|
|
1082
|
+
declare function compressionMiddleware(config?: CompressionConfig): Middleware;
|
|
1083
|
+
/**
|
|
1084
|
+
* Compress a Response body if it meets the criteria.
|
|
1085
|
+
* Use this to post-process responses after the handler runs.
|
|
1086
|
+
*
|
|
1087
|
+
* @example
|
|
1088
|
+
* const response = await handler(request)
|
|
1089
|
+
* const compressed = await compressResponse(response, 'gzip', 1024)
|
|
1090
|
+
*/
|
|
1091
|
+
declare function compressResponse(response: Response, encoding: 'gzip' | 'deflate', threshold: number): Promise<Response>;
|
|
1092
|
+
/** Check if a content type is compressible. Exported for testing. */
|
|
1093
|
+
declare function isCompressible(contentType: string): boolean;
|
|
1094
|
+
//#endregion
|
|
1095
|
+
//#region src/actions.d.ts
|
|
1096
|
+
/** Context passed to server action handlers. */
|
|
1097
|
+
interface ActionContext {
|
|
1098
|
+
/** The original request. */
|
|
1099
|
+
request: Request;
|
|
1100
|
+
/** Parsed form data (for form submissions). */
|
|
1101
|
+
formData: FormData | null;
|
|
1102
|
+
/** Parsed JSON body (for JSON submissions). */
|
|
1103
|
+
json: unknown;
|
|
1104
|
+
/** Request headers. */
|
|
1105
|
+
headers: Headers;
|
|
1106
|
+
}
|
|
1107
|
+
/** A server action handler function. */
|
|
1108
|
+
type ActionHandler<T = unknown> = (ctx: ActionContext) => T | Promise<T>;
|
|
1109
|
+
/** Client-side callable action returned by defineAction. */
|
|
1110
|
+
interface Action<T = unknown> {
|
|
1111
|
+
/** Call the action with JSON data. */
|
|
1112
|
+
(data?: unknown): Promise<T>;
|
|
1113
|
+
/** The action's unique ID. */
|
|
1114
|
+
actionId: string;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Define a server action. Returns a callable function that:
|
|
1118
|
+
* - On the **client**: sends a POST request to `/_zero/actions/<id>`
|
|
1119
|
+
* - On the **server** (SSR): executes the handler directly (no fetch)
|
|
1120
|
+
*
|
|
1121
|
+
* @example
|
|
1122
|
+
* // In a route file or module:
|
|
1123
|
+
* export const createPost = defineAction(async (ctx) => {
|
|
1124
|
+
* const data = ctx.json as { title: string; body: string }
|
|
1125
|
+
* // ... save to database
|
|
1126
|
+
* return { success: true, id: 123 }
|
|
1127
|
+
* })
|
|
1128
|
+
*
|
|
1129
|
+
* // In a component:
|
|
1130
|
+
* const result = await createPost({ title: 'Hello', body: '...' })
|
|
1131
|
+
*/
|
|
1132
|
+
declare function defineAction<T = unknown>(handler: ActionHandler<T>): Action<T>;
|
|
1133
|
+
/**
|
|
1134
|
+
* Create a middleware that handles action requests at `/_zero/actions/*`.
|
|
1135
|
+
* Mount this before the SSR handler in the server entry.
|
|
1136
|
+
*/
|
|
1137
|
+
declare function createActionMiddleware(): (ctx: MiddlewareContext) => Response | undefined | Promise<Response | undefined>;
|
|
1138
|
+
//#endregion
|
|
1139
|
+
//#region src/favicon.d.ts
|
|
1140
|
+
interface FaviconLocaleConfig {
|
|
1141
|
+
/** Locale-specific source icon (SVG or PNG). */
|
|
1142
|
+
source: string;
|
|
1143
|
+
/** Optional dark mode variant for this locale. */
|
|
1144
|
+
darkSource?: string;
|
|
1145
|
+
}
|
|
1146
|
+
interface FaviconPluginConfig {
|
|
1147
|
+
/** Path to the source icon (SVG or PNG, at least 512x512 for PNG). */
|
|
1148
|
+
source: string;
|
|
1149
|
+
/** Theme color for web manifest. Default: "#ffffff" */
|
|
1150
|
+
themeColor?: string;
|
|
1151
|
+
/** Background color for web manifest. Default: "#ffffff" */
|
|
1152
|
+
backgroundColor?: string;
|
|
1153
|
+
/** App name for web manifest. Uses package.json name if not set. */
|
|
1154
|
+
name?: string;
|
|
1155
|
+
/** Generate web manifest. Default: true */
|
|
1156
|
+
manifest?: boolean;
|
|
1157
|
+
/**
|
|
1158
|
+
* Dark mode favicon (SVG only).
|
|
1159
|
+
* When provided, the SVG favicon uses prefers-color-scheme media query
|
|
1160
|
+
* to switch between light and dark variants.
|
|
1161
|
+
*/
|
|
1162
|
+
darkSource?: string;
|
|
1163
|
+
/**
|
|
1164
|
+
* Locale-specific icon overrides. Each key is a locale code,
|
|
1165
|
+
* value is a source icon (and optional dark variant).
|
|
1166
|
+
* Locales not in this map use the base `source`.
|
|
1167
|
+
*
|
|
1168
|
+
* Generated files are placed under `/{locale}/` prefix:
|
|
1169
|
+
* /de/favicon.svg, /de/favicon-32x32.png, etc.
|
|
1170
|
+
*
|
|
1171
|
+
* @example
|
|
1172
|
+
* ```ts
|
|
1173
|
+
* faviconPlugin({
|
|
1174
|
+
* source: "./icon.svg",
|
|
1175
|
+
* locales: {
|
|
1176
|
+
* de: { source: "./icon-de.svg" },
|
|
1177
|
+
* cs: { source: "./icon-cs.svg" },
|
|
1178
|
+
* },
|
|
1179
|
+
* })
|
|
1180
|
+
* ```
|
|
1181
|
+
*/
|
|
1182
|
+
locales?: Record<string, FaviconLocaleConfig>;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Favicon generation Vite plugin.
|
|
1186
|
+
*
|
|
1187
|
+
* Generates all required favicon formats at build time from a single source.
|
|
1188
|
+
* In dev mode, serves the source directly.
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```ts
|
|
1192
|
+
* // vite.config.ts
|
|
1193
|
+
* import { faviconPlugin } from "@pyreon/zero"
|
|
1194
|
+
*
|
|
1195
|
+
* export default {
|
|
1196
|
+
* plugins: [faviconPlugin({ source: "./src/assets/icon.svg" })],
|
|
1197
|
+
* }
|
|
1198
|
+
* ```
|
|
1199
|
+
*/
|
|
1200
|
+
declare function faviconPlugin(config: FaviconPluginConfig): Plugin;
|
|
1201
|
+
/**
|
|
1202
|
+
* Get favicon link tags for a specific locale.
|
|
1203
|
+
* Returns link objects suitable for `useHead()` or direct HTML injection.
|
|
1204
|
+
*
|
|
1205
|
+
* @example
|
|
1206
|
+
* ```ts
|
|
1207
|
+
* const links = faviconLinks("de", { source: "./icon.svg", locales: { de: { source: "./icon-de.svg" } } })
|
|
1208
|
+
* // → [{ rel: "icon", type: "image/svg+xml", href: "/de/favicon.svg" }, ...]
|
|
1209
|
+
* ```
|
|
1210
|
+
*/
|
|
1211
|
+
declare function faviconLinks(locale: string | undefined, config: FaviconPluginConfig): Array<{
|
|
1212
|
+
rel: string;
|
|
1213
|
+
type?: string;
|
|
1214
|
+
sizes?: string;
|
|
1215
|
+
href: string;
|
|
1216
|
+
}>;
|
|
1217
|
+
//#endregion
|
|
1218
|
+
//#region src/og-image.d.ts
|
|
1219
|
+
interface OgImageLayer {
|
|
1220
|
+
/**
|
|
1221
|
+
* Text content. Can be:
|
|
1222
|
+
* - A string (same for all locales)
|
|
1223
|
+
* - A record mapping locale → text
|
|
1224
|
+
* - A function receiving locale and returning text
|
|
1225
|
+
*/
|
|
1226
|
+
text: string | Record<string, string> | ((locale: string) => string);
|
|
1227
|
+
/** X position — number (px) or string with % (e.g. "50%"). Default: "50%" */
|
|
1228
|
+
x?: number | string;
|
|
1229
|
+
/** Y position — number (px) or string with % (e.g. "40%"). Default: "50%" */
|
|
1230
|
+
y?: number | string;
|
|
1231
|
+
/** Font size in px. Default: 64 */
|
|
1232
|
+
fontSize?: number;
|
|
1233
|
+
/** Font family. Default: "sans-serif" */
|
|
1234
|
+
fontFamily?: string;
|
|
1235
|
+
/** Font weight. Default: "bold" */
|
|
1236
|
+
fontWeight?: string;
|
|
1237
|
+
/** Text color. Default: "#ffffff" */
|
|
1238
|
+
color?: string;
|
|
1239
|
+
/** Text anchor (alignment). Default: "middle" */
|
|
1240
|
+
textAnchor?: 'start' | 'middle' | 'end';
|
|
1241
|
+
/** Max width in px before wrapping. Default: 80% of image width. */
|
|
1242
|
+
maxWidth?: number;
|
|
1243
|
+
}
|
|
1244
|
+
interface OgImageTemplate {
|
|
1245
|
+
/** Template name — used for output file naming. */
|
|
1246
|
+
name: string;
|
|
1247
|
+
/**
|
|
1248
|
+
* Background: path to an image file, or a solid color config.
|
|
1249
|
+
*
|
|
1250
|
+
* @example "./src/assets/og-bg.jpg"
|
|
1251
|
+
* @example { color: "#0066ff", width: 1200, height: 630 }
|
|
1252
|
+
*/
|
|
1253
|
+
background: string | {
|
|
1254
|
+
color: string;
|
|
1255
|
+
width?: number;
|
|
1256
|
+
height?: number;
|
|
1257
|
+
};
|
|
1258
|
+
/** Output width. Default: 1200 */
|
|
1259
|
+
width?: number;
|
|
1260
|
+
/** Output height. Default: 630 */
|
|
1261
|
+
height?: number;
|
|
1262
|
+
/** Output format. Default: "png" */
|
|
1263
|
+
format?: 'png' | 'jpeg';
|
|
1264
|
+
/** JPEG quality (1-100). Default: 90 */
|
|
1265
|
+
quality?: number;
|
|
1266
|
+
/** Text layers to overlay on the background. */
|
|
1267
|
+
layers?: OgImageLayer[];
|
|
1268
|
+
}
|
|
1269
|
+
interface OgImagePluginConfig {
|
|
1270
|
+
/** Templates to generate. */
|
|
1271
|
+
templates: OgImageTemplate[];
|
|
1272
|
+
/** Locales to generate for. When omitted, generates a single image per template. */
|
|
1273
|
+
locales?: string[];
|
|
1274
|
+
/** Output directory prefix. Default: "og" */
|
|
1275
|
+
outDir?: string;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Compute the OG image path for a template and locale.
|
|
1279
|
+
*
|
|
1280
|
+
* @example
|
|
1281
|
+
* ```ts
|
|
1282
|
+
* ogImagePath("default", "de") // → "/og/default-de.png"
|
|
1283
|
+
* ogImagePath("default") // → "/og/default.png"
|
|
1284
|
+
* ogImagePath("hero", "en", "images") // → "/images/hero-en.png"
|
|
1285
|
+
* ```
|
|
1286
|
+
*/
|
|
1287
|
+
declare function ogImagePath(templateName: string, locale?: string, outDir?: string, format?: 'png' | 'jpeg'): string;
|
|
1288
|
+
/**
|
|
1289
|
+
* OG image generation Vite plugin.
|
|
1290
|
+
*
|
|
1291
|
+
* Generates Open Graph images at build time. In dev, generates on-demand.
|
|
1292
|
+
* Requires `sharp` as an optional dependency.
|
|
1293
|
+
*
|
|
1294
|
+
* @example
|
|
1295
|
+
* ```ts
|
|
1296
|
+
* // vite.config.ts
|
|
1297
|
+
* import { ogImagePlugin } from "@pyreon/zero/og-image"
|
|
1298
|
+
*
|
|
1299
|
+
* export default {
|
|
1300
|
+
* plugins: [
|
|
1301
|
+
* ogImagePlugin({
|
|
1302
|
+
* locales: ["en", "de"],
|
|
1303
|
+
* templates: [{
|
|
1304
|
+
* name: "default",
|
|
1305
|
+
* background: { color: "#0066ff" },
|
|
1306
|
+
* layers: [{ text: { en: "Hello", de: "Hallo" }, fontSize: 72 }],
|
|
1307
|
+
* }],
|
|
1308
|
+
* }),
|
|
1309
|
+
* ],
|
|
1310
|
+
* }
|
|
1311
|
+
* ```
|
|
1312
|
+
*/
|
|
1313
|
+
declare function ogImagePlugin(config: OgImagePluginConfig): Plugin;
|
|
1314
|
+
//#endregion
|
|
1315
|
+
//#region src/i18n-routing.d.ts
|
|
1316
|
+
interface I18nRoutingConfig {
|
|
1317
|
+
/** Supported locales. e.g. ["en", "de", "cs"] */
|
|
1318
|
+
locales: string[];
|
|
1319
|
+
/** Default locale — served without prefix (/ instead of /en/). */
|
|
1320
|
+
defaultLocale: string;
|
|
1321
|
+
/** Redirect root to detected locale. Default: true */
|
|
1322
|
+
detectLocale?: boolean;
|
|
1323
|
+
/** Cookie name to persist locale preference. Default: "locale" */
|
|
1324
|
+
cookieName?: string;
|
|
1325
|
+
/** URL strategy. Default: "prefix-except-default" */
|
|
1326
|
+
strategy?: 'prefix' | 'prefix-except-default';
|
|
1327
|
+
}
|
|
1328
|
+
interface LocaleContext {
|
|
1329
|
+
/** Current locale code. e.g. "en", "de" */
|
|
1330
|
+
locale: string;
|
|
1331
|
+
/** All supported locales. */
|
|
1332
|
+
locales: string[];
|
|
1333
|
+
/** Default locale. */
|
|
1334
|
+
defaultLocale: string;
|
|
1335
|
+
/** Build a localized path. e.g. localePath("/about", "de") → "/de/about" */
|
|
1336
|
+
localePath: (path: string, locale?: string) => string;
|
|
1337
|
+
/** Get hreflang alternates for the current path. */
|
|
1338
|
+
alternates: () => Array<{
|
|
1339
|
+
locale: string;
|
|
1340
|
+
url: string;
|
|
1341
|
+
}>;
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Detect preferred locale from Accept-Language header.
|
|
1345
|
+
*/
|
|
1346
|
+
declare function detectLocaleFromHeader(acceptLanguage: string | null | undefined, locales: string[], defaultLocale: string): string;
|
|
1347
|
+
/**
|
|
1348
|
+
* Extract locale from a URL path.
|
|
1349
|
+
* Returns { locale, pathWithoutLocale }.
|
|
1350
|
+
*/
|
|
1351
|
+
declare function extractLocaleFromPath(path: string, locales: string[], defaultLocale: string): {
|
|
1352
|
+
locale: string;
|
|
1353
|
+
pathWithoutLocale: string;
|
|
1354
|
+
};
|
|
1355
|
+
/**
|
|
1356
|
+
* Build a localized path.
|
|
1357
|
+
*/
|
|
1358
|
+
declare function buildLocalePath(path: string, locale: string, defaultLocale: string, strategy: 'prefix' | 'prefix-except-default'): string;
|
|
1359
|
+
/**
|
|
1360
|
+
* Create a LocaleContext for use in components and loaders.
|
|
1361
|
+
*/
|
|
1362
|
+
declare function createLocaleContext(locale: string, path: string, config: I18nRoutingConfig): LocaleContext;
|
|
1363
|
+
/**
|
|
1364
|
+
* I18n routing middleware for Zero's server.
|
|
1365
|
+
*
|
|
1366
|
+
* - Detects locale from URL prefix or Accept-Language header
|
|
1367
|
+
* - Redirects root to preferred locale (when detectLocale is true)
|
|
1368
|
+
* - Sets locale context for loaders and components
|
|
1369
|
+
*
|
|
1370
|
+
* @example
|
|
1371
|
+
* ```ts
|
|
1372
|
+
* // zero.config.ts
|
|
1373
|
+
* import { i18nRouting } from "@pyreon/zero"
|
|
1374
|
+
*
|
|
1375
|
+
* export default defineConfig({
|
|
1376
|
+
* plugins: [
|
|
1377
|
+
* i18nRouting({
|
|
1378
|
+
* locales: ["en", "de", "cs"],
|
|
1379
|
+
* defaultLocale: "en",
|
|
1380
|
+
* }),
|
|
1381
|
+
* ],
|
|
1382
|
+
* })
|
|
1383
|
+
* ```
|
|
1384
|
+
*/
|
|
1385
|
+
declare function i18nRouting(config: I18nRoutingConfig): Plugin;
|
|
1386
|
+
/**
|
|
1387
|
+
* Read the current locale reactively.
|
|
1388
|
+
*
|
|
1389
|
+
* Returns the locale signal value directly — reactive in both SSR and CSR.
|
|
1390
|
+
* The server middleware sets `localeSignal` per-request, and client-side
|
|
1391
|
+
* `setLocale()` updates it as well.
|
|
1392
|
+
*
|
|
1393
|
+
* @example
|
|
1394
|
+
* ```tsx
|
|
1395
|
+
* const locale = useLocale() // "en", "de", etc.
|
|
1396
|
+
* ```
|
|
1397
|
+
*/
|
|
1398
|
+
declare function useLocale(): string;
|
|
1399
|
+
/**
|
|
1400
|
+
* Set the locale client-side and update the URL.
|
|
1401
|
+
*
|
|
1402
|
+
* @example
|
|
1403
|
+
* ```tsx
|
|
1404
|
+
* <button onClick={() => setLocale('de')}>Deutsch</button>
|
|
1405
|
+
* ```
|
|
1406
|
+
*/
|
|
1407
|
+
declare function setLocale(locale: string, config: I18nRoutingConfig): void;
|
|
1408
|
+
//#endregion
|
|
1409
|
+
//#region src/meta.d.ts
|
|
1410
|
+
interface MetaProps {
|
|
1411
|
+
/** Page title. Accepts reactive accessor `() => string`. */
|
|
1412
|
+
title?: string | (() => string);
|
|
1413
|
+
/** Page description. Accepts reactive accessor. */
|
|
1414
|
+
description?: string | (() => string);
|
|
1415
|
+
/** Canonical URL. Also sets og:url. */
|
|
1416
|
+
canonical?: string;
|
|
1417
|
+
/** Open Graph image URL. Also sets twitter:image. */
|
|
1418
|
+
image?: string;
|
|
1419
|
+
/** Image alt text for accessibility. */
|
|
1420
|
+
imageAlt?: string;
|
|
1421
|
+
/** Image width in pixels (og:image:width). Helps crawlers layout before loading. */
|
|
1422
|
+
imageWidth?: number;
|
|
1423
|
+
/** Image height in pixels (og:image:height). */
|
|
1424
|
+
imageHeight?: number;
|
|
1425
|
+
/** Open Graph type. Default: "website" */
|
|
1426
|
+
type?: 'website' | 'article' | 'product' | 'profile';
|
|
1427
|
+
/** Site name for og:site_name. */
|
|
1428
|
+
siteName?: string;
|
|
1429
|
+
/** Twitter card type. Default: "summary_large_image" */
|
|
1430
|
+
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
1431
|
+
/** Twitter @handle. */
|
|
1432
|
+
twitterSite?: string;
|
|
1433
|
+
/** Twitter creator @handle. */
|
|
1434
|
+
twitterCreator?: string;
|
|
1435
|
+
/** Locale. Default: "en_US" */
|
|
1436
|
+
locale?: string;
|
|
1437
|
+
/** Alternate locales for hreflang. */
|
|
1438
|
+
alternateLocales?: Array<{
|
|
1439
|
+
locale: string;
|
|
1440
|
+
url: string;
|
|
1441
|
+
}>;
|
|
1442
|
+
/** Robots directives. Default: "index, follow" */
|
|
1443
|
+
robots?: string;
|
|
1444
|
+
/** Convenience: set `true` to emit `noindex, nofollow`. Overrides `robots`. */
|
|
1445
|
+
noIndex?: boolean;
|
|
1446
|
+
/** Published time (ISO 8601) for article type. */
|
|
1447
|
+
publishedTime?: string;
|
|
1448
|
+
/** Modified time (ISO 8601) for article type. */
|
|
1449
|
+
modifiedTime?: string;
|
|
1450
|
+
/** Article author. */
|
|
1451
|
+
author?: string;
|
|
1452
|
+
/** Article tags. */
|
|
1453
|
+
tags?: string[];
|
|
1454
|
+
/** JSON-LD structured data object. */
|
|
1455
|
+
jsonLd?: Record<string, unknown>;
|
|
1456
|
+
/** Additional custom meta tags. */
|
|
1457
|
+
extra?: Array<{
|
|
1458
|
+
name?: string;
|
|
1459
|
+
property?: string;
|
|
1460
|
+
content: string;
|
|
1461
|
+
}>;
|
|
1462
|
+
/**
|
|
1463
|
+
* Open Graph video URL. Also sets og:video:type if the URL ends with
|
|
1464
|
+
* a known extension (.mp4, .webm).
|
|
1465
|
+
*/
|
|
1466
|
+
video?: string;
|
|
1467
|
+
/** Video width in pixels. */
|
|
1468
|
+
videoWidth?: number;
|
|
1469
|
+
/** Video height in pixels. */
|
|
1470
|
+
videoHeight?: number;
|
|
1471
|
+
/**
|
|
1472
|
+
* Open Graph audio URL.
|
|
1473
|
+
*/
|
|
1474
|
+
audio?: string;
|
|
1475
|
+
/**
|
|
1476
|
+
* I18n routing config — when provided, auto-generates hreflang alternate
|
|
1477
|
+
* links for all locales based on the current path.
|
|
1478
|
+
* Also sets og:locale and og:locale:alternate.
|
|
1479
|
+
*/
|
|
1480
|
+
i18n?: I18nRoutingConfig;
|
|
1481
|
+
/** Base URL for building absolute hreflang URLs. e.g. "https://example.com" */
|
|
1482
|
+
origin?: string;
|
|
1483
|
+
/**
|
|
1484
|
+
* Favicon plugin config — when provided, injects locale-aware favicon
|
|
1485
|
+
* `<link>` tags into `<head>`. Uses the current locale to select
|
|
1486
|
+
* the correct favicon set.
|
|
1487
|
+
*/
|
|
1488
|
+
favicon?: FaviconPluginConfig;
|
|
1489
|
+
/**
|
|
1490
|
+
* OG image template name — auto-resolves to the correct locale-specific
|
|
1491
|
+
* OG image path generated by `ogImagePlugin`.
|
|
1492
|
+
* Sets both `og:image` and `twitter:image` unless `image` is also provided.
|
|
1493
|
+
*/
|
|
1494
|
+
ogTemplate?: string;
|
|
1495
|
+
/** Output directory for OG images. Default: "og" */
|
|
1496
|
+
ogImageDir?: string;
|
|
1497
|
+
/** OG image format. Default: "png" */
|
|
1498
|
+
ogImageFormat?: 'png' | 'jpeg';
|
|
1499
|
+
children?: VNodeChild;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Declarative meta component for SSR-compatible page metadata.
|
|
1503
|
+
*
|
|
1504
|
+
* Supports reactive title/description — when passed as `() => string` accessors,
|
|
1505
|
+
* they are forwarded to `useHead()` as a reactive getter so updates propagate
|
|
1506
|
+
* automatically via signal tracking.
|
|
1507
|
+
*
|
|
1508
|
+
* @example
|
|
1509
|
+
* ```tsx
|
|
1510
|
+
* <Meta title="My Page" description="..." image="/og.jpg" canonical="https://..." />
|
|
1511
|
+
* ```
|
|
1512
|
+
*
|
|
1513
|
+
* @example Reactive title
|
|
1514
|
+
* ```tsx
|
|
1515
|
+
* const count = signal(0)
|
|
1516
|
+
* <Meta title={() => `${count()} items`} />
|
|
1517
|
+
* ```
|
|
1518
|
+
*/
|
|
1519
|
+
declare function Meta(props: MetaProps): VNodeChild;
|
|
1520
|
+
interface MetaTagEntry {
|
|
1521
|
+
name?: string;
|
|
1522
|
+
property?: string;
|
|
1523
|
+
content: string;
|
|
1524
|
+
[key: string]: string | undefined;
|
|
1525
|
+
}
|
|
1526
|
+
interface LinkTagEntry {
|
|
1527
|
+
rel: string;
|
|
1528
|
+
href?: string;
|
|
1529
|
+
hreflang?: string;
|
|
1530
|
+
type?: string;
|
|
1531
|
+
sizes?: string;
|
|
1532
|
+
[key: string]: string | undefined;
|
|
1533
|
+
}
|
|
1534
|
+
interface ScriptTagEntry {
|
|
1535
|
+
type: string;
|
|
1536
|
+
children: string;
|
|
1537
|
+
}
|
|
1538
|
+
interface MetaTags {
|
|
1539
|
+
meta: MetaTagEntry[];
|
|
1540
|
+
link: LinkTagEntry[];
|
|
1541
|
+
script: ScriptTagEntry[];
|
|
1542
|
+
}
|
|
1543
|
+
declare function buildMetaTags(props: Omit<MetaProps, 'title' | 'description' | 'children'> & {
|
|
1544
|
+
title?: string;
|
|
1545
|
+
description?: string;
|
|
1546
|
+
}): MetaTags;
|
|
1547
|
+
//#endregion
|
|
1548
|
+
//#region src/csp.d.ts
|
|
1549
|
+
/**
|
|
1550
|
+
* Read the current CSP nonce in a component.
|
|
1551
|
+
*
|
|
1552
|
+
* SSR: reads from per-request `ctx.locals.cspNonce` via Pyreon's context
|
|
1553
|
+
* system — fully isolated between concurrent requests via AsyncLocalStorage.
|
|
1554
|
+
* Client/dev: falls back to module-level variable set by middleware.
|
|
1555
|
+
*
|
|
1556
|
+
* @example
|
|
1557
|
+
* ```tsx
|
|
1558
|
+
* import { useNonce } from "@pyreon/zero/csp"
|
|
1559
|
+
*
|
|
1560
|
+
* function InlineScript() {
|
|
1561
|
+
* const nonce = useNonce()
|
|
1562
|
+
* return <script nonce={nonce}>console.log("safe")</script>
|
|
1563
|
+
* }
|
|
1564
|
+
* ```
|
|
1565
|
+
*/
|
|
1566
|
+
declare function useNonce(): string;
|
|
1567
|
+
interface CspDirectives {
|
|
1568
|
+
defaultSrc?: string[];
|
|
1569
|
+
scriptSrc?: string[];
|
|
1570
|
+
styleSrc?: string[];
|
|
1571
|
+
imgSrc?: string[];
|
|
1572
|
+
fontSrc?: string[];
|
|
1573
|
+
connectSrc?: string[];
|
|
1574
|
+
mediaSrc?: string[];
|
|
1575
|
+
objectSrc?: string[];
|
|
1576
|
+
frameSrc?: string[];
|
|
1577
|
+
childSrc?: string[];
|
|
1578
|
+
workerSrc?: string[];
|
|
1579
|
+
frameAncestors?: string[];
|
|
1580
|
+
formAction?: string[];
|
|
1581
|
+
baseUri?: string[];
|
|
1582
|
+
manifestSrc?: string[];
|
|
1583
|
+
/** Reporting endpoint URL. */
|
|
1584
|
+
reportUri?: string;
|
|
1585
|
+
/** Reporting endpoint name (CSP Level 3). */
|
|
1586
|
+
reportTo?: string;
|
|
1587
|
+
/** Upgrade insecure requests. */
|
|
1588
|
+
upgradeInsecureRequests?: boolean;
|
|
1589
|
+
/** Block all mixed content. */
|
|
1590
|
+
blockAllMixedContent?: boolean;
|
|
1591
|
+
}
|
|
1592
|
+
interface CspConfig {
|
|
1593
|
+
/** CSP directives. */
|
|
1594
|
+
directives: CspDirectives;
|
|
1595
|
+
/**
|
|
1596
|
+
* Report-only mode — logs violations without blocking.
|
|
1597
|
+
* Uses Content-Security-Policy-Report-Only header instead.
|
|
1598
|
+
* Default: false
|
|
1599
|
+
*/
|
|
1600
|
+
reportOnly?: boolean;
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Build a CSP header string from directives.
|
|
1604
|
+
* Exported for testing.
|
|
1605
|
+
*/
|
|
1606
|
+
declare function buildCspHeader(directives: CspDirectives, nonce?: string): string;
|
|
1607
|
+
/**
|
|
1608
|
+
* CSP middleware — sets Content-Security-Policy header.
|
|
1609
|
+
*
|
|
1610
|
+
* When directives contain `"'nonce'"`, a fresh nonce is generated per-request
|
|
1611
|
+
* and attached to `ctx.locals.cspNonce` for use in inline script tags.
|
|
1612
|
+
*
|
|
1613
|
+
* @example
|
|
1614
|
+
* ```ts
|
|
1615
|
+
* // Apply to all routes
|
|
1616
|
+
* export default defineConfig({
|
|
1617
|
+
* middleware: [
|
|
1618
|
+
* cspMiddleware({
|
|
1619
|
+
* directives: {
|
|
1620
|
+
* defaultSrc: ["'self'"],
|
|
1621
|
+
* scriptSrc: ["'self'", "'nonce'"],
|
|
1622
|
+
* styleSrc: ["'self'", "'unsafe-inline'"],
|
|
1623
|
+
* imgSrc: ["'self'", "data:", "https:"],
|
|
1624
|
+
* },
|
|
1625
|
+
* }),
|
|
1626
|
+
* ],
|
|
1627
|
+
* })
|
|
1628
|
+
* ```
|
|
1629
|
+
*/
|
|
1630
|
+
declare function cspMiddleware(config: CspConfig): Middleware;
|
|
1631
|
+
//#endregion
|
|
1632
|
+
//#region src/logger.d.ts
|
|
1633
|
+
interface LoggerConfig {
|
|
1634
|
+
/**
|
|
1635
|
+
* Log level — controls which requests are logged.
|
|
1636
|
+
* - "all": log every request
|
|
1637
|
+
* - "none": disable logging
|
|
1638
|
+
* Default: "all"
|
|
1639
|
+
*/
|
|
1640
|
+
level?: 'all' | 'none';
|
|
1641
|
+
/**
|
|
1642
|
+
* Custom log formatter. Receives request details and returns
|
|
1643
|
+
* the string to log (or null to skip).
|
|
1644
|
+
*/
|
|
1645
|
+
format?: (entry: LogEntry) => string | null;
|
|
1646
|
+
/**
|
|
1647
|
+
* Skip logging for these path prefixes.
|
|
1648
|
+
* Default: ["/__", "/@", "/node_modules"]
|
|
1649
|
+
*/
|
|
1650
|
+
skip?: string[];
|
|
1651
|
+
/**
|
|
1652
|
+
* Enable colorized output (ANSI codes).
|
|
1653
|
+
* Default: true in development, false in production.
|
|
1654
|
+
*/
|
|
1655
|
+
colors?: boolean;
|
|
1656
|
+
}
|
|
1657
|
+
interface LogEntry {
|
|
1658
|
+
method: string;
|
|
1659
|
+
path: string;
|
|
1660
|
+
duration: number;
|
|
1661
|
+
timestamp: Date;
|
|
1662
|
+
userAgent?: string | undefined;
|
|
1663
|
+
ip?: string | undefined;
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Request logging middleware.
|
|
1667
|
+
*
|
|
1668
|
+
* Logs incoming requests with method, path, and duration.
|
|
1669
|
+
* Runs in middleware phase — logs timing from middleware start to
|
|
1670
|
+
* microtask completion (approximate request duration).
|
|
1671
|
+
*
|
|
1672
|
+
* @example
|
|
1673
|
+
* ```ts
|
|
1674
|
+
* // Basic usage
|
|
1675
|
+
* loggerMiddleware()
|
|
1676
|
+
*
|
|
1677
|
+
* // Custom format
|
|
1678
|
+
* loggerMiddleware({
|
|
1679
|
+
* format: (e) => `${e.method} ${e.path} (${e.duration}ms)`,
|
|
1680
|
+
* })
|
|
1681
|
+
* ```
|
|
1682
|
+
*/
|
|
1683
|
+
declare function loggerMiddleware(config?: LoggerConfig): Middleware;
|
|
1684
|
+
//#endregion
|
|
1685
|
+
//#region src/env.d.ts
|
|
1686
|
+
/**
|
|
1687
|
+
* Environment variable validation.
|
|
1688
|
+
*
|
|
1689
|
+
* Infers types from default values — no verbose validator imports needed.
|
|
1690
|
+
* Explicit validators (`url()`, `oneOf()`) available for special cases.
|
|
1691
|
+
*
|
|
1692
|
+
* @example
|
|
1693
|
+
* ```ts
|
|
1694
|
+
* import { validateEnv, url, oneOf } from "@pyreon/zero/env"
|
|
1695
|
+
*
|
|
1696
|
+
* const env = validateEnv({
|
|
1697
|
+
* PORT: 3000, // number, default 3000
|
|
1698
|
+
* DEBUG: false, // boolean, default false
|
|
1699
|
+
* HOST: "localhost", // string, default "localhost"
|
|
1700
|
+
* DATABASE_URL: url(), // validated URL, required
|
|
1701
|
+
* NODE_ENV: oneOf(["development", "production", "test"]),
|
|
1702
|
+
* API_KEY: String, // required string, no default
|
|
1703
|
+
* MAX_RETRIES: Number, // required number, no default
|
|
1704
|
+
* })
|
|
1705
|
+
* ```
|
|
1706
|
+
*/
|
|
1707
|
+
interface EnvValidatorOptions<T = string> {
|
|
1708
|
+
/** Whether this variable is required. Default: true */
|
|
1709
|
+
required?: boolean;
|
|
1710
|
+
/** Default value when not set. Makes the variable optional. */
|
|
1711
|
+
default?: T;
|
|
1712
|
+
/** Human-readable description for error messages. */
|
|
1713
|
+
description?: string;
|
|
1714
|
+
}
|
|
1715
|
+
interface EnvValidator<T> {
|
|
1716
|
+
__type: 'env-validator';
|
|
1717
|
+
parse: (raw: string | undefined, key: string) => T;
|
|
1718
|
+
required: boolean;
|
|
1719
|
+
defaultValue?: T | undefined;
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* String validator — accepts any non-empty string.
|
|
1723
|
+
*/
|
|
1724
|
+
declare function str(options?: EnvValidatorOptions<string>): EnvValidator<string>;
|
|
1725
|
+
/**
|
|
1726
|
+
* Number validator — parses to a number, rejects NaN.
|
|
1727
|
+
*/
|
|
1728
|
+
declare function num(options?: EnvValidatorOptions<number>): EnvValidator<number>;
|
|
1729
|
+
/**
|
|
1730
|
+
* Boolean validator — accepts "true"/"1" as true, "false"/"0" as false.
|
|
1731
|
+
*/
|
|
1732
|
+
declare function bool(options?: EnvValidatorOptions<boolean>): EnvValidator<boolean>;
|
|
1733
|
+
/**
|
|
1734
|
+
* URL validator — validates that the value is a valid URL.
|
|
1735
|
+
*/
|
|
1736
|
+
declare function url(options?: EnvValidatorOptions<string>): EnvValidator<string>;
|
|
1737
|
+
/**
|
|
1738
|
+
* Enum validator — value must be one of the allowed values.
|
|
1739
|
+
*/
|
|
1740
|
+
declare function oneOf<T extends string>(values: readonly T[], options?: EnvValidatorOptions<T>): EnvValidator<T>;
|
|
1741
|
+
/** Schema entry: plain value, constructor, or explicit validator. */
|
|
1742
|
+
type SchemaEntry = string | number | boolean | StringConstructor | NumberConstructor | BooleanConstructor | EnvValidator<any>;
|
|
1743
|
+
/** Infer the output type from a schema entry. */
|
|
1744
|
+
type InferEntry<T> = T extends EnvValidator<infer V> ? V : T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BooleanConstructor ? boolean : T extends string ? string : T extends number ? number : T extends boolean ? boolean : never;
|
|
1745
|
+
type InferEnvSchema<T> = { [K in keyof T]: InferEntry<T[K]> };
|
|
1746
|
+
/**
|
|
1747
|
+
* Validate environment variables.
|
|
1748
|
+
*
|
|
1749
|
+
* Schema values can be:
|
|
1750
|
+
* - **Default values**: `3000`, `false`, `"localhost"` → type inferred, used as default
|
|
1751
|
+
* - **Constructors**: `String`, `Number`, `Boolean` → required, no default
|
|
1752
|
+
* - **Validators**: `url()`, `oneOf([...])`, `str()`, `num()`, `bool()` → explicit validation
|
|
1753
|
+
* - **Custom**: `schema(raw => z.coerce.number().parse(raw))` — bridge to any schema library
|
|
1754
|
+
*
|
|
1755
|
+
* @example
|
|
1756
|
+
* ```ts
|
|
1757
|
+
* import { validateEnv, url, oneOf } from "@pyreon/zero/env"
|
|
1758
|
+
*
|
|
1759
|
+
* const env = validateEnv({
|
|
1760
|
+
* PORT: 3000, // optional, default 3000
|
|
1761
|
+
* DATABASE_URL: url(), // required, validated URL
|
|
1762
|
+
* NODE_ENV: oneOf(["dev", "prod", "test"]), // required, must be one of
|
|
1763
|
+
* API_KEY: String, // required string
|
|
1764
|
+
* DEBUG: false, // optional, default false
|
|
1765
|
+
* })
|
|
1766
|
+
* ```
|
|
1767
|
+
*/
|
|
1768
|
+
declare function validateEnv<T extends Record<string, SchemaEntry>>(schema: T, source?: Record<string, string | undefined>): InferEnvSchema<T>;
|
|
1769
|
+
/**
|
|
1770
|
+
* Extract public environment variables (prefixed with `ZERO_PUBLIC_`).
|
|
1771
|
+
*
|
|
1772
|
+
* @example
|
|
1773
|
+
* ```ts
|
|
1774
|
+
* const pub = publicEnv()
|
|
1775
|
+
* // → { API_URL: "https://...", APP_NAME: "MyApp" }
|
|
1776
|
+
*
|
|
1777
|
+
* const pub = publicEnv({ API_URL: url(), APP_NAME: "Default" })
|
|
1778
|
+
* // → validated against ZERO_PUBLIC_API_URL, ZERO_PUBLIC_APP_NAME
|
|
1779
|
+
* ```
|
|
1780
|
+
*/
|
|
1781
|
+
declare function publicEnv(): Record<string, string>;
|
|
1782
|
+
declare function publicEnv<T extends Record<string, SchemaEntry>>(schema: T): InferEnvSchema<T>;
|
|
1783
|
+
/**
|
|
1784
|
+
* Create an env validator from a custom parse function.
|
|
1785
|
+
* Use this to integrate any schema library (Zod, Valibot, ArkType, etc.).
|
|
1786
|
+
*
|
|
1787
|
+
* @example
|
|
1788
|
+
* ```ts
|
|
1789
|
+
* import { z } from "zod"
|
|
1790
|
+
* import { validateEnv, schema } from "@pyreon/zero/env"
|
|
1791
|
+
*
|
|
1792
|
+
* const env = validateEnv({
|
|
1793
|
+
* PORT: schema(raw => z.coerce.number().parse(raw)),
|
|
1794
|
+
* DATABASE_URL: schema(raw => z.string().url().parse(raw)),
|
|
1795
|
+
* HOST: "localhost", // plain defaults still work alongside
|
|
1796
|
+
* })
|
|
1797
|
+
* ```
|
|
1798
|
+
*/
|
|
1799
|
+
declare function schema<T>(parse: (raw: string) => T): EnvValidator<T>;
|
|
1800
|
+
//#endregion
|
|
1801
|
+
//#region src/ai.d.ts
|
|
1802
|
+
interface AiPluginConfig {
|
|
1803
|
+
/** App/API name. */
|
|
1804
|
+
name: string;
|
|
1805
|
+
/** App description for AI agents. */
|
|
1806
|
+
description: string;
|
|
1807
|
+
/** Base URL. e.g. "https://example.com" */
|
|
1808
|
+
origin: string;
|
|
1809
|
+
/** Contact email (required by OpenAI plugin spec). */
|
|
1810
|
+
contactEmail?: string;
|
|
1811
|
+
/** Legal info URL. */
|
|
1812
|
+
legalUrl?: string;
|
|
1813
|
+
/** Logo URL for the plugin. */
|
|
1814
|
+
logoUrl?: string;
|
|
1815
|
+
/** Routes directory relative to project root. Default: "src/routes" */
|
|
1816
|
+
routesDir?: string;
|
|
1817
|
+
/** API routes directory relative to project root. Default: "src/api" */
|
|
1818
|
+
apiDir?: string;
|
|
1819
|
+
/**
|
|
1820
|
+
* API route descriptions — map of pattern to description.
|
|
1821
|
+
* Used for llms.txt and ai-plugin.json.
|
|
1822
|
+
*
|
|
1823
|
+
* @example
|
|
1824
|
+
* ```ts
|
|
1825
|
+
* apiDescriptions: {
|
|
1826
|
+
* "GET /api/posts": "List all blog posts, supports ?page=N&limit=N",
|
|
1827
|
+
* "GET /api/posts/:id": "Get a single post by ID",
|
|
1828
|
+
* "POST /api/posts": "Create a new post (requires auth)",
|
|
1829
|
+
* }
|
|
1830
|
+
* ```
|
|
1831
|
+
*/
|
|
1832
|
+
apiDescriptions?: Record<string, string>;
|
|
1833
|
+
/**
|
|
1834
|
+
* Page descriptions — map of URL path to description.
|
|
1835
|
+
* Used for llms.txt. Falls back to route meta.title/description.
|
|
1836
|
+
*/
|
|
1837
|
+
pageDescriptions?: Record<string, string>;
|
|
1838
|
+
/**
|
|
1839
|
+
* Additional content to append to llms.txt.
|
|
1840
|
+
* Useful for authentication instructions, rate limits, etc.
|
|
1841
|
+
*/
|
|
1842
|
+
llmsExtra?: string;
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Generate llms.txt content from route files and config.
|
|
1846
|
+
*
|
|
1847
|
+
* Format follows the llms.txt proposal:
|
|
1848
|
+
* ```
|
|
1849
|
+
* # {name}
|
|
1850
|
+
* > {description}
|
|
1851
|
+
*
|
|
1852
|
+
* ## Pages
|
|
1853
|
+
* - [/about](/about): About page
|
|
1854
|
+
*
|
|
1855
|
+
* ## API
|
|
1856
|
+
* - GET /api/posts: List posts
|
|
1857
|
+
* ```
|
|
1858
|
+
*
|
|
1859
|
+
* @internal Exported for testing.
|
|
1860
|
+
*/
|
|
1861
|
+
declare function generateLlmsTxt(routeFiles: string[], apiFiles: string[], config: AiPluginConfig): string;
|
|
1862
|
+
/**
|
|
1863
|
+
* Generate llms-full.txt — expanded version with more detail.
|
|
1864
|
+
* Includes all route metadata and API descriptions.
|
|
1865
|
+
*
|
|
1866
|
+
* @internal Exported for testing.
|
|
1867
|
+
*/
|
|
1868
|
+
declare function generateLlmsFullTxt(routeFiles: string[], apiFiles: string[], config: AiPluginConfig): string;
|
|
1869
|
+
interface InferJsonLdOptions {
|
|
1870
|
+
/** Page URL. */
|
|
1871
|
+
url: string;
|
|
1872
|
+
/** Page title. */
|
|
1873
|
+
title?: string;
|
|
1874
|
+
/** Page description. */
|
|
1875
|
+
description?: string;
|
|
1876
|
+
/** Page image. */
|
|
1877
|
+
image?: string;
|
|
1878
|
+
/** Site name. */
|
|
1879
|
+
siteName?: string;
|
|
1880
|
+
/** Page type hint. */
|
|
1881
|
+
type?: 'website' | 'article' | 'product' | 'profile';
|
|
1882
|
+
/** Article metadata. */
|
|
1883
|
+
publishedTime?: string;
|
|
1884
|
+
/** Article author. */
|
|
1885
|
+
author?: string;
|
|
1886
|
+
/** Article tags. */
|
|
1887
|
+
tags?: string[];
|
|
1888
|
+
/** Breadcrumb path segments. */
|
|
1889
|
+
breadcrumbs?: Array<{
|
|
1890
|
+
name: string;
|
|
1891
|
+
url: string;
|
|
1892
|
+
}>;
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Auto-infer JSON-LD structured data from page metadata.
|
|
1896
|
+
*
|
|
1897
|
+
* Returns an array of JSON-LD objects (multiple schemas can apply to one page).
|
|
1898
|
+
* For example, an article page gets both `Article` and `BreadcrumbList`.
|
|
1899
|
+
*
|
|
1900
|
+
* @example
|
|
1901
|
+
* ```tsx
|
|
1902
|
+
* const schemas = inferJsonLd({
|
|
1903
|
+
* url: "https://example.com/blog/my-post",
|
|
1904
|
+
* title: "My Post",
|
|
1905
|
+
* description: "A great article",
|
|
1906
|
+
* type: "article",
|
|
1907
|
+
* author: "Vit Bokisch",
|
|
1908
|
+
* publishedTime: "2026-03-31",
|
|
1909
|
+
* })
|
|
1910
|
+
* // → [Article schema, BreadcrumbList schema]
|
|
1911
|
+
* ```
|
|
1912
|
+
*/
|
|
1913
|
+
declare function inferJsonLd(options: InferJsonLdOptions): Record<string, unknown>[];
|
|
1914
|
+
/**
|
|
1915
|
+
* Generate an OpenAI-compatible AI plugin manifest.
|
|
1916
|
+
*
|
|
1917
|
+
* Follows the /.well-known/ai-plugin.json spec.
|
|
1918
|
+
*
|
|
1919
|
+
* @internal Exported for testing.
|
|
1920
|
+
*/
|
|
1921
|
+
declare function generateAiPluginManifest(config: AiPluginConfig): Record<string, unknown>;
|
|
1922
|
+
/**
|
|
1923
|
+
* Generate a minimal OpenAPI 3.0 spec from API route descriptions.
|
|
1924
|
+
*
|
|
1925
|
+
* @internal Exported for testing.
|
|
1926
|
+
*/
|
|
1927
|
+
declare function generateOpenApiSpec(apiFiles: string[], config: AiPluginConfig): Record<string, unknown>;
|
|
1928
|
+
/**
|
|
1929
|
+
* AI integration Vite plugin.
|
|
1930
|
+
*
|
|
1931
|
+
* Generates at build time:
|
|
1932
|
+
* - `/llms.txt` — concise site summary for AI agents
|
|
1933
|
+
* - `/llms-full.txt` — detailed reference for AI agents
|
|
1934
|
+
* - `/.well-known/ai-plugin.json` — OpenAI plugin manifest
|
|
1935
|
+
* - `/.well-known/openapi.yaml` — minimal OpenAPI spec from API routes
|
|
1936
|
+
*
|
|
1937
|
+
* In dev, serves these files via middleware.
|
|
1938
|
+
*
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* import { aiPlugin } from "@pyreon/zero/ai"
|
|
1942
|
+
*
|
|
1943
|
+
* export default {
|
|
1944
|
+
* plugins: [
|
|
1945
|
+
* aiPlugin({
|
|
1946
|
+
* name: "My App",
|
|
1947
|
+
* origin: "https://example.com",
|
|
1948
|
+
* description: "A modern web application",
|
|
1949
|
+
* apiDescriptions: {
|
|
1950
|
+
* "GET /api/posts": "List blog posts",
|
|
1951
|
+
* "GET /api/posts/:id": "Get post by ID",
|
|
1952
|
+
* },
|
|
1953
|
+
* }),
|
|
1954
|
+
* ],
|
|
1955
|
+
* }
|
|
1956
|
+
* ```
|
|
1957
|
+
*/
|
|
1958
|
+
declare function aiPlugin(config: AiPluginConfig): Plugin;
|
|
1959
|
+
//#endregion
|
|
1960
|
+
export { type Action, type ActionContext, type ActionHandler, type Adapter, type AdapterBuildOptions, type AiPluginConfig, type ApiContext, type ApiHandler, type ApiRouteEntry, type ApiRouteModule, type CacheConfig, type CacheRule, type ChangeFreq, type CompressionConfig, type CorsConfig, type CreateAppOptions, type CreateServerOptions, type CspConfig, type CspDirectives, type EnvValidator, type FallbackMetrics, type FaviconLocaleConfig, type FaviconPluginConfig, type FileRoute, type FontConfig, type FontDisplay, type FormatSource, type GenerateRouteModuleOptions, type GoogleFontInput, type GoogleFontStatic, type GoogleFontVariable, type HttpMethod, type I18nRoutingConfig, type ISRConfig, Image, type ImageFormat, type ImagePluginConfig, type ImageProps, type ImageSource, type InferJsonLdOptions, type JsonLdType, Link, type LinkProps, type LinkRenderProps, type LoaderContext, type LocalFont, type LocaleContext, type LogEntry, type LoggerConfig, Meta, type MetaProps, type OgImageLayer, type OgImagePluginConfig, type OgImageTemplate, type ProcessedImage, type RateLimitConfig, type RenderMode, type RobotsConfig, type RobotsRule, type RouteMeta, type RouteMiddlewareEntry, type RouteModule, Script, type ScriptProps, type ScriptStrategy, type SeoPluginConfig, type SitemapConfig, type SitemapEntry, type Theme, ThemeToggle, type UseLinkReturn, type ZeroConfig, aiPlugin, bool, buildCspHeader, buildLocalePath, buildMetaTags, bunAdapter, cacheMiddleware, cloudflareAdapter, compose, compressResponse, compressionMiddleware, corsMiddleware, createActionMiddleware, createApiMiddleware, createApp, createISRHandler, createLink, createLocaleContext, createServer, cspMiddleware, zeroPlugin as default, defineAction, defineConfig, detectLocaleFromHeader, extractLocaleFromPath, faviconLinks, faviconPlugin, filePathToUrlPath, fontPlugin, fontVariables, generateAiPluginManifest, generateApiRouteModule, generateLlmsFullTxt, generateLlmsTxt, generateMiddlewareModule, generateOpenApiSpec, generateRobots, generateRouteModule, generateSitemap, getContext, i18nRouting, imagePlugin, inferJsonLd, initTheme, isCompressible, jsonLd, loggerMiddleware, netlifyAdapter, nodeAdapter, num, ogImagePath, ogImagePlugin, oneOf, parseFileRoutes, prefetchRoute, publicEnv, rateLimitMiddleware, render404Page, resolveAdapter, resolveConfig, resolvedTheme, scanRouteFiles, schema, securityHeaders, seoMiddleware, seoPlugin, setLocale, setSSRThemeDefault, setTheme, staticAdapter, str, theme, themeScript, toggleTheme, url, useLink, useLocale, useNonce, validateEnv, varyEncoding, vercelAdapter };
|
|
1961
|
+
//# sourceMappingURL=index2.d.ts.map
|