@pyreon/zero 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{api-routes-Ci0kVmM4.js → api-routes-CMsLztoj.js} +5 -3
- package/lib/api-routes.js +4 -2
- package/lib/favicon.js +57 -3
- package/lib/{fs-router-MewHc5SB.js → fs-router-Bacdhsq-.js} +4 -3
- package/lib/image-plugin.js +90 -19
- package/lib/index.js +96 -3
- package/lib/rate-limit.js +5 -0
- package/lib/seo.js +11 -6
- package/lib/server.js +2888 -147
- package/lib/testing.js +4 -2
- package/lib/types/config.d.ts +9 -0
- package/lib/types/favicon.d.ts +17 -1
- package/lib/types/i18n-routing.d.ts +2 -4
- package/lib/types/image-plugin.d.ts +65 -7
- package/lib/types/index.d.ts +91 -7
- package/lib/types/link.d.ts +2 -4
- package/lib/types/server.d.ts +89 -5
- package/lib/types/theme.d.ts +1 -2
- package/package.json +10 -10
- package/src/api-routes.ts +12 -2
- package/src/favicon.ts +84 -2
- package/src/fs-router.ts +7 -1
- package/src/icon.tsx +182 -0
- package/src/icons-plugin.ts +296 -0
- package/src/image-plugin.ts +200 -32
- package/src/index.ts +2 -0
- package/src/isr.ts +54 -10
- package/src/manifest.ts +99 -0
- package/src/rate-limit.ts +16 -0
- package/src/seo.ts +19 -4
- package/src/server.ts +2 -0
- package/src/sharp.d.ts +6 -0
- package/src/ssg-plugin.ts +47 -8
- package/src/types.ts +9 -0
- package/lib/vite-plugin-y0NmCLJA.js +0 -2476
package/lib/testing.js
CHANGED
|
@@ -7,17 +7,19 @@ function matchApiRoute(pattern, path) {
|
|
|
7
7
|
const patternParts = pattern.split("/").filter(Boolean);
|
|
8
8
|
const pathParts = path.split("/").filter(Boolean);
|
|
9
9
|
const params = {};
|
|
10
|
+
const isUnsafeParam = (name) => name === "__proto__" || name === "constructor" || name === "prototype";
|
|
10
11
|
for (let i = 0; i < patternParts.length; i++) {
|
|
11
12
|
const pp = patternParts[i];
|
|
12
13
|
if (!pp) continue;
|
|
13
14
|
if (pp.endsWith("*")) {
|
|
14
15
|
const paramName = pp.slice(1, -1);
|
|
15
|
-
params[paramName] = pathParts.slice(i).join("/");
|
|
16
|
+
if (!isUnsafeParam(paramName)) params[paramName] = pathParts.slice(i).join("/");
|
|
16
17
|
return params;
|
|
17
18
|
}
|
|
18
19
|
if (i >= pathParts.length) return null;
|
|
19
20
|
if (pp.startsWith(":")) {
|
|
20
|
-
|
|
21
|
+
const paramName = pp.slice(1);
|
|
22
|
+
if (!isUnsafeParam(paramName)) params[paramName] = pathParts[i];
|
|
21
23
|
continue;
|
|
22
24
|
}
|
|
23
25
|
if (pp !== pathParts[i]) return null;
|
package/lib/types/config.d.ts
CHANGED
|
@@ -25,6 +25,15 @@ interface ISRConfig {
|
|
|
25
25
|
* space (e.g. `/user/:id` where `:id` is free-form).
|
|
26
26
|
*/
|
|
27
27
|
maxEntries?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Max wall-time (ms) for a single background revalidation before it is
|
|
30
|
+
* abandoned. Without a bound, a handler that hangs leaves its key
|
|
31
|
+
* pinned in the in-flight set forever — every later request for that
|
|
32
|
+
* key short-circuits the de-dupe guard and the entry can never
|
|
33
|
+
* recover from stale. Default: `30000` (matches the Suspense
|
|
34
|
+
* streaming timeout).
|
|
35
|
+
*/
|
|
36
|
+
revalidateTimeoutMs?: number;
|
|
28
37
|
/**
|
|
29
38
|
* Cache-key derivation function. The default keys cache entries by
|
|
30
39
|
* `url.pathname` ONLY — query strings, cookies, and headers are
|
package/lib/types/favicon.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
|
|
3
3
|
//#region src/favicon.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Stable content hash (FNV-1a, 32-bit) of the favicon source file(s),
|
|
6
|
+
* rendered as a `?v=<hex>` cache-bust query for the injected `<head>`
|
|
7
|
+
* links. Browsers cache favicons extremely aggressively (often per-
|
|
8
|
+
* session / effectively forever), so with a stable URL a changed icon
|
|
9
|
+
* is never re-fetched by returning visitors. Same source bytes →
|
|
10
|
+
* identical query (no needless cache churn); changed bytes → new query
|
|
11
|
+
* → browser re-downloads. Falls back to `''` (no query, prior
|
|
12
|
+
* behaviour) if a source can't be read — never break the build over a
|
|
13
|
+
* cache-bust nicety. NOTE: this versions everything referenced via
|
|
14
|
+
* `<link>` (svg/png/apple-touch/manifest). The bare `/favicon.ico`
|
|
15
|
+
* convention request (browsers fetch it with no link tag) and the
|
|
16
|
+
* `site.webmanifest`'s internal icon entries keep stable URLs — those
|
|
17
|
+
* rely on host cache headers / are re-resolved on PWA (re)install.
|
|
18
|
+
*/
|
|
19
|
+
declare function faviconVersionQuery(paths: string[]): string;
|
|
4
20
|
interface FaviconLocaleConfig {
|
|
5
21
|
/** Locale-specific source icon (SVG or PNG). */
|
|
6
22
|
source: string;
|
|
@@ -102,5 +118,5 @@ interface IcoEntry {
|
|
|
102
118
|
/** @internal Exported for testing */
|
|
103
119
|
declare function createIcoFromPngs(entries: IcoEntry[]): Uint8Array;
|
|
104
120
|
//#endregion
|
|
105
|
-
export { FaviconLocaleConfig, FaviconPluginConfig, IcoEntry, createIcoFromPngs, faviconLinks, faviconPlugin };
|
|
121
|
+
export { FaviconLocaleConfig, FaviconPluginConfig, IcoEntry, createIcoFromPngs, faviconLinks, faviconPlugin, faviconVersionQuery };
|
|
106
122
|
//# sourceMappingURL=favicon2.d.ts.map
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import * as _$_pyreon_core0 from "@pyreon/core";
|
|
2
|
-
import * as _$_pyreon_reactivity0 from "@pyreon/reactivity";
|
|
3
1
|
import { Plugin } from "vite";
|
|
4
2
|
//#region src/types.d.ts
|
|
5
3
|
type RenderMode = 'ssr' | 'ssg' | 'spa' | 'isr';
|
|
@@ -265,9 +263,9 @@ declare function createLocaleContext(locale: string, path: string, config: I18nR
|
|
|
265
263
|
*/
|
|
266
264
|
declare function i18nRouting(config: I18nRoutingConfig): Plugin;
|
|
267
265
|
/** @internal Context for the current locale. */
|
|
268
|
-
declare const LocaleCtx:
|
|
266
|
+
declare const LocaleCtx: import("@pyreon/core").Context<string>;
|
|
269
267
|
/** Current locale signal — set by the server middleware or client-side detection. */
|
|
270
|
-
declare const localeSignal:
|
|
268
|
+
declare const localeSignal: import("@pyreon/reactivity").Signal<string>;
|
|
271
269
|
/**
|
|
272
270
|
* Read the current locale reactively.
|
|
273
271
|
*
|
|
@@ -17,8 +17,32 @@ declare const cdnProviders: {
|
|
|
17
17
|
readonly vercel: () => ImageCdnProvider; /** Bunny CDN: `https://{pullZone}.b-cdn.net/...?width=...&quality=...` */
|
|
18
18
|
readonly bunny: (pullZone: string) => ImageCdnProvider;
|
|
19
19
|
};
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Placeholder generation strategy.
|
|
22
|
+
*
|
|
23
|
+
* - `'blur'` — tiny downscaled + blurred WebP data URI (a few hundred bytes).
|
|
24
|
+
* The richest preview; faithfully previews the image's content.
|
|
25
|
+
* - `'color'` — the image's dominant colour as a ~200-byte flat SVG data
|
|
26
|
+
* URI. Constant size regardless of source complexity (a blurred WebP
|
|
27
|
+
* grows with image content; this doesn't), zero decode, instant paint,
|
|
28
|
+
* zero layout shift. For real photos it's far smaller than `'blur'`; for
|
|
29
|
+
* trivial/solid sources `'blur'` can be the smaller of the two. Best when
|
|
30
|
+
* you want a clean solid backdrop rather than a blurry preview.
|
|
31
|
+
* - `'none'` — no placeholder (`placeholder: ''`). Skips all placeholder work.
|
|
32
|
+
*
|
|
33
|
+
* `'dominant-color'` is a deprecated alias of `'color'` — it was typed from
|
|
34
|
+
* the plugin's inception but never implemented (the build + dev paths always
|
|
35
|
+
* fell through to blur). It now resolves to `'color'`; prefer the shorter
|
|
36
|
+
* name in new code.
|
|
37
|
+
*/
|
|
38
|
+
type PlaceholderStrategy = 'blur' | 'color' | 'dominant-color' | 'none';
|
|
39
|
+
/** Quality per output format (1-100), or a single number applied to all. */
|
|
40
|
+
type ImageQuality = number | Partial<Record<ImageFormat, number>>;
|
|
41
|
+
/**
|
|
42
|
+
* Normalize the public {@link PlaceholderStrategy} to an internal kind.
|
|
43
|
+
* @internal Exported for testing.
|
|
44
|
+
*/
|
|
45
|
+
declare function normalizePlaceholder(s: PlaceholderStrategy): 'blur' | 'color' | 'none';
|
|
22
46
|
/** SVG processing options for ?component imports. */
|
|
23
47
|
interface SvgOptions {
|
|
24
48
|
/** Replace fill/stroke with currentColor. Default: true */
|
|
@@ -33,11 +57,23 @@ interface ImagePluginConfig {
|
|
|
33
57
|
widths?: number[];
|
|
34
58
|
/** Output formats. Default: ["webp"] */
|
|
35
59
|
formats?: ImageFormat[];
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Quality for lossy formats (1-100). Default: 80.
|
|
62
|
+
*
|
|
63
|
+
* Accepts a single number applied to every format, OR a per-format map so
|
|
64
|
+
* you can tune each codec independently — AVIF tolerates a much lower
|
|
65
|
+
* number than WebP/JPEG for the same perceived quality:
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* imagePlugin({ formats: ['avif', 'webp'], quality: { avif: 55, webp: 75 } })
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* Formats omitted from the map fall back to 80.
|
|
72
|
+
*/
|
|
73
|
+
quality?: ImageQuality;
|
|
74
|
+
/** Blur placeholder size in px (only used by the `'blur'` strategy). Default: 16 */
|
|
39
75
|
placeholderSize?: number;
|
|
40
|
-
/** Placeholder strategy. Default: "blur" */
|
|
76
|
+
/** Placeholder strategy. Default: `"blur"`. See {@link PlaceholderStrategy}. */
|
|
41
77
|
placeholder?: PlaceholderStrategy;
|
|
42
78
|
/** File patterns to process. Default: /\.(jpe?g|png|webp|avif)$/i */
|
|
43
79
|
include?: RegExp;
|
|
@@ -126,6 +162,28 @@ declare function parseWebPDimensions(buffer: Buffer): {
|
|
|
126
162
|
width: number;
|
|
127
163
|
height: number;
|
|
128
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Resolve the public {@link ImageQuality} config into a per-format lookup.
|
|
167
|
+
*
|
|
168
|
+
* - `undefined` → every format gets {@link DEFAULT_QUALITY}.
|
|
169
|
+
* - `number` → that number for every format (backward-compatible).
|
|
170
|
+
* - `Partial<Record<ImageFormat, number>>` → per-format; formats omitted
|
|
171
|
+
* from the map fall back to {@link DEFAULT_QUALITY}.
|
|
172
|
+
*
|
|
173
|
+
* @internal Exported for testing.
|
|
174
|
+
*/
|
|
175
|
+
declare function resolveQuality(q: ImageQuality | undefined): (format: ImageFormat) => number;
|
|
176
|
+
/**
|
|
177
|
+
* Dispatch placeholder generation by strategy. Single source of truth used
|
|
178
|
+
* by every code path (CDN / dev / build) — pre-fix each path open-coded
|
|
179
|
+
* `generateBlurPlaceholder`, so `'none'` was honoured only in the CDN path
|
|
180
|
+
* and `'dominant-color'` (typed since the plugin's inception) was never
|
|
181
|
+
* implemented anywhere — the exact typed-but-unimplemented bug class the
|
|
182
|
+
* `audit-types` gate exists to catch.
|
|
183
|
+
*
|
|
184
|
+
* @internal Exported for testing.
|
|
185
|
+
*/
|
|
186
|
+
declare function generatePlaceholder(input: string, strategy: 'blur' | 'color' | 'none', size: number): Promise<string>;
|
|
129
187
|
//#endregion
|
|
130
|
-
export { FormatSource, ImageCdnProvider, ImageFormat, ImagePluginConfig, PlaceholderStrategy, ProcessedImage, SvgOptions, cdnProviders, imagePlugin, parseJpegDimensions, parseWebPDimensions };
|
|
188
|
+
export { FormatSource, ImageCdnProvider, ImageFormat, ImagePluginConfig, ImageQuality, PlaceholderStrategy, ProcessedImage, SvgOptions, cdnProviders, generatePlaceholder, imagePlugin, normalizePlaceholder, parseJpegDimensions, parseWebPDimensions, resolveQuality };
|
|
131
189
|
//# sourceMappingURL=image-plugin2.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,9 +1,84 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ComponentFn, Ref, VNodeChild } from "@pyreon/core";
|
|
3
|
-
import * as _$_pyreon_reactivity0 from "@pyreon/reactivity";
|
|
1
|
+
import { ComponentFn, Ref, SvgAttributes, VNodeChild } from "@pyreon/core";
|
|
4
2
|
import { LoaderContext, NavigationGuard } from "@pyreon/router";
|
|
5
3
|
import { Middleware } from "@pyreon/server";
|
|
6
4
|
|
|
5
|
+
//#region src/icon.d.ts
|
|
6
|
+
/** An imported SVG component (`import X from './x.svg?component'`). */
|
|
7
|
+
type SvgComponent = (props: SvgAttributes) => VNodeChild;
|
|
8
|
+
/**
|
|
9
|
+
* Props for {@link Icon}. The standard `<svg>` attribute surface
|
|
10
|
+
* (`fill`, `class`, `style`, `aria-*`, `onClick`, …) — every one passed
|
|
11
|
+
* straight through and overriding the container-fill defaults — plus the
|
|
12
|
+
* two source props.
|
|
13
|
+
*/
|
|
14
|
+
interface IconProps extends SvgAttributes {
|
|
15
|
+
/**
|
|
16
|
+
* An imported SVG component, e.g. `import X from './icon.svg?component'`.
|
|
17
|
+
* Rendered directly with no host wrapper. Recommended over `svg`.
|
|
18
|
+
*/
|
|
19
|
+
as?: SvgComponent | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* A full `<svg>…</svg>` markup string, e.g.
|
|
22
|
+
* `import x from './icon.svg?raw'`. Inlined inside a single `<span>` host.
|
|
23
|
+
*/
|
|
24
|
+
svg?: string | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Render a loaded SVG — container-filling, theme-aware, props-transparent.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* import Check from './check.svg?component'
|
|
31
|
+
* <span style="width:2rem"><Icon as={Check} /></span>
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* import check from './check.svg?raw'
|
|
35
|
+
* <span style="width:2rem"><Icon svg={check} /></span>
|
|
36
|
+
*/
|
|
37
|
+
declare function Icon(props: IconProps): VNodeChild;
|
|
38
|
+
/**
|
|
39
|
+
* Build a reusable icon component from a loaded svg — a markup string OR an
|
|
40
|
+
* imported SVG component. The result is still just `<Icon>`, so it's
|
|
41
|
+
* container-sizable + theme-aware with every prop passed through.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* import check from './check.svg?raw'
|
|
45
|
+
* export const Check = createIcon(check)
|
|
46
|
+
*
|
|
47
|
+
* import StarSvg from './star.svg?component'
|
|
48
|
+
* export const Star = createIcon(StarSvg)
|
|
49
|
+
*
|
|
50
|
+
* // …sized + themed entirely by the consumer:
|
|
51
|
+
* <span style="width:48px"><Check class="text-green-600" /></span>
|
|
52
|
+
*/
|
|
53
|
+
declare function createIcon(source: string | SvgComponent): (props: SvgAttributes) => VNodeChild;
|
|
54
|
+
/** How a named icon set renders each entry. */
|
|
55
|
+
type IconMode = 'inline' | 'image';
|
|
56
|
+
/** Props of a component built by {@link createNamedIcon}. */
|
|
57
|
+
type NamedIconProps<R extends Record<string, string>> = {
|
|
58
|
+
/** A name from the scanned set — strictly typed to the available files. */name: keyof R & string; /** `<img>` alt text (image mode). Defaults to `""` (decorative). */
|
|
59
|
+
alt?: string;
|
|
60
|
+
} & Omit<IconProps, 'as' | 'svg'>;
|
|
61
|
+
/**
|
|
62
|
+
* Build a strictly-typed `<Icon name="…" />` from a name→source registry.
|
|
63
|
+
*
|
|
64
|
+
* - `mode: 'inline'` (default) — `source` is raw `<svg>` markup; rendered via
|
|
65
|
+
* {@link Icon} so it's `currentColor`-themeable (system icons you recolor).
|
|
66
|
+
* - `mode: 'image'` — `source` is an asset URL; rendered as `<img>` with NO
|
|
67
|
+
* svg mutation, original colors preserved (colorful / brand icons).
|
|
68
|
+
*
|
|
69
|
+
* Either way it stays container-filling + props-transparent. Not called by
|
|
70
|
+
* hand normally — `iconsPlugin` emits the generated file that calls it.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // icons.gen.tsx (auto-generated):
|
|
74
|
+
* export const Icon = createNamedIcon({ 'check-circle': '<svg…' })
|
|
75
|
+
* // app:
|
|
76
|
+
* <span style="width:2rem"><Icon name="check-circle" /></span>
|
|
77
|
+
*/
|
|
78
|
+
declare function createNamedIcon<R extends Record<string, string>>(registry: R, options?: {
|
|
79
|
+
mode?: IconMode;
|
|
80
|
+
}): (props: NamedIconProps<R>) => VNodeChild;
|
|
81
|
+
//#endregion
|
|
7
82
|
//#region src/image-plugin.d.ts
|
|
8
83
|
/** Per-format source set for <picture> <source> elements. */
|
|
9
84
|
interface FormatSource {
|
|
@@ -195,7 +270,7 @@ interface LinkProps {
|
|
|
195
270
|
/** Props passed to a custom component via createLink. */
|
|
196
271
|
interface LinkRenderProps {
|
|
197
272
|
href: string;
|
|
198
|
-
ref:
|
|
273
|
+
ref: import('@pyreon/core').Ref<HTMLAnchorElement>;
|
|
199
274
|
onClick: (e: MouseEvent) => void;
|
|
200
275
|
onMouseEnter: () => void;
|
|
201
276
|
onTouchStart: () => void;
|
|
@@ -212,7 +287,7 @@ interface LinkRenderProps {
|
|
|
212
287
|
/** Return type of useLink. */
|
|
213
288
|
interface UseLinkReturn {
|
|
214
289
|
/** Ref object — attach to the root element for viewport-based prefetch. */
|
|
215
|
-
ref:
|
|
290
|
+
ref: import('@pyreon/core').Ref<HTMLAnchorElement>;
|
|
216
291
|
/** Click handler — performs client-side navigation. */
|
|
217
292
|
handleClick: (e: MouseEvent) => void;
|
|
218
293
|
/** Mouse enter handler — triggers hover prefetch. */
|
|
@@ -443,6 +518,15 @@ interface ISRConfig {
|
|
|
443
518
|
* space (e.g. `/user/:id` where `:id` is free-form).
|
|
444
519
|
*/
|
|
445
520
|
maxEntries?: number;
|
|
521
|
+
/**
|
|
522
|
+
* Max wall-time (ms) for a single background revalidation before it is
|
|
523
|
+
* abandoned. Without a bound, a handler that hangs leaves its key
|
|
524
|
+
* pinned in the in-flight set forever — every later request for that
|
|
525
|
+
* key short-circuits the de-dupe guard and the entry can never
|
|
526
|
+
* recover from stale. Default: `30000` (matches the Suspense
|
|
527
|
+
* streaming timeout).
|
|
528
|
+
*/
|
|
529
|
+
revalidateTimeoutMs?: number;
|
|
446
530
|
/**
|
|
447
531
|
* Cache-key derivation function. The default keys cache entries by
|
|
448
532
|
* `url.pathname` ONLY — query strings, cookies, and headers are
|
|
@@ -1105,7 +1189,7 @@ declare function buildMetaTags(props: Omit<MetaProps, 'title' | 'description' |
|
|
|
1105
1189
|
//#region src/theme.d.ts
|
|
1106
1190
|
type Theme = 'light' | 'dark' | 'system';
|
|
1107
1191
|
/** Reactive theme signal. */
|
|
1108
|
-
declare const theme:
|
|
1192
|
+
declare const theme: import("@pyreon/reactivity").Signal<Theme>;
|
|
1109
1193
|
/**
|
|
1110
1194
|
* Set the default theme for SSR (when `matchMedia` is unavailable).
|
|
1111
1195
|
* Call once at server startup before rendering.
|
|
@@ -1167,5 +1251,5 @@ declare function ogImagePlugin(..._: unknown[]): never;
|
|
|
1167
1251
|
/** @deprecated Import from `@pyreon/zero/ai` instead */
|
|
1168
1252
|
declare function aiPlugin(..._: unknown[]): never;
|
|
1169
1253
|
//#endregion
|
|
1170
|
-
export { type Adapter, type AdapterBuildOptions, type FileRoute, type I18nRoutingConfig, type ISRConfig, Image, type ImageProps, type ImageRenderProps, type ImageSource, Link, type LinkProps, type LinkRenderProps, type LoaderContext, type LocaleContext, Meta, type MetaProps, type RenderMode, type RouteMeta, type RouteMiddlewareEntry, type RouteModule, Script, type ScriptProps, type ScriptRenderProps, type ScriptStrategy, type Theme, ThemeToggle, type UseImageReturn, type UseLinkReturn, type UseScriptReturn, type ZeroConfig, aiPlugin, buildLocalePath, buildMetaTags, createImage, createLink, createScript, createServer, defineConfig, extractLocaleFromPath, faviconPlugin, initTheme, ogImagePlugin, prefetchRoute, resolvedTheme, seoPlugin, setLocale, setSSRThemeDefault, setTheme, theme, themeScript, toggleTheme, useImage, useLink, useLocale, useScript, validateEnv };
|
|
1254
|
+
export { type Adapter, type AdapterBuildOptions, type FileRoute, type I18nRoutingConfig, type ISRConfig, Icon, type IconMode, type IconProps, Image, type ImageProps, type ImageRenderProps, type ImageSource, Link, type LinkProps, type LinkRenderProps, type LoaderContext, type LocaleContext, Meta, type MetaProps, type NamedIconProps, type RenderMode, type RouteMeta, type RouteMiddlewareEntry, type RouteModule, Script, type ScriptProps, type ScriptRenderProps, type ScriptStrategy, type SvgComponent, type Theme, ThemeToggle, type UseImageReturn, type UseLinkReturn, type UseScriptReturn, type ZeroConfig, aiPlugin, buildLocalePath, buildMetaTags, createIcon, createImage, createLink, createNamedIcon, createScript, createServer, defineConfig, extractLocaleFromPath, faviconPlugin, initTheme, ogImagePlugin, prefetchRoute, resolvedTheme, seoPlugin, setLocale, setSSRThemeDefault, setTheme, theme, themeScript, toggleTheme, useImage, useLink, useLocale, useScript, validateEnv };
|
|
1171
1255
|
//# sourceMappingURL=index2.d.ts.map
|
package/lib/types/link.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import * as _$_pyreon_core0 from "@pyreon/core";
|
|
2
|
-
|
|
3
1
|
//#region src/link.d.ts
|
|
4
2
|
interface LinkProps {
|
|
5
3
|
/** Target URL path. */
|
|
@@ -26,7 +24,7 @@ interface LinkProps {
|
|
|
26
24
|
/** Props passed to a custom component via createLink. */
|
|
27
25
|
interface LinkRenderProps {
|
|
28
26
|
href: string;
|
|
29
|
-
ref:
|
|
27
|
+
ref: import('@pyreon/core').Ref<HTMLAnchorElement>;
|
|
30
28
|
onClick: (e: MouseEvent) => void;
|
|
31
29
|
onMouseEnter: () => void;
|
|
32
30
|
onTouchStart: () => void;
|
|
@@ -43,7 +41,7 @@ interface LinkRenderProps {
|
|
|
43
41
|
/** Return type of useLink. */
|
|
44
42
|
interface UseLinkReturn {
|
|
45
43
|
/** Ref object — attach to the root element for viewport-based prefetch. */
|
|
46
|
-
ref:
|
|
44
|
+
ref: import('@pyreon/core').Ref<HTMLAnchorElement>;
|
|
47
45
|
/** Click handler — performs client-side navigation. */
|
|
48
46
|
handleClick: (e: MouseEvent) => void;
|
|
49
47
|
/** Mouse enter handler — triggers hover prefetch. */
|
package/lib/types/server.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import * as _$_pyreon_core0 from "@pyreon/core";
|
|
2
1
|
import { ComponentFn } from "@pyreon/core";
|
|
3
|
-
import * as _$_pyreon_router0 from "@pyreon/router";
|
|
4
2
|
import { RouteRecord } from "@pyreon/router";
|
|
5
3
|
import { Middleware, MiddlewareContext } from "@pyreon/server";
|
|
6
4
|
import { Plugin } from "vite";
|
|
@@ -36,8 +34,8 @@ interface CreateAppOptions {
|
|
|
36
34
|
* Used internally by entry-server and entry-client.
|
|
37
35
|
*/
|
|
38
36
|
declare function createApp(options: CreateAppOptions): {
|
|
39
|
-
App: () =>
|
|
40
|
-
router:
|
|
37
|
+
App: () => import("@pyreon/core").VNode;
|
|
38
|
+
router: import("@pyreon/router").Router<string>;
|
|
41
39
|
};
|
|
42
40
|
//#endregion
|
|
43
41
|
//#region src/api-routes.d.ts
|
|
@@ -146,6 +144,15 @@ interface ISRConfig {
|
|
|
146
144
|
* space (e.g. `/user/:id` where `:id` is free-form).
|
|
147
145
|
*/
|
|
148
146
|
maxEntries?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Max wall-time (ms) for a single background revalidation before it is
|
|
149
|
+
* abandoned. Without a bound, a handler that hangs leaves its key
|
|
150
|
+
* pinned in the in-flight set forever — every later request for that
|
|
151
|
+
* key short-circuits the de-dupe guard and the entry can never
|
|
152
|
+
* recover from stale. Default: `30000` (matches the Suspense
|
|
153
|
+
* streaming timeout).
|
|
154
|
+
*/
|
|
155
|
+
revalidateTimeoutMs?: number;
|
|
149
156
|
/**
|
|
150
157
|
* Cache-key derivation function. The default keys cache entries by
|
|
151
158
|
* `url.pathname` ONLY — query strings, cookies, and headers are
|
|
@@ -1106,6 +1113,83 @@ declare function faviconLinks(locale: string | undefined, config: FaviconPluginC
|
|
|
1106
1113
|
href: string;
|
|
1107
1114
|
}>;
|
|
1108
1115
|
//#endregion
|
|
1116
|
+
//#region src/icon.d.ts
|
|
1117
|
+
/** How a named icon set renders each entry. */
|
|
1118
|
+
type IconMode = 'inline' | 'image';
|
|
1119
|
+
//#endregion
|
|
1120
|
+
//#region src/icons-plugin.d.ts
|
|
1121
|
+
/** One named set in the multi-set form. */
|
|
1122
|
+
interface IconSetConfig {
|
|
1123
|
+
/** Folder of `*.svg` files to scan for this set. */
|
|
1124
|
+
dir: string;
|
|
1125
|
+
/**
|
|
1126
|
+
* `'inline'` (default — system icons, `currentColor`-themeable) or
|
|
1127
|
+
* `'image'` (colorful / brand icons, rendered `<img>`, no mutation).
|
|
1128
|
+
*/
|
|
1129
|
+
mode?: IconMode;
|
|
1130
|
+
}
|
|
1131
|
+
interface IconsPluginConfig {
|
|
1132
|
+
/**
|
|
1133
|
+
* Single-set form: a folder of `*.svg` files → one `<Icon name="…" />`
|
|
1134
|
+
* with a single `IconName` union. Mutually exclusive with `sets`.
|
|
1135
|
+
*/
|
|
1136
|
+
dir?: string;
|
|
1137
|
+
/**
|
|
1138
|
+
* Named multi-set form: `{ ui: { dir }, brand: { dir, mode } }` → one
|
|
1139
|
+
* generated file exporting a strictly-typed component PER set with
|
|
1140
|
+
* NAMESPACED types so they never clash:
|
|
1141
|
+
* `ui` → `<UiIcon name="…" />` + `type UiIconName`
|
|
1142
|
+
* `brand` → `<BrandIcon name="…" />` + `type BrandIconName`
|
|
1143
|
+
* Mutually exclusive with `dir`.
|
|
1144
|
+
*/
|
|
1145
|
+
sets?: Record<string, IconSetConfig>;
|
|
1146
|
+
/**
|
|
1147
|
+
* Where to write the generated `.tsx`. Single-set default: `icons.gen.tsx`
|
|
1148
|
+
* next to `dir` (e.g. `src/icons` → `src/icons.gen.tsx`). Multi-set
|
|
1149
|
+
* default: `src/icons.gen.tsx` under the project root. Recommend
|
|
1150
|
+
* gitignoring it — it's a build artifact.
|
|
1151
|
+
*/
|
|
1152
|
+
out?: string;
|
|
1153
|
+
/** Single-set form only — render mode (`'inline'` default | `'image'`). */
|
|
1154
|
+
mode?: IconMode;
|
|
1155
|
+
}
|
|
1156
|
+
/** Set key → exported component name. `ui` → `UiIcon`, `brand-marks` → `BrandMarksIcon`. */
|
|
1157
|
+
declare function componentNameFromSetKey(key: string): string;
|
|
1158
|
+
/** Filename stem → registry key. `Check-Circle.svg` → `check-circle`. */
|
|
1159
|
+
declare function iconNameFromFile(file: string): string;
|
|
1160
|
+
/** List the `*.svg` filenames in `dir` (sorted, stable). Empty if missing. */
|
|
1161
|
+
declare function scanIconDir(dir: string): string[];
|
|
1162
|
+
/**
|
|
1163
|
+
* Render the generated `.tsx` source for a set of svg filenames. Pure —
|
|
1164
|
+
* unit-tested directly; the plugin only adds fs + watch around it.
|
|
1165
|
+
*/
|
|
1166
|
+
declare function generateIconSetSource(files: string[], opts: {
|
|
1167
|
+
mode: IconMode;
|
|
1168
|
+
importDir: string;
|
|
1169
|
+
}): string;
|
|
1170
|
+
/** One resolved set for the multi-set generator. */
|
|
1171
|
+
interface NamedSetInput {
|
|
1172
|
+
/** Set key (`ui`) — becomes `<UiIcon>` + `type UiIconName`. */
|
|
1173
|
+
key: string;
|
|
1174
|
+
files: string[];
|
|
1175
|
+
mode: IconMode;
|
|
1176
|
+
/** Relative import dir from the generated file to this set's folder. */
|
|
1177
|
+
importDir: string;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Render the generated `.tsx` for the NAMED MULTI-SET form. One file, one
|
|
1181
|
+
* `createNamedIcon` import, one strictly-typed component PER set with
|
|
1182
|
+
* namespaced types (`UiIcon`/`UiIconName`, `BrandIcon`/`BrandIconName`) so
|
|
1183
|
+
* sets never clash. Bindings are per-set-prefixed so two sets sharing a
|
|
1184
|
+
* glyph filename don't collide.
|
|
1185
|
+
*/
|
|
1186
|
+
declare function generateNamedIconSetsSource(sets: NamedSetInput[]): string;
|
|
1187
|
+
/**
|
|
1188
|
+
* Vite plugin: scan `dir` for `*.svg`, write a strictly-typed
|
|
1189
|
+
* `icons.gen.tsx`, regenerate on add / unlink in dev.
|
|
1190
|
+
*/
|
|
1191
|
+
declare function iconsPlugin(cfg: IconsPluginConfig): Plugin;
|
|
1192
|
+
//#endregion
|
|
1109
1193
|
//#region src/seo.d.ts
|
|
1110
1194
|
interface SitemapConfig {
|
|
1111
1195
|
/** Base URL of the site (required). e.g. "https://example.com" */
|
|
@@ -1499,5 +1583,5 @@ declare function inferJsonLd(options: InferJsonLdOptions): Record<string, unknow
|
|
|
1499
1583
|
*/
|
|
1500
1584
|
declare function aiPlugin(config: AiPluginConfig): Plugin;
|
|
1501
1585
|
//#endregion
|
|
1502
|
-
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 };
|
|
1586
|
+
export { type AiPluginConfig, type CreateAppOptions, type CreateServerOptions, type FaviconLocaleConfig, type FaviconPluginConfig, type GenerateRouteModuleOptions, type GetStaticPaths, type IconSetConfig, type IconsPluginConfig, type InferJsonLdOptions, type NamedSetInput, type OgImageLayer, type OgImagePluginConfig, type OgImageTemplate, type RobotsConfig, type SeoPluginConfig, type SitemapConfig, type VercelRevalidateHandlerOptions, _resetVercelRevalidateHandlerCache, aiPlugin, bunAdapter, cloudflareAdapter, componentNameFromSetKey, compose, createApp, createISRHandler, createLocaleContext, createServer, zeroPlugin as default, defineConfig, detectLocaleFromHeader, faviconLinks, faviconPlugin, filePathToUrlPath, generateIconSetSource, generateLlmsFullTxt, generateLlmsTxt, generateMiddlewareModule, generateNamedIconSetsSource, generateRobots, generateRouteModule, generateSitemap, getContext, getZeroPluginConfig, i18nRouting, iconNameFromFile, iconsPlugin, inferJsonLd, jsonLd, netlifyAdapter, nodeAdapter, ogImagePath, ogImagePlugin, parseFileRoutes, render404Page, resolveAdapter, resolveConfig, scanIconDir, scanRouteFiles, seoMiddleware, seoPlugin, staticAdapter, vercelAdapter, vercelRevalidateHandler };
|
|
1503
1587
|
//# sourceMappingURL=server2.d.ts.map
|
package/lib/types/theme.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import * as _$_pyreon_reactivity0 from "@pyreon/reactivity";
|
|
2
1
|
import { VNodeChild } from "@pyreon/core";
|
|
3
2
|
|
|
4
3
|
//#region src/theme.d.ts
|
|
5
4
|
type Theme = 'light' | 'dark' | 'system';
|
|
6
5
|
/** Reactive theme signal. */
|
|
7
|
-
declare const theme:
|
|
6
|
+
declare const theme: import("@pyreon/reactivity").Signal<Theme>;
|
|
8
7
|
/**
|
|
9
8
|
* Set the default theme for SSR (when `matchMedia` is unavailable).
|
|
10
9
|
* Call once at server startup before rendering.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/zero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Pyreon Zero — zero-config full-stack framework powered by Pyreon and Vite",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vit Bokisch",
|
|
@@ -168,15 +168,15 @@
|
|
|
168
168
|
"lint": "oxlint ."
|
|
169
169
|
},
|
|
170
170
|
"dependencies": {
|
|
171
|
-
"@pyreon/core": "^0.
|
|
172
|
-
"@pyreon/head": "^0.
|
|
173
|
-
"@pyreon/meta": "^0.
|
|
174
|
-
"@pyreon/reactivity": "^0.
|
|
175
|
-
"@pyreon/router": "^0.
|
|
176
|
-
"@pyreon/runtime-dom": "^0.
|
|
177
|
-
"@pyreon/runtime-server": "^0.
|
|
178
|
-
"@pyreon/server": "^0.
|
|
179
|
-
"@pyreon/vite-plugin": "^0.
|
|
171
|
+
"@pyreon/core": "^0.20.0",
|
|
172
|
+
"@pyreon/head": "^0.20.0",
|
|
173
|
+
"@pyreon/meta": "^0.20.0",
|
|
174
|
+
"@pyreon/reactivity": "^0.20.0",
|
|
175
|
+
"@pyreon/router": "^0.20.0",
|
|
176
|
+
"@pyreon/runtime-dom": "^0.20.0",
|
|
177
|
+
"@pyreon/runtime-server": "^0.20.0",
|
|
178
|
+
"@pyreon/server": "^0.20.0",
|
|
179
|
+
"@pyreon/vite-plugin": "^0.20.0",
|
|
180
180
|
"vite": "^8.0.0"
|
|
181
181
|
},
|
|
182
182
|
"devDependencies": {
|
package/src/api-routes.ts
CHANGED
|
@@ -52,6 +52,15 @@ export function matchApiRoute(pattern: string, path: string): Record<string, str
|
|
|
52
52
|
const pathParts = path.split('/').filter(Boolean)
|
|
53
53
|
const params: Record<string, string> = {}
|
|
54
54
|
|
|
55
|
+
// A param NAME comes from the route pattern (file-based route like
|
|
56
|
+
// `[__proto__].ts`) — developer-controlled, so this is defense-in-depth
|
|
57
|
+
// rather than an attacker vector, but assigning `params['__proto__'] =
|
|
58
|
+
// …` still mutates the result object's prototype instead of setting a
|
|
59
|
+
// param. Skip the dangerous names (consistent with reconcile / i18n
|
|
60
|
+
// deepMerge guards) so a stray route name can't pollute.
|
|
61
|
+
const isUnsafeParam = (name: string): boolean =>
|
|
62
|
+
name === '__proto__' || name === 'constructor' || name === 'prototype'
|
|
63
|
+
|
|
55
64
|
for (let i = 0; i < patternParts.length; i++) {
|
|
56
65
|
const pp = patternParts[i]
|
|
57
66
|
if (!pp) continue
|
|
@@ -59,7 +68,7 @@ export function matchApiRoute(pattern: string, path: string): Record<string, str
|
|
|
59
68
|
// Catch-all: :param*
|
|
60
69
|
if (pp.endsWith('*')) {
|
|
61
70
|
const paramName = pp.slice(1, -1)
|
|
62
|
-
params[paramName] = pathParts.slice(i).join('/')
|
|
71
|
+
if (!isUnsafeParam(paramName)) params[paramName] = pathParts.slice(i).join('/')
|
|
63
72
|
return params
|
|
64
73
|
}
|
|
65
74
|
|
|
@@ -68,7 +77,8 @@ export function matchApiRoute(pattern: string, path: string): Record<string, str
|
|
|
68
77
|
|
|
69
78
|
// Dynamic segment: :param
|
|
70
79
|
if (pp.startsWith(':')) {
|
|
71
|
-
|
|
80
|
+
const paramName = pp.slice(1)
|
|
81
|
+
if (!isUnsafeParam(paramName)) params[paramName] = pathParts[i]!
|
|
72
82
|
continue
|
|
73
83
|
}
|
|
74
84
|
|