@pyreon/zero 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/lib/cache.js +80 -0
  4. package/lib/cache.js.map +1 -0
  5. package/lib/client.js +58 -0
  6. package/lib/client.js.map +1 -0
  7. package/lib/config.js +35 -0
  8. package/lib/config.js.map +1 -0
  9. package/lib/font.js +251 -0
  10. package/lib/font.js.map +1 -0
  11. package/lib/fs-router-BkbIWqek.js +30 -0
  12. package/lib/fs-router-BkbIWqek.js.map +1 -0
  13. package/lib/fs-router-jfd1QGLB.js +261 -0
  14. package/lib/fs-router-jfd1QGLB.js.map +1 -0
  15. package/lib/image-plugin.js +289 -0
  16. package/lib/image-plugin.js.map +1 -0
  17. package/lib/image.js +113 -0
  18. package/lib/image.js.map +1 -0
  19. package/lib/index.js +1665 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/link.js +186 -0
  22. package/lib/link.js.map +1 -0
  23. package/lib/script.js +102 -0
  24. package/lib/script.js.map +1 -0
  25. package/lib/seo.js +136 -0
  26. package/lib/seo.js.map +1 -0
  27. package/lib/theme.js +165 -0
  28. package/lib/theme.js.map +1 -0
  29. package/lib/types/adapters/bun.d.ts +6 -0
  30. package/lib/types/adapters/bun.d.ts.map +1 -0
  31. package/lib/types/adapters/index.d.ts +10 -0
  32. package/lib/types/adapters/index.d.ts.map +1 -0
  33. package/lib/types/adapters/node.d.ts +6 -0
  34. package/lib/types/adapters/node.d.ts.map +1 -0
  35. package/lib/types/adapters/static.d.ts +7 -0
  36. package/lib/types/adapters/static.d.ts.map +1 -0
  37. package/lib/types/app.d.ts +24 -0
  38. package/lib/types/app.d.ts.map +1 -0
  39. package/lib/types/cache.d.ts +54 -0
  40. package/lib/types/cache.d.ts.map +1 -0
  41. package/lib/types/client.d.ts +19 -0
  42. package/lib/types/client.d.ts.map +1 -0
  43. package/lib/types/config.d.ts +18 -0
  44. package/lib/types/config.d.ts.map +1 -0
  45. package/lib/types/entry-server.d.ts +26 -0
  46. package/lib/types/entry-server.d.ts.map +1 -0
  47. package/lib/types/font.d.ts +119 -0
  48. package/lib/types/font.d.ts.map +1 -0
  49. package/lib/types/fs-router.d.ts +33 -0
  50. package/lib/types/fs-router.d.ts.map +1 -0
  51. package/lib/types/image-plugin.d.ts +79 -0
  52. package/lib/types/image-plugin.d.ts.map +1 -0
  53. package/lib/types/image.d.ts +50 -0
  54. package/lib/types/image.d.ts.map +1 -0
  55. package/lib/types/index.d.ts +27 -0
  56. package/lib/types/index.d.ts.map +1 -0
  57. package/lib/types/isr.d.ts +9 -0
  58. package/lib/types/isr.d.ts.map +1 -0
  59. package/lib/types/link.d.ts +116 -0
  60. package/lib/types/link.d.ts.map +1 -0
  61. package/lib/types/script.d.ts +34 -0
  62. package/lib/types/script.d.ts.map +1 -0
  63. package/lib/types/seo.d.ts +88 -0
  64. package/lib/types/seo.d.ts.map +1 -0
  65. package/lib/types/theme.d.ts +38 -0
  66. package/lib/types/theme.d.ts.map +1 -0
  67. package/lib/types/types.d.ts +104 -0
  68. package/lib/types/types.d.ts.map +1 -0
  69. package/lib/types/utils/use-intersection-observer.d.ts +10 -0
  70. package/lib/types/utils/use-intersection-observer.d.ts.map +1 -0
  71. package/lib/types/utils/with-headers.d.ts +6 -0
  72. package/lib/types/utils/with-headers.d.ts.map +1 -0
  73. package/lib/types/vite-plugin.d.ts +17 -0
  74. package/lib/types/vite-plugin.d.ts.map +1 -0
  75. package/package.json +100 -0
  76. package/src/adapters/bun.ts +65 -0
  77. package/src/adapters/index.ts +29 -0
  78. package/src/adapters/node.ts +113 -0
  79. package/src/adapters/static.ts +17 -0
  80. package/src/app.ts +62 -0
  81. package/src/cache.ts +149 -0
  82. package/src/client.ts +43 -0
  83. package/src/config.ts +36 -0
  84. package/src/entry-server.ts +51 -0
  85. package/src/font.ts +461 -0
  86. package/src/fs-router.ts +380 -0
  87. package/src/image-plugin.ts +452 -0
  88. package/src/image.tsx +167 -0
  89. package/src/index.ts +119 -0
  90. package/src/isr.ts +95 -0
  91. package/src/link.tsx +266 -0
  92. package/src/script.tsx +133 -0
  93. package/src/seo.ts +281 -0
  94. package/src/sharp.d.ts +20 -0
  95. package/src/theme.tsx +162 -0
  96. package/src/types.ts +130 -0
  97. package/src/utils/use-intersection-observer.ts +36 -0
  98. package/src/utils/with-headers.ts +16 -0
  99. package/src/vite-plugin.ts +92 -0
