@pyreon/zero 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/lib/index.js +1476 -82
  2. package/lib/index.js.map +1 -1
  3. package/lib/types/adapters/cloudflare.d.ts +26 -0
  4. package/lib/types/adapters/cloudflare.d.ts.map +1 -0
  5. package/lib/types/adapters/index.d.ts +3 -0
  6. package/lib/types/adapters/index.d.ts.map +1 -1
  7. package/lib/types/adapters/netlify.d.ts +21 -0
  8. package/lib/types/adapters/netlify.d.ts.map +1 -0
  9. package/lib/types/adapters/vercel.d.ts +21 -0
  10. package/lib/types/adapters/vercel.d.ts.map +1 -0
  11. package/lib/types/ai.d.ts +182 -0
  12. package/lib/types/ai.d.ts.map +1 -0
  13. package/lib/types/csp.d.ts +107 -0
  14. package/lib/types/csp.d.ts.map +1 -0
  15. package/lib/types/env.d.ts +118 -0
  16. package/lib/types/env.d.ts.map +1 -0
  17. package/lib/types/favicon.d.ts +42 -0
  18. package/lib/types/favicon.d.ts.map +1 -1
  19. package/lib/types/index.d.ts +13 -3
  20. package/lib/types/index.d.ts.map +1 -1
  21. package/lib/types/logger.d.ts +68 -0
  22. package/lib/types/logger.d.ts.map +1 -0
  23. package/lib/types/meta.d.ts +36 -0
  24. package/lib/types/meta.d.ts.map +1 -1
  25. package/lib/types/og-image.d.ts +107 -0
  26. package/lib/types/og-image.d.ts.map +1 -0
  27. package/lib/types/types.d.ts +1 -1
  28. package/lib/types/types.d.ts.map +1 -1
  29. package/package.json +35 -10
  30. package/src/adapters/cloudflare.ts +82 -0
  31. package/src/adapters/index.ts +13 -1
  32. package/src/adapters/netlify.ts +84 -0
  33. package/src/adapters/vercel.ts +84 -0
  34. package/src/ai.ts +623 -0
  35. package/src/csp.ts +207 -0
  36. package/src/env.ts +344 -0
  37. package/src/favicon.ts +221 -80
  38. package/src/index.ts +41 -2
  39. package/src/logger.ts +144 -0
  40. package/src/meta.tsx +84 -2
  41. package/src/og-image.ts +378 -0
  42. package/src/types.ts +1 -1
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Request logging middleware.
3
+ *
4
+ * Logs HTTP requests with method, path, status, and duration.
5
+ * Supports custom formatters and log levels.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { loggerMiddleware } from "@pyreon/zero"
10
+ *
11
+ * export default defineConfig({
12
+ * middleware: [loggerMiddleware()],
13
+ * })
14
+ * ```
15
+ */
16
+ import type { Middleware } from '@pyreon/server';
17
+ export interface LoggerConfig {
18
+ /**
19
+ * Log level — controls which requests are logged.
20
+ * - "all": log every request
21
+ * - "none": disable logging
22
+ * Default: "all"
23
+ */
24
+ level?: 'all' | 'none';
25
+ /**
26
+ * Custom log formatter. Receives request details and returns
27
+ * the string to log (or null to skip).
28
+ */
29
+ format?: (entry: LogEntry) => string | null;
30
+ /**
31
+ * Skip logging for these path prefixes.
32
+ * Default: ["/__", "/@", "/node_modules"]
33
+ */
34
+ skip?: string[];
35
+ /**
36
+ * Enable colorized output (ANSI codes).
37
+ * Default: true in development, false in production.
38
+ */
39
+ colors?: boolean;
40
+ }
41
+ export interface LogEntry {
42
+ method: string;
43
+ path: string;
44
+ duration: number;
45
+ timestamp: Date;
46
+ userAgent?: string | undefined;
47
+ ip?: string | undefined;
48
+ }
49
+ /**
50
+ * Request logging middleware.
51
+ *
52
+ * Logs incoming requests with method, path, and duration.
53
+ * Runs in middleware phase — logs timing from middleware start to
54
+ * microtask completion (approximate request duration).
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * // Basic usage
59
+ * loggerMiddleware()
60
+ *
61
+ * // Custom format
62
+ * loggerMiddleware({
63
+ * format: (e) => `${e.method} ${e.path} (${e.duration}ms)`,
64
+ * })
65
+ * ```
66
+ */
67
+ export declare function loggerMiddleware(config?: LoggerConfig): Middleware;
68
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAA;AAEnE,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,MAAM,GAAG,IAAI,CAAA;IAC3C;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxB;AAsCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,UAAU,CAsClE"}
@@ -1,4 +1,5 @@
1
1
  import type { VNodeChild } from '@pyreon/core';