package/lib/seo.js ADDED
@@ -0,0 +1,136 @@
1
+ //#region src/seo.ts
2
+ /**
3
+ * Generate a sitemap.xml string from route file paths.
4
+ */
5
+ function generateSitemap(routeFiles, config) {
6
+ const { origin, exclude = [], changefreq = "weekly", priority = .7 } = config;
7
+ return `<?xml version="1.0" encoding="UTF-8"?>
8
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
9
+ ${[...routeFiles.filter((f) => {
10
+ const name = f.split("/").pop()?.replace(/\.\w+$/, "");
11
+ return name !== "_layout" && name !== "_error" && name !== "_loading";
12
+ }).map((f) => {
13
+ let path = f.replace(/\.\w+$/, "").replace(/\/index$/, "/").replace(/^index$/, "/");
14
+ if (path.includes("[")) return null;
15
+ path = path.replace(/\([\w-]+\)\//g, "");
16
+ if (!path.startsWith("/")) path = `/${path}`;
17
+ return path;
18
+ }).filter((p) => p !== null).filter((p) => !exclude.some((e) => p.startsWith(e))).map((p) => ({
19
+ path: p,
20
+ changefreq,
21
+ priority
22
+ })), ...config.additionalPaths ?? []].map((entry) => {
23
+ return ` <url>
24
+ <loc>${escapeXml(`${origin}${entry.path === "/" ? "" : entry.path}`)}</loc>
25
+ <changefreq>${entry.changefreq ?? changefreq}</changefreq>
26
+ <priority>${entry.priority ?? priority}</priority>${entry.lastmod ? `\n <lastmod>${entry.lastmod}</lastmod>` : ""}
27
+ </url>`;
28
+ }).join("\n")}
29
+ </urlset>`;
30
+ }
31
+ function escapeXml(str) {
32
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
33
+ }
34
+ /**
35
+ * Generate a robots.txt string.
36
+ */
37
+ function generateRobots(config = {}) {
38
+ const { rules = [{
39
+ userAgent: "*",
40
+ allow: ["/"]
41
+ }], sitemap, host } = config;
42
+ const lines = [];
43
+ for (const rule of rules) {
44
+ lines.push(`User-agent: ${rule.userAgent}`);
45
+ if (rule.allow) for (const path of rule.allow) lines.push(`Allow: ${path}`);
46
+ if (rule.disallow) for (const path of rule.disallow) lines.push(`Disallow: ${path}`);
47
+ if (rule.crawlDelay) lines.push(`Crawl-delay: ${rule.crawlDelay}`);
48
+ lines.push("");
49
+ }
50
+ if (sitemap) lines.push(`Sitemap: ${sitemap}`);
51
+ if (host) lines.push(`Host: ${host}`);
52
+ return lines.join("\n");
53
+ }
54
+ /**
55
+ * Generate a JSON-LD script tag string for structured data.
56
+ *
57
+ * @example
58
+ * useHead({
59
+ * script: [jsonLd({
60
+ * "@type": "WebSite",
61
+ * name: "My Site",
62
+ * url: "https://example.com",
63
+ * })],
64
+ * })
65
+ */
66
+ function jsonLd(data) {
67
+ const ld = {
68
+ "@context": "https://schema.org",
69
+ ...data
70
+ };
71
+ return `<script type="application/ld+json">${JSON.stringify(ld)}<\/script>`;
72
+ }
73
+ /**
74
+ * Zero SEO Vite plugin.
75
+ * Generates sitemap.xml and robots.txt at build time.
76
+ *
77
+ * @example
78
+ * import { seoPlugin } from "@pyreon/zero/seo"
79
+ *
80
+ * export default {
81
+ * plugins: [
82
+ * pyreon(),
83
+ * zero(),
84
+ * seoPlugin({
85
+ * sitemap: { origin: "https://example.com" },
86
+ * robots: { sitemap: "https://example.com/sitemap.xml" },
87
+ * }),
88
+ * ],
89
+ * }
90
+ */
91
+ function seoPlugin(config = {}) {
92
+ return {
93
+ name: "pyreon-zero-seo",
94
+ apply: "build",
95
+ async generateBundle(_, _bundle) {
96
+ if (config.sitemap) {
97
+ const { scanRouteFiles } = await import("./fs-router-BkbIWqek.js");
98
+ const routesDir = `${process.cwd()}/src/routes`;
99
+ try {
100
+ const sitemap = generateSitemap(await scanRouteFiles(routesDir), config.sitemap);
101
+ this.emitFile({
102
+ type: "asset",
103
+ fileName: "sitemap.xml",
104
+ source: sitemap
105
+ });
106
+ } catch {}
107
+ }
108
+ if (config.robots) {
109
+ const robots = generateRobots(config.robots);
110
+ this.emitFile({
111
+ type: "asset",
112
+ fileName: "robots.txt",
113
+ source: robots
114
+ });
115
+ }
116
+ }
117
+ };
118
+ }
119
+ /**
120
+ * SEO middleware for dev server.
121
+ * Serves sitemap.xml and robots.txt dynamically during development.
122
+ */
123
+ function seoMiddleware(config = {}) {
124
+ return async (ctx) => {
125
+ if (ctx.url.pathname === "/robots.txt" && config.robots) return new Response(generateRobots(config.robots), { headers: { "Content-Type": "text/plain" } });
126
+ if (ctx.url.pathname === "/sitemap.xml" && config.sitemap) try {
127
+ const { scanRouteFiles } = await import("./fs-router-BkbIWqek.js");
128
+ const sitemap = generateSitemap(await scanRouteFiles(`${process.cwd()}/src/routes`), config.sitemap);
129
+ return new Response(sitemap, { headers: { "Content-Type": "application/xml" } });
130
+ } catch {}
131
+ };
132
+ }
133
+
134
+ //#endregion
135
+ export { generateRobots, generateSitemap, jsonLd, seoMiddleware, seoPlugin };
136
+ //# sourceMappingURL=seo.js.map
package/lib/seo.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.js","names":[],"sources":["../src/seo.ts"],"sourcesContent":["import type { Middleware } from '@pyreon/server'\nimport type { Plugin } from 'vite'\n\n// ─── SEO utilities ──────────────────────────────────────────────────────────\n//\n// Zero provides built-in SEO tooling:\n// - Automatic sitemap.xml generation from file-based routes\n// - Configurable robots.txt\n// - Structured data (JSON-LD) helpers\n// - Open Graph / Twitter Card meta helpers\n\nexport interface SitemapConfig {\n /** Base URL of the site (required). e.g. \"https://example.com\" */\n origin: string\n /** Paths to exclude from the sitemap. */\n exclude?: string[]\n /** Default change frequency. Default: \"weekly\" */\n changefreq?: ChangeFreq\n /** Default priority. Default: 0.7 */\n priority?: number\n /** Additional URLs to include (for dynamic routes). */\n additionalPaths?: SitemapEntry[]\n}\n\nexport interface SitemapEntry {\n path: string\n changefreq?: ChangeFreq\n priority?: number\n lastmod?: string\n}\n\nexport type ChangeFreq =\n | 'always'\n | 'hourly'\n | 'daily'\n | 'weekly'\n | 'monthly'\n | 'yearly'\n | 'never'\n\n/**\n * Generate a sitemap.xml string from route file paths.\n */\nexport function generateSitemap(\n routeFiles: string[],\n config: SitemapConfig,\n): string {\n const { origin, exclude = [], changefreq = 'weekly', priority = 0.7 } = config\n\n const paths = routeFiles\n .filter((f) => {\n // Exclude layout, error, loading files\n const name = f\n .split('/')\n .pop()\n ?.replace(/\\.\\w+$/, '')\n return name !== '_layout' && name !== '_error' && name !== '_loading'\n })\n .map((f) => {\n // Convert file path to URL\n let path = f\n .replace(/\\.\\w+$/, '')\n .replace(/\\/index$/, '/')\n .replace(/^index$/, '/')\n\n // Skip dynamic routes — they need additionalPaths\n if (path.includes('[')) return null\n\n // Strip route groups\n path = path.replace(/\\([\\w-]+\\)\\//g, '')\n\n if (!path.startsWith('/')) path = `/${path}`\n return path\n })\n .filter((p): p is string => p !== null)\n .filter((p) => !exclude.some((e) => p.startsWith(e)))\n\n const allPaths: SitemapEntry[] = [\n ...paths.map((p) => ({ path: p, changefreq, priority })),\n ...(config.additionalPaths ?? []),\n ]\n\n const entries = allPaths\n .map((entry) => {\n const loc = `${origin}${entry.path === '/' ? '' : entry.path}`\n return ` <url>\n <loc>${escapeXml(loc)}</loc>\n <changefreq>${entry.changefreq ?? changefreq}</changefreq>\n <priority>${entry.priority ?? priority}</priority>${entry.lastmod ? `\\n <lastmod>${entry.lastmod}</lastmod>` : ''}\n </url>`\n })\n .join('\\n')\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${entries}\n</urlset>`\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;')\n}\n\n// ─── Robots.txt ─────────────────────────────────────────────────────────────\n\nexport interface RobotsConfig {\n /** Rules per user-agent. */\n rules?: RobotsRule[]\n /** Sitemap URL. */\n sitemap?: string\n /** Host directive. */\n host?: string\n}\n\nexport interface RobotsRule {\n userAgent: string\n allow?: string[]\n disallow?: string[]\n crawlDelay?: number\n}\n\n/**\n * Generate a robots.txt string.\n */\nexport function generateRobots(config: RobotsConfig = {}): string {\n const { rules = [{ userAgent: '*', allow: ['/'] }], sitemap, host } = config\n const lines: string[] = []\n\n for (const rule of rules) {\n lines.push(`User-agent: ${rule.userAgent}`)\n if (rule.allow) {\n for (const path of rule.allow) lines.push(`Allow: ${path}`)\n }\n if (rule.disallow) {\n for (const path of rule.disallow) lines.push(`Disallow: ${path}`)\n }\n if (rule.crawlDelay) lines.push(`Crawl-delay: ${rule.crawlDelay}`)\n lines.push('')\n }\n\n if (sitemap) lines.push(`Sitemap: ${sitemap}`)\n if (host) lines.push(`Host: ${host}`)\n\n return lines.join('\\n')\n}\n\n// ─── Structured data (JSON-LD) ──────────────────────────────────────────────\n\nexport type JsonLdType =\n | 'WebSite'\n | 'WebPage'\n | 'Article'\n | 'BlogPosting'\n | 'Product'\n | 'Organization'\n | 'Person'\n | 'BreadcrumbList'\n | 'FAQPage'\n | (string & {})\n\n/**\n * Generate a JSON-LD script tag string for structured data.\n *\n * @example\n * useHead({\n * script: [jsonLd({\n * \"@type\": \"WebSite\",\n * name: \"My Site\",\n * url: \"https://example.com\",\n * })],\n * })\n */\nexport function jsonLd(data: Record<string, unknown>): string {\n const ld = {\n '@context': 'https://schema.org',\n ...data,\n }\n return `<script type=\"application/ld+json\">${JSON.stringify(ld)}</script>`\n}\n\n// ─── SEO Vite plugin ────────────────────────────────────────────────────────\n\nexport interface SeoPluginConfig {\n /** Sitemap configuration. */\n sitemap?: SitemapConfig\n /** Robots.txt configuration. */\n robots?: RobotsConfig\n}\n\n/**\n * Zero SEO Vite plugin.\n * Generates sitemap.xml and robots.txt at build time.\n *\n * @example\n * import { seoPlugin } from \"@pyreon/zero/seo\"\n *\n * export default {\n * plugins: [\n * pyreon(),\n * zero(),\n * seoPlugin({\n * sitemap: { origin: \"https://example.com\" },\n * robots: { sitemap: \"https://example.com/sitemap.xml\" },\n * }),\n * ],\n * }\n */\nexport function seoPlugin(config: SeoPluginConfig = {}): Plugin {\n return {\n name: 'pyreon-zero-seo',\n apply: 'build',\n\n async generateBundle(_, _bundle) {\n // Generate sitemap.xml\n if (config.sitemap) {\n const { scanRouteFiles } = await import('./fs-router')\n const routesDir = `${process.cwd()}/src/routes`\n\n try {\n const files = await scanRouteFiles(routesDir)\n const sitemap = generateSitemap(files, config.sitemap)\n\n this.emitFile({\n type: 'asset',\n fileName: 'sitemap.xml',\n source: sitemap,\n })\n } catch {\n // Sitemap generation failed — skip silently\n }\n }\n\n // Generate robots.txt\n if (config.robots) {\n const robots = generateRobots(config.robots)\n\n this.emitFile({\n type: 'asset',\n fileName: 'robots.txt',\n source: robots,\n })\n }\n },\n }\n}\n\n// ─── SEO middleware (serve sitemap/robots in dev) ────────────────────────────\n\n/**\n * SEO middleware for dev server.\n * Serves sitemap.xml and robots.txt dynamically during development.\n */\nexport function seoMiddleware(config: SeoPluginConfig = {}): Middleware {\n return async (ctx) => {\n if (ctx.url.pathname === '/robots.txt' && config.robots) {\n return new Response(generateRobots(config.robots), {\n headers: { 'Content-Type': 'text/plain' },\n })\n }\n\n if (ctx.url.pathname === '/sitemap.xml' && config.sitemap) {\n try {\n const { scanRouteFiles } = await import('./fs-router')\n const routesDir = `${process.cwd()}/src/routes`\n const files = await scanRouteFiles(routesDir)\n const sitemap = generateSitemap(files, config.sitemap)\n\n return new Response(sitemap, {\n headers: { 'Content-Type': 'application/xml' },\n })\n } catch {\n // Sitemap generation failed — continue to rendering\n }\n }\n }\n}\n"],"mappings":"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"}
package/lib/theme.js ADDED
@@ -0,0 +1,165 @@
1
+ import { onMount, onUnmount } from "@pyreon/core";
2
+ import { effect, signal } from "@pyreon/reactivity";
3
+ import { jsx, jsxs } from "@pyreon/core/jsx-runtime";
4
+
5
+ //#region src/theme.tsx
6
+ const STORAGE_KEY = "zero-theme";
7
+ /** Reactive theme signal. */
8
+ const theme = signal("system");
9
+ /** Computed resolved theme (what's actually applied). */
10
+ function resolvedTheme() {
11
+ const t = theme();
12
+ if (t === "system") {
13
+ if (typeof window === "undefined") return "dark";
14
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
15
+ }
16
+ return t;
17
+ }
18
+ /** Toggle between light and dark. */
19
+ function toggleTheme() {
20
+ setTheme(resolvedTheme() === "dark" ? "light" : "dark");
21
+ }
22
+ /** Set theme explicitly. */
23
+ function setTheme(t) {
24
+ theme.set(t);
25
+ if (typeof document !== "undefined") {
26
+ document.documentElement.dataset.theme = resolvedTheme();
27
+ try {
28
+ localStorage.setItem(STORAGE_KEY, t);
29
+ } catch {}
30
+ }
31
+ }
32
+ /**
33
+ * Initialize the theme system. Call once in your app entry or layout.
34
+ * Reads from localStorage, listens for system preference changes.
35
+ */
36
+ function initTheme() {
37
+ onMount(() => {
38
+ try {
39
+ const stored = localStorage.getItem(STORAGE_KEY);
40
+ if (stored === "light" || stored === "dark" || stored === "system") theme.set(stored);
41
+ } catch {}
42
+ document.documentElement.dataset.theme = resolvedTheme();
43
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
44
+ function onChange() {
45
+ if (theme() === "system") document.documentElement.dataset.theme = resolvedTheme();
46
+ }
47
+ mq.addEventListener("change", onChange);
48
+ onUnmount(() => mq.removeEventListener("change", onChange));
49
+ const dispose = effect(() => {
50
+ document.documentElement.dataset.theme = resolvedTheme();
51
+ });
52
+ if (dispose) onUnmount(() => dispose.dispose());
53
+ });
54
+ }
55
+ /**
56
+ * Theme toggle button component.
57
+ *
58
+ * @example
59
+ * import { ThemeToggle } from "@pyreon/zero/theme"
60
+ * <ThemeToggle />
61
+ */
62
+ function ThemeToggle(props) {
63
+ initTheme();
64
+ return /* @__PURE__ */ jsx("button", {
65
+ class: props.class,
66
+ style: props.style,
67
+ onclick: toggleTheme,
68
+ "aria-label": "Toggle theme",
69
+ title: "Toggle theme",
70
+ type: "button",
71
+ children: () => resolvedTheme() === "dark" ? /* @__PURE__ */ jsxs("svg", {
72
+ width: "18",
73
+ height: "18",
74
+ viewBox: "0 0 24 24",
75
+ fill: "none",
76
+ stroke: "currentColor",
77
+ "stroke-width": "2",
78
+ "stroke-linecap": "round",
79
+ "stroke-linejoin": "round",
80
+ "aria-hidden": "true",
81
+ children: [
82
+ /* @__PURE__ */ jsx("circle", {
83
+ cx: "12",
84
+ cy: "12",
85
+ r: "5"
86
+ }),
87
+ /* @__PURE__ */ jsx("line", {
88
+ x1: "12",
89
+ y1: "1",
90
+ x2: "12",
91
+ y2: "3"
92
+ }),
93
+ /* @__PURE__ */ jsx("line", {
94
+ x1: "12",
95
+ y1: "21",
96
+ x2: "12",
97
+ y2: "23"
98
+ }),
99
+ /* @__PURE__ */ jsx("line", {
100
+ x1: "4.22",
101
+ y1: "4.22",
102
+ x2: "5.64",
103
+ y2: "5.64"
104
+ }),
105
+ /* @__PURE__ */ jsx("line", {
106
+ x1: "18.36",
107
+ y1: "18.36",
108
+ x2: "19.78",
109
+ y2: "19.78"
110
+ }),
111
+ /* @__PURE__ */ jsx("line", {
112
+ x1: "1",
113
+ y1: "12",
114
+ x2: "3",
115
+ y2: "12"
116
+ }),
117
+ /* @__PURE__ */ jsx("line", {
118
+ x1: "21",
119
+ y1: "12",
120
+ x2: "23",
121
+ y2: "12"
122
+ }),
123
+ /* @__PURE__ */ jsx("line", {
124
+ x1: "4.22",
125
+ y1: "19.78",
126
+ x2: "5.64",
127
+ y2: "18.36"
128
+ }),
129
+ /* @__PURE__ */ jsx("line", {
130
+ x1: "18.36",
131
+ y1: "5.64",
132
+ x2: "19.78",
133
+ y2: "4.22"
134
+ })
135
+ ]
136
+ }) : /* @__PURE__ */ jsx("svg", {
137
+ width: "18",
138
+ height: "18",
139
+ viewBox: "0 0 24 24",
140
+ fill: "none",
141
+ stroke: "currentColor",
142
+ "stroke-width": "2",
143
+ "stroke-linecap": "round",
144
+ "stroke-linejoin": "round",
145
+ "aria-hidden": "true",
146
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
147
+ })
148
+ });
149
+ }
150
+ /**
151
+ * Inline script to prevent flash of wrong theme.
152
+ * Include this in your index.html <head> BEFORE any stylesheets.
153
+ *
154
+ * @example
155
+ * // index.html
156
+ * <head>
157
+ * <script>{themeScript}<\/script>
158
+ * ...
159
+ * </head>
160
+ */
161
+ const themeScript = `(function(){try{var t=localStorage.getItem("${STORAGE_KEY}");var r=t==="light"?"light":t==="dark"?"dark":window.matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light";document.documentElement.dataset.theme=r}catch(e){}})()`;
162
+
163
+ //#endregion
164
+ export { ThemeToggle, initTheme, resolvedTheme, setTheme, theme, themeScript, toggleTheme };
165
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","names":[],"sources":["../src/theme.tsx"],"sourcesContent":["import { onMount, onUnmount } from '@pyreon/core'\nimport { effect, signal } from '@pyreon/reactivity'\n\n// ─── Theme system ───────────────────────────────────────────────────────────\n//\n// Provides dark/light/system theme support with:\n// - System preference detection via matchMedia\n// - Persistent preference via localStorage\n// - No flash of wrong theme (inline script in HTML)\n// - Reactive theme signal for components\n\nexport type Theme = 'light' | 'dark' | 'system'\n\nconst STORAGE_KEY = 'zero-theme'\n\n/** Reactive theme signal. */\nexport const theme = signal<Theme>('system')\n\n/** Computed resolved theme (what's actually applied). */\nexport function resolvedTheme(): 'light' | 'dark' {\n const t = theme()\n if (t === 'system') {\n if (typeof window === 'undefined') return 'dark'\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n }\n return t\n}\n\n/** Toggle between light and dark. */\nexport function toggleTheme() {\n const current = resolvedTheme()\n setTheme(current === 'dark' ? 'light' : 'dark')\n}\n\n/** Set theme explicitly. */\nexport function setTheme(t: Theme) {\n theme.set(t)\n if (typeof document !== 'undefined') {\n document.documentElement.dataset.theme = resolvedTheme()\n try {\n localStorage.setItem(STORAGE_KEY, t)\n } catch {\n // localStorage may not be available (SSR, private browsing)\n }\n }\n}\n\n/**\n * Initialize the theme system. Call once in your app entry or layout.\n * Reads from localStorage, listens for system preference changes.\n */\nexport function initTheme() {\n onMount(() => {\n // Read persisted preference\n try {\n const stored = localStorage.getItem(STORAGE_KEY) as Theme | null\n if (stored === 'light' || stored === 'dark' || stored === 'system') {\n theme.set(stored)\n }\n } catch {\n // localStorage may not be available\n }\n\n // Apply to document\n document.documentElement.dataset.theme = resolvedTheme()\n\n // Watch for system preference changes\n const mq = window.matchMedia('(prefers-color-scheme: dark)')\n function onChange() {\n if (theme() === 'system') {\n document.documentElement.dataset.theme = resolvedTheme()\n }\n }\n mq.addEventListener('change', onChange)\n onUnmount(() => mq.removeEventListener('change', onChange))\n\n // Re-apply when theme signal changes\n const dispose = effect(() => {\n document.documentElement.dataset.theme = resolvedTheme()\n })\n if (dispose) onUnmount(() => dispose.dispose())\n\n return undefined\n })\n}\n\n/**\n * Theme toggle button component.\n *\n * @example\n * import { ThemeToggle } from \"@pyreon/zero/theme\"\n * <ThemeToggle />\n */\nexport function ThemeToggle(props: { class?: string; style?: string }) {\n initTheme()\n\n return (\n <button\n class={props.class}\n style={props.style}\n onclick={toggleTheme}\n aria-label=\"Toggle theme\"\n title=\"Toggle theme\"\n type=\"button\"\n >\n {() =>\n resolvedTheme() === 'dark' ? (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"5\" />\n <line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"3\" />\n <line x1=\"12\" y1=\"21\" x2=\"12\" y2=\"23\" />\n <line x1=\"4.22\" y1=\"4.22\" x2=\"5.64\" y2=\"5.64\" />\n <line x1=\"18.36\" y1=\"18.36\" x2=\"19.78\" y2=\"19.78\" />\n <line x1=\"1\" y1=\"12\" x2=\"3\" y2=\"12\" />\n <line x1=\"21\" y1=\"12\" x2=\"23\" y2=\"12\" />\n <line x1=\"4.22\" y1=\"19.78\" x2=\"5.64\" y2=\"18.36\" />\n <line x1=\"18.36\" y1=\"5.64\" x2=\"19.78\" y2=\"4.22\" />\n </svg>\n ) : (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n )\n }\n </button>\n )\n}\n\n/**\n * Inline script to prevent flash of wrong theme.\n * Include this in your index.html <head> BEFORE any stylesheets.\n *\n * @example\n * // index.html\n * <head>\n * <script>{themeScript}</script>\n * ...\n * </head>\n */\nexport const themeScript = `(function(){try{var t=localStorage.getItem(\"${STORAGE_KEY}\");var r=t===\"light\"?\"light\":t===\"dark\"?\"dark\":window.matchMedia(\"(prefers-color-scheme:dark)\").matches?\"dark\":\"light\";document.documentElement.dataset.theme=r}catch(e){}})()`\n"],"mappings":";;;;;AAaA,MAAM,cAAc;;AAGpB,MAAa,QAAQ,OAAc,SAAS;;AAG5C,SAAgB,gBAAkC;CAChD,MAAM,IAAI,OAAO;AACjB,KAAI,MAAM,UAAU;AAClB,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,WAAW,+BAA+B,CAAC,UACrD,SACA;;AAEN,QAAO;;;AAIT,SAAgB,cAAc;AAE5B,UADgB,eAAe,KACV,SAAS,UAAU,OAAO;;;AAIjD,SAAgB,SAAS,GAAU;AACjC,OAAM,IAAI,EAAE;AACZ,KAAI,OAAO,aAAa,aAAa;AACnC,WAAS,gBAAgB,QAAQ,QAAQ,eAAe;AACxD,MAAI;AACF,gBAAa,QAAQ,aAAa,EAAE;UAC9B;;;;;;;AAUZ,SAAgB,YAAY;AAC1B,eAAc;AAEZ,MAAI;GACF,MAAM,SAAS,aAAa,QAAQ,YAAY;AAChD,OAAI,WAAW,WAAW,WAAW,UAAU,WAAW,SACxD,OAAM,IAAI,OAAO;UAEb;AAKR,WAAS,gBAAgB,QAAQ,QAAQ,eAAe;EAGxD,MAAM,KAAK,OAAO,WAAW,+BAA+B;EAC5D,SAAS,WAAW;AAClB,OAAI,OAAO,KAAK,SACd,UAAS,gBAAgB,QAAQ,QAAQ,eAAe;;AAG5D,KAAG,iBAAiB,UAAU,SAAS;AACvC,kBAAgB,GAAG,oBAAoB,UAAU,SAAS,CAAC;EAG3D,MAAM,UAAU,aAAa;AAC3B,YAAS,gBAAgB,QAAQ,QAAQ,eAAe;IACxD;AACF,MAAI,QAAS,iBAAgB,QAAQ,SAAS,CAAC;GAG/C;;;;;;;;;AAUJ,SAAgB,YAAY,OAA2C;AACrE,YAAW;AAEX,QACE,oBAAC,UAAD;EACE,OAAO,MAAM;EACb,OAAO,MAAM;EACb,SAAS;EACT,cAAW;EACX,OAAM;EACN,MAAK;kBAGH,eAAe,KAAK,SAClB,qBAAC,OAAD;GACE,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,QAAO;GACP,gBAAa;GACb,kBAAe;GACf,mBAAgB;GAChB,eAAY;aATd;IAWE,oBAAC,UAAD;KAAQ,IAAG;KAAK,IAAG;KAAK,GAAE;KAAM;IAChC,oBAAC,QAAD;KAAM,IAAG;KAAK,IAAG;KAAI,IAAG;KAAK,IAAG;KAAM;IACtC,oBAAC,QAAD;KAAM,IAAG;KAAK,IAAG;KAAK,IAAG;KAAK,IAAG;KAAO;IACxC,oBAAC,QAAD;KAAM,IAAG;KAAO,IAAG;KAAO,IAAG;KAAO,IAAG;KAAS;IAChD,oBAAC,QAAD;KAAM,IAAG;KAAQ,IAAG;KAAQ,IAAG;KAAQ,IAAG;KAAU;IACpD,oBAAC,QAAD;KAAM,IAAG;KAAI,IAAG;KAAK,IAAG;KAAI,IAAG;KAAO;IACtC,oBAAC,QAAD;KAAM,IAAG;KAAK,IAAG;KAAK,IAAG;KAAK,IAAG;KAAO;IACxC,oBAAC,QAAD;KAAM,IAAG;KAAO,IAAG;KAAQ,IAAG;KAAO,IAAG;KAAU;IAClD,oBAAC,QAAD;KAAM,IAAG;KAAQ,IAAG;KAAO,IAAG;KAAQ,IAAG;KAAS;IAC9C;OAEN,oBAAC,OAAD;GACE,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,QAAO;GACP,gBAAa;GACb,kBAAe;GACf,mBAAgB;GAChB,eAAY;aAEZ,oBAAC,QAAD,EAAM,GAAE,mDAAoD;GACxD;EAGH;;;;;;;;;;;;;AAeb,MAAa,cAAc,+CAA+C,YAAY"}
@@ -0,0 +1,6 @@
1
+ import type { Adapter } from '../types';
2
+ /**
3
+ * Bun adapter — generates a standalone Bun.serve() entry.
4
+ */
5
+ export declare function bunAdapter(): Adapter;
6
+ //# sourceMappingURL=bun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../../../src/adapters/bun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAuB,MAAM,UAAU,CAAA;AAE5D;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CA2DpC"}
@@ -0,0 +1,10 @@
1
+ export { bunAdapter } from './bun';
2
+ export { nodeAdapter } from './node';
3
+ export { staticAdapter } from './static';
4
+ import type { Adapter, ZeroConfig } from '../types';
5
+ /**
6
+ * Resolve the adapter from config.
7
+ * Returns a built-in adapter or throws if unknown.
8
+ */
9
+ export declare function resolveAdapter(config: ZeroConfig): Adapter;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAKnD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAe1D"}
@@ -0,0 +1,6 @@
1
+ import type { Adapter } from '../types';
2
+ /**
3
+ * Node.js adapter — generates a standalone server entry using node:http.
4
+ */
5
+ export declare function nodeAdapter(): Adapter;
6
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/adapters/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAuB,MAAM,UAAU,CAAA;AAE5D;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CA2GrC"}
@@ -0,0 +1,7 @@
1
+ import type { Adapter } from '../types';
2
+ /**
3
+ * Static adapter — just copies the client build output.
4
+ * Used with SSG mode where all pages are pre-rendered at build time.
5
+ */
6
+ export declare function staticAdapter(): Adapter;
7
+ //# sourceMappingURL=static.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../../../src/adapters/static.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAuB,MAAM,UAAU,CAAA;AAE5D;;;GAGG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAUvC"}
@@ -0,0 +1,24 @@
1
+ import type { ComponentFn } from '@pyreon/core';
2
+ import type { RouteRecord } from '@pyreon/router';
3
+ export interface CreateAppOptions {
4
+ /** Route definitions (from file-based routing or manual). */
5
+ routes: RouteRecord[];
6
+ /** Router mode. Default: "history" for SSR, "hash" for SPA. */
7
+ routerMode?: 'hash' | 'history';
8
+ /** Initial URL for SSR. */
9
+ url?: string;
10
+ /** Root layout component wrapping all routes. */
11
+ layout?: ComponentFn;
12
+ /** Global error component. */
13
+ errorComponent?: ComponentFn;
14
+ }
15
+ /**
16
+ * Create a full Zero app — assembles router, head provider, and root layout.
17
+ *
18
+ * Used internally by entry-server and entry-client.
19
+ */
20
+ export declare function createApp(options: CreateAppOptions): {
21
+ App: () => import("@pyreon/core").VNode;
22
+ router: import("@pyreon/router").Router;
23
+ };
24
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAS,MAAM,cAAc,CAAA;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAKjD,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,MAAM,EAAE,WAAW,EAAE,CAAA;IAErB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/B,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,iDAAiD;IACjD,MAAM,CAAC,EAAE,WAAW,CAAA;IAEpB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,WAAW,CAAA;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB;;;EAuBlD"}
@@ -0,0 +1,54 @@
1
+ import type { Middleware } from '@pyreon/server';
2
+ export interface CacheConfig {
3
+ /** Cache duration for immutable hashed assets (seconds). Default: 31536000 (1 year) */
4
+ immutable?: number;
5
+ /** Cache duration for static assets like images/fonts (seconds). Default: 86400 (1 day) */
6
+ static?: number;
7
+ /** Cache duration for pages (seconds). Default: 0 (no cache) */
8
+ pages?: number;
9
+ /** Stale-while-revalidate window for pages (seconds). Default: 60 */
10
+ staleWhileRevalidate?: number;
11
+ /** Custom rules by URL pattern. */
12
+ rules?: CacheRule[];
13
+ }
14
+ export interface CacheRule {
15
+ /** URL pattern to match (glob-style). e.g. "/api/*" */
16
+ match: string;
17
+ /** Cache-Control header value. */
18
+ control: string;
19
+ }
20
+ /** @internal Exported for testing */
21
+ export declare function matchGlob(pattern: string, path: string): boolean;
22
+ /**
23
+ * Cache control middleware for Zero.
24
+ * Sets Cache-Control headers on the response based on asset type.
25
+ *
26
+ * @example
27
+ * import { cacheMiddleware } from "@pyreon/zero/cache"
28
+ *
29
+ * export default createHandler({
30
+ * routes,
31
+ * middleware: [
32
+ * cacheMiddleware({
33
+ * pages: 60,
34
+ * staleWhileRevalidate: 300,
35
+ * rules: [
36
+ * { match: "/api/*", control: "no-store" },
37
+ * ],
38
+ * }),
39
+ * ],
40
+ * })
41
+ */
42
+ export declare function cacheMiddleware(config?: CacheConfig): Middleware;
43
+ /**
44
+ * Security headers middleware.
45
+ * Adds common security headers to all responses.
46
+ */
47
+ export declare function securityHeaders(): Middleware;
48
+ /**
49
+ * Compression detection middleware.
50
+ * Sets Vary: Accept-Encoding header so caches can serve compressed variants.
51
+ * Actual compression is handled by the runtime (Bun/Node) or reverse proxy.
52
+ */
53
+ export declare function varyEncoding(): Middleware;
54
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAA;AAanE,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,mCAAmC;IACnC,KAAK,CAAC,EAAE,SAAS,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAA;CAChB;AAOD,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKhE;AAwBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,MAAM,GAAE,WAAgB,GAAG,UAAU,CA0BpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAW5C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,UAAU,CAUzC"}
@@ -0,0 +1,19 @@
1
+ import type { ComponentFn } from '@pyreon/core';
2
+ import type { RouteRecord } from '@pyreon/router';
3
+ export interface StartClientOptions {
4
+ /** Route definitions. */
5
+ routes: RouteRecord[];
6
+ /** Root layout component. */
7
+ layout?: ComponentFn;
8
+ }
9
+ /**
10
+ * Start the client-side app — hydrates SSR content or mounts fresh for SPA.
11
+ *
12
+ * @example
13
+ * import { routes } from "virtual:zero/routes"
14
+ * import { startClient } from "@pyreon/zero/client"
15
+ *
16
+ * startClient({ routes })
17
+ */
18
+ export declare function startClient(options: StartClientOptions): () => void;
19
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAMjD,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,cAkBtD"}
@@ -0,0 +1,18 @@
1
+ import type { ZeroConfig } from './types';
2
+ /**
3
+ * Define a Zero configuration.
4
+ * Used in `zero.config.ts` at the project root.
5
+ *
6
+ * @example
7
+ * import { defineConfig } from "@pyreon/zero/config"
8
+ *
9
+ * export default defineConfig({
10
+ * mode: "ssr",
11
+ * ssr: { mode: "stream" },
12
+ * port: 3000,
13
+ * })
14
+ */
15
+ export declare function defineConfig(config: ZeroConfig): ZeroConfig;
16
+ /** Merge user config with defaults. */
17
+ export declare function resolveConfig(userConfig?: ZeroConfig): Required<Pick<ZeroConfig, 'mode' | 'base' | 'port' | 'adapter'>> & ZeroConfig;
18
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAE3D;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAC3B,UAAU,GAAE,UAAe,GAC1B,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC,GACjE,UAAU,CAYX"}
@@ -0,0 +1,26 @@
1
+ import type { RouteRecord } from '@pyreon/router';
2
+ import type { Middleware } from '@pyreon/server';
3
+ import type { ZeroConfig } from './types';
4
+ export interface CreateServerOptions {
5
+ /** Route definitions. */
6
+ routes: RouteRecord[];
7
+ /** Zero config. */
8
+ config?: ZeroConfig;
9
+ /** Additional middleware. */
10
+ middleware?: Middleware[];
11
+ /** HTML template override. */
12
+ template?: string;
13
+ /** Client entry path. */
14
+ clientEntry?: string;
15
+ }
16
+ /**
17
+ * Create the SSR request handler for production.
18
+ *
19
+ * @example
20
+ * import { routes } from "virtual:zero/routes"
21
+ * import { createServer } from "@pyreon/zero"
22
+ *
23
+ * export default createServer({ routes })
24
+ */
25
+ export declare function createServer(options: CreateServerOptions): (req: Request) => Promise<Response>;
26
+ //# sourceMappingURL=entry-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-server.d.ts","sourceRoot":"","sources":["../../src/entry-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIzC,MAAM,WAAW,mBAAmB;IAClC,yBAAyB;IACzB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,mBAAmB;IACnB,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IACzB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,uCAoBxD"}