2
+ import type { FaviconPluginConfig } from './favicon';
2
3
  import type { I18nRoutingConfig } from './i18n-routing';
3
4
  export interface MetaProps {
4
5
  /** Page title. Accepts reactive accessor `() => string`. */
@@ -11,6 +12,10 @@ export interface MetaProps {
11
12
  image?: string;
12
13
  /** Image alt text for accessibility. */
13
14
  imageAlt?: string;
15
+ /** Image width in pixels (og:image:width). Helps crawlers layout before loading. */
16
+ imageWidth?: number;
17
+ /** Image height in pixels (og:image:height). */
18
+ imageHeight?: number;
14
19
  /** Open Graph type. Default: "website" */
15
20
  type?: 'website' | 'article' | 'product' | 'profile';
16
21
  /** Site name for og:site_name. */
@@ -30,6 +35,8 @@ export interface MetaProps {
30
35
  }>;
31
36
  /** Robots directives. Default: "index, follow" */
32
37
  robots?: string;
38
+ /** Convenience: set `true` to emit `noindex, nofollow`. Overrides `robots`. */
39
+ noIndex?: boolean;
33
40
  /** Published time (ISO 8601) for article type. */
34
41
  publishedTime?: string;
35
42
  /** Modified time (ISO 8601) for article type. */
@@ -46,6 +53,19 @@ export interface MetaProps {
46
53
  property?: string;
47
54
  content: string;
48
55
  }>;
56
+ /**
57
+ * Open Graph video URL. Also sets og:video:type if the URL ends with
58
+ * a known extension (.mp4, .webm).
59
+ */
60
+ video?: string;
61
+ /** Video width in pixels. */
62
+ videoWidth?: number;
63
+ /** Video height in pixels. */
64
+ videoHeight?: number;
65
+ /**
66
+ * Open Graph audio URL.
67
+ */
68
+ audio?: string;
49
69
  /**
50
70
  * I18n routing config — when provided, auto-generates hreflang alternate
51
71
  * links for all locales based on the current path.
@@ -54,6 +74,22 @@ export interface MetaProps {
54
74
  i18n?: I18nRoutingConfig;
55
75
  /** Base URL for building absolute hreflang URLs. e.g. "https://example.com" */
56
76
  origin?: string;
77
+ /**
78
+ * Favicon plugin config — when provided, injects locale-aware favicon
79
+ * `<link>` tags into `<head>`. Uses the current locale to select
80
+ * the correct favicon set.
81
+ */
82
+ favicon?: FaviconPluginConfig;
83
+ /**
84
+ * OG image template name — auto-resolves to the correct locale-specific
85
+ * OG image path generated by `ogImagePlugin`.
86
+ * Sets both `og:image` and `twitter:image` unless `image` is also provided.
87
+ */
88
+ ogTemplate?: string;
89
+ /** Output directory for OG images. Default: "og" */
90
+ ogImageDir?: string;
91
+ /** OG image format. Default: "png" */
92
+ ogImageFormat?: 'png' | 'jpeg';
57
93
  children?: VNodeChild;
58
94
  }
59
95
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/meta.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAKvD,MAAM,WAAW,SAAS;IACxB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;IAC/B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;IACrC,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;IACpD,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,WAAW,CAAC,EAAE,SAAS,GAAG,qBAAqB,GAAG,KAAK,GAAG,QAAQ,CAAA;IAClE,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+BAA+B;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACzD,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,mCAAmC;IACnC,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACpE;;;;OAIG;IACH,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACxB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,UAAU,CAAA;CACtB;AAKD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAqBjD;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClD;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,aAAa,GAAG,UAAU,CAAC,GAAG;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GACA,QAAQ,CAiGV"}
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/meta.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ import type { Plugin } from 'vite';
2
+ export interface OgImageLayer {
3
+ /**
4
+ * Text content. Can be:
5
+ * - A string (same for all locales)
6
+ * - A record mapping locale → text
7
+ * - A function receiving locale and returning text
8
+ */
9
+ text: string | Record<string, string> | ((locale: string) => string);
10
+ /** X position — number (px) or string with % (e.g. "50%"). Default: "50%" */
11
+ x?: number | string;
12
+ /** Y position — number (px) or string with % (e.g. "40%"). Default: "50%" */
13
+ y?: number | string;
14
+ /** Font size in px. Default: 64 */
15
+ fontSize?: number;
16
+ /** Font family. Default: "sans-serif" */
17
+ fontFamily?: string;
18
+ /** Font weight. Default: "bold" */
19
+ fontWeight?: string;
20
+ /** Text color. Default: "#ffffff" */
21
+ color?: string;
22
+ /** Text anchor (alignment). Default: "middle" */
23
+ textAnchor?: 'start' | 'middle' | 'end';
24
+ /** Max width in px before wrapping. Default: 80% of image width. */
25
+ maxWidth?: number;
26
+ }
27
+ export interface OgImageTemplate {
28
+ /** Template name — used for output file naming. */
29
+ name: string;
30
+ /**
31
+ * Background: path to an image file, or a solid color config.
32
+ *
33
+ * @example "./src/assets/og-bg.jpg"
34
+ * @example { color: "#0066ff", width: 1200, height: 630 }
35
+ */
36
+ background: string | {
37
+ color: string;
38
+ width?: number;
39
+ height?: number;
40
+ };
41
+ /** Output width. Default: 1200 */
42
+ width?: number;
43
+ /** Output height. Default: 630 */
44
+ height?: number;
45
+ /** Output format. Default: "png" */
46
+ format?: 'png' | 'jpeg';
47
+ /** JPEG quality (1-100). Default: 90 */
48
+ quality?: number;
49
+ /** Text layers to overlay on the background. */
50
+ layers?: OgImageLayer[];
51
+ }
52
+ export interface OgImagePluginConfig {
53
+ /** Templates to generate. */
54
+ templates: OgImageTemplate[];
55
+ /** Locales to generate for. When omitted, generates a single image per template. */
56
+ locales?: string[];
57
+ /** Output directory prefix. Default: "og" */
58
+ outDir?: string;
59
+ }
60
+ /**
61
+ * Build an SVG overlay with text layers.
62
+ * @internal Exported for testing.
63
+ */
64
+ export declare function buildTextOverlaySvg(layers: OgImageLayer[], width: number, height: number, locale: string): string;
65
+ /**
66
+ * Render an OG image from a template for a specific locale.
67
+ * @internal Exported for testing.
68
+ */
69
+ export declare function renderOgImage(template: OgImageTemplate, locale: string, rootDir: string): Promise<Uint8Array | null>;
70
+ /**
71
+ * Compute the OG image path for a template and locale.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * ogImagePath("default", "de") // → "/og/default-de.png"
76
+ * ogImagePath("default") // → "/og/default.png"
77
+ * ogImagePath("hero", "en", "images") // → "/images/hero-en.png"
78
+ * ```
79
+ */
80
+ export declare function ogImagePath(templateName: string, locale?: string, outDir?: string, format?: 'png' | 'jpeg'): string;
81
+ /**
82
+ * OG image generation Vite plugin.
83
+ *
84
+ * Generates Open Graph images at build time. In dev, generates on-demand.
85
+ * Requires `sharp` as an optional dependency.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * // vite.config.ts
90
+ * import { ogImagePlugin } from "@pyreon/zero/og-image"
91
+ *
92
+ * export default {
93
+ * plugins: [
94
+ * ogImagePlugin({
95
+ * locales: ["en", "de"],
96
+ * templates: [{
97
+ * name: "default",
98
+ * background: { color: "#0066ff" },
99
+ * layers: [{ text: { en: "Hello", de: "Hallo" }, fontSize: 72 }],
100
+ * }],
101
+ * }),
102
+ * ],
103
+ * }
104
+ * ```
105
+ */
106
+ export declare function ogImagePlugin(config: OgImagePluginConfig): Plugin;
107
+ //# sourceMappingURL=og-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"og-image.d.ts","sourceRoot":"","sources":["../../src/og-image.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAclC,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACpE,6EAA6E;IAC7E,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,6EAA6E;IAC7E,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IACvC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,UAAU,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACvE,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACvB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAA;IAC5B,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AA0BD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EAAE,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,MAAM,CA0DR;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAuC5B;AAID;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,SAAO,EACb,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,MAAM,CAIR;AAID;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAkFjE"}
@@ -58,7 +58,7 @@ export interface ZeroConfig {
58
58
  /** ISR config — only used when mode is "isr". */
59
59
  isr?: ISRConfig;
60
60
  /** Deploy adapter. Default: "node" */
61
- adapter?: 'node' | 'bun' | 'static';
61
+ adapter?: 'node' | 'bun' | 'static' | 'vercel' | 'cloudflare' | 'netlify';
62
62
  /** Base URL path. Default: "/" */
63
63
  base?: string;
64
64
  /** App-level middleware applied to all routes. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/zero",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Pyreon Zero — zero-config full-stack framework powered by Pyreon and Vite",
5
5
  "license": "MIT",
6
6
  "author": "Vit Bokisch",
@@ -117,15 +117,40 @@
117
117
  "import": "./lib/favicon.js",
118
118
  "types": "./lib/types/favicon.d.ts"
119
119
  },
120
+ "./og-image": {
121
+ "bun": "./src/og-image.ts",
122
+ "import": "./lib/og-image.js",
123
+ "types": "./lib/types/og-image.d.ts"
124
+ },
120
125
  "./i18n": {
121
126
  "bun": "./src/i18n-routing.ts",
122
127
  "import": "./lib/i18n-routing.js",
123
128
  "types": "./lib/types/i18n-routing.d.ts"
124
129
  },
130
+ "./ai": {
131
+ "bun": "./src/ai.ts",
132
+ "import": "./lib/ai.js",
133
+ "types": "./lib/types/ai.d.ts"
134
+ },
125
135
  "./middleware": {
126
136
  "bun": "./src/middleware.ts",
127
137
  "import": "./lib/middleware.js",
128
138
  "types": "./lib/types/middleware.d.ts"
139
+ },
140
+ "./csp": {
141
+ "bun": "./src/csp.ts",
142
+ "import": "./lib/csp.js",
143
+ "types": "./lib/types/csp.d.ts"
144
+ },
145
+ "./env": {
146
+ "bun": "./src/env.ts",
147
+ "import": "./lib/env.js",
148
+ "types": "./lib/types/env.d.ts"
149
+ },
150
+ "./logger": {
151
+ "bun": "./src/logger.ts",
152
+ "import": "./lib/logger.js",
153
+ "types": "./lib/types/logger.d.ts"
129
154
  }
130
155
  },
131
156
  "scripts": {
@@ -136,17 +161,17 @@
136
161
  "lint": "oxlint ."
137
162
  },
138
163
  "dependencies": {
139
- "@pyreon/core": "^0.12.1",
140
- "@pyreon/head": "^0.12.1",
141
- "@pyreon/meta": "^0.12.1",
142
- "@pyreon/router": "^0.12.1",
143
- "@pyreon/runtime-dom": "^0.12.1",
144
- "@pyreon/runtime-server": "^0.12.1",
145
- "@pyreon/server": "^0.12.1",
146
- "@pyreon/vite-plugin": "^0.12.1",
164
+ "@pyreon/core": "^0.12.2",
165
+ "@pyreon/head": "^0.12.2",
166
+ "@pyreon/meta": "^0.12.2",
167
+ "@pyreon/router": "^0.12.2",
168
+ "@pyreon/runtime-dom": "^0.12.2",
169
+ "@pyreon/runtime-server": "^0.12.2",
170
+ "@pyreon/server": "^0.12.2",
171
+ "@pyreon/vite-plugin": "^0.12.2",
147
172
  "vite": "^8.0.0"
148
173
  },
149
174
  "peerDependencies": {
150
- "@pyreon/reactivity": "^0.12.1"
175
+ "@pyreon/reactivity": "^0.12.2"
151
176
  }
152
177
  }
@@ -0,0 +1,82 @@
1
+ import type { Adapter, AdapterBuildOptions } from '../types'
2
+
3
+ /**
4
+ * Cloudflare Pages adapter — generates output for Cloudflare Pages with Functions.
5
+ *
6
+ * Produces:
7
+ * - Client assets in the output directory root (served as static)
8
+ * - `_worker.js` — Cloudflare Pages Function for SSR
9
+ *
10
+ * Note: Cloudflare Pages Functions have a ~1MB module size limit.
11
+ * For large apps, configure Vite's SSR build to bundle server code:
12
+ * `ssr: { noExternal: true }` in vite.config.ts.
13
+ *
14
+ * Deploy with: `npx wrangler pages deploy ./dist`
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // zero.config.ts
19
+ * import { defineConfig } from "@pyreon/zero"
20
+ *
21
+ * export default defineConfig({
22
+ * adapter: "cloudflare",
23
+ * })
24
+ * ```
25
+ */
26
+ export function cloudflareAdapter(): Adapter {
27
+ return {
28
+ name: 'cloudflare',
29
+ async build(options: AdapterBuildOptions) {
30
+ const { writeFile, cp, mkdir } = await import('node:fs/promises')
31
+ const { join } = await import('node:path')
32
+
33
+ const outDir = options.outDir
34
+ await mkdir(outDir, { recursive: true })
35
+
36
+ // Copy client assets to root (Cloudflare serves static files from root)
37
+ await cp(options.clientOutDir, outDir, { recursive: true })
38
+
39
+ // Copy server build
40
+ await cp(join(options.serverEntry, '..'), join(outDir, '_server'), {
41
+ recursive: true,
42
+ })
43
+
44
+ // Generate Cloudflare Pages _worker.js (ES module format)
45
+ const workerEntry = `
46
+ import handler from "./_server/entry-server.js"
47
+
48
+ export default {
49
+ async fetch(request, env, ctx) {
50
+ const url = new URL(request.url)
51
+
52
+ // Let Cloudflare serve static assets (files with extensions)
53
+ // This check is a fallback — Pages routes static files automatically
54
+ const ext = url.pathname.split(".").pop()
55
+ if (ext && ext !== url.pathname && !url.pathname.endsWith("/")) {
56
+ // Cloudflare Pages handles static assets automatically via its asset binding
57
+ // Only reach here if the file doesn't exist — fall through to SSR
58
+ }
59
+
60
+ // SSR handler
61
+ try {
62
+ return await handler(request)
63
+ } catch (err) {
64
+ return new Response("Internal Server Error", { status: 500 })
65
+ }
66
+ },
67
+ }
68
+ `.trimStart()
69
+
70
+ await writeFile(join(outDir, '_worker.js'), workerEntry)
71
+
72
+ // Cloudflare Pages config — _routes.json for routing
73
+ const routesConfig = {
74
+ version: 1,
75
+ include: ['/*'],
76
+ exclude: ['/assets/*', '/favicon.*', '/site.webmanifest', '/robots.txt', '/sitemap.xml'],
77
+ }
78
+
79
+ await writeFile(join(outDir, '_routes.json'), JSON.stringify(routesConfig, null, 2))
80
+ },
81
+ }
82
+ }
@@ -1,11 +1,17 @@
1
1
  export { bunAdapter } from './bun'
2
+ export { cloudflareAdapter } from './cloudflare'
3
+ export { netlifyAdapter } from './netlify'
2
4
  export { nodeAdapter } from './node'
3
5
  export { staticAdapter } from './static'
6
+ export { vercelAdapter } from './vercel'
4
7
 
5
8
  import type { Adapter, ZeroConfig } from '../types'
6
9
  import { bunAdapter } from './bun'
10
+ import { cloudflareAdapter } from './cloudflare'
11
+ import { netlifyAdapter } from './netlify'
7
12
  import { nodeAdapter } from './node'
8
13
  import { staticAdapter } from './static'
14
+ import { vercelAdapter } from './vercel'
9
15
 
10
16
  /**
11
17
  * Resolve the adapter from config.
@@ -21,7 +27,13 @@ export function resolveAdapter(config: ZeroConfig): Adapter {
21
27
  return bunAdapter()
22
28
  case 'static':
23
29
  return staticAdapter()
30
+ case 'vercel':
31
+ return vercelAdapter()
32
+ case 'cloudflare':
33
+ return cloudflareAdapter()
34
+ case 'netlify':
35
+ return netlifyAdapter()
24
36
  default:
25
- throw new Error(`[zero] Unknown adapter: "${name}". Use "node", "bun", or "static".`)
37
+ throw new Error(`[zero] Unknown adapter: "${name}". Use "node", "bun", "static", "vercel", "cloudflare", or "netlify".`)
26
38
  }
27
39
  }
@@ -0,0 +1,84 @@
1
+ import type { Adapter, AdapterBuildOptions } from '../types'
2
+
3
+ /**
4
+ * Netlify adapter — generates output for Netlify Functions (v2).
5
+ *
6
+ * Produces:
7
+ * - Client assets in `publish/` directory
8
+ * - `netlify/functions/ssr.mjs` — Netlify Function for SSR
9
+ * - `netlify.toml` — routing configuration
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // zero.config.ts
14
+ * import { defineConfig } from "@pyreon/zero"
15
+ *
16
+ * export default defineConfig({
17
+ * adapter: "netlify",
18
+ * })
19
+ * ```
20
+ */
21
+ export function netlifyAdapter(): Adapter {
22
+ return {
23
+ name: 'netlify',
24
+ async build(options: AdapterBuildOptions) {
25
+ const { writeFile, cp, mkdir } = await import('node:fs/promises')
26
+ const { join } = await import('node:path')
27
+
28
+ const outDir = options.outDir
29
+ const publishDir = join(outDir, 'publish')
30
+ const functionsDir = join(outDir, 'netlify', 'functions')
31
+
32
+ await mkdir(publishDir, { recursive: true })
33
+ await mkdir(functionsDir, { recursive: true })
34
+
35
+ // Copy client assets to publish/
36
+ await cp(options.clientOutDir, publishDir, { recursive: true })
37
+
38
+ // Copy server build to functions directory
39
+ await cp(join(options.serverEntry, '..'), join(functionsDir, '_server'), {
40
+ recursive: true,
41
+ })
42
+
43
+ // Generate Netlify Function (v2 format — ESM, Web-standard Request/Response)
44
+ const funcEntry = `
45
+ import handler from "./_server/entry-server.js"
46
+
47
+ export default async function(req, context) {
48
+ try {
49
+ return await handler(req)
50
+ } catch (err) {
51
+ return new Response("Internal Server Error", { status: 500 })
52
+ }
53
+ }
54
+
55
+ export const config = {
56
+ path: "/*",
57
+ preferStatic: true,
58
+ }
59
+ `.trimStart()
60
+
61
+ await writeFile(join(functionsDir, 'ssr.mjs'), funcEntry)
62
+
63
+ // Generate netlify.toml
64
+ const toml = `
65
+ [build]
66
+ publish = "publish"
67
+ functions = "netlify/functions"
68
+
69
+ [[headers]]
70
+ for = "/assets/*"
71
+ [headers.values]
72
+ Cache-Control = "public, max-age=31536000, immutable"
73
+
74
+ [[redirects]]
75
+ from = "/*"
76
+ to = "/.netlify/functions/ssr"
77
+ status = 200
78
+ conditions = {Role = ["admin", "user", ""]}
79
+ `.trimStart()
80
+
81
+ await writeFile(join(outDir, 'netlify.toml'), toml)
82
+ },
83
+ }
84
+ }
@@ -0,0 +1,84 @@
1
+ import type { Adapter, AdapterBuildOptions } from '../types'
2
+
3
+ /**
4
+ * Vercel adapter — generates output for Vercel's Build Output API v3.
5
+ *
6
+ * Produces a `.vercel/output` directory with:
7
+ * - `static/` — client-side assets (JS, CSS, images)
8
+ * - `functions/ssr.func/` — serverless function for SSR
9
+ * - `config.json` — routing configuration
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // zero.config.ts
14
+ * import { defineConfig } from "@pyreon/zero"
15
+ *
16
+ * export default defineConfig({
17
+ * adapter: "vercel",
18
+ * })
19
+ * ```
20
+ */
21
+ export function vercelAdapter(): Adapter {
22
+ return {
23
+ name: 'vercel',
24
+ async build(options: AdapterBuildOptions) {
25
+ const { writeFile, cp, mkdir } = await import('node:fs/promises')
26
+ const { join } = await import('node:path')
27
+
28
+ const vercelDir = join(options.outDir, '.vercel', 'output')
29
+ const staticDir = join(vercelDir, 'static')
30
+ const funcDir = join(vercelDir, 'functions', 'ssr.func')
31
+
32
+ await mkdir(staticDir, { recursive: true })
33
+ await mkdir(funcDir, { recursive: true })
34
+
35
+ // Copy client assets to static/
36
+ await cp(options.clientOutDir, staticDir, { recursive: true })
37
+
38
+ // Copy server build to function directory
39
+ await cp(join(options.serverEntry, '..'), funcDir, { recursive: true })
40
+
41
+ // Generate serverless function entry
42
+ const funcEntry = `
43
+ export default async function handler(req) {
44
+ const handler = (await import("./entry-server.js")).default
45
+ return handler(req)
46
+ }
47
+ `.trimStart()
48
+
49
+ await writeFile(join(funcDir, 'index.js'), funcEntry)
50
+
51
+ // Function config
52
+ await writeFile(
53
+ join(funcDir, '.vc-config.json'),
54
+ JSON.stringify(
55
+ {
56
+ runtime: 'nodejs20.x',
57
+ handler: 'index.js',
58
+ launcherType: 'Nodejs',
59
+ },
60
+ null,
61
+ 2,
62
+ ),
63
+ )
64
+
65
+ // Vercel Build Output config
66
+ const config = {
67
+ version: 3,
68
+ routes: [
69
+ // Serve static assets directly
70
+ {
71
+ src: '/assets/(.*)',
72
+ headers: { 'Cache-Control': 'public, max-age=31536000, immutable' },
73
+ },
74
+ // Favicon and manifest
75
+ { src: '/(favicon\\..*|site\\.webmanifest|robots\\.txt|sitemap\\.xml)', dest: '/$1' },
76
+ // All other routes → SSR function
77
+ { src: '/(.*)', dest: '/ssr' },
78
+ ],
79
+ }
80
+
81
+ await writeFile(join(vercelDir, 'config.json'), JSON.stringify(config, null, 2))
82
+ },
83
+ }
84
+ }