@pyreon/zero 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/lib/actions.js +97 -0
  2. package/lib/actions.js.map +1 -0
  3. package/lib/ai.js +503 -0
  4. package/lib/ai.js.map +1 -0
  5. package/lib/api-routes.js +137 -0
  6. package/lib/api-routes.js.map +1 -0
  7. package/lib/compression.js +80 -0
  8. package/lib/compression.js.map +1 -0
  9. package/lib/cors.js +57 -0
  10. package/lib/cors.js.map +1 -0
  11. package/lib/csp.js +119 -0
  12. package/lib/csp.js.map +1 -0
  13. package/lib/env.js +217 -0
  14. package/lib/env.js.map +1 -0
  15. package/lib/favicon.js +424 -0
  16. package/lib/favicon.js.map +1 -0
  17. package/lib/i18n-routing.js +167 -0
  18. package/lib/i18n-routing.js.map +1 -0
  19. package/lib/index.js +1631 -179
  20. package/lib/index.js.map +1 -1
  21. package/lib/link.js +5 -0
  22. package/lib/link.js.map +1 -1
  23. package/lib/logger.js +78 -0
  24. package/lib/logger.js.map +1 -0
  25. package/lib/meta.js +336 -0
  26. package/lib/meta.js.map +1 -0
  27. package/lib/middleware.js +53 -0
  28. package/lib/middleware.js.map +1 -0
  29. package/lib/og-image.js +233 -0
  30. package/lib/og-image.js.map +1 -0
  31. package/lib/rate-limit.js +76 -0
  32. package/lib/rate-limit.js.map +1 -0
  33. package/lib/testing.js +179 -0
  34. package/lib/testing.js.map +1 -0
  35. package/lib/theme.js +11 -2
  36. package/lib/theme.js.map +1 -1
  37. package/lib/types/actions.d.ts +27 -24
  38. package/lib/types/actions.d.ts.map +1 -1
  39. package/lib/types/ai.d.ts +163 -0
  40. package/lib/types/ai.d.ts.map +1 -0
  41. package/lib/types/api-routes.d.ts +37 -33
  42. package/lib/types/api-routes.d.ts.map +1 -1
  43. package/lib/types/cache.d.ts +26 -22
  44. package/lib/types/cache.d.ts.map +1 -1
  45. package/lib/types/client.d.ts +13 -9
  46. package/lib/types/client.d.ts.map +1 -1
  47. package/lib/types/compression.d.ts +14 -10
  48. package/lib/types/compression.d.ts.map +1 -1
  49. package/lib/types/config.d.ts +39 -4
  50. package/lib/types/config.d.ts.map +1 -1
  51. package/lib/types/cors.d.ts +20 -16
  52. package/lib/types/cors.d.ts.map +1 -1
  53. package/lib/types/csp.d.ts +88 -0
  54. package/lib/types/csp.d.ts.map +1 -0
  55. package/lib/types/env.d.ts +118 -0
  56. package/lib/types/env.d.ts.map +1 -0
  57. package/lib/types/favicon.d.ts +70 -24
  58. package/lib/types/favicon.d.ts.map +1 -1
  59. package/lib/types/font.d.ts +68 -65
  60. package/lib/types/font.d.ts.map +1 -1
  61. package/lib/types/i18n-routing.d.ts +43 -37
  62. package/lib/types/i18n-routing.d.ts.map +1 -1
  63. package/lib/types/image-plugin.d.ts +49 -45
  64. package/lib/types/image-plugin.d.ts.map +1 -1
  65. package/lib/types/image.d.ts +47 -36
  66. package/lib/types/image.d.ts.map +1 -1
  67. package/lib/types/index.d.ts +1961 -46
  68. package/lib/types/index.d.ts.map +1 -1
  69. package/lib/types/link.d.ts +61 -56
  70. package/lib/types/link.d.ts.map +1 -1
  71. package/lib/types/logger.d.ts +57 -0
  72. package/lib/types/logger.d.ts.map +1 -0
  73. package/lib/types/meta.d.ts +180 -69
  74. package/lib/types/meta.d.ts.map +1 -1
  75. package/lib/types/middleware.d.ts +8 -4
  76. package/lib/types/middleware.d.ts.map +1 -1
  77. package/lib/types/og-image.d.ts +111 -0
  78. package/lib/types/og-image.d.ts.map +1 -0
  79. package/lib/types/rate-limit.d.ts +20 -16
  80. package/lib/types/rate-limit.d.ts.map +1 -1
  81. package/lib/types/script.d.ts +23 -19
  82. package/lib/types/script.d.ts.map +1 -1
  83. package/lib/types/seo.d.ts +47 -43
  84. package/lib/types/seo.d.ts.map +1 -1
  85. package/lib/types/testing.d.ts +64 -27
  86. package/lib/types/testing.d.ts.map +1 -1
  87. package/lib/types/theme.d.ts +22 -12
  88. package/lib/types/theme.d.ts.map +1 -1
  89. package/package.json +37 -12
  90. package/src/actions.ts +1 -3
  91. package/src/adapters/bun.ts +2 -0
  92. package/src/adapters/cloudflare.ts +84 -0
  93. package/src/adapters/index.ts +13 -1
  94. package/src/adapters/netlify.ts +86 -0
  95. package/src/adapters/node.ts +2 -0
  96. package/src/adapters/validate.ts +16 -0
  97. package/src/adapters/vercel.ts +86 -0
  98. package/src/ai.ts +623 -0
  99. package/src/compression.ts +19 -3
  100. package/src/csp.ts +207 -0
  101. package/src/entry-server.ts +28 -5
  102. package/src/env.ts +344 -0
  103. package/src/favicon.ts +221 -80
  104. package/src/index.ts +42 -2
  105. package/src/link.tsx +6 -0
  106. package/src/logger.ts +144 -0
  107. package/src/meta.tsx +124 -14
  108. package/src/og-image.ts +378 -0
  109. package/src/rate-limit.ts +11 -9
  110. package/src/theme.tsx +12 -1
  111. package/src/types.ts +1 -1
  112. package/src/vite-plugin.ts +5 -1
  113. package/lib/types/adapters/bun.d.ts +0 -6
  114. package/lib/types/adapters/bun.d.ts.map +0 -1
  115. package/lib/types/adapters/index.d.ts +0 -10
  116. package/lib/types/adapters/index.d.ts.map +0 -1
  117. package/lib/types/adapters/node.d.ts +0 -6
  118. package/lib/types/adapters/node.d.ts.map +0 -1
  119. package/lib/types/adapters/static.d.ts +0 -7
  120. package/lib/types/adapters/static.d.ts.map +0 -1
  121. package/lib/types/app.d.ts +0 -24
  122. package/lib/types/app.d.ts.map +0 -1
  123. package/lib/types/entry-server.d.ts +0 -37
  124. package/lib/types/entry-server.d.ts.map +0 -1
  125. package/lib/types/error-overlay.d.ts +0 -6
  126. package/lib/types/error-overlay.d.ts.map +0 -1
  127. package/lib/types/fs-router.d.ts +0 -47
  128. package/lib/types/fs-router.d.ts.map +0 -1
  129. package/lib/types/isr.d.ts +0 -9
  130. package/lib/types/isr.d.ts.map +0 -1
  131. package/lib/types/not-found.d.ts +0 -7
  132. package/lib/types/not-found.d.ts.map +0 -1
  133. package/lib/types/types.d.ts +0 -111
  134. package/lib/types/types.d.ts.map +0 -1
  135. package/lib/types/utils/use-intersection-observer.d.ts +0 -10
  136. package/lib/types/utils/use-intersection-observer.d.ts.map +0 -1
  137. package/lib/types/utils/with-headers.d.ts +0 -6
  138. package/lib/types/utils/with-headers.d.ts.map +0 -1
  139. package/lib/types/vite-plugin.d.ts +0 -17
  140. package/lib/types/vite-plugin.d.ts.map +0 -1
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["flattenRoutePatterns","h","sharpWarned","warnSharpMissing"],"sources":["../src/app.ts","../src/api-routes.ts","../src/not-found.ts","../src/entry-server.ts","../src/config.ts","../src/error-overlay.ts","../src/vite-plugin.ts","../src/isr.ts","../src/adapters/bun.ts","../src/adapters/node.ts","../src/adapters/static.ts","../src/adapters/index.ts","../src/utils/use-intersection-observer.ts","../../../core/core/lib/jsx-runtime.js","../src/image.tsx","../src/link.tsx","../src/script.tsx","../src/cache.ts","../src/middleware.ts","../src/font.ts","../src/image-plugin.ts","../src/theme.tsx","../src/seo.ts","../src/cors.ts","../src/rate-limit.ts","../src/compression.ts","../src/actions.ts","../src/favicon.ts","../src/i18n-routing.ts","../src/meta.tsx"],"sourcesContent":["import type { ComponentFn, Props } from '@pyreon/core'\nimport { Fragment, h } from '@pyreon/core'\nimport { HeadProvider } from '@pyreon/head'\nimport type { RouteRecord } from '@pyreon/router'\nimport { createRouter, RouterProvider, RouterView } from '@pyreon/router'\n\n// ─── App assembly ────────────────────────────────────────────────────────────\n\nexport interface CreateAppOptions {\n /** Route definitions (from file-based routing or manual). */\n routes: RouteRecord[]\n\n /** Router mode. Default: \"history\" for SSR, \"hash\" for SPA. */\n routerMode?: 'hash' | 'history'\n\n /** Initial URL for SSR. */\n url?: string\n\n /** Root layout component wrapping all routes. */\n layout?: ComponentFn\n\n /** Global error component. */\n errorComponent?: ComponentFn\n}\n\n/**\n * Create a full Zero app — assembles router, head provider, and root layout.\n *\n * Used internally by entry-server and entry-client.\n */\nexport function createApp(options: CreateAppOptions) {\n const router = createRouter({\n routes: options.routes,\n mode: options.routerMode ?? 'history',\n ...(options.url ? { url: options.url } : {}),\n scrollBehavior: 'top',\n })\n\n const Layout = options.layout ?? DefaultLayout\n\n function App() {\n return h(\n HeadProvider,\n null,\n h(\n RouterProvider as ComponentFn<Props>,\n { router },\n h(Layout, null, h(RouterView as ComponentFn<Props>, null)),\n ),\n )\n }\n\n return { App, router }\n}\n\nfunction DefaultLayout(props: Props) {\n return h(Fragment, null, ...(Array.isArray(props.children) ? props.children : [props.children]))\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** HTTP methods supported by API routes. */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\n/** Context passed to API route handlers. */\nexport interface ApiContext {\n /** The incoming request. */\n request: Request\n /** Parsed URL. */\n url: URL\n /** URL path. */\n path: string\n /** Dynamic route parameters (e.g., { id: \"123\" }). */\n params: Record<string, string>\n /** Request headers. */\n headers: Headers\n}\n\n/** An API route handler function. */\nexport type ApiHandler = (ctx: ApiContext) => Response | Promise<Response>\n\n/** An API route module — exports named HTTP method handlers. */\nexport interface ApiRouteModule {\n GET?: ApiHandler\n POST?: ApiHandler\n PUT?: ApiHandler\n PATCH?: ApiHandler\n DELETE?: ApiHandler\n HEAD?: ApiHandler\n OPTIONS?: ApiHandler\n}\n\n/** A registered API route entry. */\nexport interface ApiRouteEntry {\n /** URL pattern (e.g., \"/api/posts/:id\"). */\n pattern: string\n /** The route module with method handlers. */\n module: ApiRouteModule\n}\n\n// ─── Pattern matching ────────────────────────────────────────────────────────\n\n/**\n * Match a URL path against an API route pattern.\n * Returns extracted params or null if no match.\n */\nexport function matchApiRoute(pattern: string, path: string): Record<string, string> | null {\n const patternParts = pattern.split('/').filter(Boolean)\n const pathParts = path.split('/').filter(Boolean)\n const params: Record<string, string> = {}\n\n for (let i = 0; i < patternParts.length; i++) {\n const pp = patternParts[i]\n if (!pp) continue\n\n // Catch-all: :param*\n if (pp.endsWith('*')) {\n const paramName = pp.slice(1, -1)\n params[paramName] = pathParts.slice(i).join('/')\n return params\n }\n\n // No more path segments\n if (i >= pathParts.length) return null\n\n // Dynamic segment: :param\n if (pp.startsWith(':')) {\n params[pp.slice(1)] = pathParts[i]!\n continue\n }\n\n // Static segment\n if (pp !== pathParts[i]) return null\n }\n\n return patternParts.length === pathParts.length ? params : null\n}\n\n// ─── Middleware ───────────────────────────────────────────────────────────────\n\nconst HTTP_METHODS: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']\n\n/**\n * Create a middleware that dispatches API route requests.\n * API routes are matched by URL pattern and HTTP method.\n */\nexport function createApiMiddleware(routes: ApiRouteEntry[]): Middleware {\n return async (ctx: MiddlewareContext) => {\n for (const route of routes) {\n const params = matchApiRoute(route.pattern, ctx.path)\n if (!params) continue\n\n const method = ctx.req.method.toUpperCase() as HttpMethod\n const handler = route.module[method]\n\n if (!handler) {\n // Route matched but method not supported\n const allowed = HTTP_METHODS.filter((m) => route.module[m]).join(', ')\n return new Response(null, {\n status: 405,\n headers: {\n Allow: allowed,\n 'Content-Type': 'application/json',\n },\n })\n }\n\n return handler({\n request: ctx.req,\n url: ctx.url,\n path: ctx.path,\n params,\n headers: ctx.req.headers,\n })\n }\n }\n}\n\n// ─── Virtual module generation ───────────────────────────────────────────────\n\n/**\n * Detect whether a route file is an API route.\n * API routes are `.ts` or `.js` files inside an `api/` directory.\n */\nexport function isApiRoute(filePath: string): boolean {\n const normalized = filePath.replace(/\\\\/g, '/')\n return (\n normalized.startsWith('api/') &&\n (normalized.endsWith('.ts') || normalized.endsWith('.js')) &&\n !normalized.endsWith('.tsx') &&\n !normalized.endsWith('.jsx')\n )\n}\n\n/**\n * Convert an API route file path to a URL pattern.\n *\n * Examples:\n * \"api/posts.ts\" → \"/api/posts\"\n * \"api/posts/index.ts\" → \"/api/posts\"\n * \"api/posts/[id].ts\" → \"/api/posts/:id\"\n * \"api/[...path].ts\" → \"/api/:path*\"\n */\nexport function apiFilePathToPattern(filePath: string): string {\n let route = filePath\n // Remove extension\n for (const ext of ['.ts', '.js']) {\n if (route.endsWith(ext)) {\n route = route.slice(0, -ext.length)\n break\n }\n }\n\n const segments = route.split('/')\n const urlSegments: string[] = []\n\n for (const seg of segments) {\n if (seg === 'index') continue\n\n // Catch-all: [...param]\n const catchAll = seg.match(/^\\[\\.\\.\\.(\\w+)\\]$/)\n if (catchAll) {\n urlSegments.push(`:${catchAll[1]}*`)\n continue\n }\n\n // Dynamic: [param]\n const dynamic = seg.match(/^\\[(\\w+)\\]$/)\n if (dynamic) {\n urlSegments.push(`:${dynamic[1]}`)\n continue\n }\n\n urlSegments.push(seg)\n }\n\n return `/${urlSegments.join('/')}`\n}\n\n/**\n * Generate a virtual module that exports API route entries.\n * Each entry maps a URL pattern to a module with HTTP method handlers.\n */\nexport function generateApiRouteModule(files: string[], routesDir: string): string {\n const apiFiles = files.filter(isApiRoute)\n\n if (apiFiles.length === 0) {\n return 'export const apiRoutes = []\\n'\n }\n\n const imports: string[] = []\n const entries: string[] = []\n\n for (let i = 0; i < apiFiles.length; i++) {\n const name = `_api${i}`\n const file = apiFiles[i]\n if (!file) continue\n const fullPath = `${routesDir}/${file}`\n const pattern = apiFilePathToPattern(file)\n\n imports.push(`import * as ${name} from \"${fullPath}\"`)\n entries.push(` { pattern: ${JSON.stringify(pattern)}, module: ${name} }`)\n }\n\n return [...imports, '', 'export const apiRoutes = [', entries.join(',\\n'), ']'].join('\\n')\n}\n","import type { ComponentFn } from \"@pyreon/core\";\nimport { h } from \"@pyreon/core\";\nimport { renderToString } from \"@pyreon/runtime-server\";\n\n// ─── 404 Not Found rendering ────────────────────────────────────────────────\n//\n// Shared utility for rendering 404 pages in both dev (vite-plugin) and\n// production (entry-server). Renders the notFoundComponent into HTML\n// and wraps it in a minimal document if no template is provided.\n\nconst DEFAULT_404_BODY =\n\t\"<h1>404 — Not Found</h1><p>The page you requested does not exist.</p>\";\n\n/**\n * Render a 404 component to a full HTML string.\n * If no component is provided, returns a default 404 page.\n */\nexport async function render404Page(\n\tcomponent: ComponentFn | undefined,\n\ttemplate?: string,\n): Promise<string> {\n\tlet body: string;\n\tif (component) {\n\t\tbody = await renderToString(h(component, null));\n\t} else {\n\t\tbody = DEFAULT_404_BODY;\n\t}\n\n\tif (template?.includes(\"<!--pyreon-app-->\")) {\n\t\treturn template.replace(\"<!--pyreon-app-->\", body);\n\t}\n\n\treturn `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>404 — Not Found</title>\n</head>\n<body>\n ${body}\n</body>\n</html>`;\n}\n","import type { ComponentFn } from \"@pyreon/core\";\nimport type { RouteRecord } from \"@pyreon/router\";\nimport type { Middleware, MiddlewareContext } from \"@pyreon/server\";\nimport { createHandler } from \"@pyreon/server\";\nimport type { ApiRouteEntry } from \"./api-routes\";\nimport { createApiMiddleware } from \"./api-routes\";\nimport { createApp } from \"./app\";\nimport { render404Page } from \"./not-found\";\nimport type { RouteMiddlewareEntry, ZeroConfig } from \"./types\";\n\n// ─── Server entry factory ───────────────────────────────────────────────────\n\nexport interface CreateServerOptions {\n\t/** Route definitions. */\n\troutes: RouteRecord[];\n\t/** Zero config. */\n\tconfig?: ZeroConfig;\n\t/** Additional middleware. */\n\tmiddleware?: Middleware[];\n\t/** Per-route middleware from virtual:zero/route-middleware. */\n\trouteMiddleware?: RouteMiddlewareEntry[];\n\t/** API route entries from virtual:zero/api-routes. */\n\tapiRoutes?: ApiRouteEntry[];\n\t/** HTML template override. */\n\ttemplate?: string;\n\t/** Client entry path. */\n\tclientEntry?: string;\n\t/** Component to render when no route matches (from _404.tsx). */\n\tnotFoundComponent?: ComponentFn;\n}\n\n/**\n * Create a middleware that dispatches per-route middleware based on URL pattern matching.\n */\nfunction createRouteMiddlewareDispatcher(\n\tentries: RouteMiddlewareEntry[],\n): Middleware {\n\treturn async (ctx: MiddlewareContext) => {\n\t\tfor (const entry of entries) {\n\t\t\tif (matchPattern(entry.pattern, ctx.path)) {\n\t\t\t\tconst mw = Array.isArray(entry.middleware)\n\t\t\t\t\t? entry.middleware\n\t\t\t\t\t: [entry.middleware];\n\t\t\t\tfor (const fn of mw) {\n\t\t\t\t\tconst result = await fn(ctx);\n\t\t\t\t\tif (result) return result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n/** Simple URL pattern matcher supporting :param and :param* segments. */\nexport function matchPattern(pattern: string, path: string): boolean {\n\tconst patternParts = pattern.split(\"/\").filter(Boolean);\n\tconst pathParts = path.split(\"/\").filter(Boolean);\n\n\tfor (let i = 0; i < patternParts.length; i++) {\n\t\tconst pp = patternParts[i];\n\t\tif (!pp) continue;\n\t\tif (pp.endsWith(\"*\")) return true; // catch-all matches everything after\n\t\tif (pp.startsWith(\":\")) continue; // dynamic segment matches anything\n\t\tif (pp !== pathParts[i]) return false;\n\t}\n\n\treturn patternParts.length === pathParts.length;\n}\n\n/**\n * Create the SSR request handler for production.\n *\n * @example\n * import { routes } from \"virtual:zero/routes\"\n * import { routeMiddleware } from \"virtual:zero/route-middleware\"\n * import { createServer } from \"@pyreon/zero\"\n *\n * export default createServer({ routes, routeMiddleware, apiRoutes })\n */\nexport function createServer(options: CreateServerOptions) {\n\tconst config = options.config ?? {};\n\n\tconst allMiddleware: Middleware[] = [];\n\n\t// API routes run first — they short-circuit before SSR\n\tif (options.apiRoutes?.length) {\n\t\tallMiddleware.push(createApiMiddleware(options.apiRoutes));\n\t}\n\n\t// Per-route middleware runs next\n\tif (options.routeMiddleware?.length) {\n\t\tallMiddleware.push(\n\t\t\tcreateRouteMiddlewareDispatcher(options.routeMiddleware),\n\t\t);\n\t}\n\n\t// Then global middleware from config and options\n\tallMiddleware.push(...(config.middleware ?? []));\n\tallMiddleware.push(...(options.middleware ?? []));\n\n\tconst { App } = createApp({\n\t\troutes: options.routes,\n\t\trouterMode: \"history\",\n\t});\n\n\tconst handler = createHandler({\n\t\tApp,\n\t\troutes: options.routes,\n\t\tmiddleware: allMiddleware,\n\t\tmode: config.ssr?.mode ?? \"string\",\n\t\t...(options.template ? { template: options.template } : {}),\n\t\t...(options.clientEntry ? { clientEntry: options.clientEntry } : {}),\n\t});\n\n\t// Wrap handler with 404 detection when a notFoundComponent is provided\n\tif (!options.notFoundComponent) return handler;\n\n\tconst NotFound = options.notFoundComponent;\n\tconst routePatterns = flattenRoutePatterns(options.routes);\n\n\treturn async (req: Request) => {\n\t\tconst url = new URL(req.url);\n\t\tconst pathname = url.pathname;\n\n\t\t// Check if any defined route matches this path\n\t\tif (!routePatterns.some((pattern) => matchPattern(pattern, pathname))) {\n\t\t\tconst fullHtml = await render404Page(NotFound, options.template);\n\t\t\treturn new Response(fullHtml, {\n\t\t\t\tstatus: 404,\n\t\t\t\theaders: { \"Content-Type\": \"text/html; charset=utf-8\" },\n\t\t\t});\n\t\t}\n\n\t\treturn handler(req);\n\t};\n}\n\n/** Extract all URL patterns from a nested route tree. */\nfunction flattenRoutePatterns(routes: RouteRecord[], prefix = \"\"): string[] {\n\tconst patterns: string[] = [];\n\tfor (const route of routes) {\n\t\tconst fullPath =\n\t\t\troute.path === \"/\" && prefix ? prefix : `${prefix}${route.path}`;\n\t\tpatterns.push(fullPath);\n\t\tif (route.children) {\n\t\t\tpatterns.push(\n\t\t\t\t...flattenRoutePatterns(route.children as RouteRecord[], fullPath),\n\t\t\t);\n\t\t}\n\t}\n\treturn patterns;\n}\n","import type { ZeroConfig } from './types'\n\n/**\n * Define a Zero configuration.\n * Used in `zero.config.ts` at the project root.\n *\n * @example\n * import { defineConfig } from \"@pyreon/zero/config\"\n *\n * export default defineConfig({\n * mode: \"ssr\",\n * ssr: { mode: \"stream\" },\n * port: 3000,\n * })\n */\nexport function defineConfig(config: ZeroConfig): ZeroConfig {\n return config\n}\n\n/** Merge user config with defaults. */\nexport function resolveConfig(\n userConfig: ZeroConfig = {},\n): Required<Pick<ZeroConfig, 'mode' | 'base' | 'port' | 'adapter'>> & ZeroConfig {\n return {\n mode: 'ssr',\n base: '/',\n port: 3000,\n adapter: 'node',\n ...userConfig,\n ssr: {\n mode: 'string',\n ...userConfig.ssr,\n },\n }\n}\n","/**\n * Dev-only error overlay for SSR/loader errors.\n * Renders a styled HTML page with the error stack trace.\n */\nexport function renderErrorOverlay(error: Error): string {\n const title = escapeHtml(error.message || 'Unknown error')\n const stack = escapeHtml(error.stack || '')\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>SSR Error — Pyreon Zero</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: ui-monospace, \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace;\n background: #1a1a2e;\n color: #e0e0e0;\n min-height: 100vh;\n padding: 2rem;\n }\n .overlay {\n max-width: 900px;\n margin: 0 auto;\n }\n .header {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n }\n .badge {\n background: #e74c3c;\n color: white;\n padding: 0.25rem 0.75rem;\n border-radius: 4px;\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n .label {\n color: #888;\n font-size: 0.85rem;\n }\n .message {\n font-size: 1.25rem;\n color: #ff6b6b;\n margin-bottom: 1.5rem;\n line-height: 1.5;\n word-break: break-word;\n }\n .stack {\n background: #16213e;\n border: 1px solid #2a2a4a;\n border-radius: 8px;\n padding: 1.25rem;\n overflow-x: auto;\n font-size: 0.8rem;\n line-height: 1.7;\n white-space: pre-wrap;\n word-break: break-all;\n }\n .stack .at { color: #888; }\n .stack .file { color: #4ecdc4; }\n .hint {\n margin-top: 1.5rem;\n padding: 1rem;\n background: #1e2a45;\n border-radius: 6px;\n border-left: 3px solid #3498db;\n font-size: 0.8rem;\n color: #aaa;\n line-height: 1.5;\n }\n </style>\n</head>\n<body>\n <div class=\"overlay\">\n <div class=\"header\">\n <span class=\"badge\">SSR Error</span>\n <span class=\"label\">Pyreon Zero — Dev Mode</span>\n </div>\n <div class=\"message\">${title}</div>\n <pre class=\"stack\">${formatStack(stack)}</pre>\n <div class=\"hint\">\n This error occurred during server-side rendering. Check the terminal for\n the full stack trace. This overlay is only shown in development.\n </div>\n </div>\n</body>\n</html>`\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction formatStack(stack: string): string {\n return stack\n .split('\\n')\n .map((line) => {\n if (line.includes('at ')) {\n const fileMatch = line.match(/\\(([^)]+)\\)/)\n if (fileMatch) {\n return line.replace(fileMatch[0], `(<span class=\"file\">${fileMatch[1]}</span>)`)\n }\n }\n return line\n })\n .join('\\n')\n}\n","import { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\nimport { generateApiRouteModule } from './api-routes'\nimport { resolveConfig } from './config'\n\n/**\n * Scan node_modules/@pyreon/ to discover all installed Pyreon packages.\n * Returns package names to exclude from Vite's dep optimizer.\n */\nfunction scanPyreonPackages(root: string): string[] {\n const pyreonDir = join(root, 'node_modules', '@pyreon')\n if (!existsSync(pyreonDir)) return []\n\n try {\n return readdirSync(pyreonDir)\n .filter((name) => !name.startsWith('.'))\n .map((name) => `@pyreon/${name}`)\n } catch {\n return []\n }\n}\nimport { matchPattern } from \"./entry-server\";\nimport { renderErrorOverlay } from \"./error-overlay\";\nimport {\n\tgenerateMiddlewareModule,\n\tgenerateRouteModule,\n\tscanRouteFiles,\n} from \"./fs-router\";\nimport { render404Page } from \"./not-found\";\nimport type { ZeroConfig } from \"./types\";\n\nconst VIRTUAL_ROUTES_ID = \"virtual:zero/routes\";\nconst RESOLVED_VIRTUAL_ROUTES_ID = `\\0${VIRTUAL_ROUTES_ID}`;\n\nconst VIRTUAL_MIDDLEWARE_ID = \"virtual:zero/route-middleware\";\nconst RESOLVED_VIRTUAL_MIDDLEWARE_ID = `\\0${VIRTUAL_MIDDLEWARE_ID}`;\n\nconst VIRTUAL_API_ROUTES_ID = \"virtual:zero/api-routes\";\nconst RESOLVED_VIRTUAL_API_ROUTES_ID = `\\0${VIRTUAL_API_ROUTES_ID}`;\n\n/**\n * Zero Vite plugin — adds file-based routing and zero-config conventions\n * on top of @pyreon/vite-plugin.\n *\n * @example\n * // vite.config.ts\n * import pyreon from \"@pyreon/vite-plugin\"\n * import zero from \"@pyreon/zero\"\n *\n * export default {\n * plugins: [pyreon(), zero()],\n * }\n */\nexport function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {\n\tconst config = resolveConfig(userConfig);\n\tlet routesDir: string;\n\tlet root: string;\n\n\tconst plugin: Plugin & { _zeroConfig: ZeroConfig } = {\n\t\tname: \"pyreon-zero\",\n\t\tenforce: \"pre\",\n\t\t_zeroConfig: userConfig,\n\n\t\tconfigResolved(resolvedConfig) {\n\t\t\troot = resolvedConfig.root;\n\t\t\troutesDir = `${root}/src/routes`;\n\t\t},\n\n\t\tresolveId(id) {\n\t\t\tif (id === VIRTUAL_ROUTES_ID) return RESOLVED_VIRTUAL_ROUTES_ID;\n\t\t\tif (id === VIRTUAL_MIDDLEWARE_ID) return RESOLVED_VIRTUAL_MIDDLEWARE_ID;\n\t\t\tif (id === VIRTUAL_API_ROUTES_ID) return RESOLVED_VIRTUAL_API_ROUTES_ID;\n\t\t},\n\n\t\tasync load(id) {\n\t\t\tif (id === RESOLVED_VIRTUAL_ROUTES_ID) {\n\t\t\t\ttry {\n\t\t\t\t\tconst files = await scanRouteFiles(routesDir);\n\t\t\t\t\treturn generateRouteModule(files, routesDir, {\n\t\t\t\t\tstaticImports: config.mode === 'ssg',\n\t\t\t\t});\n\t\t\t\t} catch (_err) {\n\t\t\t\t\treturn `export const routes = []`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (id === RESOLVED_VIRTUAL_MIDDLEWARE_ID) {\n\t\t\t\ttry {\n\t\t\t\t\tconst files = await scanRouteFiles(routesDir);\n\t\t\t\t\treturn generateMiddlewareModule(files, routesDir);\n\t\t\t\t} catch (_err) {\n\t\t\t\t\treturn `export const routeMiddleware = []`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (id === RESOLVED_VIRTUAL_API_ROUTES_ID) {\n\t\t\t\ttry {\n\t\t\t\t\tconst files = await scanRouteFiles(routesDir);\n\t\t\t\t\treturn generateApiRouteModule(files, routesDir);\n\t\t\t\t} catch (_err) {\n\t\t\t\t\treturn `export const apiRoutes = []`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tconfigureServer(server) {\n\t\t\t// 404 handler — check if the requested path matches any route.\n\t\t\t// If not, render the nearest _404.tsx component with a 404 status.\n\t\t\t// Uses a sync wrapper that calls the async handler, since Connect\n\t\t\t// middleware does not natively support async functions.\n\t\t\tserver.middlewares.use((req, res, next) => {\n\t\t\t\tconst accept = req.headers.accept ?? \"\";\n\t\t\t\t// Accept HTML requests and wildcard requests (fetch without explicit Accept header)\n\t\t\t\tif (!accept.includes(\"text/html\") && !accept.includes(\"*/*\"))\n\t\t\t\t\treturn next();\n\n\t\t\t\tconst pathname = req.url?.split(\"?\")[0] ?? \"/\";\n\n\t\t\t\t// Skip static assets, Vite internal requests, and file-like paths (with extensions)\n\t\t\t\tif (pathname.startsWith(\"/@\") || pathname.startsWith(\"/__\"))\n\t\t\t\t\treturn next();\n\t\t\t\tif (/\\.\\w+$/.test(pathname)) return next();\n\n\t\t\t\thandle404(server, routesDir, pathname, res).then(\n\t\t\t\t\t(handled) => {\n\t\t\t\t\t\tif (!handled) next();\n\t\t\t\t\t},\n\t\t\t\t\t() => next(), // On error, fall through to Vite's default handling\n\t\t\t\t);\n\t\t\t});\n\n\t\t\t// SSR error overlay — intercept HTML requests and catch SSR errors\n\t\t\t// This runs as a late middleware (return function) so it wraps\n\t\t\t// Vite's own SSR handling and catches rendering failures.\n\t\t\tserver.middlewares.use((req, res, next) => {\n\t\t\t\tconst accept = req.headers.accept ?? \"\";\n\t\t\t\tif (!accept.includes(\"text/html\")) return next();\n\n\t\t\t\tconst originalEnd = res.end.bind(res);\n\t\t\t\tlet errored = false;\n\n\t\t\t\tconst handleError = (err: unknown) => {\n\t\t\t\t\tif (errored) return;\n\t\t\t\t\terrored = true;\n\t\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.ssrFixStacktrace(error);\n\t\t\t\t\tconst html = renderErrorOverlay(error);\n\t\t\t\t\tres.statusCode = 500;\n\t\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\t\tres.setHeader(\"Content-Length\", Buffer.byteLength(html));\n\t\t\t\t\toriginalEnd(html);\n\t\t\t\t};\n\n\t\t\t\tres.on(\"error\", handleError);\n\n\t\t\t\t// Wrap next() in try/catch to handle both sync and async errors.\n\t\t\t\t// Express-style middleware may throw synchronously or pass errors\n\t\t\t\t// through next(err), and Vite's SSR pipeline may reject promises.\n\t\t\t\ttry {\n\t\t\t\t\tconst result = next() as unknown;\n\t\t\t\t\t// Handle async errors from Vite's SSR pipeline\n\t\t\t\t\tif (\n\t\t\t\t\t\tresult &&\n\t\t\t\t\t\ttypeof (result as Promise<unknown>).catch === \"function\"\n\t\t\t\t\t) {\n\t\t\t\t\t\t(result as Promise<unknown>).catch(handleError);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\thandleError(err);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Watch routes directory for changes\n\t\t\tserver.watcher.add(`${routesDir}/**/*.{tsx,jsx,ts,js}`);\n\n\t\t\t// Invalidate virtual modules when route files change\n\t\t\tserver.watcher.on(\"all\", (event, path) => {\n\t\t\t\tif (\n\t\t\t\t\tpath.startsWith(routesDir) &&\n\t\t\t\t\t(event === \"add\" || event === \"unlink\")\n\t\t\t\t) {\n\t\t\t\t\tfor (const resolvedId of [\n\t\t\t\t\t\tRESOLVED_VIRTUAL_ROUTES_ID,\n\t\t\t\t\t\tRESOLVED_VIRTUAL_MIDDLEWARE_ID,\n\t\t\t\t\t\tRESOLVED_VIRTUAL_API_ROUTES_ID,\n\t\t\t\t\t]) {\n\t\t\t\t\t\tconst mod = server.moduleGraph.getModuleById(resolvedId);\n\t\t\t\t\t\tif (mod) server.moduleGraph.invalidateModule(mod);\n\t\t\t\t\t}\n\t\t\t\t\tserver.ws.send({ type: \"full-reload\" });\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tconfig(userConfig) {\n\t\t\t// Discover all @pyreon/* packages installed in node_modules.\n\t\t\t// The \"bun\" export condition points to TS source — esbuild's\n\t\t\t// dep optimizer would compile them with the wrong JSX runtime.\n\t\t\tconst root = userConfig.root ?? process.cwd()\n\t\t\tconst pyreonExclude = scanPyreonPackages(root)\n\n\t\t\treturn {\n\t\t\t\tresolve: {\n\t\t\t\t\tconditions: ['bun'],\n\t\t\t\t},\n\t\t\t\toptimizeDeps: {\n\t\t\t\t\texclude: pyreonExclude,\n\t\t\t\t},\n\t\t\t\tserver: {\n\t\t\t\t\tport: config.port,\n\t\t\t\t},\n\t\t\t\tdefine: {\n\t\t\t\t\t__ZERO_MODE__: JSON.stringify(config.mode),\n\t\t\t\t\t__ZERO_BASE__: JSON.stringify(config.base),\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n\n\treturn plugin;\n}\n\n/**\n * Check if the requested path matches any route. If not, render a 404 page.\n * Returns true if the 404 was handled (response sent), false otherwise.\n *\n * In dev mode, the _404.tsx component cannot be SSR-rendered because\n * the compiler emits _tpl() calls that require `document`. Instead,\n * we return a static 404 page. The actual component rendering happens\n * on the client side when the SPA loads.\n */\nasync function handle404(\n\tserver: import(\"vite\").ViteDevServer,\n\t_routesDir: string,\n\tpathname: string,\n\tres: import(\"http\").ServerResponse,\n): Promise<boolean> {\n\tconst mod = await server.ssrLoadModule(VIRTUAL_ROUTES_ID);\n\tconst routes = mod.routes as Array<{ path?: string; children?: unknown[] }>;\n\tconst patterns = flattenRoutePatterns(routes);\n\n\tif (patterns.some((pattern) => matchPattern(pattern, pathname))) {\n\t\treturn false; // Route matches — not a 404\n\t}\n\n\t// No route matched — return a 404.\n\t// In dev, we return a static page since the compiler emits _tpl() calls\n\t// that require document (unavailable in SSR). The _404.tsx component\n\t// renders on the client side after hydration.\n\tconst html = await render404Page(undefined);\n\n\tres.statusCode = 404;\n\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\tres.setHeader(\"Content-Length\", Buffer.byteLength(html));\n\tres.end(html);\n\treturn true;\n}\n\n/** Extract all URL patterns from a nested route tree. */\nfunction flattenRoutePatterns(\n\troutes: Array<{ path?: string; children?: unknown[] }>,\n\tprefix = \"\",\n): string[] {\n\tconst patterns: string[] = [];\n\tfor (const route of routes) {\n\t\tif (!route.path) continue;\n\t\tconst fullPath =\n\t\t\troute.path === \"/\" && prefix ? prefix : `${prefix}${route.path}`;\n\t\tpatterns.push(fullPath);\n\t\tif (route.children) {\n\t\t\tpatterns.push(\n\t\t\t\t...flattenRoutePatterns(\n\t\t\t\t\troute.children as Array<{ path?: string; children?: unknown[] }>,\n\t\t\t\t\tfullPath,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\treturn patterns;\n}\n","import type { ISRConfig } from './types'\n\n// ─── ISR Cache ───────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n html: string\n headers: Record<string, string>\n timestamp: number\n}\n\n/**\n * In-memory ISR cache with stale-while-revalidate semantics.\n *\n * Wraps an SSR handler and caches responses per URL path.\n * Serves stale content immediately while revalidating in the background.\n */\nexport function createISRHandler(\n handler: (req: Request) => Promise<Response>,\n config: ISRConfig,\n): (req: Request) => Promise<Response> {\n const cache = new Map<string, CacheEntry>()\n const revalidating = new Set<string>()\n const revalidateMs = config.revalidate * 1000\n\n async function revalidate(url: URL) {\n const key = url.pathname\n if (revalidating.has(key)) return\n revalidating.add(key)\n\n try {\n const req = new Request(url.href, { method: 'GET' })\n const res = await handler(req)\n const html = await res.text()\n const headers: Record<string, string> = {}\n res.headers.forEach((v, k) => {\n headers[k] = v\n })\n\n cache.set(key, { html, headers, timestamp: Date.now() })\n } catch {\n // Revalidation failed — stale cache entry remains valid\n } finally {\n revalidating.delete(key)\n }\n }\n\n return async (req: Request): Promise<Response> => {\n // Only cache GET requests\n if (req.method !== 'GET') {\n return handler(req)\n }\n\n const url = new URL(req.url)\n const key = url.pathname\n const entry = cache.get(key)\n\n if (entry) {\n const age = Date.now() - entry.timestamp\n\n if (age > revalidateMs) {\n // Stale — serve cached but revalidate in background\n revalidate(url)\n }\n\n return new Response(entry.html, {\n status: 200,\n headers: {\n ...entry.headers,\n 'content-type': 'text/html; charset=utf-8',\n 'x-isr-cache': age > revalidateMs ? 'STALE' : 'HIT',\n 'x-isr-age': String(Math.round(age / 1000)),\n },\n })\n }\n\n // Cache miss — render, cache, and return\n const res = await handler(req)\n const html = await res.text()\n const headers: Record<string, string> = {}\n res.headers.forEach((v, k) => {\n headers[k] = v\n })\n\n cache.set(key, { html, headers, timestamp: Date.now() })\n\n return new Response(html, {\n status: 200,\n headers: {\n ...headers,\n 'content-type': 'text/html; charset=utf-8',\n 'x-isr-cache': 'MISS',\n },\n })\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\n\n/**\n * Bun adapter — generates a standalone Bun.serve() entry.\n */\nexport function bunAdapter(): Adapter {\n return {\n name: 'bun',\n async build(options: AdapterBuildOptions) {\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const outDir = options.outDir\n await mkdir(outDir, { recursive: true })\n\n // Copy server and client builds\n await cp(options.clientOutDir, join(outDir, 'client'), {\n recursive: true,\n })\n await cp(join(options.serverEntry, '..'), join(outDir, 'server'), {\n recursive: true,\n })\n\n const port = options.config.port ?? 3000\n const serverEntry = `\nconst handler = (await import(\"./server/entry-server.js\")).default\nconst clientDir = new URL(\"./client/\", import.meta.url).pathname\n\nBun.serve({\n port: ${port},\n async fetch(req) {\n const url = new URL(req.url)\n\n // Try static files first\n if (req.method === \"GET\") {\n const filePath = clientDir + (url.pathname === \"/\" ? \"index.html\" : url.pathname)\n // Prevent path traversal — ensure resolved path stays within clientDir\n const resolved = Bun.resolveSync(filePath, \".\")\n if (!resolved.startsWith(Bun.resolveSync(clientDir, \".\"))) {\n return new Response(\"Forbidden\", { status: 403 })\n }\n const file = Bun.file(filePath)\n if (await file.exists()) {\n return new Response(file, {\n headers: {\n \"cache-control\": filePath.endsWith(\".js\") || filePath.endsWith(\".css\")\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=3600\",\n },\n })\n }\n }\n\n // Fall through to SSR handler\n return handler(req)\n },\n})\n\nconsole.log(\"\\\\n ⚡ Zero production server running on http://localhost:${port}\\\\n\")\n`.trimStart()\n\n await writeFile(join(outDir, 'index.ts'), serverEntry)\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\n\n/**\n * Node.js adapter — generates a standalone server entry using node:http.\n */\nexport function nodeAdapter(): Adapter {\n return {\n name: 'node',\n async build(options: AdapterBuildOptions) {\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const outDir = options.outDir\n await mkdir(outDir, { recursive: true })\n\n // Copy server and client builds\n await cp(options.clientOutDir, join(outDir, 'client'), {\n recursive: true,\n })\n await cp(join(options.serverEntry, '..'), join(outDir, 'server'), {\n recursive: true,\n })\n\n // Generate standalone server entry\n const port = options.config.port ?? 3000\n const serverEntry = `\nimport { createServer } from \"node:http\"\nimport { readFile } from \"node:fs/promises\"\nimport { join, extname } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url))\nconst handler = (await import(\"./server/entry-server.js\")).default\nconst clientDir = join(__dirname, \"client\")\n\nconst MIME_TYPES = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".svg\": \"image/svg+xml\",\n \".woff2\": \"font/woff2\",\n \".woff\": \"font/woff\",\n \".ico\": \"image/x-icon\",\n}\n\nconst server = createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", \"http://localhost\")\n\n // Try to serve static files first\n if (req.method === \"GET\") {\n try {\n const filePath = join(clientDir, url.pathname === \"/\" ? \"index.html\" : url.pathname)\n // Prevent path traversal — ensure resolved path stays within clientDir\n const { resolve } = await import(\"node:path\")\n const resolved = resolve(filePath)\n if (!resolved.startsWith(resolve(clientDir))) {\n res.writeHead(403)\n res.end(\"Forbidden\")\n return\n }\n const ext = extname(filePath)\n if (ext && ext !== \".html\") {\n const data = await readFile(filePath)\n const mime = MIME_TYPES[ext] || \"application/octet-stream\"\n res.writeHead(200, {\n \"content-type\": mime,\n \"cache-control\": ext === \".js\" || ext === \".css\"\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=3600\",\n })\n res.end(data)\n return\n }\n } catch {}\n }\n\n // Fall through to SSR handler\n const headers = {}\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) headers[key] = Array.isArray(value) ? value.join(\", \") : value\n }\n\n const request = new Request(url.href, {\n method: req.method,\n headers,\n })\n\n const response = await handler(request)\n const body = await response.text()\n\n const responseHeaders = {}\n response.headers.forEach((v, k) => { responseHeaders[k] = v })\n\n res.writeHead(response.status, responseHeaders)\n res.end(body)\n})\n\nserver.listen(${port}, () => {\n console.log(\"\\\\n ⚡ Zero production server running on http://localhost:${port}\\\\n\")\n})\n`.trimStart()\n\n await writeFile(join(outDir, 'index.js'), serverEntry)\n await writeFile(join(outDir, 'package.json'), JSON.stringify({ type: 'module' }, null, 2))\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\n\n/**\n * Static adapter — just copies the client build output.\n * Used with SSG mode where all pages are pre-rendered at build time.\n */\nexport function staticAdapter(): Adapter {\n return {\n name: 'static',\n async build(options: AdapterBuildOptions) {\n const { cp, mkdir } = await import('node:fs/promises')\n\n await mkdir(options.outDir, { recursive: true })\n await cp(options.clientOutDir, options.outDir, { recursive: true })\n },\n }\n}\n","export { bunAdapter } from './bun'\nexport { nodeAdapter } from './node'\nexport { staticAdapter } from './static'\n\nimport type { Adapter, ZeroConfig } from '../types'\nimport { bunAdapter } from './bun'\nimport { nodeAdapter } from './node'\nimport { staticAdapter } from './static'\n\n/**\n * Resolve the adapter from config.\n * Returns a built-in adapter or throws if unknown.\n */\nexport function resolveAdapter(config: ZeroConfig): Adapter {\n const name = config.adapter ?? 'node'\n\n switch (name) {\n case 'node':\n return nodeAdapter()\n case 'bun':\n return bunAdapter()\n case 'static':\n return staticAdapter()\n default:\n throw new Error(`[zero] Unknown adapter: \"${name}\". Use \"node\", \"bun\", or \"static\".`)\n }\n}\n","import { onMount, onUnmount } from '@pyreon/core'\n\n/**\n * Observes an element and calls `onIntersect` once it enters the viewport.\n * Automatically disconnects after the first intersection.\n *\n * @param getElement - Getter for the target element (may be undefined before mount).\n * @param onIntersect - Callback fired when the element becomes visible.\n * @param rootMargin - IntersectionObserver rootMargin. Default: \"200px\".\n */\nexport function useIntersectionObserver(\n getElement: () => HTMLElement | undefined,\n onIntersect: () => void,\n rootMargin = '200px',\n) {\n onMount(() => {\n const el = getElement()\n if (!el) return undefined\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n onIntersect()\n observer.disconnect()\n }\n }\n },\n { rootMargin },\n )\n\n observer.observe(el)\n onUnmount(() => observer.disconnect())\n return undefined\n })\n}\n","//#region src/h.ts\n/** Marker for fragment nodes — renders children without a wrapper element */\nconst Fragment = Symbol(\"Pyreon.Fragment\");\n/**\n* Hyperscript function — the compiled output of JSX.\n* `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n*\n* Generic on P so TypeScript validates props match the component's signature\n* at the call site, then stores the result in the loosely-typed VNode.\n*/\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nconst EMPTY_PROPS = {};\nfunction h(type, props, ...children) {\n\treturn {\n\t\ttype,\n\t\tprops: props ?? EMPTY_PROPS,\n\t\tchildren: normalizeChildren(children),\n\t\tkey: props?.key ?? null\n\t};\n}\nfunction normalizeChildren(children) {\n\tfor (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);\n\treturn children;\n}\nfunction flattenChildren(children) {\n\tconst result = [];\n\tfor (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));\n\telse result.push(child);\n\treturn result;\n}\n\n//#endregion\n//#region src/jsx-runtime.ts\n/**\n* JSX automatic runtime.\n*\n* When tsconfig has `\"jsxImportSource\": \"@pyreon/core\"`, the TS/bundler compiler\n* rewrites JSX to imports from this file automatically:\n* <div class=\"x\" /> → jsx(\"div\", { class: \"x\" })\n*/\nfunction jsx(type, props, key) {\n\tconst { children, ...rest } = props;\n\tconst propsWithKey = key != null ? {\n\t\t...rest,\n\t\tkey\n\t} : rest;\n\tif (typeof type === \"function\") return h(type, children !== void 0 ? {\n\t\t...propsWithKey,\n\t\tchildren\n\t} : propsWithKey);\n\treturn h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);\n}\nconst jsxs = jsx;\n\n//#endregion\nexport { Fragment, jsx, jsxs };\n//# sourceMappingURL=jsx-runtime.js.map","import type { VNodeChild } from '@pyreon/core'\nimport { createRef } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\nimport type { FormatSource } from './image-plugin'\nimport { useIntersectionObserver } from './utils/use-intersection-observer'\n\n// ─── Image optimization component ───────────────────────────────────────────\n//\n// <Image> provides:\n// - Lazy loading via IntersectionObserver (loads when near viewport)\n// - Automatic width/height to prevent CLS (Cumulative Layout Shift)\n// - Responsive srcset generation from width descriptors\n// - Multi-format support via <picture> (WebP/AVIF with fallback)\n// - Blur-up placeholder while loading\n// - Priority loading for above-the-fold images\n\nexport interface ImageProps {\n /** Image source URL. */\n src: string\n /** Alt text (required for accessibility). */\n alt: string\n /** Intrinsic width of the image. */\n width: number\n /** Intrinsic height of the image. */\n height: number\n /** Responsive sizes attribute. Default: \"100vw\" */\n sizes?: string\n /** Responsive srcset string or source array. */\n srcset?: string | ImageSource[]\n /** Per-format source sets for <picture>. Provided automatically by imagePlugin. */\n formats?: FormatSource[]\n /** Loading strategy. \"lazy\" uses IntersectionObserver, \"eager\" loads immediately. Default: \"lazy\" */\n loading?: 'lazy' | 'eager'\n /** Mark as priority (LCP image). Disables lazy loading, adds fetchPriority=\"high\". */\n priority?: boolean\n /** Low-quality placeholder image URL or base64 data URI for blur-up effect. */\n placeholder?: string\n /** CSS class name. */\n class?: string\n /** Inline styles. */\n style?: string\n /** CSS object-fit. Default: \"cover\" */\n fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'\n /** Decode async. Default: true */\n decoding?: 'sync' | 'async' | 'auto'\n}\n\nexport interface ImageSource {\n src: string\n width: number\n}\n\n/**\n * Optimized image component with lazy loading, responsive images,\n * multi-format <picture> support, and blur-up placeholders.\n *\n * @example\n * // With imagePlugin — spread the import directly\n * import hero from \"./hero.jpg?optimize\"\n * <Image {...hero} alt=\"Hero\" priority />\n *\n * @example\n * // Manual usage\n * <Image src=\"/hero.jpg\" alt=\"Hero\" width={1200} height={630} />\n */\nexport function Image(props: ImageProps): VNodeChild {\n const isEager = props.priority || props.loading === 'eager'\n const loaded = signal(isEager)\n const inView = signal(isEager)\n const containerRef = createRef<HTMLElement>()\n\n // Resolve srcset from string or array\n const resolvedSrcset =\n typeof props.srcset === 'string'\n ? props.srcset\n : props.srcset?.map((s) => `${s.src} ${s.width}w`).join(', ')\n\n const sizes = props.sizes ?? '100vw'\n const fit = props.fit ?? 'cover'\n const hasFormats = props.formats && props.formats.length > 0\n const aspectRatio = `${props.width} / ${props.height}`\n\n if (!isEager) {\n useIntersectionObserver(\n () => containerRef.current ?? undefined,\n () => inView.set(true),\n )\n }\n\n // Static styles (don't depend on signals)\n const containerStyle = [\n 'position: relative',\n 'overflow: hidden',\n `aspect-ratio: ${aspectRatio}`,\n `max-width: ${props.width}px`,\n 'width: 100%',\n props.style,\n ]\n .filter(Boolean)\n .join('; ')\n\n const imgEl = (\n <img\n src={() => (inView() ? props.src : '')}\n srcSet={() => (!hasFormats && inView() && resolvedSrcset ? resolvedSrcset : '')}\n sizes={resolvedSrcset ? sizes : undefined}\n alt={props.alt}\n width={props.width}\n height={props.height}\n loading={isEager ? 'eager' : 'lazy'}\n decoding={props.decoding ?? 'async'}\n fetchPriority={props.priority ? 'high' : undefined}\n onLoad={() => loaded.set(true)}\n style={() =>\n [\n 'display: block',\n 'width: 100%',\n 'height: 100%',\n `object-fit: ${fit}`,\n 'transition: opacity 0.3s ease',\n props.placeholder && !loaded() ? 'opacity: 0' : 'opacity: 1',\n ].join('; ')\n }\n />\n )\n\n return (\n <div ref={containerRef} class={props.class} style={containerStyle}>\n {props.placeholder && (\n <img\n src={props.placeholder}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"eager\"\n style={() =>\n [\n 'position: absolute',\n 'inset: 0',\n 'width: 100%',\n 'height: 100%',\n 'object-fit: cover',\n 'filter: blur(20px)',\n 'transform: scale(1.1)',\n 'transition: opacity 0.4s ease',\n loaded() ? 'opacity: 0; pointer-events: none' : 'opacity: 1',\n ].join('; ')\n }\n />\n )}\n {hasFormats ? (\n <picture>\n {props.formats?.map((fmt) => (\n <source\n type={fmt.type}\n srcSet={() => (inView() ? (fmt.srcset ?? '') : '')}\n sizes={sizes}\n />\n ))}\n {imgEl}\n </picture>\n ) : (\n imgEl\n )}\n </div>\n )\n}\n","import { createRef } from '@pyreon/core'\nimport { useRouter } from '@pyreon/router'\nimport { useIntersectionObserver } from './utils/use-intersection-observer'\n\n// ─── Link component with prefetching ────────────────────────────────────────\n//\n// Provides client-side navigation, prefetching, and active state tracking.\n// Three levels of API:\n//\n// 1. useLink(props) — composable returning handlers, state, and ref callback\n// 2. createLink(Comp) — HOC wrapping any component with link behavior\n// 3. Link — default <a>-based link (built on createLink)\n\nexport interface LinkProps {\n /** Target URL path. */\n href: string\n /** Link content. */\n children?: any\n /** CSS class name. */\n class?: string\n /** Class applied when this link matches the current route. */\n activeClass?: string\n /** Class applied when this link exactly matches the current route. */\n exactActiveClass?: string\n /** Prefetch strategy. Default: \"hover\" */\n prefetch?: 'hover' | 'viewport' | 'none'\n /** Open in new tab. */\n external?: boolean\n /** Inline styles. */\n style?: string\n /** ARIA label. */\n 'aria-label'?: string\n /** Additional click handler — called before navigation. Call e.preventDefault() to cancel. */\n onClick?: ((e: MouseEvent) => void) | undefined\n}\n\n/** Props passed to a custom component via createLink. */\nexport interface LinkRenderProps {\n href: string\n ref: import('@pyreon/core').Ref<HTMLAnchorElement>\n onClick: (e: MouseEvent) => void\n onMouseEnter: () => void\n onTouchStart: () => void\n isActive: () => boolean\n isExactActive: () => boolean\n /** Reactive class string — pass directly to element for auto-updates on route change. */\n class: (() => string) | string | undefined\n style?: string\n target?: string\n rel?: string\n 'aria-label'?: string\n children?: any\n}\n\n/** Return type of useLink. */\nexport interface UseLinkReturn {\n /** Ref object — attach to the root element for viewport-based prefetch. */\n ref: import('@pyreon/core').Ref<HTMLAnchorElement>\n /** Click handler — performs client-side navigation. */\n handleClick: (e: MouseEvent) => void\n /** Mouse enter handler — triggers hover prefetch. */\n handleMouseEnter: () => void\n /** Touch start handler — triggers prefetch on mobile. */\n handleTouchStart: () => void\n /** Whether the link partially matches the current route. */\n isActive: () => boolean\n /** Whether the link exactly matches the current route. */\n isExactActive: () => boolean\n /** Resolved class string including active classes. */\n classes: () => string\n}\n\nconst prefetched = new Set<string>()\n\nfunction doPrefetch(href: string) {\n if (prefetched.has(href)) return\n prefetched.add(href)\n\n const docLink = document.createElement('link')\n docLink.rel = 'prefetch'\n docLink.href = href\n docLink.as = 'document'\n document.head.appendChild(docLink)\n\n try {\n const chunkHint = document.createElement('link')\n chunkHint.rel = 'modulepreload'\n chunkHint.href = href\n document.head.appendChild(chunkHint)\n } catch {\n // modulepreload is a hint, not critical\n }\n}\n\n/**\n * Prefetch a route's JS chunk by injecting `<link rel=\"prefetch\">` into the\n * document head. Deduplicates — calling with the same href twice is a no-op.\n *\n * @example\n * prefetchRoute('/about')\n * prefetchRoute('/dashboard')\n */\nexport function prefetchRoute(href: string): void {\n doPrefetch(href)\n}\n\n/**\n * Composable that provides all link behavior — navigation, prefetching,\n * active state, and viewport observation.\n *\n * Use this for full control when `createLink` is too opinionated.\n *\n * @example\n * function MyLink(props: LinkProps) {\n * const link = useLink(props)\n * return (\n * <button ref={link.ref} class={link.classes()} onClick={link.handleClick}>\n * {props.children}\n * </button>\n * )\n * }\n */\nexport function useLink(props: LinkProps): UseLinkReturn {\n const router = useRouter()\n const elementRef = createRef<HTMLAnchorElement>()\n const strategy = props.prefetch ?? 'hover'\n\n function handleClick(e: MouseEvent) {\n // Call user's onClick first — they may call e.preventDefault()\n if (props.onClick) {\n ;(props.onClick as (e: MouseEvent) => void)(e)\n }\n\n if (\n e.defaultPrevented ||\n e.button !== 0 ||\n e.metaKey ||\n e.ctrlKey ||\n e.shiftKey ||\n e.altKey ||\n props.external\n ) {\n return\n }\n e.preventDefault()\n router.push(props.href)\n }\n\n function handleMouseEnter() {\n if (strategy === 'hover') {\n doPrefetch(props.href)\n }\n }\n\n function handleTouchStart() {\n if (strategy === 'hover' || strategy === 'viewport') {\n doPrefetch(props.href)\n }\n }\n\n if (strategy === 'viewport') {\n useIntersectionObserver(\n () => elementRef.current ?? undefined,\n () => doPrefetch(props.href),\n )\n }\n\n const isActive = () => {\n const currentPath = router.currentRoute()?.path\n if (!currentPath || !props.href) return false\n if (props.href === '/') return currentPath === '/'\n return currentPath.startsWith(props.href)\n }\n\n const isExactActive = () => {\n const currentPath = router.currentRoute()?.path\n if (!currentPath) return false\n return currentPath === props.href\n }\n\n const classes = () => {\n const cls: string[] = []\n if (props.class) cls.push(props.class)\n if (props.activeClass && isActive()) cls.push(props.activeClass)\n if (props.exactActiveClass && isExactActive()) cls.push(props.exactActiveClass)\n return cls.join(' ')\n }\n\n return {\n ref: elementRef,\n handleClick,\n handleMouseEnter,\n handleTouchStart,\n isActive,\n isExactActive,\n classes,\n }\n}\n\n/**\n * Higher-order component that wraps any component with link behavior.\n *\n * The wrapped component receives {@link LinkRenderProps} with all handlers,\n * active state, and accessibility attributes pre-wired.\n *\n * @example\n * // Custom button link\n * const ButtonLink = createLink((props) => (\n * <button\n * ref={props.ref}\n * class={props.class}\n * onClick={props.onClick}\n * onMouseEnter={props.onMouseEnter}\n * >\n * {props.children}\n * </button>\n * ))\n *\n * // Custom styled component\n * const CardLink = createLink((props) => (\n * <div\n * ref={props.ref}\n * class={`card ${props.isActive() ? \"card--active\" : \"\"}`}\n * onClick={props.onClick}\n * onMouseEnter={props.onMouseEnter}\n * >\n * {props.children}\n * </div>\n * ))\n *\n * // Usage\n * <ButtonLink href=\"/about\">About</ButtonLink>\n * <CardLink href=\"/posts\" prefetch=\"viewport\">Posts</CardLink>\n */\nexport function createLink(Component: (props: LinkRenderProps) => any): (props: LinkProps) => any {\n return function WrappedLink(props: LinkProps) {\n const link = useLink(props)\n\n return (\n <Component\n href={props.href}\n ref={link.ref}\n onClick={link.handleClick}\n onMouseEnter={link.handleMouseEnter}\n onTouchStart={link.handleTouchStart}\n isActive={link.isActive}\n isExactActive={link.isExactActive}\n class={link.classes}\n {...(props.style ? { style: props.style } : {})}\n {...(props.external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n {...(props['aria-label'] ? { 'aria-label': props['aria-label'] } : {})}\n children={props.children}\n />\n )\n }\n}\n\n/**\n * Default navigation link built on an `<a>` tag.\n *\n * @example\n * <Link href=\"/about\" prefetch=\"viewport\">About</Link>\n * <Link href=\"/posts\" activeClass=\"nav-active\">Posts</Link>\n */\nexport const Link = createLink((props: LinkRenderProps) => (\n <a\n ref={props.ref}\n href={props.href}\n {...(props.class ? { class: props.class } : {})}\n {...(props.style ? { style: props.style } : {})}\n {...(props.target ? { target: props.target } : {})}\n {...(props.rel ? { rel: props.rel } : {})}\n {...(props['aria-label'] ? { 'aria-label': props['aria-label'] } : {})}\n {...(props.isExactActive() ? { 'aria-current': 'page' as const } : {})}\n onClick={props.onClick}\n onMouseEnter={props.onMouseEnter}\n onTouchStart={props.onTouchStart}\n >\n {props.children}\n </a>\n))\n","import type { VNodeChild } from '@pyreon/core'\nimport { createRef, onMount, onUnmount } from '@pyreon/core'\nimport { useIntersectionObserver } from './utils/use-intersection-observer'\n\n// ─── Script optimization component ─────────────────────────────────────────\n//\n// <Script> provides optimized third-party script loading:\n// - Defer loading until after hydration\n// - Load on idle (requestIdleCallback)\n// - Load on interaction (click, scroll, etc.)\n// - Load on viewport entry\n// - Worker offloading for analytics scripts\n\nexport interface ScriptProps {\n /** Script source URL. */\n src: string\n /** Loading strategy. Default: \"afterHydration\" */\n strategy?: ScriptStrategy\n /** Inline script content (alternative to src). */\n children?: string\n /** Script id for deduplication. */\n id?: string\n /** Async attribute. Default: true */\n async?: boolean\n /** onLoad callback. */\n onLoad?: () => void\n /** onError callback. */\n onError?: (error: Error) => void\n}\n\nexport type ScriptStrategy =\n | 'beforeHydration'\n | 'afterHydration'\n | 'onIdle'\n | 'onInteraction'\n | 'onViewport'\n\n/**\n * Optimized script loading component.\n *\n * @example\n * // Load analytics after page is interactive\n * <Script src=\"https://analytics.example.com/script.js\" strategy=\"onIdle\" />\n *\n * // Load chat widget when user scrolls\n * <Script src=\"/chat-widget.js\" strategy=\"onViewport\" />\n *\n * // Inline script with deferred execution\n * <Script strategy=\"afterHydration\">\n * {`console.log(\"App hydrated!\")`}\n * </Script>\n */\nexport function Script(props: ScriptProps): VNodeChild {\n function loadScript() {\n // Deduplication\n if (props.id && document.getElementById(props.id)) return\n\n const script = document.createElement('script')\n if (props.src) script.src = props.src\n if (props.id) script.id = props.id\n script.async = props.async !== false\n\n if (props.onLoad) script.onload = props.onLoad\n if (props.onError) {\n script.onerror = () => props.onError?.(new Error(`Failed to load: ${props.src}`))\n }\n\n if (props.children && !props.src) {\n script.textContent = props.children\n }\n\n document.head.appendChild(script)\n }\n\n onMount(() => {\n const strategy = props.strategy ?? 'afterHydration'\n\n switch (strategy) {\n case 'beforeHydration':\n // Already in HTML — do nothing\n break\n\n case 'afterHydration':\n // Load immediately after mount (hydration is complete)\n loadScript()\n break\n\n case 'onIdle':\n if ('requestIdleCallback' in window) {\n requestIdleCallback(() => loadScript(), { timeout: 5000 })\n } else {\n setTimeout(loadScript, 200)\n }\n break\n\n case 'onInteraction': {\n const events = ['click', 'scroll', 'keydown', 'touchstart']\n function handler() {\n for (const e of events) document.removeEventListener(e, handler)\n loadScript()\n }\n for (const e of events) {\n document.addEventListener(e, handler, { once: true, passive: true })\n }\n onUnmount(() => {\n for (const e of events) document.removeEventListener(e, handler)\n })\n break\n }\n\n case 'onViewport':\n // Handled below via useIntersectionObserver on the sentinel element\n break\n }\n return undefined\n })\n\n const sentinelRef = createRef<HTMLElement>()\n const strategy = props.strategy ?? 'afterHydration'\n\n if (strategy === 'onViewport') {\n useIntersectionObserver(\n () => sentinelRef.current ?? undefined,\n () => loadScript(),\n )\n }\n\n if (strategy === 'onViewport') {\n return <div ref={sentinelRef} style=\"width:0;height:0;overflow:hidden\" />\n }\n\n return null\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Cache control middleware ───────────────────────────────────────────────\n//\n// Smart caching middleware that sets appropriate cache headers based on\n// asset type, URL patterns, and build hashes.\n//\n// Strategies:\n// - Immutable: hashed assets (JS/CSS bundles) — cached forever\n// - Static: images, fonts, media — long cache with revalidation\n// - Dynamic: HTML pages — short or no cache, stale-while-revalidate\n// - API: JSON responses — no cache by default\n\nexport interface CacheConfig {\n /** Cache duration for immutable hashed assets (seconds). Default: 31536000 (1 year) */\n immutable?: number\n /** Cache duration for static assets like images/fonts (seconds). Default: 86400 (1 day) */\n static?: number\n /** Cache duration for pages (seconds). Default: 0 (no cache) */\n pages?: number\n /** Stale-while-revalidate window for pages (seconds). Default: 60 */\n staleWhileRevalidate?: number\n /** Custom rules by URL pattern. */\n rules?: CacheRule[]\n}\n\nexport interface CacheRule {\n /** URL pattern to match (glob-style). e.g. \"/api/*\" */\n match: string\n /** Cache-Control header value. */\n control: string\n}\n\nconst HASHED_ASSET = /\\.[a-f0-9]{8,}\\.\\w+$/\nconst STATIC_EXT = /\\.(png|jpe?g|gif|svg|webp|avif|ico|woff2?|ttf|otf|eot|mp4|webm|ogg|mp3|wav)$/i\nconst SCRIPT_EXT = /\\.(js|css|mjs)$/i\n\n/** @internal Exported for testing */\nexport function matchGlob(pattern: string, path: string): boolean {\n // Escape regex special chars, then convert glob wildcards\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n const regex = escaped.replace(/\\*/g, '.*').replace(/\\?/g, '.')\n return new RegExp(`^${regex}$`).test(path)\n}\n\nfunction resolveControl(\n path: string,\n immutableDuration: number,\n staticDuration: number,\n pageDuration: number,\n swr: number,\n): string {\n if (HASHED_ASSET.test(path)) {\n return `public, max-age=${immutableDuration}, immutable`\n }\n if (SCRIPT_EXT.test(path)) {\n return `public, max-age=3600, stale-while-revalidate=${swr}`\n }\n if (STATIC_EXT.test(path)) {\n return `public, max-age=${staticDuration}, stale-while-revalidate=${swr}`\n }\n if (pageDuration > 0) {\n return `public, max-age=${pageDuration}, stale-while-revalidate=${swr}`\n }\n return 'no-cache'\n}\n\n/**\n * Cache control middleware for Zero.\n * Sets Cache-Control headers on the response based on asset type.\n *\n * @example\n * import { cacheMiddleware } from \"@pyreon/zero/cache\"\n *\n * export default createHandler({\n * routes,\n * middleware: [\n * cacheMiddleware({\n * pages: 60,\n * staleWhileRevalidate: 300,\n * rules: [\n * { match: \"/api/*\", control: \"no-store\" },\n * ],\n * }),\n * ],\n * })\n */\nexport function cacheMiddleware(config: CacheConfig = {}): Middleware {\n const immutableDuration = config.immutable ?? 31536000\n const staticDuration = config.static ?? 86400\n const pageDuration = config.pages ?? 0\n const swr = config.staleWhileRevalidate ?? 60\n const rules = config.rules ?? []\n\n return (ctx: MiddlewareContext) => {\n const path = ctx.url.pathname\n\n for (const rule of rules) {\n if (matchGlob(rule.match, path)) {\n ctx.headers.set('Cache-Control', rule.control)\n return\n }\n }\n\n const control = resolveControl(path, immutableDuration, staticDuration, pageDuration, swr)\n ctx.headers.set('Cache-Control', control)\n }\n}\n\n/**\n * Security headers middleware.\n * Adds common security headers to all responses.\n */\nexport function securityHeaders(): Middleware {\n return (ctx: MiddlewareContext) => {\n ctx.headers.set('X-Content-Type-Options', 'nosniff')\n ctx.headers.set('X-Frame-Options', 'DENY')\n ctx.headers.set('X-XSS-Protection', '1; mode=block')\n ctx.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')\n ctx.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')\n }\n}\n\n/**\n * Compression detection middleware.\n * Sets Vary: Accept-Encoding header so caches can serve compressed variants.\n * Actual compression is handled by the runtime (Bun/Node) or reverse proxy.\n */\nexport function varyEncoding(): Middleware {\n return (ctx: MiddlewareContext) => {\n const existing = ctx.headers.get('Vary')\n if (!existing?.includes('Accept-Encoding')) {\n ctx.headers.set('Vary', existing ? `${existing}, Accept-Encoding` : 'Accept-Encoding')\n }\n }\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Middleware composition ─────────────────────────────────────────────────\n//\n// Chains multiple middleware functions into a single middleware.\n// Each middleware runs in order. If any returns a Response, the chain\n// short-circuits and that Response is returned. If all return void,\n// the composed middleware returns void (continues to rendering).\n\n/**\n * Compose multiple middleware into a single middleware function.\n * Middleware runs sequentially — if any returns a Response, the chain stops.\n *\n * @example\n * import { compose } from \"@pyreon/zero/middleware\"\n * import { corsMiddleware } from \"@pyreon/zero/cors\"\n * import { rateLimitMiddleware } from \"@pyreon/zero/rate-limit\"\n *\n * const combined = compose(\n * corsMiddleware({ origin: \"*\" }),\n * rateLimitMiddleware({ max: 100 }),\n * cacheMiddleware(),\n * )\n */\nexport function compose(...middlewares: Middleware[]): Middleware {\n return async (ctx: MiddlewareContext) => {\n for (const mw of middlewares) {\n const result = await mw(ctx)\n if (result instanceof Response) return result\n }\n }\n}\n\n// ─── Shared request context ─────────────────────────────────────────────────\n//\n// Lightweight context bag attached to MiddlewareContext.locals so middleware\n// can communicate without coupling. Uses a namespaced key to avoid collisions\n// with user-defined locals.\n\nconst ZERO_CTX_KEY = '__zeroCtx'\n\n/**\n * Get the shared Zero context from a middleware context.\n * Creates one if it doesn't exist. Middleware can use this to\n * pass data to downstream middleware without polluting `ctx.locals`.\n *\n * @example\n * const authMiddleware: Middleware = (ctx) => {\n * const zctx = getContext(ctx)\n * zctx.userId = \"user_123\"\n * }\n *\n * const loggingMiddleware: Middleware = (ctx) => {\n * const zctx = getContext(ctx)\n * console.log(\"User:\", zctx.userId)\n * }\n */\nexport function getContext(ctx: MiddlewareContext): Record<string, unknown> {\n let zctx = ctx.locals[ZERO_CTX_KEY] as Record<string, unknown> | undefined\n if (!zctx) {\n zctx = {}\n ctx.locals[ZERO_CTX_KEY] = zctx\n }\n return zctx\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\n\n// ─── Font optimization ──────────────────────────────────────────────────────\n//\n// Zero provides automatic font optimization:\n// - Downloads and self-hosts Google Fonts at build time (privacy + performance)\n// - Falls back to CDN link in dev mode (for fast dev startup)\n// - Injects preconnect/preload hints into the HTML\n// - Sets font-display: swap to prevent FOIT (Flash of Invisible Text)\n// - Generates optimized @font-face declarations\n// - Size-adjusted fallback fonts to reduce CLS\n\nexport interface FontConfig {\n /**\n * Google Fonts families.\n *\n * Accepts both string shorthand and structured objects:\n * - String: \"Inter:wght@400;500;700\" or \"Inter:wght@100..900\"\n * - Object: { family: \"Inter\", weights: [400, 500, 700] }\n * - Variable: { family: \"Inter\", variable: true, weightRange: [100, 900] }\n */\n google?: GoogleFontInput[]\n /** Local font files. */\n local?: LocalFont[]\n /** Default font-display strategy. Default: \"swap\" */\n display?: FontDisplay\n /** Preload critical fonts. Default: true */\n preload?: boolean\n /** Self-host Google Fonts at build time. Default: true */\n selfHost?: boolean\n /** Fallback font metrics for reducing CLS. */\n fallbacks?: Record<string, FallbackMetrics>\n}\n\n/** Static Google Font config. */\nexport interface GoogleFontStatic {\n family: string\n weights: number[]\n italic?: boolean\n variable?: false\n}\n\n/** Variable Google Font config. */\nexport interface GoogleFontVariable {\n family: string\n /** Weight range as [min, max] tuple. e.g. [100, 900] */\n weightRange: [number, number]\n italic?: boolean\n variable: true\n}\n\n/** Google font input: structured object or string shorthand. */\nexport type GoogleFontInput = GoogleFontStatic | GoogleFontVariable | string\n\nexport interface LocalFont {\n family: string\n src: string\n /** Single weight (400) or variable range (\"100 900\"). */\n weight?: number | `${number} ${number}`\n style?: 'normal' | 'italic'\n display?: FontDisplay\n}\n\nexport type FontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional'\n\n/** Metrics for generating size-adjusted fallback fonts to reduce CLS. */\nexport interface FallbackMetrics {\n /** The fallback font to adjust. e.g. \"Arial\", \"Georgia\" */\n fallback: string\n /** Size adjustment factor. e.g. 1.05 */\n sizeAdjust?: number\n /** Ascent override percentage. e.g. 90 */\n ascentOverride?: number\n /** Descent override percentage. e.g. 22 */\n descentOverride?: number\n /** Line gap override percentage. e.g. 0 */\n lineGapOverride?: number\n}\n\ninterface ResolvedFontBase {\n family: string\n italic: boolean\n}\n\ninterface StaticFont extends ResolvedFontBase {\n variable: false\n weights: number[]\n}\n\ninterface VariableFont extends ResolvedFontBase {\n variable: true\n weightRange: [number, number]\n}\n\ntype ResolvedFont = StaticFont | VariableFont\n\n/**\n * Normalize a GoogleFontInput (string or object) into a ResolvedFont.\n */\nexport function resolveGoogleFont(input: GoogleFontInput): ResolvedFont {\n if (typeof input === 'string') {\n return parseGoogleFamily(input)\n }\n\n if (input.variable) {\n return {\n family: input.family,\n italic: input.italic ?? false,\n variable: true,\n weightRange: input.weightRange,\n }\n }\n\n return {\n family: input.family,\n italic: input.italic ?? false,\n variable: false,\n weights: input.weights,\n }\n}\n\n/**\n * Parse Google Fonts family string shorthand.\n *\n * Static weights: \"Inter:wght@400;500;700\"\n * Variable range: \"Inter:wght@100..900\"\n * Variable with italic: \"Inter:ital,wght@100..900\"\n */\nexport function parseGoogleFamily(input: string): ResolvedFont {\n const parts = input.split(':')\n const family = (parts[0] ?? '').trim()\n const spec = parts[1]\n let italic = false\n\n if (spec) {\n italic = spec.includes('ital')\n\n // Variable font range syntax: wght@100..900\n const rangeMatch = spec.match(/wght@(\\d+)\\.\\.(\\d+)/)\n if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {\n return {\n family,\n italic,\n variable: true,\n weightRange: [Number(rangeMatch[1]), Number(rangeMatch[2])],\n }\n }\n\n // Static weights — two formats:\n // Simple: \"wght@400;500;700\"\n // Tuples: \"ital,wght@0,300;0,500;1,300;1,500\" (ital_flag,weight pairs)\n const afterAt = spec.split('@')[1]\n if (afterAt) {\n const entries = afterAt.split(';').filter(Boolean)\n const weights = new Set<number>()\n\n for (const entry of entries) {\n if (entry.includes(',')) {\n // Tuple format: \"0,300\" or \"1,500\" — last value is the weight\n const parts = entry.split(',')\n const weight = Number(parts[parts.length - 1])\n if (weight > 0) weights.add(weight)\n // Detect italic from tuple: \"1,xxx\" means italic\n if (parts[0] === '1') italic = true\n } else if (entry.includes('..')) {\n // Variable range already handled above — skip\n } else {\n // Simple weight: \"400\"\n const weight = Number(entry)\n if (weight > 0) weights.add(weight)\n }\n }\n\n if (weights.size > 0) {\n return {\n family,\n italic,\n variable: false,\n weights: [...weights].sort((a, b) => a - b),\n }\n }\n }\n }\n\n return { family, italic, variable: false, weights: [400] }\n}\n\n/**\n * Generate a Google Fonts CSS URL.\n */\nexport function googleFontsUrl(families: ResolvedFont[], display: FontDisplay = 'swap'): string {\n const params = families\n .map((f) => {\n const axes = f.italic ? 'ital,wght' : 'wght'\n const name = f.family.replace(/ /g, '+')\n\n if (f.variable) {\n const range = `${f.weightRange[0]}..${f.weightRange[1]}`\n const value = f.italic ? `0,${range};1,${range}` : range\n return `family=${name}:${axes}@${value}`\n }\n\n const values = f.weights.map((w) => (f.italic ? `0,${w};1,${w}` : String(w))).join(';')\n return `family=${name}:${axes}@${values}`\n })\n .join('&')\n\n return `https://fonts.googleapis.com/css2?${params}&display=${display}`\n}\n\n/**\n * Generate @font-face CSS for local fonts.\n */\nfunction localFontFaces(fonts: LocalFont[], display: FontDisplay): string {\n return fonts\n .map(\n (f) => `@font-face {\n font-family: \"${f.family}\";\n src: url(\"${f.src}\");\n font-weight: ${f.weight ?? '400'};\n font-style: ${f.style ?? 'normal'};\n font-display: ${f.display ?? display};\n}`,\n )\n .join('\\n\\n')\n}\n\n/**\n * Generate size-adjusted fallback @font-face declarations to reduce CLS.\n */\nfunction fallbackFontFaces(fallbacks: Record<string, FallbackMetrics>): string {\n return Object.entries(fallbacks)\n .map(([family, metrics]) => {\n const overrides: string[] = []\n if (metrics.sizeAdjust != null) overrides.push(` size-adjust: ${metrics.sizeAdjust * 100}%;`)\n if (metrics.ascentOverride != null)\n overrides.push(` ascent-override: ${metrics.ascentOverride}%;`)\n if (metrics.descentOverride != null)\n overrides.push(` descent-override: ${metrics.descentOverride}%;`)\n if (metrics.lineGapOverride != null)\n overrides.push(` line-gap-override: ${metrics.lineGapOverride}%;`)\n\n return `@font-face {\n font-family: \"${family} Fallback\";\n src: local(\"${metrics.fallback}\");\n${overrides.join('\\n')}\n}`\n })\n .join('\\n\\n')\n}\n\n/**\n * Generate preload link tags for critical font files.\n */\nfunction preloadTags(fonts: LocalFont[]): string {\n return fonts\n .map((f) => {\n const ext = f.src.split('.').pop()\n const type =\n ext === 'woff2'\n ? 'font/woff2'\n : ext === 'woff'\n ? 'font/woff'\n : ext === 'ttf'\n ? 'font/ttf'\n : 'font/otf'\n return `<link rel=\"preload\" href=\"${f.src}\" as=\"font\" type=\"${type}\" crossorigin>`\n })\n .join('\\n')\n}\n\n/**\n * Download Google Fonts CSS with woff2 user agent.\n */\nasync function downloadGoogleFontsCSS(url: string): Promise<string> {\n const response = await fetch(url, {\n headers: {\n 'User-Agent':\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\n },\n })\n if (!response.ok) {\n throw new Error(`Failed to fetch Google Fonts CSS: ${response.status}`)\n }\n return response.text()\n}\n\n/**\n * Download a font file.\n */\nasync function downloadFontFile(url: string): Promise<Buffer> {\n const response = await fetch(url)\n if (!response.ok) throw new Error(`Failed to download font: ${url}`)\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n}\n\n/**\n * Extract font file URLs from Google Fonts CSS.\n */\nfunction extractFontUrls(css: string): string[] {\n const urls: string[] = []\n const regex = /url\\((https:\\/\\/fonts\\.gstatic\\.com\\/[^)]+)\\)/g\n for (const match of css.matchAll(regex)) {\n if (match[1]) urls.push(match[1])\n }\n return urls\n}\n\n/**\n * Self-host Google Fonts: download CSS + font files, rewrite URLs to local paths.\n */\nasync function selfHostFonts(\n cssUrl: string,\n fontsSubDir: string,\n root: string,\n): Promise<{\n css: string\n fontFiles: Array<{ name: string; content: Buffer }>\n}> {\n // Cache fonts between builds to avoid re-downloading (~6s penalty)\n const cacheDir = join(root, 'node_modules', '.cache', 'zero-fonts')\n const cacheKey = Buffer.from(cssUrl).toString('base64url')\n const cachePath = join(cacheDir, `${cacheKey}.json`)\n\n try {\n const cached = JSON.parse(await readFile(cachePath, 'utf-8'))\n if (cached.css && cached.fontFiles) {\n return {\n css: cached.css,\n fontFiles: cached.fontFiles.map((f: any) => ({\n name: f.name,\n content: Buffer.from(f.content, 'base64'),\n })),\n }\n }\n } catch {\n // No cache — download fresh\n }\n\n const css = await downloadGoogleFontsCSS(cssUrl)\n const fontUrls = extractFontUrls(css)\n const fontFiles: Array<{ name: string; content: Buffer }> = []\n\n let rewrittenCss = css\n\n for (const url of fontUrls) {\n const urlParts = url.split('/')\n const fileName = urlParts.at(-1)?.split('?')[0] ?? 'font'\n const content = await downloadFontFile(url)\n\n fontFiles.push({ name: fileName, content })\n rewrittenCss = rewrittenCss.replace(url, `/${fontsSubDir}/${fileName}`)\n }\n\n // Write cache\n try {\n await mkdir(cacheDir, { recursive: true })\n await writeFile(cachePath, JSON.stringify({\n css: rewrittenCss,\n fontFiles: fontFiles.map((f) => ({ name: f.name, content: f.content.toString('base64') })),\n }))\n } catch {\n // Cache write failure is non-fatal\n }\n\n return { css: rewrittenCss, fontFiles }\n}\n\n/**\n * Zero font optimization Vite plugin.\n *\n * Dev mode: injects Google Fonts CDN link for fast startup.\n * Build mode: downloads and self-hosts fonts for maximum performance + privacy.\n *\n * @example\n * import { fontPlugin } from \"@pyreon/zero/font\"\n *\n * export default {\n * plugins: [\n * pyreon(),\n * zero(),\n * fontPlugin({\n * google: [\"Inter:wght@400;500;600;700\", \"JetBrains Mono:wght@400\"],\n * fallbacks: {\n * \"Inter\": { fallback: \"Arial\", sizeAdjust: 1.07, ascentOverride: 90 },\n * },\n * }),\n * ],\n * }\n */\nexport function fontPlugin(config: FontConfig = {}): Plugin {\n const display = config.display ?? 'swap'\n const shouldPreload = config.preload !== false\n const shouldSelfHost = config.selfHost !== false\n const googleFamilies = (config.google ?? []).map(resolveGoogleFont)\n\n let isBuild = false\n let root = ''\n let selfHostedCSS = ''\n let selfHostedFontFiles: Array<{ name: string; content: Buffer }> = []\n\n return {\n name: 'pyreon-zero-fonts',\n\n configResolved(resolvedConfig) {\n isBuild = resolvedConfig.command === 'build'\n root = resolvedConfig.root\n },\n\n async buildStart() {\n if (isBuild && shouldSelfHost && googleFamilies.length > 0) {\n const cssUrl = googleFontsUrl(googleFamilies, display)\n try {\n const result = await selfHostFonts(cssUrl, 'assets/fonts', root)\n selfHostedCSS = result.css\n selfHostedFontFiles = result.fontFiles\n } catch {\n // Self-hosting failed — fall back to CDN link\n }\n }\n },\n\n generateBundle() {\n // Emit self-hosted font files as assets\n for (const file of selfHostedFontFiles) {\n this.emitFile({\n type: 'asset',\n fileName: `assets/fonts/${file.name}`,\n source: file.content,\n })\n }\n },\n\n transformIndexHtml(html) {\n const tags: string[] = []\n\n collectGoogleFontTags(tags, {\n isBuild,\n selfHostedCSS,\n selfHostedFontFiles,\n shouldPreload,\n googleFamilies,\n display,\n })\n collectLocalFontTags(tags, config, shouldPreload, display)\n\n if (tags.length === 0) return html\n return html.replace('</head>', `${tags.join('\\n')}\\n</head>`)\n },\n }\n}\n\nfunction collectGoogleFontTags(\n tags: string[],\n opts: {\n isBuild: boolean\n selfHostedCSS: string\n selfHostedFontFiles: Array<{ name: string; content: Buffer }>\n shouldPreload: boolean\n googleFamilies: ResolvedFont[]\n display: FontDisplay\n },\n) {\n if (opts.isBuild && opts.selfHostedCSS) {\n tags.push(`<style>${opts.selfHostedCSS}</style>`)\n if (opts.shouldPreload) {\n for (const file of opts.selfHostedFontFiles.slice(0, opts.googleFamilies.length)) {\n const ext = file.name.split('.').pop()\n const type = ext === 'woff2' ? 'font/woff2' : 'font/woff'\n tags.push(\n `<link rel=\"preload\" href=\"/assets/fonts/${file.name}\" as=\"font\" type=\"${type}\" crossorigin>`,\n )\n }\n }\n } else if (opts.googleFamilies.length > 0) {\n const cssUrl = googleFontsUrl(opts.googleFamilies, opts.display)\n tags.push(`<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">`)\n tags.push(`<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>`)\n tags.push(`<link rel=\"stylesheet\" href=\"${cssUrl}\">`)\n }\n}\n\nfunction collectLocalFontTags(\n tags: string[],\n config: FontConfig,\n shouldPreload: boolean,\n display: FontDisplay,\n) {\n if (shouldPreload && config.local?.length) {\n tags.push(preloadTags(config.local))\n }\n if (config.local?.length) {\n tags.push(`<style>${localFontFaces(config.local, display)}</style>`)\n }\n if (config.fallbacks && Object.keys(config.fallbacks).length > 0) {\n tags.push(`<style>${fallbackFontFaces(config.fallbacks)}</style>`)\n }\n}\n\n/**\n * Generate CSS variables for font families.\n */\nexport function fontVariables(families: Record<string, string>): string {\n const vars = Object.entries(families)\n .map(([key, value]) => ` --font-${key}: ${value};`)\n .join('\\n')\n return `:root {\\n${vars}\\n}`\n}\n","import { existsSync } from 'node:fs'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { basename, extname, join } from 'node:path'\nimport type { Plugin } from 'vite'\n\nlet sharpWarned = false\nfunction warnSharpMissing() {\n if (sharpWarned) return\n sharpWarned = true\n // oxlint-disable-next-line no-console\n console.warn(\n '\\n[zero:image] sharp not installed — images will not be optimized. Install for full support: bun add -D sharp\\n',\n )\n}\n\n// ─── Image processing Vite plugin ──────────────────────────────────────────\n//\n// Processes images at build time:\n// - Generates multiple sizes for responsive srcset\n// - Converts to modern formats (WebP, AVIF)\n// - Creates tiny blur placeholders (base64 inline)\n// - Outputs optimized images to the build directory\n//\n// Usage in code:\n// import heroImg from \"./hero.jpg?optimize\"\n// // → { src, srcset, width, height, placeholder }\n//\n// Or use the component helper:\n// import { Image } from \"@pyreon/zero/image\"\n// <Image src=\"/hero.jpg\" width={1920} height={1080} optimize />\n\nexport interface ImagePluginConfig {\n /** Output directory for processed images. Default: \"assets/img\" */\n outDir?: string\n /** Default widths for responsive images. Default: [640, 1024, 1920] */\n widths?: number[]\n /** Output formats. Default: [\"webp\"] */\n formats?: ImageFormat[]\n /** Quality for lossy formats (1-100). Default: 80 */\n quality?: number\n /** Blur placeholder size in px. Default: 16 */\n placeholderSize?: number\n /** File patterns to process. Default: /\\.(jpe?g|png|webp|avif)$/i */\n include?: RegExp\n}\n\nexport type ImageFormat = 'webp' | 'avif' | 'jpeg' | 'png'\n\n/** Per-format source set for <picture> <source> elements. */\nexport interface FormatSource {\n /** MIME type. e.g. \"image/webp\", \"image/avif\" */\n type: string\n /** srcset string for this format. e.g. \"/img-640.webp 640w, /img-1920.webp 1920w\" */\n srcset: string\n}\n\nexport interface ProcessedImage {\n /** Fallback source path (last format, largest width). */\n src: string\n /** Fallback srcset string (last format). */\n srcset: string\n /** Intrinsic width. */\n width: number\n /** Intrinsic height. */\n height: number\n /** Base64 blur placeholder data URI. */\n placeholder: string\n /** Per-format source sets for <picture> element. Ordered by priority (best format first). */\n formats: FormatSource[]\n /** Flat list of all sources. */\n sources: Array<{ src: string; width: number; format: string }>\n}\n\nconst IMAGE_EXT_RE = /\\.(jpe?g|png|webp|avif)$/i\n\n/**\n * Zero image processing Vite plugin.\n *\n * Transforms image imports with query params into optimized responsive images:\n *\n * @example\n * // vite.config.ts\n * import { imagePlugin } from \"@pyreon/zero/image-plugin\"\n *\n * export default {\n * plugins: [\n * pyreon(),\n * zero(),\n * imagePlugin({ widths: [480, 960, 1440], quality: 85 }),\n * ],\n * }\n *\n * @example\n * // In a component — import with ?optimize\n * import hero from \"./images/hero.jpg?optimize\"\n * // hero = { src, srcset, width, height, placeholder }\n *\n * <Image {...hero} alt=\"Hero\" priority />\n */\nexport function imagePlugin(config: ImagePluginConfig = {}): Plugin {\n const defaultWidths = config.widths ?? [640, 1024, 1920]\n const defaultFormats = config.formats ?? ['webp']\n const quality = config.quality ?? 80\n const placeholderSize = config.placeholderSize ?? 16\n const outSubDir = config.outDir ?? 'assets/img'\n const include = config.include ?? IMAGE_EXT_RE\n\n let root = ''\n let outDir = ''\n let isBuild = false\n\n return {\n name: 'pyreon-zero-images',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n root = resolvedConfig.root\n outDir = resolvedConfig.build.outDir\n isBuild = resolvedConfig.command === 'build'\n },\n\n async resolveId(id) {\n // Handle ?optimize query on image imports\n if (id.includes('?optimize') && include.test(id.split('?')[0]!)) {\n return `\\0virtual:zero-image:${id}`\n }\n return null\n },\n\n async load(id) {\n if (!id.startsWith('\\0virtual:zero-image:')) return null\n\n const rawPath = id.replace('\\0virtual:zero-image:', '').split('?')[0] ?? id\n const absPath = rawPath.startsWith('/') ? join(root, 'public', rawPath) : rawPath\n\n if (!isBuild) {\n const result = await loadDevImage(absPath, rawPath, placeholderSize)\n return `export default ${JSON.stringify(result)}`\n }\n\n const processed = await processImage(absPath, {\n widths: defaultWidths,\n formats: defaultFormats,\n quality,\n placeholderSize,\n outSubDir,\n outDir: join(root, outDir),\n })\n\n await emitProcessedSources(processed, outSubDir, this)\n rebuildFormatSrcsets(processed, absPath)\n\n return `export default ${JSON.stringify(processed)}`\n },\n }\n}\n\nasync function loadDevImage(\n absPath: string,\n rawPath: string,\n placeholderSize: number,\n): Promise<ProcessedImage> {\n const metadata = await getImageMetadata(absPath)\n const publicPath = rawPath.startsWith('/') ? rawPath : `/@fs/${absPath}`\n\n return {\n src: publicPath,\n srcset: '',\n width: metadata.width,\n height: metadata.height,\n placeholder: await generateBlurPlaceholder(absPath, placeholderSize),\n formats: [],\n sources: [{ src: publicPath, width: metadata.width, format: 'original' }],\n }\n}\n\nasync function emitProcessedSources(\n processed: ProcessedImage,\n outSubDir: string,\n ctx: {\n emitFile: (f: { type: 'asset'; fileName: string; source: Uint8Array }) => void\n },\n) {\n for (const source of processed.sources) {\n const fileName = join(outSubDir, basename(source.src))\n const content = await readFile(source.src)\n ctx.emitFile({ type: 'asset', fileName, source: content })\n source.src = `/${fileName}`\n }\n}\n\nfunction rebuildFormatSrcsets(processed: ProcessedImage, fallbackPath: string) {\n const formatGroups = new Map<string, string[]>()\n for (const s of processed.sources) {\n let group = formatGroups.get(s.format)\n if (!group) {\n group = []\n formatGroups.set(s.format, group)\n }\n group.push(`${s.src} ${s.width}w`)\n }\n processed.formats = [...formatGroups.entries()].map(([fmt, entries]) => ({\n type: `image/${fmt}`,\n srcset: entries.join(', '),\n }))\n\n const lastFormat = processed.formats.at(-1)\n processed.srcset = lastFormat?.srcset ?? ''\n processed.src = processed.sources.at(-1)?.src ?? fallbackPath\n}\n\n// ─── Image processing utilities ─────────────────────────────────────────────\n\ninterface ProcessOptions {\n widths: number[]\n formats: ImageFormat[]\n quality: number\n placeholderSize: number\n outSubDir: string\n outDir: string\n}\n\nasync function processImage(absPath: string, opts: ProcessOptions): Promise<ProcessedImage> {\n const metadata = await getImageMetadata(absPath)\n const ext = extname(absPath)\n const name = basename(absPath, ext)\n const sources: Array<{ src: string; width: number; format: string }> = []\n\n // Ensure output directory exists\n const processedDir = join(opts.outDir, opts.outSubDir)\n if (!existsSync(processedDir)) {\n await mkdir(processedDir, { recursive: true })\n }\n\n // Generate resized variants — iterate formats first so sources are grouped by format\n for (const format of opts.formats) {\n for (const targetWidth of opts.widths) {\n // Don't upscale\n const width = Math.min(targetWidth, metadata.width)\n const outName = `${name}-${width}.${format}`\n const outPath = join(processedDir, outName)\n\n await resizeImage(absPath, outPath, width, format, opts.quality)\n sources.push({ src: outPath, width, format })\n }\n }\n\n // Build per-format source sets for <picture>\n const formatGroups = new Map<string, Array<{ src: string; width: number }>>()\n for (const s of sources) {\n let group = formatGroups.get(s.format)\n if (!group) {\n group = []\n formatGroups.set(s.format, group)\n }\n group.push({ src: s.src, width: s.width })\n }\n\n const formats: FormatSource[] = [...formatGroups.entries()].map(([fmt, group]) => ({\n type: `image/${fmt === 'jpeg' ? 'jpeg' : fmt}`,\n srcset: group.map((s) => `${s.src} ${s.width}w`).join(', '),\n }))\n\n // Fallback: last format's srcset\n const fallbackFormat = formats[formats.length - 1]\n const fallbackSources = formatGroups.get([...formatGroups.keys()].pop()!)!\n\n // Generate blur placeholder\n const placeholder = await generateBlurPlaceholder(absPath, opts.placeholderSize)\n\n return {\n src: fallbackSources[fallbackSources.length - 1]?.src ?? absPath,\n srcset: fallbackFormat?.srcset ?? '',\n width: metadata.width,\n height: metadata.height,\n placeholder,\n formats,\n sources,\n }\n}\n\ninterface ImageMetadata {\n width: number\n height: number\n format: string\n}\n\n/**\n * Read basic image metadata.\n * Uses minimal binary header parsing — no external dependencies.\n */\nasync function getImageMetadata(absPath: string): Promise<ImageMetadata> {\n const buffer = await readFile(absPath)\n const ext = extname(absPath).toLowerCase()\n\n if (ext === '.png') {\n // PNG: width at bytes 16-19, height at 20-23 (big-endian)\n const width = buffer.readUInt32BE(16)\n const height = buffer.readUInt32BE(20)\n return { width, height, format: 'png' }\n }\n\n if (ext === '.jpg' || ext === '.jpeg') {\n // JPEG: scan for SOF markers\n const dimensions = parseJpegDimensions(buffer)\n return { ...dimensions, format: 'jpeg' }\n }\n\n if (ext === '.webp') {\n // WebP: VP8 header\n const dimensions = parseWebPDimensions(buffer)\n return { ...dimensions, format: 'webp' }\n }\n\n // Fallback\n return { width: 0, height: 0, format: ext.slice(1) }\n}\n\n/** @internal Exported for testing */\nexport function parseJpegDimensions(buffer: Buffer): {\n width: number\n height: number\n} {\n let offset = 2 // Skip SOI marker\n while (offset < buffer.length) {\n if (buffer[offset] !== 0xff) break\n const marker = buffer[offset + 1]!\n // SOF markers (0xC0-0xCF except 0xC4, 0xC8, 0xCC)\n if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {\n const height = buffer.readUInt16BE(offset + 5)\n const width = buffer.readUInt16BE(offset + 7)\n return { width, height }\n }\n const length = buffer.readUInt16BE(offset + 2)\n offset += 2 + length\n }\n return { width: 0, height: 0 }\n}\n\n/** @internal Exported for testing */\nexport function parseWebPDimensions(buffer: Buffer): {\n width: number\n height: number\n} {\n // RIFF header: bytes 0-3 = \"RIFF\", 8-11 = \"WEBP\"\n const chunk = buffer.toString('ascii', 12, 16)\n if (chunk === 'VP8 ') {\n // Lossy VP8\n const width = buffer.readUInt16LE(26) & 0x3fff\n const height = buffer.readUInt16LE(28) & 0x3fff\n return { width, height }\n }\n if (chunk === 'VP8L') {\n // Lossless VP8L\n const bits = buffer.readUInt32LE(21)\n const width = (bits & 0x3fff) + 1\n const height = ((bits >> 14) & 0x3fff) + 1\n return { width, height }\n }\n if (chunk === 'VP8X') {\n // Extended VP8X\n const width = 1 + ((buffer[24]! | (buffer[25]! << 8) | (buffer[26]! << 16)) & 0xffffff)\n const height = 1 + ((buffer[27]! | (buffer[28]! << 8) | (buffer[29]! << 16)) & 0xffffff)\n return { width, height }\n }\n return { width: 0, height: 0 }\n}\n\n/**\n * Resize an image using native platform capabilities.\n * Uses sharp if available, falls back to canvas API.\n */\nasync function resizeImage(\n input: string,\n output: string,\n width: number,\n format: ImageFormat,\n quality: number,\n): Promise<void> {\n try {\n // Try sharp (the standard Node.js image processing library)\n const sharp = await import('sharp').then((m) => m.default ?? m)\n let pipeline = sharp(input).resize(width)\n\n switch (format) {\n case 'webp':\n pipeline = pipeline.webp({ quality })\n break\n case 'avif':\n pipeline = pipeline.avif({ quality })\n break\n case 'jpeg':\n pipeline = pipeline.jpeg({ quality, mozjpeg: true })\n break\n case 'png':\n pipeline = pipeline.png({ compressionLevel: 9 })\n break\n }\n\n await pipeline.toFile(output)\n } catch {\n // sharp not available — copy original as fallback\n warnSharpMissing()\n const content = await readFile(input)\n await writeFile(output, content)\n }\n}\n\n/**\n * Generate a tiny blur placeholder as a base64 data URI.\n */\nasync function generateBlurPlaceholder(input: string, size: number): Promise<string> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n const buffer = await sharp(input)\n .resize(size, size, { fit: 'inside' })\n .blur(2)\n .webp({ quality: 20 })\n .toBuffer()\n\n return `data:image/webp;base64,${buffer.toString('base64')}`\n } catch {\n // sharp not available — return a transparent placeholder\n return \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'%3E%3C/svg%3E\"\n }\n}\n","import type { VNodeChild } from '@pyreon/core'\nimport { 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 ? 'dark' : '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 }): VNodeChild {\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","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 = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n\n/**\n * Generate a sitemap.xml string from route file paths.\n */\nexport function generateSitemap(routeFiles: string[], config: SitemapConfig): 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","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── CORS middleware ────────────────────────────────────────────────────────\n\nexport interface CorsConfig {\n /** Allowed origins. Use `\"*\"` for any origin. Default: `\"*\"` */\n origin?: string | string[] | ((origin: string) => boolean)\n /** Allowed HTTP methods. Default: `[\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"]` */\n methods?: string[]\n /** Allowed request headers. Default: `[\"Content-Type\", \"Authorization\"]` */\n allowedHeaders?: string[]\n /** Headers exposed to the client. Default: `[]` */\n exposedHeaders?: string[]\n /** Allow credentials (cookies, auth headers). Default: `false` */\n credentials?: boolean\n /** Preflight cache duration in seconds. Default: `86400` (24 hours) */\n maxAge?: number\n}\n\nconst DEFAULT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']\nconst DEFAULT_HEADERS = ['Content-Type', 'Authorization']\n\n/**\n * CORS middleware — handles preflight requests and sets appropriate\n * Access-Control headers on all responses.\n *\n * @example\n * import { corsMiddleware } from \"@pyreon/zero/cors\"\n *\n * corsMiddleware({ origin: \"https://example.com\", credentials: true })\n *\n * // Allow any origin\n * corsMiddleware({ origin: \"*\" })\n *\n * // Multiple origins\n * corsMiddleware({ origin: [\"https://app.com\", \"https://admin.com\"] })\n */\nexport function corsMiddleware(config: CorsConfig = {}): Middleware {\n const {\n origin = '*',\n methods = DEFAULT_METHODS,\n allowedHeaders = DEFAULT_HEADERS,\n exposedHeaders = [],\n credentials = false,\n maxAge = 86400,\n } = config\n\n return (ctx: MiddlewareContext) => {\n const requestOrigin = ctx.req.headers.get('origin') ?? ''\n const resolvedOrigin = resolveOrigin(origin, requestOrigin)\n\n if (!resolvedOrigin) return\n\n // Set CORS headers on all responses\n ctx.headers.set('Access-Control-Allow-Origin', resolvedOrigin)\n if (credentials) {\n ctx.headers.set('Access-Control-Allow-Credentials', 'true')\n }\n if (exposedHeaders.length > 0) {\n ctx.headers.set('Access-Control-Expose-Headers', exposedHeaders.join(', '))\n }\n if (resolvedOrigin !== '*') {\n ctx.headers.append('Vary', 'Origin')\n }\n\n // Handle preflight\n if (ctx.req.method === 'OPTIONS') {\n return new Response(null, {\n status: 204,\n headers: {\n 'Access-Control-Allow-Origin': resolvedOrigin,\n 'Access-Control-Allow-Methods': methods.join(', '),\n 'Access-Control-Allow-Headers': allowedHeaders.join(', '),\n 'Access-Control-Max-Age': String(maxAge),\n ...(credentials ? { 'Access-Control-Allow-Credentials': 'true' } : {}),\n },\n })\n }\n }\n}\n\nfunction resolveOrigin(config: CorsConfig['origin'], requestOrigin: string): string | null {\n if (config === '*') return '*'\n if (typeof config === 'string') {\n return config === requestOrigin ? config : null\n }\n if (typeof config === 'function') {\n return config(requestOrigin) ? requestOrigin : null\n }\n if (Array.isArray(config)) {\n return config.includes(requestOrigin) ? requestOrigin : null\n }\n return null\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Rate limiting middleware ───────────────────────────────────────────────\n\nexport interface RateLimitConfig {\n /** Maximum requests per window. Default: `100` */\n max?: number\n /** Time window in seconds. Default: `60` */\n window?: number\n /** Function to extract the client identifier. Default: IP from headers. */\n keyFn?: (ctx: MiddlewareContext) => string\n /** Custom response when rate limited. */\n onLimit?: (ctx: MiddlewareContext) => Response\n /** URL patterns to rate limit (glob-style). Default: all paths. */\n include?: string[]\n /** URL patterns to exclude from rate limiting. */\n exclude?: string[]\n}\n\ninterface RateLimitEntry {\n count: number\n resetAt: number\n}\n\n/**\n * Rate limiting middleware — limits requests per client within a time window.\n * Uses an in-memory store (suitable for single-instance deployments).\n *\n * @example\n * import { rateLimitMiddleware } from \"@pyreon/zero/rate-limit\"\n *\n * // 100 requests per minute (default)\n * rateLimitMiddleware()\n *\n * // Strict API rate limiting\n * rateLimitMiddleware({\n * max: 20,\n * window: 60,\n * include: [\"/api/*\"],\n * })\n */\nexport function rateLimitMiddleware(config: RateLimitConfig = {}): Middleware {\n const {\n max = 100,\n window: windowSec = 60,\n keyFn = defaultKeyFn,\n onLimit,\n include,\n exclude,\n } = config\n\n const windowMs = windowSec * 1000\n const store = new Map<string, RateLimitEntry>()\n\n // Periodic cleanup of expired entries\n const cleanupInterval = setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of store) {\n if (entry.resetAt <= now) store.delete(key)\n }\n }, windowMs)\n\n // Allow GC to clean up the interval\n if (typeof cleanupInterval === 'object' && 'unref' in cleanupInterval) {\n cleanupInterval.unref()\n }\n\n return (ctx: MiddlewareContext) => {\n // Check include/exclude patterns\n if (include && !include.some((p) => matchSimpleGlob(p, ctx.path))) return\n if (exclude?.some((p) => matchSimpleGlob(p, ctx.path))) return\n\n const key = keyFn(ctx)\n const now = Date.now()\n let entry = store.get(key)\n\n if (!entry || entry.resetAt <= now) {\n entry = { count: 0, resetAt: now + windowMs }\n store.set(key, entry)\n }\n\n entry.count++\n const remaining = Math.max(0, max - entry.count)\n const resetSeconds = Math.ceil((entry.resetAt - now) / 1000)\n\n // Set rate limit headers on all responses\n ctx.headers.set('X-RateLimit-Limit', String(max))\n ctx.headers.set('X-RateLimit-Remaining', String(remaining))\n ctx.headers.set('X-RateLimit-Reset', String(resetSeconds))\n\n if (entry.count > max) {\n if (onLimit) return onLimit(ctx)\n\n return new Response(JSON.stringify({ error: 'Too many requests' }), {\n status: 429,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': String(resetSeconds),\n 'X-RateLimit-Limit': String(max),\n 'X-RateLimit-Remaining': '0',\n 'X-RateLimit-Reset': String(resetSeconds),\n },\n })\n }\n }\n}\n\nfunction defaultKeyFn(ctx: MiddlewareContext): string {\n return (\n ctx.req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n ctx.req.headers.get('x-real-ip') ??\n 'unknown'\n )\n}\n\n/** Simple glob matching for path patterns. Supports trailing `*`. */\nfunction matchSimpleGlob(pattern: string, path: string): boolean {\n if (pattern.endsWith('/*')) {\n return path.startsWith(pattern.slice(0, -1))\n }\n return pattern === path\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Compression middleware ─────────────────────────────────────────────────\n\nexport interface CompressionConfig {\n /** Minimum response size in bytes to compress. Default: `1024` (1KB) */\n threshold?: number\n /** Encoding preference order. Default: `[\"gzip\", \"deflate\"]` */\n encodings?: ('gzip' | 'deflate')[]\n}\n\n/**\n * Compression middleware — compresses responses using gzip or deflate\n * based on the client's Accept-Encoding header.\n *\n * Only compresses text-based content types (HTML, JSON, JS, CSS, XML, SVG).\n * Skips responses below the size threshold and already-encoded responses.\n *\n * @example\n * import { compressionMiddleware } from \"@pyreon/zero/compression\"\n *\n * compressionMiddleware() // gzip with 1KB threshold\n * compressionMiddleware({ threshold: 512, encodings: [\"gzip\"] })\n */\nexport function compressionMiddleware(config: CompressionConfig = {}): Middleware {\n const { threshold = 1024, encodings = ['gzip', 'deflate'] } = config\n\n return (ctx: MiddlewareContext) => {\n const acceptEncoding = ctx.req.headers.get('accept-encoding') ?? ''\n\n // Find the best supported encoding\n const encoding = encodings.find((enc) => acceptEncoding.includes(enc))\n if (!encoding) return\n\n // Store the encoding choice for post-processing\n ctx.locals.__compressionEncoding = encoding\n ctx.locals.__compressionThreshold = threshold\n ctx.headers.append('Vary', 'Accept-Encoding')\n }\n}\n\n/**\n * Compress a Response body if it meets the criteria.\n * Use this to post-process responses after the handler runs.\n *\n * @example\n * const response = await handler(request)\n * const compressed = await compressResponse(response, 'gzip', 1024)\n */\nexport async function compressResponse(\n response: Response,\n encoding: 'gzip' | 'deflate',\n threshold: number,\n): Promise<Response> {\n const contentType = response.headers.get('content-type') ?? ''\n\n // Only compress text-based content\n if (!isCompressible(contentType)) return response\n\n // Skip if already encoded\n if (response.headers.get('content-encoding')) return response\n\n const body = await response.arrayBuffer()\n\n // Skip below threshold\n if (body.byteLength < threshold) return response\n\n const compressed = await compress(body, encoding)\n\n const headers = new Headers(response.headers)\n headers.set('Content-Encoding', encoding)\n headers.delete('Content-Length')\n headers.append('Vary', 'Accept-Encoding')\n\n return new Response(compressed, {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n}\n\nconst COMPRESSIBLE_TYPES = [\n 'text/',\n 'application/json',\n 'application/javascript',\n 'application/xml',\n 'application/xhtml+xml',\n 'image/svg+xml',\n]\n\n/** Check if a content type is compressible. Exported for testing. */\nexport function isCompressible(contentType: string): boolean {\n return COMPRESSIBLE_TYPES.some((t) => contentType.includes(t))\n}\n\nasync function compress(data: ArrayBuffer, encoding: 'gzip' | 'deflate'): Promise<ArrayBuffer> {\n const format = encoding === 'gzip' ? 'gzip' : 'deflate'\n const stream = new Blob([data]).stream().pipeThrough(new CompressionStream(format))\n return new Response(stream).arrayBuffer()\n}\n","import type { MiddlewareContext } from '@pyreon/server'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Context passed to server action handlers. */\nexport interface ActionContext {\n /** The original request. */\n request: Request\n /** Parsed form data (for form submissions). */\n formData: FormData | null\n /** Parsed JSON body (for JSON submissions). */\n json: unknown\n /** Request headers. */\n headers: Headers\n}\n\n/** A server action handler function. */\nexport type ActionHandler<T = unknown> = (ctx: ActionContext) => T | Promise<T>\n\n/** A registered action with its ID and handler. */\ninterface RegisteredAction {\n id: string\n handler: ActionHandler\n}\n\n/** Client-side callable action returned by defineAction. */\nexport interface Action<T = unknown> {\n /** Call the action with JSON data. */\n (data?: unknown): Promise<T>\n /** The action's unique ID. */\n actionId: string\n}\n\n// ─── Registry ────────────────────────────────────────────────────────────────\n\nconst actionRegistry = new Map<string, RegisteredAction>()\nlet actionCounter = 0\n\n/**\n * Define a server action. Returns a callable function that:\n * - On the **client**: sends a POST request to `/_zero/actions/<id>`\n * - On the **server** (SSR): executes the handler directly (no fetch)\n *\n * @example\n * // In a route file or module:\n * export const createPost = defineAction(async (ctx) => {\n * const data = ctx.json as { title: string; body: string }\n * // ... save to database\n * return { success: true, id: 123 }\n * })\n *\n * // In a component:\n * const result = await createPost({ title: 'Hello', body: '...' })\n */\nexport function defineAction<T = unknown>(handler: ActionHandler<T>): Action<T> {\n const id = `action_${actionCounter++}`\n\n actionRegistry.set(id, { id, handler: handler as ActionHandler })\n\n const callable = async (data?: unknown): Promise<T> => {\n // Server-side: execute handler directly (no network round-trip)\n if (typeof globalThis.window === 'undefined') {\n return handler({\n request: new Request(`http://localhost/_zero/actions/${id}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data ?? null),\n }),\n formData: null,\n json: data ?? null,\n headers: new Headers({ 'Content-Type': 'application/json' }),\n })\n }\n\n // Client-side: POST to the action endpoint\n const response = await fetch(`/_zero/actions/${id}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data ?? null),\n })\n if (!response.ok) {\n const body = await response.json().catch(() => ({}))\n throw new Error((body as { error?: string }).error ?? `Action failed: ${response.statusText}`)\n }\n return response.json()\n }\n\n callable.actionId = id\n return callable as Action<T>\n}\n\n/** Get all registered actions. Useful for testing. */\nexport function getRegisteredActions(): Map<string, RegisteredAction> {\n return actionRegistry\n}\n\n/**\n * Reset the action registry. Useful for testing.\n * @internal\n */\nexport function _resetActions(): void {\n actionRegistry.clear()\n actionCounter = 0\n}\n\n// ─── Server handler ──────────────────────────────────────────────────────────\n\n/**\n * Create a middleware that handles action requests at `/_zero/actions/*`.\n * Mount this before the SSR handler in the server entry.\n */\nexport function createActionMiddleware(): (\n ctx: MiddlewareContext,\n) => Response | undefined | Promise<Response | undefined> {\n return async (ctx: MiddlewareContext) => {\n if (!ctx.path.startsWith('/_zero/actions/')) return\n\n const actionId = ctx.path.slice('/_zero/actions/'.length)\n const action = actionRegistry.get(actionId)\n\n if (!action) {\n return Response.json({ error: 'Action not found' }, { status: 404 })\n }\n\n if (ctx.req.method !== 'POST') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n\n return executeAction(action, ctx.req)\n }\n}\n\nasync function executeAction(action: RegisteredAction, req: Request): Promise<Response> {\n try {\n const contentType = req.headers.get('content-type') ?? ''\n let formData: FormData | null = null\n let json: unknown = null\n\n if (contentType.includes('application/json')) {\n json = await req.json()\n } else if (\n contentType.includes('multipart/form-data') ||\n contentType.includes('application/x-www-form-urlencoded')\n ) {\n formData = await req.formData()\n }\n\n const result = await action.handler({\n request: req,\n formData,\n json,\n headers: req.headers,\n })\n\n return Response.json(result ?? null)\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Internal server error'\n return Response.json({ error: message }, { status: 500 })\n }\n}\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\n\nlet sharpWarned = false\nfunction warnSharpMissing() {\n if (sharpWarned) return\n sharpWarned = true\n // oxlint-disable-next-line no-console\n console.warn(\n '\\n[zero:favicon] sharp not installed — favicons will not be generated. Install for full support: bun add -D sharp\\n',\n )\n}\n\n// ─── Favicon generation plugin ──────────────────────────────────────────────\n//\n// Generates all favicon formats from a single source file (SVG or PNG):\n// - favicon.ico (16x16 + 32x32 combined)\n// - favicon.svg (copied if source is SVG)\n// - apple-touch-icon.png (180x180)\n// - icon-192.png (for web manifest)\n// - icon-512.png (for web manifest)\n// - site.webmanifest\n//\n// Usage:\n// import { faviconPlugin } from \"@pyreon/zero\"\n// export default { plugins: [zero(), faviconPlugin({ source: \"./icon.svg\" })] }\n\nexport interface FaviconPluginConfig {\n /** Path to the source icon (SVG or PNG, at least 512x512 for PNG). */\n source: string\n /** Theme color for web manifest. Default: \"#ffffff\" */\n themeColor?: string\n /** Background color for web manifest. Default: \"#ffffff\" */\n backgroundColor?: string\n /** App name for web manifest. Uses package.json name if not set. */\n name?: string\n /** Generate web manifest. Default: true */\n manifest?: boolean\n /**\n * Dark mode favicon (SVG only).\n * When provided, the SVG favicon uses prefers-color-scheme media query\n * to switch between light and dark variants.\n */\n darkSource?: string\n}\n\ninterface FaviconSize {\n size: number\n name: string\n}\n\nconst SIZES: FaviconSize[] = [\n { size: 16, name: 'favicon-16x16.png' },\n { size: 32, name: 'favicon-32x32.png' },\n { size: 180, name: 'apple-touch-icon.png' },\n { size: 192, name: 'icon-192.png' },\n { size: 512, name: 'icon-512.png' },\n]\n\n/**\n * Favicon generation Vite plugin.\n *\n * Generates all required favicon formats at build time from a single source.\n * In dev mode, serves the source directly.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { faviconPlugin } from \"@pyreon/zero\"\n *\n * export default {\n * plugins: [faviconPlugin({ source: \"./src/assets/icon.svg\" })],\n * }\n * ```\n */\nexport function faviconPlugin(config: FaviconPluginConfig): Plugin {\n const themeColor = config.themeColor ?? '#ffffff'\n const backgroundColor = config.backgroundColor ?? '#ffffff'\n const generateManifest = config.manifest !== false\n\n let root = ''\n let isBuild = false\n\n return {\n name: 'pyreon-zero-favicon',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n root = resolvedConfig.root\n isBuild = resolvedConfig.command === 'build'\n },\n\n // Dev server: serve generated favicons on-the-fly\n configureServer(server) {\n const sourcePath = join(root, config.source)\n\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? ''\n\n // Serve source as favicon.svg in dev\n if (url === '/favicon.svg' && config.source.endsWith('.svg')) {\n try {\n const content = await readFile(sourcePath, 'utf-8')\n res.setHeader('Content-Type', 'image/svg+xml')\n res.end(content)\n return\n } catch { /* fall through */ }\n }\n\n // Serve generated PNGs on-demand\n const sizeMatch = SIZES.find((s) => url === `/${s.name}`)\n if (sizeMatch) {\n const png = await resizeToPng(sourcePath, sizeMatch.size)\n if (png) {\n res.setHeader('Content-Type', 'image/png')\n res.setHeader('Cache-Control', 'no-cache')\n res.end(Buffer.from(png))\n return\n }\n }\n\n // Serve generated ICO on-demand\n if (url === '/favicon.ico') {\n const ico = await generateIco(sourcePath)\n if (ico) {\n res.setHeader('Content-Type', 'image/x-icon')\n res.setHeader('Cache-Control', 'no-cache')\n res.end(Buffer.from(ico))\n return\n }\n }\n\n // Serve manifest\n if (url === '/site.webmanifest' && generateManifest) {\n const manifest = {\n name: config.name ?? 'App',\n short_name: config.name ?? 'App',\n icons: [\n { src: '/icon-192.png', sizes: '192x192', type: 'image/png' },\n { src: '/icon-512.png', sizes: '512x512', type: 'image/png' },\n ],\n theme_color: themeColor,\n background_color: backgroundColor,\n display: 'standalone',\n }\n res.setHeader('Content-Type', 'application/manifest+json')\n res.end(JSON.stringify(manifest, null, 2))\n return\n }\n\n next()\n })\n },\n\n // Inject favicon <link> tags into HTML\n transformIndexHtml() {\n const isSvg = config.source.endsWith('.svg')\n const tags: Array<{\n tag: string\n attrs: Record<string, string>\n injectTo: 'head'\n }> = []\n\n if (isSvg) {\n tags.push({\n tag: 'link',\n attrs: { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },\n injectTo: 'head',\n })\n }\n\n tags.push(\n {\n tag: 'link',\n attrs: { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' },\n injectTo: 'head',\n },\n {\n tag: 'link',\n attrs: { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' },\n injectTo: 'head',\n },\n {\n tag: 'link',\n attrs: { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' },\n injectTo: 'head',\n },\n )\n\n if (generateManifest) {\n tags.push({\n tag: 'link',\n attrs: { rel: 'manifest', href: '/site.webmanifest' },\n injectTo: 'head',\n })\n }\n\n tags.push({\n tag: 'meta',\n attrs: { name: 'theme-color', content: themeColor },\n injectTo: 'head',\n })\n\n return tags\n },\n\n async generateBundle() {\n if (!isBuild) return\n\n const sourcePath = join(root, config.source)\n if (!existsSync(sourcePath)) {\n // oxlint-disable-next-line no-console\n console.warn(`[zero:favicon] Source not found: ${sourcePath}`)\n return\n }\n\n const isSvg = config.source.endsWith('.svg')\n\n // Copy SVG as favicon.svg\n if (isSvg) {\n const svgContent = await readFile(sourcePath, 'utf-8')\n let finalSvg = svgContent\n\n // If dark mode variant provided, wrap in media query\n if (config.darkSource) {\n const darkPath = join(root, config.darkSource)\n if (existsSync(darkPath)) {\n const darkSvg = await readFile(darkPath, 'utf-8')\n finalSvg = wrapSvgWithDarkMode(svgContent, darkSvg)\n }\n }\n\n this.emitFile({\n type: 'asset',\n fileName: 'favicon.svg',\n source: finalSvg,\n })\n }\n\n // Generate PNG sizes via sharp\n for (const { size, name } of SIZES) {\n const pngBuffer = await resizeToPng(sourcePath, size)\n if (pngBuffer) {\n this.emitFile({\n type: 'asset',\n fileName: name,\n source: pngBuffer,\n })\n }\n }\n\n // Generate favicon.ico (16 + 32)\n const ico = await generateIco(sourcePath)\n if (ico) {\n this.emitFile({\n type: 'asset',\n fileName: 'favicon.ico',\n source: ico,\n })\n }\n\n // Generate web manifest\n if (generateManifest) {\n const manifest = {\n name: config.name ?? 'App',\n short_name: config.name ?? 'App',\n icons: [\n { src: '/icon-192.png', sizes: '192x192', type: 'image/png' },\n { src: '/icon-512.png', sizes: '512x512', type: 'image/png' },\n ],\n theme_color: themeColor,\n background_color: backgroundColor,\n display: 'standalone',\n }\n\n this.emitFile({\n type: 'asset',\n fileName: 'site.webmanifest',\n source: JSON.stringify(manifest, null, 2),\n })\n }\n },\n }\n}\n\n/**\n * Wrap two SVGs into a single SVG that switches based on prefers-color-scheme.\n */\nfunction wrapSvgWithDarkMode(lightSvg: string, darkSvg: string): string {\n // Extract viewBox from light SVG\n const viewBoxMatch = lightSvg.match(/viewBox=\"([^\"]*)\"/)\n const viewBox = viewBoxMatch?.[1] ?? '0 0 32 32'\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\">\n <style>\n :root { color-scheme: light dark; }\n @media (prefers-color-scheme: dark) { .light { display: none; } }\n @media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) { .dark { display: none; } }\n </style>\n <g class=\"light\">${stripSvgWrapper(lightSvg)}</g>\n <g class=\"dark\">${stripSvgWrapper(darkSvg)}</g>\n</svg>`\n}\n\nfunction stripSvgWrapper(svg: string): string {\n return svg\n .replace(/<svg[^>]*>/, '')\n .replace(/<\\/svg>\\s*$/, '')\n .trim()\n}\n\nasync function resizeToPng(input: string, size: number): Promise<Uint8Array | null> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n return await sharp(input).resize(size, size, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } } as any).png().toBuffer()\n } catch {\n warnSharpMissing()\n return null\n }\n}\n\nasync function generateIco(input: string): Promise<Uint8Array | null> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n const png16 = await sharp(input).resize(16, 16, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } } as any).png().toBuffer()\n const png32 = await sharp(input).resize(32, 32, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } } as any).png().toBuffer()\n\n // ICO format: header + directory entries + PNG data\n return createIcoFromPngs([\n { buffer: png16, size: 16 },\n { buffer: png32, size: 32 },\n ])\n } catch {\n warnSharpMissing()\n return null\n }\n}\n\nexport interface IcoEntry {\n buffer: Buffer\n size: number\n}\n\n/** @internal Exported for testing */\nexport function createIcoFromPngs(entries: IcoEntry[]): Uint8Array {\n const headerSize = 6\n const dirEntrySize = 16\n const dirSize = dirEntrySize * entries.length\n let dataOffset = headerSize + dirSize\n\n // ICO header\n const header = Buffer.alloc(headerSize)\n header.writeUInt16LE(0, 0) // reserved\n header.writeUInt16LE(1, 2) // type: icon\n header.writeUInt16LE(entries.length, 4) // count\n\n // Directory entries\n const dirEntries = Buffer.alloc(dirSize)\n const dataBuffers: Buffer[] = []\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i]!\n const offset = i * dirEntrySize\n dirEntries.writeUInt8(entry.size === 256 ? 0 : entry.size, offset) // width\n dirEntries.writeUInt8(entry.size === 256 ? 0 : entry.size, offset + 1) // height\n dirEntries.writeUInt8(0, offset + 2) // palette\n dirEntries.writeUInt8(0, offset + 3) // reserved\n dirEntries.writeUInt16LE(1, offset + 4) // color planes\n dirEntries.writeUInt16LE(32, offset + 6) // bits per pixel\n dirEntries.writeUInt32LE(entry.buffer.length, offset + 8) // size\n dirEntries.writeUInt32LE(dataOffset, offset + 12) // offset\n\n dataOffset += entry.buffer.length\n dataBuffers.push(entry.buffer)\n }\n\n return Buffer.concat([header, dirEntries, ...dataBuffers])\n}\n","import { createContext } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\nimport type { Plugin } from 'vite'\n\n// ─── Localized routing ─────────────────────────────────────────────────────\n//\n// Adds locale-prefixed routes to Zero's file-system router:\n// - /about → /en/about, /de/about, /cs/about\n// - / → /en, /de, /cs (or default locale without prefix)\n// - Automatic locale detection from Accept-Language header\n// - Redirect to preferred locale\n// - hreflang link generation\n//\n// Usage:\n// import { i18nRouting } from \"@pyreon/zero\"\n// export default { plugins: [zero(), i18nRouting({ locales: [\"en\", \"de\"], defaultLocale: \"en\" })] }\n\nexport interface I18nRoutingConfig {\n /** Supported locales. e.g. [\"en\", \"de\", \"cs\"] */\n locales: string[]\n /** Default locale — served without prefix (/ instead of /en/). */\n defaultLocale: string\n /** Redirect root to detected locale. Default: true */\n detectLocale?: boolean\n /** Cookie name to persist locale preference. Default: \"locale\" */\n cookieName?: string\n /** URL strategy. Default: \"prefix-except-default\" */\n strategy?: 'prefix' | 'prefix-except-default'\n}\n\nexport interface LocaleContext {\n /** Current locale code. e.g. \"en\", \"de\" */\n locale: string\n /** All supported locales. */\n locales: string[]\n /** Default locale. */\n defaultLocale: string\n /** Build a localized path. e.g. localePath(\"/about\", \"de\") → \"/de/about\" */\n localePath: (path: string, locale?: string) => string\n /** Get hreflang alternates for the current path. */\n alternates: () => Array<{ locale: string; url: string }>\n}\n\n/**\n * Detect preferred locale from Accept-Language header.\n */\nexport function detectLocaleFromHeader(\n acceptLanguage: string | null | undefined,\n locales: string[],\n defaultLocale: string,\n): string {\n if (!acceptLanguage) return defaultLocale\n\n // Parse Accept-Language: en-US,en;q=0.9,de;q=0.8\n const preferred = acceptLanguage\n .split(',')\n .map((part) => {\n const [lang, q] = part.trim().split(';q=')\n return {\n lang: lang?.split('-')[0]?.toLowerCase() ?? '',\n quality: q ? Number.parseFloat(q) : 1,\n }\n })\n .sort((a, b) => b.quality - a.quality)\n\n for (const { lang } of preferred) {\n if (locales.includes(lang)) return lang\n }\n\n return defaultLocale\n}\n\n/**\n * Extract locale from a URL path.\n * Returns { locale, pathWithoutLocale }.\n */\nexport function extractLocaleFromPath(\n path: string,\n locales: string[],\n defaultLocale: string,\n): { locale: string; pathWithoutLocale: string } {\n const segments = path.split('/').filter(Boolean)\n const firstSegment = segments[0]?.toLowerCase()\n\n if (firstSegment && locales.includes(firstSegment)) {\n return {\n locale: firstSegment,\n pathWithoutLocale: '/' + segments.slice(1).join('/') || '/',\n }\n }\n\n return { locale: defaultLocale, pathWithoutLocale: path }\n}\n\n/**\n * Build a localized path.\n */\nexport function buildLocalePath(\n path: string,\n locale: string,\n defaultLocale: string,\n strategy: 'prefix' | 'prefix-except-default',\n): string {\n const clean = path === '/' ? '' : path\n if (strategy === 'prefix-except-default' && locale === defaultLocale) {\n return path\n }\n return `/${locale}${clean}`\n}\n\n/**\n * Create a LocaleContext for use in components and loaders.\n */\nexport function createLocaleContext(\n locale: string,\n path: string,\n config: I18nRoutingConfig,\n): LocaleContext {\n const strategy = config.strategy ?? 'prefix-except-default'\n\n return {\n locale,\n locales: config.locales,\n defaultLocale: config.defaultLocale,\n\n localePath(targetPath: string, targetLocale?: string) {\n return buildLocalePath(\n targetPath,\n targetLocale ?? locale,\n config.defaultLocale,\n strategy,\n )\n },\n\n alternates() {\n const { pathWithoutLocale } = extractLocaleFromPath(\n path,\n config.locales,\n config.defaultLocale,\n )\n return config.locales.map((loc) => ({\n locale: loc,\n url: buildLocalePath(pathWithoutLocale, loc, config.defaultLocale, strategy),\n }))\n },\n }\n}\n\n/**\n * I18n routing middleware for Zero's server.\n *\n * - Detects locale from URL prefix or Accept-Language header\n * - Redirects root to preferred locale (when detectLocale is true)\n * - Sets locale context for loaders and components\n *\n * @example\n * ```ts\n * // zero.config.ts\n * import { i18nRouting } from \"@pyreon/zero\"\n *\n * export default defineConfig({\n * plugins: [\n * i18nRouting({\n * locales: [\"en\", \"de\", \"cs\"],\n * defaultLocale: \"en\",\n * }),\n * ],\n * })\n * ```\n */\nexport function i18nRouting(config: I18nRoutingConfig): Plugin {\n const strategy = config.strategy ?? 'prefix-except-default'\n const detectEnabled = config.detectLocale !== false\n const cookieName = config.cookieName ?? 'locale'\n\n return {\n name: 'pyreon-zero-i18n-routing',\n\n // Route duplication is NOT handled here. The fs-router's `scanRouteFiles`\n // consumes the i18n config to duplicate routes per locale at build time.\n // This plugin only provides: (1) the server middleware for locale detection\n // and (2) the runtime hooks (useLocale, setLocale) for client-side use.\n configResolved() {},\n\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url ?? '/'\n\n // Skip static assets\n if (url.startsWith('/@') || url.startsWith('/__') || url.includes('.')) {\n return next()\n }\n\n const { locale } = extractLocaleFromPath(\n url,\n config.locales,\n config.defaultLocale,\n )\n\n // Redirect root to detected locale\n if (detectEnabled && url === '/') {\n const cookies = parseCookies(req.headers.cookie)\n const preferredFromCookie = cookies[cookieName]\n const preferredFromHeader = detectLocaleFromHeader(\n req.headers['accept-language'],\n config.locales,\n config.defaultLocale,\n )\n const preferred = preferredFromCookie && config.locales.includes(preferredFromCookie)\n ? preferredFromCookie\n : preferredFromHeader\n\n if (strategy === 'prefix' || preferred !== config.defaultLocale) {\n res.writeHead(302, { Location: `/${preferred}/` })\n res.end()\n return\n }\n }\n\n // Attach locale context to request for loaders\n ;(req as any).__locale = locale\n ;(req as any).__localeContext = createLocaleContext(locale, url, config)\n\n // Update the module-level signal so useLocale() returns the correct value\n localeSignal.set(locale)\n\n next()\n })\n },\n }\n}\n\nfunction parseCookies(header: string | undefined): Record<string, string> {\n if (!header) return {}\n const result: Record<string, string> = {}\n for (const pair of header.split(';')) {\n const [key, value] = pair.trim().split('=')\n if (key && value) result[key] = decodeURIComponent(value)\n }\n return result\n}\n\n// ─── Reactive locale hook ───────────────────────────────────────────────────\n\n/** @internal Context for the current locale. */\nexport const LocaleCtx = createContext<string>('en')\n\n/** Current locale signal — set by the server middleware or client-side detection. */\nexport const localeSignal = signal('en')\n\n/**\n * Read the current locale reactively.\n *\n * Returns the locale signal value directly — reactive in both SSR and CSR.\n * The server middleware sets `localeSignal` per-request, and client-side\n * `setLocale()` updates it as well.\n *\n * @example\n * ```tsx\n * const locale = useLocale() // \"en\", \"de\", etc.\n * ```\n */\nexport function useLocale(): string {\n return localeSignal()\n}\n\n/**\n * Set the locale client-side and update the URL.\n *\n * @example\n * ```tsx\n * <button onClick={() => setLocale('de')}>Deutsch</button>\n * ```\n */\nexport function setLocale(\n locale: string,\n config: I18nRoutingConfig,\n): void {\n localeSignal.set(locale)\n\n // Persist to cookie\n if (typeof document !== 'undefined') {\n document.cookie = `${config.cookieName ?? 'locale'}=${locale}; path=/; max-age=31536000`\n }\n\n // Navigate to localized URL — use pushState to avoid full page reload\n if (typeof window !== 'undefined') {\n const strategy = config.strategy ?? 'prefix-except-default'\n const { pathWithoutLocale } = extractLocaleFromPath(\n window.location.pathname,\n config.locales,\n config.defaultLocale,\n )\n const newPath = buildLocalePath(pathWithoutLocale, locale, config.defaultLocale, strategy)\n window.history.pushState(null, '', newPath)\n // Dispatch popstate so @pyreon/router picks up the URL change\n window.dispatchEvent(new PopStateEvent('popstate'))\n }\n}\n","import type { VNodeChild } from '@pyreon/core'\nimport { useHead } from '@pyreon/head'\nimport type { I18nRoutingConfig } from './i18n-routing'\nimport { extractLocaleFromPath } from './i18n-routing'\n\n// ─── Meta component ────────────────────────────────────────────────────────\n\nexport interface MetaProps {\n /** Page title. Accepts reactive accessor `() => string`. */\n title?: string | (() => string)\n /** Page description. Accepts reactive accessor. */\n description?: string | (() => string)\n /** Canonical URL. Also sets og:url. */\n canonical?: string\n /** Open Graph image URL. Also sets twitter:image. */\n image?: string\n /** Image alt text for accessibility. */\n imageAlt?: string\n /** Open Graph type. Default: \"website\" */\n type?: 'website' | 'article' | 'product' | 'profile'\n /** Site name for og:site_name. */\n siteName?: string\n /** Twitter card type. Default: \"summary_large_image\" */\n twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player'\n /** Twitter @handle. */\n twitterSite?: string\n /** Twitter creator @handle. */\n twitterCreator?: string\n /** Locale. Default: \"en_US\" */\n locale?: string\n /** Alternate locales for hreflang. */\n alternateLocales?: Array<{ locale: string; url: string }>\n /** Robots directives. Default: \"index, follow\" */\n robots?: string\n /** Published time (ISO 8601) for article type. */\n publishedTime?: string\n /** Modified time (ISO 8601) for article type. */\n modifiedTime?: string\n /** Article author. */\n author?: string\n /** Article tags. */\n tags?: string[]\n /** JSON-LD structured data object. */\n jsonLd?: Record<string, unknown>\n /** Additional custom meta tags. */\n extra?: Array<{ name?: string; property?: string; content: string }>\n /**\n * I18n routing config — when provided, auto-generates hreflang alternate\n * links for all locales based on the current path.\n * Also sets og:locale and og:locale:alternate.\n */\n i18n?: I18nRoutingConfig\n /** Base URL for building absolute hreflang URLs. e.g. \"https://example.com\" */\n origin?: string\n children?: VNodeChild\n}\n\nconst resolveStr = (v: string | (() => string) | undefined): string | undefined =>\n typeof v === 'function' ? v() : v\n\n/**\n * Declarative meta component for SSR-compatible page metadata.\n *\n * Supports reactive title/description — when passed as `() => string` accessors,\n * they are forwarded to `useHead()` as a reactive getter so updates propagate\n * automatically via signal tracking.\n *\n * @example\n * ```tsx\n * <Meta title=\"My Page\" description=\"...\" image=\"/og.jpg\" canonical=\"https://...\" />\n * ```\n *\n * @example Reactive title\n * ```tsx\n * const count = signal(0)\n * <Meta title={() => `${count()} items`} />\n * ```\n */\nexport function Meta(props: MetaProps): VNodeChild {\n const hasReactiveTitle = typeof props.title === 'function'\n const hasReactiveDescription = typeof props.description === 'function'\n\n // If title or description are reactive accessors, pass a getter to useHead\n // so it re-evaluates when the signals change.\n if (hasReactiveTitle || hasReactiveDescription) {\n useHead((() => {\n const title = resolveStr(props.title)\n const description = resolveStr(props.description)\n const tags = buildMetaTags({ ...props, title, description } as any)\n return { title, meta: tags.meta, link: tags.link, script: tags.script }\n }) as any)\n } else {\n const title = resolveStr(props.title)\n const description = resolveStr(props.description)\n const tags = buildMetaTags({ ...props, title, description } as any)\n useHead({ title, meta: tags.meta, link: tags.link, script: tags.script } as any)\n }\n\n return props.children ?? null\n}\n\ninterface MetaTags {\n meta: Array<Record<string, string>>\n link: Array<Record<string, string>>\n script: Array<{ type: string; children: string }>\n}\n\nexport function buildMetaTags(\n props: Omit<MetaProps, 'title' | 'description' | 'children'> & {\n title?: string\n description?: string\n },\n): MetaTags {\n const meta: Array<Record<string, string>> = []\n const link: Array<Record<string, string>> = []\n const script: Array<{ type: string; children: string }> = []\n\n const {\n title, description, canonical, image, imageAlt,\n type = 'website', siteName,\n twitterCard = 'summary_large_image', twitterSite, twitterCreator,\n locale = 'en_US', alternateLocales,\n robots = 'index, follow',\n publishedTime, modifiedTime, author, tags, jsonLd, extra,\n } = props\n\n if (description) meta.push({ name: 'description', content: description })\n if (robots) meta.push({ name: 'robots', content: robots })\n if (author) meta.push({ name: 'author', content: author })\n\n if (title) meta.push({ property: 'og:title', content: title })\n if (description) meta.push({ property: 'og:description', content: description })\n if (canonical) meta.push({ property: 'og:url', content: canonical })\n if (image) meta.push({ property: 'og:image', content: image })\n if (imageAlt) meta.push({ property: 'og:image:alt', content: imageAlt })\n meta.push({ property: 'og:type', content: type })\n if (siteName) meta.push({ property: 'og:site_name', content: siteName })\n meta.push({ property: 'og:locale', content: locale })\n\n if (type === 'article') {\n if (publishedTime) meta.push({ property: 'article:published_time', content: publishedTime })\n if (modifiedTime) meta.push({ property: 'article:modified_time', content: modifiedTime })\n if (author) meta.push({ property: 'article:author', content: author })\n if (tags) for (const tag of tags) meta.push({ property: 'article:tag', content: tag })\n }\n\n meta.push({ name: 'twitter:card', content: twitterCard })\n if (title) meta.push({ name: 'twitter:title', content: title })\n if (description) meta.push({ name: 'twitter:description', content: description })\n if (image) meta.push({ name: 'twitter:image', content: image })\n if (imageAlt) meta.push({ name: 'twitter:image:alt', content: imageAlt })\n if (twitterSite) meta.push({ name: 'twitter:site', content: twitterSite })\n if (twitterCreator) meta.push({ name: 'twitter:creator', content: twitterCreator })\n\n if (canonical) link.push({ rel: 'canonical', href: canonical })\n if (alternateLocales) {\n for (const alt of alternateLocales) {\n link.push({ rel: 'alternate', hreflang: alt.locale, href: alt.url })\n }\n }\n\n if (jsonLd) {\n script.push({\n type: 'application/ld+json',\n children: JSON.stringify({ '@context': 'https://schema.org', ...jsonLd }),\n })\n }\n\n if (extra) for (const tag of extra) meta.push(tag)\n\n // I18n: auto-generate hreflang alternates from i18nRouting config\n if (props.i18n) {\n const i18nConfig = props.i18n\n const origin = props.origin ?? ''\n const currentPath = canonical?.replace(origin, '') ?? '/'\n const { pathWithoutLocale } = extractLocaleFromPath(\n currentPath,\n i18nConfig.locales,\n i18nConfig.defaultLocale,\n )\n const strategy = i18nConfig.strategy ?? 'prefix-except-default'\n\n for (const loc of i18nConfig.locales) {\n const localizedPath =\n strategy === 'prefix-except-default' && loc === i18nConfig.defaultLocale\n ? pathWithoutLocale\n : `/${loc}${pathWithoutLocale === '/' ? '' : pathWithoutLocale}`\n\n link.push({\n rel: 'alternate',\n hreflang: loc,\n href: `${origin}${localizedPath}`,\n })\n\n // og:locale:alternate for non-current locales\n if (loc !== locale) {\n meta.push({ property: 'og:locale:alternate', content: loc })\n }\n }\n\n // x-default hreflang pointing to default locale\n link.push({\n rel: 'alternate',\n hreflang: 'x-default',\n href: `${origin}${pathWithoutLocale}`,\n })\n }\n\n return { meta, link, script }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["flattenRoutePatterns","h","sharpWarned","warnSharpMissing","escapeXml","sharpWarned","warnSharpMissing"],"sources":["../src/app.ts","../src/api-routes.ts","../src/not-found.ts","../src/entry-server.ts","../src/config.ts","../src/error-overlay.ts","../src/vite-plugin.ts","../src/isr.ts","../src/adapters/validate.ts","../src/adapters/bun.ts","../src/adapters/cloudflare.ts","../src/adapters/netlify.ts","../src/adapters/node.ts","../src/adapters/static.ts","../src/adapters/vercel.ts","../src/adapters/index.ts","../src/utils/use-intersection-observer.ts","../../../core/core/lib/jsx-runtime.js","../src/image.tsx","../src/link.tsx","../src/script.tsx","../src/cache.ts","../src/middleware.ts","../src/font.ts","../src/image-plugin.ts","../src/theme.tsx","../src/seo.ts","../src/cors.ts","../src/rate-limit.ts","../src/compression.ts","../src/actions.ts","../src/favicon.ts","../src/og-image.ts","../src/i18n-routing.ts","../src/meta.tsx","../src/csp.ts","../src/logger.ts","../src/env.ts","../src/ai.ts"],"sourcesContent":["import type { ComponentFn, Props } from '@pyreon/core'\nimport { Fragment, h } from '@pyreon/core'\nimport { HeadProvider } from '@pyreon/head'\nimport type { RouteRecord } from '@pyreon/router'\nimport { createRouter, RouterProvider, RouterView } from '@pyreon/router'\n\n// ─── App assembly ────────────────────────────────────────────────────────────\n\nexport interface CreateAppOptions {\n /** Route definitions (from file-based routing or manual). */\n routes: RouteRecord[]\n\n /** Router mode. Default: \"history\" for SSR, \"hash\" for SPA. */\n routerMode?: 'hash' | 'history'\n\n /** Initial URL for SSR. */\n url?: string\n\n /** Root layout component wrapping all routes. */\n layout?: ComponentFn\n\n /** Global error component. */\n errorComponent?: ComponentFn\n}\n\n/**\n * Create a full Zero app — assembles router, head provider, and root layout.\n *\n * Used internally by entry-server and entry-client.\n */\nexport function createApp(options: CreateAppOptions) {\n const router = createRouter({\n routes: options.routes,\n mode: options.routerMode ?? 'history',\n ...(options.url ? { url: options.url } : {}),\n scrollBehavior: 'top',\n })\n\n const Layout = options.layout ?? DefaultLayout\n\n function App() {\n return h(\n HeadProvider,\n null,\n h(\n RouterProvider as ComponentFn<Props>,\n { router },\n h(Layout, null, h(RouterView as ComponentFn<Props>, null)),\n ),\n )\n }\n\n return { App, router }\n}\n\nfunction DefaultLayout(props: Props) {\n return h(Fragment, null, ...(Array.isArray(props.children) ? props.children : [props.children]))\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** HTTP methods supported by API routes. */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\n/** Context passed to API route handlers. */\nexport interface ApiContext {\n /** The incoming request. */\n request: Request\n /** Parsed URL. */\n url: URL\n /** URL path. */\n path: string\n /** Dynamic route parameters (e.g., { id: \"123\" }). */\n params: Record<string, string>\n /** Request headers. */\n headers: Headers\n}\n\n/** An API route handler function. */\nexport type ApiHandler = (ctx: ApiContext) => Response | Promise<Response>\n\n/** An API route module — exports named HTTP method handlers. */\nexport interface ApiRouteModule {\n GET?: ApiHandler\n POST?: ApiHandler\n PUT?: ApiHandler\n PATCH?: ApiHandler\n DELETE?: ApiHandler\n HEAD?: ApiHandler\n OPTIONS?: ApiHandler\n}\n\n/** A registered API route entry. */\nexport interface ApiRouteEntry {\n /** URL pattern (e.g., \"/api/posts/:id\"). */\n pattern: string\n /** The route module with method handlers. */\n module: ApiRouteModule\n}\n\n// ─── Pattern matching ────────────────────────────────────────────────────────\n\n/**\n * Match a URL path against an API route pattern.\n * Returns extracted params or null if no match.\n */\nexport function matchApiRoute(pattern: string, path: string): Record<string, string> | null {\n const patternParts = pattern.split('/').filter(Boolean)\n const pathParts = path.split('/').filter(Boolean)\n const params: Record<string, string> = {}\n\n for (let i = 0; i < patternParts.length; i++) {\n const pp = patternParts[i]\n if (!pp) continue\n\n // Catch-all: :param*\n if (pp.endsWith('*')) {\n const paramName = pp.slice(1, -1)\n params[paramName] = pathParts.slice(i).join('/')\n return params\n }\n\n // No more path segments\n if (i >= pathParts.length) return null\n\n // Dynamic segment: :param\n if (pp.startsWith(':')) {\n params[pp.slice(1)] = pathParts[i]!\n continue\n }\n\n // Static segment\n if (pp !== pathParts[i]) return null\n }\n\n return patternParts.length === pathParts.length ? params : null\n}\n\n// ─── Middleware ───────────────────────────────────────────────────────────────\n\nconst HTTP_METHODS: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']\n\n/**\n * Create a middleware that dispatches API route requests.\n * API routes are matched by URL pattern and HTTP method.\n */\nexport function createApiMiddleware(routes: ApiRouteEntry[]): Middleware {\n return async (ctx: MiddlewareContext) => {\n for (const route of routes) {\n const params = matchApiRoute(route.pattern, ctx.path)\n if (!params) continue\n\n const method = ctx.req.method.toUpperCase() as HttpMethod\n const handler = route.module[method]\n\n if (!handler) {\n // Route matched but method not supported\n const allowed = HTTP_METHODS.filter((m) => route.module[m]).join(', ')\n return new Response(null, {\n status: 405,\n headers: {\n Allow: allowed,\n 'Content-Type': 'application/json',\n },\n })\n }\n\n return handler({\n request: ctx.req,\n url: ctx.url,\n path: ctx.path,\n params,\n headers: ctx.req.headers,\n })\n }\n }\n}\n\n// ─── Virtual module generation ───────────────────────────────────────────────\n\n/**\n * Detect whether a route file is an API route.\n * API routes are `.ts` or `.js` files inside an `api/` directory.\n */\nexport function isApiRoute(filePath: string): boolean {\n const normalized = filePath.replace(/\\\\/g, '/')\n return (\n normalized.startsWith('api/') &&\n (normalized.endsWith('.ts') || normalized.endsWith('.js')) &&\n !normalized.endsWith('.tsx') &&\n !normalized.endsWith('.jsx')\n )\n}\n\n/**\n * Convert an API route file path to a URL pattern.\n *\n * Examples:\n * \"api/posts.ts\" → \"/api/posts\"\n * \"api/posts/index.ts\" → \"/api/posts\"\n * \"api/posts/[id].ts\" → \"/api/posts/:id\"\n * \"api/[...path].ts\" → \"/api/:path*\"\n */\nexport function apiFilePathToPattern(filePath: string): string {\n let route = filePath\n // Remove extension\n for (const ext of ['.ts', '.js']) {\n if (route.endsWith(ext)) {\n route = route.slice(0, -ext.length)\n break\n }\n }\n\n const segments = route.split('/')\n const urlSegments: string[] = []\n\n for (const seg of segments) {\n if (seg === 'index') continue\n\n // Catch-all: [...param]\n const catchAll = seg.match(/^\\[\\.\\.\\.(\\w+)\\]$/)\n if (catchAll) {\n urlSegments.push(`:${catchAll[1]}*`)\n continue\n }\n\n // Dynamic: [param]\n const dynamic = seg.match(/^\\[(\\w+)\\]$/)\n if (dynamic) {\n urlSegments.push(`:${dynamic[1]}`)\n continue\n }\n\n urlSegments.push(seg)\n }\n\n return `/${urlSegments.join('/')}`\n}\n\n/**\n * Generate a virtual module that exports API route entries.\n * Each entry maps a URL pattern to a module with HTTP method handlers.\n */\nexport function generateApiRouteModule(files: string[], routesDir: string): string {\n const apiFiles = files.filter(isApiRoute)\n\n if (apiFiles.length === 0) {\n return 'export const apiRoutes = []\\n'\n }\n\n const imports: string[] = []\n const entries: string[] = []\n\n for (let i = 0; i < apiFiles.length; i++) {\n const name = `_api${i}`\n const file = apiFiles[i]\n if (!file) continue\n const fullPath = `${routesDir}/${file}`\n const pattern = apiFilePathToPattern(file)\n\n imports.push(`import * as ${name} from \"${fullPath}\"`)\n entries.push(` { pattern: ${JSON.stringify(pattern)}, module: ${name} }`)\n }\n\n return [...imports, '', 'export const apiRoutes = [', entries.join(',\\n'), ']'].join('\\n')\n}\n","import type { ComponentFn } from \"@pyreon/core\";\nimport { h } from \"@pyreon/core\";\nimport { renderToString } from \"@pyreon/runtime-server\";\n\n// ─── 404 Not Found rendering ────────────────────────────────────────────────\n//\n// Shared utility for rendering 404 pages in both dev (vite-plugin) and\n// production (entry-server). Renders the notFoundComponent into HTML\n// and wraps it in a minimal document if no template is provided.\n\nconst DEFAULT_404_BODY =\n\t\"<h1>404 — Not Found</h1><p>The page you requested does not exist.</p>\";\n\n/**\n * Render a 404 component to a full HTML string.\n * If no component is provided, returns a default 404 page.\n */\nexport async function render404Page(\n\tcomponent: ComponentFn | undefined,\n\ttemplate?: string,\n): Promise<string> {\n\tlet body: string;\n\tif (component) {\n\t\tbody = await renderToString(h(component, null));\n\t} else {\n\t\tbody = DEFAULT_404_BODY;\n\t}\n\n\tif (template?.includes(\"<!--pyreon-app-->\")) {\n\t\treturn template.replace(\"<!--pyreon-app-->\", body);\n\t}\n\n\treturn `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>404 — Not Found</title>\n</head>\n<body>\n ${body}\n</body>\n</html>`;\n}\n","import type { ComponentFn } from \"@pyreon/core\";\nimport type { RouteRecord } from \"@pyreon/router\";\nimport type { Middleware, MiddlewareContext } from \"@pyreon/server\";\nimport { createHandler } from \"@pyreon/server\";\nimport type { ApiRouteEntry } from \"./api-routes\";\nimport { createApiMiddleware } from \"./api-routes\";\nimport { createApp } from \"./app\";\nimport { render404Page } from \"./not-found\";\nimport type { RouteMiddlewareEntry, ZeroConfig } from \"./types\";\n\n// ─── Server entry factory ───────────────────────────────────────────────────\n\nexport interface CreateServerOptions {\n\t/** Route definitions. */\n\troutes: RouteRecord[];\n\t/** Zero config. */\n\tconfig?: ZeroConfig;\n\t/** Additional middleware. */\n\tmiddleware?: Middleware[];\n\t/** Per-route middleware from virtual:zero/route-middleware. */\n\trouteMiddleware?: RouteMiddlewareEntry[];\n\t/** API route entries from virtual:zero/api-routes. */\n\tapiRoutes?: ApiRouteEntry[];\n\t/** HTML template override. */\n\ttemplate?: string;\n\t/** Client entry path. */\n\tclientEntry?: string;\n\t/** Component to render when no route matches (from _404.tsx). */\n\tnotFoundComponent?: ComponentFn;\n}\n\n/**\n * Create a middleware that dispatches per-route middleware based on URL pattern matching.\n */\nfunction createRouteMiddlewareDispatcher(\n\tentries: RouteMiddlewareEntry[],\n): Middleware {\n\treturn async (ctx: MiddlewareContext) => {\n\t\tfor (const entry of entries) {\n\t\t\tif (matchPattern(entry.pattern, ctx.path)) {\n\t\t\t\tconst mw = Array.isArray(entry.middleware)\n\t\t\t\t\t? entry.middleware\n\t\t\t\t\t: [entry.middleware];\n\t\t\t\tfor (const fn of mw) {\n\t\t\t\t\tconst result = await fn(ctx);\n\t\t\t\t\tif (result) return result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * URL pattern matcher supporting :param and :param* segments.\n *\n * Rules:\n * - Static segments must match exactly\n * - `:param` matches a single path segment\n * - `:param*` matches all remaining segments (must be last, and path must\n * have matched all preceding segments)\n * - Path length must match pattern length (unless catch-all)\n */\nexport function matchPattern(pattern: string, path: string): boolean {\n\tconst patternParts = pattern.split(\"/\").filter(Boolean);\n\tconst pathParts = path.split(\"/\").filter(Boolean);\n\n\tfor (let i = 0; i < patternParts.length; i++) {\n\t\tconst pp = patternParts[i]!;\n\n\t\t// Catch-all: matches remaining segments, but only if we've matched\n\t\t// all preceding segments up to this point\n\t\tif (pp.endsWith(\"*\")) {\n\t\t\t// All segments before the catch-all must have matched (we got here)\n\t\t\t// and there must be at least one remaining path segment\n\t\t\treturn i <= pathParts.length;\n\t\t}\n\n\t\t// No more path segments to match against\n\t\tif (i >= pathParts.length) return false;\n\n\t\t// Dynamic segment matches any single segment\n\t\tif (pp.startsWith(\":\")) continue;\n\n\t\t// Static segment must match exactly\n\t\tif (pp !== pathParts[i]) return false;\n\t}\n\n\t// All pattern parts consumed — path must also be fully consumed\n\treturn patternParts.length === pathParts.length;\n}\n\n/**\n * Create the SSR request handler for production.\n *\n * @example\n * import { routes } from \"virtual:zero/routes\"\n * import { routeMiddleware } from \"virtual:zero/route-middleware\"\n * import { createServer } from \"@pyreon/zero\"\n *\n * export default createServer({ routes, routeMiddleware, apiRoutes })\n */\nexport function createServer(options: CreateServerOptions) {\n\tconst config = options.config ?? {};\n\n\tconst allMiddleware: Middleware[] = [];\n\n\t// API routes run first — they short-circuit before SSR\n\tif (options.apiRoutes?.length) {\n\t\tallMiddleware.push(createApiMiddleware(options.apiRoutes));\n\t}\n\n\t// Per-route middleware runs next\n\tif (options.routeMiddleware?.length) {\n\t\tallMiddleware.push(\n\t\t\tcreateRouteMiddlewareDispatcher(options.routeMiddleware),\n\t\t);\n\t}\n\n\t// Then global middleware from config and options\n\tallMiddleware.push(...(config.middleware ?? []));\n\tallMiddleware.push(...(options.middleware ?? []));\n\n\tconst { App } = createApp({\n\t\troutes: options.routes,\n\t\trouterMode: \"history\",\n\t});\n\n\tconst handler = createHandler({\n\t\tApp,\n\t\troutes: options.routes,\n\t\tmiddleware: allMiddleware,\n\t\tmode: config.ssr?.mode ?? \"string\",\n\t\t...(options.template ? { template: options.template } : {}),\n\t\t...(options.clientEntry ? { clientEntry: options.clientEntry } : {}),\n\t});\n\n\t// Wrap handler with 404 detection when a notFoundComponent is provided\n\tif (!options.notFoundComponent) return handler;\n\n\tconst NotFound = options.notFoundComponent;\n\tconst routePatterns = flattenRoutePatterns(options.routes);\n\n\treturn async (req: Request) => {\n\t\tconst url = new URL(req.url);\n\t\tconst pathname = url.pathname;\n\n\t\t// Check if any defined route matches this path\n\t\tif (!routePatterns.some((pattern) => matchPattern(pattern, pathname))) {\n\t\t\tconst fullHtml = await render404Page(NotFound, options.template);\n\t\t\treturn new Response(fullHtml, {\n\t\t\t\tstatus: 404,\n\t\t\t\theaders: { \"Content-Type\": \"text/html; charset=utf-8\" },\n\t\t\t});\n\t\t}\n\n\t\treturn handler(req);\n\t};\n}\n\n/** Extract all URL patterns from a nested route tree. */\nfunction flattenRoutePatterns(routes: RouteRecord[], prefix = \"\"): string[] {\n\tconst patterns: string[] = [];\n\tfor (const route of routes) {\n\t\tconst fullPath =\n\t\t\troute.path === \"/\" && prefix ? prefix : `${prefix}${route.path}`;\n\t\tpatterns.push(fullPath);\n\t\tif (route.children) {\n\t\t\tpatterns.push(\n\t\t\t\t...flattenRoutePatterns(route.children as RouteRecord[], fullPath),\n\t\t\t);\n\t\t}\n\t}\n\treturn patterns;\n}\n","import type { ZeroConfig } from './types'\n\n/**\n * Define a Zero configuration.\n * Used in `zero.config.ts` at the project root.\n *\n * @example\n * import { defineConfig } from \"@pyreon/zero/config\"\n *\n * export default defineConfig({\n * mode: \"ssr\",\n * ssr: { mode: \"stream\" },\n * port: 3000,\n * })\n */\nexport function defineConfig(config: ZeroConfig): ZeroConfig {\n return config\n}\n\n/** Merge user config with defaults. */\nexport function resolveConfig(\n userConfig: ZeroConfig = {},\n): Required<Pick<ZeroConfig, 'mode' | 'base' | 'port' | 'adapter'>> & ZeroConfig {\n return {\n mode: 'ssr',\n base: '/',\n port: 3000,\n adapter: 'node',\n ...userConfig,\n ssr: {\n mode: 'string',\n ...userConfig.ssr,\n },\n }\n}\n","/**\n * Dev-only error overlay for SSR/loader errors.\n * Renders a styled HTML page with the error stack trace.\n */\nexport function renderErrorOverlay(error: Error): string {\n const title = escapeHtml(error.message || 'Unknown error')\n const stack = escapeHtml(error.stack || '')\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>SSR Error — Pyreon Zero</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: ui-monospace, \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace;\n background: #1a1a2e;\n color: #e0e0e0;\n min-height: 100vh;\n padding: 2rem;\n }\n .overlay {\n max-width: 900px;\n margin: 0 auto;\n }\n .header {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n }\n .badge {\n background: #e74c3c;\n color: white;\n padding: 0.25rem 0.75rem;\n border-radius: 4px;\n font-size: 0.75rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n .label {\n color: #888;\n font-size: 0.85rem;\n }\n .message {\n font-size: 1.25rem;\n color: #ff6b6b;\n margin-bottom: 1.5rem;\n line-height: 1.5;\n word-break: break-word;\n }\n .stack {\n background: #16213e;\n border: 1px solid #2a2a4a;\n border-radius: 8px;\n padding: 1.25rem;\n overflow-x: auto;\n font-size: 0.8rem;\n line-height: 1.7;\n white-space: pre-wrap;\n word-break: break-all;\n }\n .stack .at { color: #888; }\n .stack .file { color: #4ecdc4; }\n .hint {\n margin-top: 1.5rem;\n padding: 1rem;\n background: #1e2a45;\n border-radius: 6px;\n border-left: 3px solid #3498db;\n font-size: 0.8rem;\n color: #aaa;\n line-height: 1.5;\n }\n </style>\n</head>\n<body>\n <div class=\"overlay\">\n <div class=\"header\">\n <span class=\"badge\">SSR Error</span>\n <span class=\"label\">Pyreon Zero — Dev Mode</span>\n </div>\n <div class=\"message\">${title}</div>\n <pre class=\"stack\">${formatStack(stack)}</pre>\n <div class=\"hint\">\n This error occurred during server-side rendering. Check the terminal for\n the full stack trace. This overlay is only shown in development.\n </div>\n </div>\n</body>\n</html>`\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction formatStack(stack: string): string {\n return stack\n .split('\\n')\n .map((line) => {\n if (line.includes('at ')) {\n const fileMatch = line.match(/\\(([^)]+)\\)/)\n if (fileMatch) {\n return line.replace(fileMatch[0], `(<span class=\"file\">${fileMatch[1]}</span>)`)\n }\n }\n return line\n })\n .join('\\n')\n}\n","import { existsSync, readdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\nimport { generateApiRouteModule } from './api-routes'\nimport { resolveConfig } from './config'\n\n/**\n * Scan node_modules/@pyreon/ to discover all installed Pyreon packages.\n * Returns package names to exclude from Vite's dep optimizer.\n */\nfunction scanPyreonPackages(root: string): string[] {\n const pyreonDir = join(root, 'node_modules', '@pyreon')\n if (!existsSync(pyreonDir)) return []\n\n try {\n return readdirSync(pyreonDir)\n .filter((name) => !name.startsWith('.'))\n .map((name) => `@pyreon/${name}`)\n } catch {\n return []\n }\n}\nimport { matchPattern } from \"./entry-server\";\nimport { renderErrorOverlay } from \"./error-overlay\";\nimport {\n\tgenerateMiddlewareModule,\n\tgenerateRouteModule,\n\tscanRouteFiles,\n} from \"./fs-router\";\nimport { render404Page } from \"./not-found\";\nimport type { ZeroConfig } from \"./types\";\n\nconst VIRTUAL_ROUTES_ID = \"virtual:zero/routes\";\nconst RESOLVED_VIRTUAL_ROUTES_ID = `\\0${VIRTUAL_ROUTES_ID}`;\n\nconst VIRTUAL_MIDDLEWARE_ID = \"virtual:zero/route-middleware\";\nconst RESOLVED_VIRTUAL_MIDDLEWARE_ID = `\\0${VIRTUAL_MIDDLEWARE_ID}`;\n\nconst VIRTUAL_API_ROUTES_ID = \"virtual:zero/api-routes\";\nconst RESOLVED_VIRTUAL_API_ROUTES_ID = `\\0${VIRTUAL_API_ROUTES_ID}`;\n\n/**\n * Zero Vite plugin — adds file-based routing and zero-config conventions\n * on top of @pyreon/vite-plugin.\n *\n * @example\n * // vite.config.ts\n * import pyreon from \"@pyreon/vite-plugin\"\n * import zero from \"@pyreon/zero\"\n *\n * export default {\n * plugins: [pyreon(), zero()],\n * }\n */\nexport function zeroPlugin(userConfig: ZeroConfig = {}): Plugin {\n\tconst config = resolveConfig(userConfig);\n\tlet routesDir: string;\n\tlet root: string;\n\n\tconst plugin: Plugin & { _zeroConfig: ZeroConfig } = {\n\t\tname: \"pyreon-zero\",\n\t\tenforce: \"pre\",\n\t\t_zeroConfig: userConfig,\n\n\t\tconfigResolved(resolvedConfig) {\n\t\t\troot = resolvedConfig.root;\n\t\t\troutesDir = `${root}/src/routes`;\n\t\t},\n\n\t\tresolveId(id) {\n\t\t\tif (id === VIRTUAL_ROUTES_ID) return RESOLVED_VIRTUAL_ROUTES_ID;\n\t\t\tif (id === VIRTUAL_MIDDLEWARE_ID) return RESOLVED_VIRTUAL_MIDDLEWARE_ID;\n\t\t\tif (id === VIRTUAL_API_ROUTES_ID) return RESOLVED_VIRTUAL_API_ROUTES_ID;\n\t\t},\n\n\t\tasync load(id) {\n\t\t\tif (id === RESOLVED_VIRTUAL_ROUTES_ID) {\n\t\t\t\ttry {\n\t\t\t\t\tconst files = await scanRouteFiles(routesDir);\n\t\t\t\t\treturn generateRouteModule(files, routesDir, {\n\t\t\t\t\tstaticImports: config.mode === 'ssg',\n\t\t\t\t});\n\t\t\t\t} catch (_err) {\n\t\t\t\t\treturn `export const routes = []`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (id === RESOLVED_VIRTUAL_MIDDLEWARE_ID) {\n\t\t\t\ttry {\n\t\t\t\t\tconst files = await scanRouteFiles(routesDir);\n\t\t\t\t\treturn generateMiddlewareModule(files, routesDir);\n\t\t\t\t} catch (_err) {\n\t\t\t\t\treturn `export const routeMiddleware = []`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (id === RESOLVED_VIRTUAL_API_ROUTES_ID) {\n\t\t\t\ttry {\n\t\t\t\t\tconst files = await scanRouteFiles(routesDir);\n\t\t\t\t\treturn generateApiRouteModule(files, routesDir);\n\t\t\t\t} catch (_err) {\n\t\t\t\t\treturn `export const apiRoutes = []`;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tconfigureServer(server) {\n\t\t\t// 404 handler — check if the requested path matches any route.\n\t\t\t// If not, render the nearest _404.tsx component with a 404 status.\n\t\t\t// Uses a sync wrapper that calls the async handler, since Connect\n\t\t\t// middleware does not natively support async functions.\n\t\t\tserver.middlewares.use((req, res, next) => {\n\t\t\t\tconst accept = req.headers.accept ?? \"\";\n\t\t\t\t// Accept HTML requests and wildcard requests (fetch without explicit Accept header)\n\t\t\t\tif (!accept.includes(\"text/html\") && !accept.includes(\"*/*\"))\n\t\t\t\t\treturn next();\n\n\t\t\t\tconst pathname = req.url?.split(\"?\")[0] ?? \"/\";\n\n\t\t\t\t// Skip static assets, Vite internal requests, and file-like paths (with extensions)\n\t\t\t\tif (pathname.startsWith(\"/@\") || pathname.startsWith(\"/__\"))\n\t\t\t\t\treturn next();\n\t\t\t\tif (/\\.\\w+$/.test(pathname)) return next();\n\n\t\t\t\thandle404(server, routesDir, pathname, res).then(\n\t\t\t\t\t(handled) => {\n\t\t\t\t\t\tif (!handled) next();\n\t\t\t\t\t},\n\t\t\t\t\t(err) => {\n\t\t\t\t\t\t// oxlint-disable-next-line no-console\n\t\t\t\t\t\tconsole.error('[zero] Error in 404 handler:', err);\n\t\t\t\t\t\tnext();\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\n\t\t\t// SSR error overlay — intercept HTML requests and catch SSR errors\n\t\t\t// This runs as a late middleware (return function) so it wraps\n\t\t\t// Vite's own SSR handling and catches rendering failures.\n\t\t\tserver.middlewares.use((req, res, next) => {\n\t\t\t\tconst accept = req.headers.accept ?? \"\";\n\t\t\t\tif (!accept.includes(\"text/html\")) return next();\n\n\t\t\t\tconst originalEnd = res.end.bind(res);\n\t\t\t\tlet errored = false;\n\n\t\t\t\tconst handleError = (err: unknown) => {\n\t\t\t\t\tif (errored) return;\n\t\t\t\t\terrored = true;\n\t\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\tserver.ssrFixStacktrace(error);\n\t\t\t\t\tconst html = renderErrorOverlay(error);\n\t\t\t\t\tres.statusCode = 500;\n\t\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\t\tres.setHeader(\"Content-Length\", Buffer.byteLength(html));\n\t\t\t\t\toriginalEnd(html);\n\t\t\t\t};\n\n\t\t\t\tres.on(\"error\", handleError);\n\n\t\t\t\t// Wrap next() in try/catch to handle both sync and async errors.\n\t\t\t\t// Express-style middleware may throw synchronously or pass errors\n\t\t\t\t// through next(err), and Vite's SSR pipeline may reject promises.\n\t\t\t\ttry {\n\t\t\t\t\tconst result = next() as unknown;\n\t\t\t\t\t// Handle async errors from Vite's SSR pipeline\n\t\t\t\t\tif (\n\t\t\t\t\t\tresult &&\n\t\t\t\t\t\ttypeof (result as Promise<unknown>).catch === \"function\"\n\t\t\t\t\t) {\n\t\t\t\t\t\t(result as Promise<unknown>).catch(handleError);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\thandleError(err);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Watch routes directory for changes\n\t\t\tserver.watcher.add(`${routesDir}/**/*.{tsx,jsx,ts,js}`);\n\n\t\t\t// Invalidate virtual modules when route files change\n\t\t\tserver.watcher.on(\"all\", (event, path) => {\n\t\t\t\tif (\n\t\t\t\t\tpath.startsWith(routesDir) &&\n\t\t\t\t\t(event === \"add\" || event === \"unlink\")\n\t\t\t\t) {\n\t\t\t\t\tfor (const resolvedId of [\n\t\t\t\t\t\tRESOLVED_VIRTUAL_ROUTES_ID,\n\t\t\t\t\t\tRESOLVED_VIRTUAL_MIDDLEWARE_ID,\n\t\t\t\t\t\tRESOLVED_VIRTUAL_API_ROUTES_ID,\n\t\t\t\t\t]) {\n\t\t\t\t\t\tconst mod = server.moduleGraph.getModuleById(resolvedId);\n\t\t\t\t\t\tif (mod) server.moduleGraph.invalidateModule(mod);\n\t\t\t\t\t}\n\t\t\t\t\tserver.ws.send({ type: \"full-reload\" });\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tconfig(userConfig) {\n\t\t\t// Discover all @pyreon/* packages installed in node_modules.\n\t\t\t// The \"bun\" export condition points to TS source — esbuild's\n\t\t\t// dep optimizer would compile them with the wrong JSX runtime.\n\t\t\tconst root = userConfig.root ?? process.cwd()\n\t\t\tconst pyreonExclude = scanPyreonPackages(root)\n\n\t\t\treturn {\n\t\t\t\tresolve: {\n\t\t\t\t\tconditions: ['bun'],\n\t\t\t\t},\n\t\t\t\toptimizeDeps: {\n\t\t\t\t\texclude: pyreonExclude,\n\t\t\t\t},\n\t\t\t\tserver: {\n\t\t\t\t\tport: config.port,\n\t\t\t\t},\n\t\t\t\tdefine: {\n\t\t\t\t\t__ZERO_MODE__: JSON.stringify(config.mode),\n\t\t\t\t\t__ZERO_BASE__: JSON.stringify(config.base),\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n\n\treturn plugin;\n}\n\n/**\n * Check if the requested path matches any route. If not, render a 404 page.\n * Returns true if the 404 was handled (response sent), false otherwise.\n *\n * In dev mode, the _404.tsx component cannot be SSR-rendered because\n * the compiler emits _tpl() calls that require `document`. Instead,\n * we return a static 404 page. The actual component rendering happens\n * on the client side when the SPA loads.\n */\nasync function handle404(\n\tserver: import(\"vite\").ViteDevServer,\n\t_routesDir: string,\n\tpathname: string,\n\tres: import(\"http\").ServerResponse,\n): Promise<boolean> {\n\tconst mod = await server.ssrLoadModule(VIRTUAL_ROUTES_ID);\n\tconst routes = mod.routes as Array<{ path?: string; children?: unknown[] }>;\n\tconst patterns = flattenRoutePatterns(routes);\n\n\tif (patterns.some((pattern) => matchPattern(pattern, pathname))) {\n\t\treturn false; // Route matches — not a 404\n\t}\n\n\t// No route matched — return a 404.\n\t// In dev, we return a static page since the compiler emits _tpl() calls\n\t// that require document (unavailable in SSR). The _404.tsx component\n\t// renders on the client side after hydration.\n\tconst html = await render404Page(undefined);\n\n\tres.statusCode = 404;\n\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\tres.setHeader(\"Content-Length\", Buffer.byteLength(html));\n\tres.end(html);\n\treturn true;\n}\n\n/** Extract all URL patterns from a nested route tree. */\nfunction flattenRoutePatterns(\n\troutes: Array<{ path?: string; children?: unknown[] }>,\n\tprefix = \"\",\n): string[] {\n\tconst patterns: string[] = [];\n\tfor (const route of routes) {\n\t\tif (!route.path) continue;\n\t\tconst fullPath =\n\t\t\troute.path === \"/\" && prefix ? prefix : `${prefix}${route.path}`;\n\t\tpatterns.push(fullPath);\n\t\tif (route.children) {\n\t\t\tpatterns.push(\n\t\t\t\t...flattenRoutePatterns(\n\t\t\t\t\troute.children as Array<{ path?: string; children?: unknown[] }>,\n\t\t\t\t\tfullPath,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\treturn patterns;\n}\n","import type { ISRConfig } from './types'\n\n// ─── ISR Cache ───────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n html: string\n headers: Record<string, string>\n timestamp: number\n}\n\n/**\n * In-memory ISR cache with stale-while-revalidate semantics.\n *\n * Wraps an SSR handler and caches responses per URL path.\n * Serves stale content immediately while revalidating in the background.\n */\nexport function createISRHandler(\n handler: (req: Request) => Promise<Response>,\n config: ISRConfig,\n): (req: Request) => Promise<Response> {\n const cache = new Map<string, CacheEntry>()\n const revalidating = new Set<string>()\n const revalidateMs = config.revalidate * 1000\n\n async function revalidate(url: URL) {\n const key = url.pathname\n if (revalidating.has(key)) return\n revalidating.add(key)\n\n try {\n const req = new Request(url.href, { method: 'GET' })\n const res = await handler(req)\n const html = await res.text()\n const headers: Record<string, string> = {}\n res.headers.forEach((v, k) => {\n headers[k] = v\n })\n\n cache.set(key, { html, headers, timestamp: Date.now() })\n } catch {\n // Revalidation failed — stale cache entry remains valid\n } finally {\n revalidating.delete(key)\n }\n }\n\n return async (req: Request): Promise<Response> => {\n // Only cache GET requests\n if (req.method !== 'GET') {\n return handler(req)\n }\n\n const url = new URL(req.url)\n const key = url.pathname\n const entry = cache.get(key)\n\n if (entry) {\n const age = Date.now() - entry.timestamp\n\n if (age > revalidateMs) {\n // Stale — serve cached but revalidate in background\n revalidate(url)\n }\n\n return new Response(entry.html, {\n status: 200,\n headers: {\n ...entry.headers,\n 'content-type': 'text/html; charset=utf-8',\n 'x-isr-cache': age > revalidateMs ? 'STALE' : 'HIT',\n 'x-isr-age': String(Math.round(age / 1000)),\n },\n })\n }\n\n // Cache miss — render, cache, and return\n const res = await handler(req)\n const html = await res.text()\n const headers: Record<string, string> = {}\n res.headers.forEach((v, k) => {\n headers[k] = v\n })\n\n cache.set(key, { html, headers, timestamp: Date.now() })\n\n return new Response(html, {\n status: 200,\n headers: {\n ...headers,\n 'content-type': 'text/html; charset=utf-8',\n 'x-isr-cache': 'MISS',\n },\n })\n }\n}\n","import type { AdapterBuildOptions } from '../types'\n\n/**\n * Validate that adapter build inputs exist before copying.\n * Throws with a clear error message if directories are missing.\n * @internal\n */\nexport async function validateBuildInputs(options: AdapterBuildOptions): Promise<void> {\n const { existsSync } = await import('node:fs')\n if (!existsSync(options.clientOutDir)) {\n throw new Error(`[zero:adapter] Client build output not found: ${options.clientOutDir}. Run \"vite build\" first.`)\n }\n if (!existsSync(options.serverEntry)) {\n throw new Error(`[zero:adapter] Server entry not found: ${options.serverEntry}. Run \"vite build --ssr\" first.`)\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\nimport { validateBuildInputs } from './validate'\n\n/**\n * Bun adapter — generates a standalone Bun.serve() entry.\n */\nexport function bunAdapter(): Adapter {\n return {\n name: 'bun',\n async build(options: AdapterBuildOptions) {\n await validateBuildInputs(options)\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const outDir = options.outDir\n await mkdir(outDir, { recursive: true })\n\n // Copy server and client builds\n await cp(options.clientOutDir, join(outDir, 'client'), {\n recursive: true,\n })\n await cp(join(options.serverEntry, '..'), join(outDir, 'server'), {\n recursive: true,\n })\n\n const port = options.config.port ?? 3000\n const serverEntry = `\nconst handler = (await import(\"./server/entry-server.js\")).default\nconst clientDir = new URL(\"./client/\", import.meta.url).pathname\n\nBun.serve({\n port: ${port},\n async fetch(req) {\n const url = new URL(req.url)\n\n // Try static files first\n if (req.method === \"GET\") {\n const filePath = clientDir + (url.pathname === \"/\" ? \"index.html\" : url.pathname)\n // Prevent path traversal — ensure resolved path stays within clientDir\n const resolved = Bun.resolveSync(filePath, \".\")\n if (!resolved.startsWith(Bun.resolveSync(clientDir, \".\"))) {\n return new Response(\"Forbidden\", { status: 403 })\n }\n const file = Bun.file(filePath)\n if (await file.exists()) {\n return new Response(file, {\n headers: {\n \"cache-control\": filePath.endsWith(\".js\") || filePath.endsWith(\".css\")\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=3600\",\n },\n })\n }\n }\n\n // Fall through to SSR handler\n return handler(req)\n },\n})\n\nconsole.log(\"\\\\n ⚡ Zero production server running on http://localhost:${port}\\\\n\")\n`.trimStart()\n\n await writeFile(join(outDir, 'index.ts'), serverEntry)\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\nimport { validateBuildInputs } from './validate'\n\n/**\n * Cloudflare Pages adapter — generates output for Cloudflare Pages with Functions.\n *\n * Produces:\n * - Client assets in the output directory root (served as static)\n * - `_worker.js` — Cloudflare Pages Function for SSR\n *\n * Note: Cloudflare Pages Functions have a ~1MB module size limit.\n * For large apps, configure Vite's SSR build to bundle server code:\n * `ssr: { noExternal: true }` in vite.config.ts.\n *\n * Deploy with: `npx wrangler pages deploy ./dist`\n *\n * @example\n * ```ts\n * // zero.config.ts\n * import { defineConfig } from \"@pyreon/zero\"\n *\n * export default defineConfig({\n * adapter: \"cloudflare\",\n * })\n * ```\n */\nexport function cloudflareAdapter(): Adapter {\n return {\n name: 'cloudflare',\n async build(options: AdapterBuildOptions) {\n await validateBuildInputs(options)\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const outDir = options.outDir\n await mkdir(outDir, { recursive: true })\n\n // Copy client assets to root (Cloudflare serves static files from root)\n await cp(options.clientOutDir, outDir, { recursive: true })\n\n // Copy server build\n await cp(join(options.serverEntry, '..'), join(outDir, '_server'), {\n recursive: true,\n })\n\n // Generate Cloudflare Pages _worker.js (ES module format)\n const workerEntry = `\nimport handler from \"./_server/entry-server.js\"\n\nexport default {\n async fetch(request, env, ctx) {\n const url = new URL(request.url)\n\n // Let Cloudflare serve static assets (files with extensions)\n // This check is a fallback — Pages routes static files automatically\n const ext = url.pathname.split(\".\").pop()\n if (ext && ext !== url.pathname && !url.pathname.endsWith(\"/\")) {\n // Cloudflare Pages handles static assets automatically via its asset binding\n // Only reach here if the file doesn't exist — fall through to SSR\n }\n\n // SSR handler\n try {\n return await handler(request)\n } catch (err) {\n return new Response(\"Internal Server Error\", { status: 500 })\n }\n },\n}\n`.trimStart()\n\n await writeFile(join(outDir, '_worker.js'), workerEntry)\n\n // Cloudflare Pages config — _routes.json for routing\n const routesConfig = {\n version: 1,\n include: ['/*'],\n exclude: ['/assets/*', '/favicon.*', '/site.webmanifest', '/robots.txt', '/sitemap.xml'],\n }\n\n await writeFile(join(outDir, '_routes.json'), JSON.stringify(routesConfig, null, 2))\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\nimport { validateBuildInputs } from './validate'\n\n/**\n * Netlify adapter — generates output for Netlify Functions (v2).\n *\n * Produces:\n * - Client assets in `publish/` directory\n * - `netlify/functions/ssr.mjs` — Netlify Function for SSR\n * - `netlify.toml` — routing configuration\n *\n * @example\n * ```ts\n * // zero.config.ts\n * import { defineConfig } from \"@pyreon/zero\"\n *\n * export default defineConfig({\n * adapter: \"netlify\",\n * })\n * ```\n */\nexport function netlifyAdapter(): Adapter {\n return {\n name: 'netlify',\n async build(options: AdapterBuildOptions) {\n await validateBuildInputs(options)\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const outDir = options.outDir\n const publishDir = join(outDir, 'publish')\n const functionsDir = join(outDir, 'netlify', 'functions')\n\n await mkdir(publishDir, { recursive: true })\n await mkdir(functionsDir, { recursive: true })\n\n // Copy client assets to publish/\n await cp(options.clientOutDir, publishDir, { recursive: true })\n\n // Copy server build to functions directory\n await cp(join(options.serverEntry, '..'), join(functionsDir, '_server'), {\n recursive: true,\n })\n\n // Generate Netlify Function (v2 format — ESM, Web-standard Request/Response)\n const funcEntry = `\nimport handler from \"./_server/entry-server.js\"\n\nexport default async function(req, context) {\n try {\n return await handler(req)\n } catch (err) {\n return new Response(\"Internal Server Error\", { status: 500 })\n }\n}\n\nexport const config = {\n path: \"/*\",\n preferStatic: true,\n}\n`.trimStart()\n\n await writeFile(join(functionsDir, 'ssr.mjs'), funcEntry)\n\n // Generate netlify.toml\n const toml = `\n[build]\n publish = \"publish\"\n functions = \"netlify/functions\"\n\n[[headers]]\n for = \"/assets/*\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n\n[[redirects]]\n from = \"/*\"\n to = \"/.netlify/functions/ssr\"\n status = 200\n conditions = {Role = [\"admin\", \"user\", \"\"]}\n`.trimStart()\n\n await writeFile(join(outDir, 'netlify.toml'), toml)\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\nimport { validateBuildInputs } from './validate'\n\n/**\n * Node.js adapter — generates a standalone server entry using node:http.\n */\nexport function nodeAdapter(): Adapter {\n return {\n name: 'node',\n async build(options: AdapterBuildOptions) {\n await validateBuildInputs(options)\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const outDir = options.outDir\n await mkdir(outDir, { recursive: true })\n\n // Copy server and client builds\n await cp(options.clientOutDir, join(outDir, 'client'), {\n recursive: true,\n })\n await cp(join(options.serverEntry, '..'), join(outDir, 'server'), {\n recursive: true,\n })\n\n // Generate standalone server entry\n const port = options.config.port ?? 3000\n const serverEntry = `\nimport { createServer } from \"node:http\"\nimport { readFile } from \"node:fs/promises\"\nimport { join, extname } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url))\nconst handler = (await import(\"./server/entry-server.js\")).default\nconst clientDir = join(__dirname, \"client\")\n\nconst MIME_TYPES = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".svg\": \"image/svg+xml\",\n \".woff2\": \"font/woff2\",\n \".woff\": \"font/woff\",\n \".ico\": \"image/x-icon\",\n}\n\nconst server = createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", \"http://localhost\")\n\n // Try to serve static files first\n if (req.method === \"GET\") {\n try {\n const filePath = join(clientDir, url.pathname === \"/\" ? \"index.html\" : url.pathname)\n // Prevent path traversal — ensure resolved path stays within clientDir\n const { resolve } = await import(\"node:path\")\n const resolved = resolve(filePath)\n if (!resolved.startsWith(resolve(clientDir))) {\n res.writeHead(403)\n res.end(\"Forbidden\")\n return\n }\n const ext = extname(filePath)\n if (ext && ext !== \".html\") {\n const data = await readFile(filePath)\n const mime = MIME_TYPES[ext] || \"application/octet-stream\"\n res.writeHead(200, {\n \"content-type\": mime,\n \"cache-control\": ext === \".js\" || ext === \".css\"\n ? \"public, max-age=31536000, immutable\"\n : \"public, max-age=3600\",\n })\n res.end(data)\n return\n }\n } catch {}\n }\n\n // Fall through to SSR handler\n const headers = {}\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) headers[key] = Array.isArray(value) ? value.join(\", \") : value\n }\n\n const request = new Request(url.href, {\n method: req.method,\n headers,\n })\n\n const response = await handler(request)\n const body = await response.text()\n\n const responseHeaders = {}\n response.headers.forEach((v, k) => { responseHeaders[k] = v })\n\n res.writeHead(response.status, responseHeaders)\n res.end(body)\n})\n\nserver.listen(${port}, () => {\n console.log(\"\\\\n ⚡ Zero production server running on http://localhost:${port}\\\\n\")\n})\n`.trimStart()\n\n await writeFile(join(outDir, 'index.js'), serverEntry)\n await writeFile(join(outDir, 'package.json'), JSON.stringify({ type: 'module' }, null, 2))\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\n\n/**\n * Static adapter — just copies the client build output.\n * Used with SSG mode where all pages are pre-rendered at build time.\n */\nexport function staticAdapter(): Adapter {\n return {\n name: 'static',\n async build(options: AdapterBuildOptions) {\n const { cp, mkdir } = await import('node:fs/promises')\n\n await mkdir(options.outDir, { recursive: true })\n await cp(options.clientOutDir, options.outDir, { recursive: true })\n },\n }\n}\n","import type { Adapter, AdapterBuildOptions } from '../types'\nimport { validateBuildInputs } from './validate'\n\n/**\n * Vercel adapter — generates output for Vercel's Build Output API v3.\n *\n * Produces a `.vercel/output` directory with:\n * - `static/` — client-side assets (JS, CSS, images)\n * - `functions/ssr.func/` — serverless function for SSR\n * - `config.json` — routing configuration\n *\n * @example\n * ```ts\n * // zero.config.ts\n * import { defineConfig } from \"@pyreon/zero\"\n *\n * export default defineConfig({\n * adapter: \"vercel\",\n * })\n * ```\n */\nexport function vercelAdapter(): Adapter {\n return {\n name: 'vercel',\n async build(options: AdapterBuildOptions) {\n await validateBuildInputs(options)\n const { writeFile, cp, mkdir } = await import('node:fs/promises')\n const { join } = await import('node:path')\n\n const vercelDir = join(options.outDir, '.vercel', 'output')\n const staticDir = join(vercelDir, 'static')\n const funcDir = join(vercelDir, 'functions', 'ssr.func')\n\n await mkdir(staticDir, { recursive: true })\n await mkdir(funcDir, { recursive: true })\n\n // Copy client assets to static/\n await cp(options.clientOutDir, staticDir, { recursive: true })\n\n // Copy server build to function directory\n await cp(join(options.serverEntry, '..'), funcDir, { recursive: true })\n\n // Generate serverless function entry\n const funcEntry = `\nexport default async function handler(req) {\n const handler = (await import(\"./entry-server.js\")).default\n return handler(req)\n}\n`.trimStart()\n\n await writeFile(join(funcDir, 'index.js'), funcEntry)\n\n // Function config\n await writeFile(\n join(funcDir, '.vc-config.json'),\n JSON.stringify(\n {\n runtime: 'nodejs20.x',\n handler: 'index.js',\n launcherType: 'Nodejs',\n },\n null,\n 2,\n ),\n )\n\n // Vercel Build Output config\n const config = {\n version: 3,\n routes: [\n // Serve static assets directly\n {\n src: '/assets/(.*)',\n headers: { 'Cache-Control': 'public, max-age=31536000, immutable' },\n },\n // Favicon and manifest\n { src: '/(favicon\\\\..*|site\\\\.webmanifest|robots\\\\.txt|sitemap\\\\.xml)', dest: '/$1' },\n // All other routes → SSR function\n { src: '/(.*)', dest: '/ssr' },\n ],\n }\n\n await writeFile(join(vercelDir, 'config.json'), JSON.stringify(config, null, 2))\n },\n }\n}\n","export { bunAdapter } from './bun'\nexport { cloudflareAdapter } from './cloudflare'\nexport { netlifyAdapter } from './netlify'\nexport { nodeAdapter } from './node'\nexport { staticAdapter } from './static'\nexport { vercelAdapter } from './vercel'\n\nimport type { Adapter, ZeroConfig } from '../types'\nimport { bunAdapter } from './bun'\nimport { cloudflareAdapter } from './cloudflare'\nimport { netlifyAdapter } from './netlify'\nimport { nodeAdapter } from './node'\nimport { staticAdapter } from './static'\nimport { vercelAdapter } from './vercel'\n\n/**\n * Resolve the adapter from config.\n * Returns a built-in adapter or throws if unknown.\n */\nexport function resolveAdapter(config: ZeroConfig): Adapter {\n const name = config.adapter ?? 'node'\n\n switch (name) {\n case 'node':\n return nodeAdapter()\n case 'bun':\n return bunAdapter()\n case 'static':\n return staticAdapter()\n case 'vercel':\n return vercelAdapter()\n case 'cloudflare':\n return cloudflareAdapter()\n case 'netlify':\n return netlifyAdapter()\n default:\n throw new Error(`[zero] Unknown adapter: \"${name}\". Use \"node\", \"bun\", \"static\", \"vercel\", \"cloudflare\", or \"netlify\".`)\n }\n}\n","import { onMount, onUnmount } from '@pyreon/core'\n\n/**\n * Observes an element and calls `onIntersect` once it enters the viewport.\n * Automatically disconnects after the first intersection.\n *\n * @param getElement - Getter for the target element (may be undefined before mount).\n * @param onIntersect - Callback fired when the element becomes visible.\n * @param rootMargin - IntersectionObserver rootMargin. Default: \"200px\".\n */\nexport function useIntersectionObserver(\n getElement: () => HTMLElement | undefined,\n onIntersect: () => void,\n rootMargin = '200px',\n) {\n onMount(() => {\n const el = getElement()\n if (!el) return undefined\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n onIntersect()\n observer.disconnect()\n }\n }\n },\n { rootMargin },\n )\n\n observer.observe(el)\n onUnmount(() => observer.disconnect())\n return undefined\n })\n}\n","//#region src/h.ts\n/** Marker for fragment nodes — renders children without a wrapper element */\nconst Fragment = Symbol(\"Pyreon.Fragment\");\n/**\n* Hyperscript function — the compiled output of JSX.\n* `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n*\n* Generic on P so TypeScript validates props match the component's signature\n* at the call site, then stores the result in the loosely-typed VNode.\n*/\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nconst EMPTY_PROPS = {};\nfunction h(type, props, ...children) {\n\treturn {\n\t\ttype,\n\t\tprops: props ?? EMPTY_PROPS,\n\t\tchildren: normalizeChildren(children),\n\t\tkey: props?.key ?? null\n\t};\n}\nfunction normalizeChildren(children) {\n\tfor (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);\n\treturn children;\n}\nfunction flattenChildren(children) {\n\tconst result = [];\n\tfor (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));\n\telse result.push(child);\n\treturn result;\n}\n\n//#endregion\n//#region src/jsx-runtime.ts\n/**\n* JSX automatic runtime.\n*\n* When tsconfig has `\"jsxImportSource\": \"@pyreon/core\"`, the TS/bundler compiler\n* rewrites JSX to imports from this file automatically:\n* <div class=\"x\" /> → jsx(\"div\", { class: \"x\" })\n*/\nfunction jsx(type, props, key) {\n\tconst { children, ...rest } = props;\n\tconst propsWithKey = key != null ? {\n\t\t...rest,\n\t\tkey\n\t} : rest;\n\tif (typeof type === \"function\") return h(type, children !== void 0 ? {\n\t\t...propsWithKey,\n\t\tchildren\n\t} : propsWithKey);\n\treturn h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);\n}\nconst jsxs = jsx;\n\n//#endregion\nexport { Fragment, jsx, jsxs };\n//# sourceMappingURL=jsx-runtime.js.map","import type { VNodeChild } from '@pyreon/core'\nimport { createRef } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\nimport type { FormatSource } from './image-plugin'\nimport { useIntersectionObserver } from './utils/use-intersection-observer'\n\n// ─── Image optimization component ───────────────────────────────────────────\n//\n// <Image> provides:\n// - Lazy loading via IntersectionObserver (loads when near viewport)\n// - Automatic width/height to prevent CLS (Cumulative Layout Shift)\n// - Responsive srcset generation from width descriptors\n// - Multi-format support via <picture> (WebP/AVIF with fallback)\n// - Blur-up placeholder while loading\n// - Priority loading for above-the-fold images\n\nexport interface ImageProps {\n /** Image source URL. */\n src: string\n /** Alt text (required for accessibility). */\n alt: string\n /** Intrinsic width of the image. */\n width: number\n /** Intrinsic height of the image. */\n height: number\n /** Responsive sizes attribute. Default: \"100vw\" */\n sizes?: string\n /** Responsive srcset string or source array. */\n srcset?: string | ImageSource[]\n /** Per-format source sets for <picture>. Provided automatically by imagePlugin. */\n formats?: FormatSource[]\n /** Loading strategy. \"lazy\" uses IntersectionObserver, \"eager\" loads immediately. Default: \"lazy\" */\n loading?: 'lazy' | 'eager'\n /** Mark as priority (LCP image). Disables lazy loading, adds fetchPriority=\"high\". */\n priority?: boolean\n /** Low-quality placeholder image URL or base64 data URI for blur-up effect. */\n placeholder?: string\n /** CSS class name. */\n class?: string\n /** Inline styles. */\n style?: string\n /** CSS object-fit. Default: \"cover\" */\n fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'\n /** Decode async. Default: true */\n decoding?: 'sync' | 'async' | 'auto'\n}\n\nexport interface ImageSource {\n src: string\n width: number\n}\n\n/**\n * Optimized image component with lazy loading, responsive images,\n * multi-format <picture> support, and blur-up placeholders.\n *\n * @example\n * // With imagePlugin — spread the import directly\n * import hero from \"./hero.jpg?optimize\"\n * <Image {...hero} alt=\"Hero\" priority />\n *\n * @example\n * // Manual usage\n * <Image src=\"/hero.jpg\" alt=\"Hero\" width={1200} height={630} />\n */\nexport function Image(props: ImageProps): VNodeChild {\n const isEager = props.priority || props.loading === 'eager'\n const loaded = signal(isEager)\n const inView = signal(isEager)\n const containerRef = createRef<HTMLElement>()\n\n // Resolve srcset from string or array\n const resolvedSrcset =\n typeof props.srcset === 'string'\n ? props.srcset\n : props.srcset?.map((s) => `${s.src} ${s.width}w`).join(', ')\n\n const sizes = props.sizes ?? '100vw'\n const fit = props.fit ?? 'cover'\n const hasFormats = props.formats && props.formats.length > 0\n const aspectRatio = `${props.width} / ${props.height}`\n\n if (!isEager) {\n useIntersectionObserver(\n () => containerRef.current ?? undefined,\n () => inView.set(true),\n )\n }\n\n // Static styles (don't depend on signals)\n const containerStyle = [\n 'position: relative',\n 'overflow: hidden',\n `aspect-ratio: ${aspectRatio}`,\n `max-width: ${props.width}px`,\n 'width: 100%',\n props.style,\n ]\n .filter(Boolean)\n .join('; ')\n\n const imgEl = (\n <img\n src={() => (inView() ? props.src : '')}\n srcSet={() => (!hasFormats && inView() && resolvedSrcset ? resolvedSrcset : '')}\n sizes={resolvedSrcset ? sizes : undefined}\n alt={props.alt}\n width={props.width}\n height={props.height}\n loading={isEager ? 'eager' : 'lazy'}\n decoding={props.decoding ?? 'async'}\n fetchPriority={props.priority ? 'high' : undefined}\n onLoad={() => loaded.set(true)}\n style={() =>\n [\n 'display: block',\n 'width: 100%',\n 'height: 100%',\n `object-fit: ${fit}`,\n 'transition: opacity 0.3s ease',\n props.placeholder && !loaded() ? 'opacity: 0' : 'opacity: 1',\n ].join('; ')\n }\n />\n )\n\n return (\n <div ref={containerRef} class={props.class} style={containerStyle}>\n {props.placeholder && (\n <img\n src={props.placeholder}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"eager\"\n style={() =>\n [\n 'position: absolute',\n 'inset: 0',\n 'width: 100%',\n 'height: 100%',\n 'object-fit: cover',\n 'filter: blur(20px)',\n 'transform: scale(1.1)',\n 'transition: opacity 0.4s ease',\n loaded() ? 'opacity: 0; pointer-events: none' : 'opacity: 1',\n ].join('; ')\n }\n />\n )}\n {hasFormats ? (\n <picture>\n {props.formats?.map((fmt) => (\n <source\n type={fmt.type}\n srcSet={() => (inView() ? (fmt.srcset ?? '') : '')}\n sizes={sizes}\n />\n ))}\n {imgEl}\n </picture>\n ) : (\n imgEl\n )}\n </div>\n )\n}\n","import { createRef } from '@pyreon/core'\nimport { useRouter } from '@pyreon/router'\nimport { useIntersectionObserver } from './utils/use-intersection-observer'\n\n// ─── Link component with prefetching ────────────────────────────────────────\n//\n// Provides client-side navigation, prefetching, and active state tracking.\n// Three levels of API:\n//\n// 1. useLink(props) — composable returning handlers, state, and ref callback\n// 2. createLink(Comp) — HOC wrapping any component with link behavior\n// 3. Link — default <a>-based link (built on createLink)\n\nexport interface LinkProps {\n /** Target URL path. */\n href: string\n /** Link content. */\n children?: any\n /** CSS class name. */\n class?: string\n /** Class applied when this link matches the current route. */\n activeClass?: string\n /** Class applied when this link exactly matches the current route. */\n exactActiveClass?: string\n /** Prefetch strategy. Default: \"hover\" */\n prefetch?: 'hover' | 'viewport' | 'none'\n /** Open in new tab. */\n external?: boolean\n /** Inline styles. */\n style?: string\n /** ARIA label. */\n 'aria-label'?: string\n /** Additional click handler — called before navigation. Call e.preventDefault() to cancel. */\n onClick?: ((e: MouseEvent) => void) | undefined\n}\n\n/** Props passed to a custom component via createLink. */\nexport interface LinkRenderProps {\n href: string\n ref: import('@pyreon/core').Ref<HTMLAnchorElement>\n onClick: (e: MouseEvent) => void\n onMouseEnter: () => void\n onTouchStart: () => void\n isActive: () => boolean\n isExactActive: () => boolean\n /** Reactive class string — pass directly to element for auto-updates on route change. */\n class: (() => string) | string | undefined\n style?: string\n target?: string\n rel?: string\n 'aria-label'?: string\n children?: any\n}\n\n/** Return type of useLink. */\nexport interface UseLinkReturn {\n /** Ref object — attach to the root element for viewport-based prefetch. */\n ref: import('@pyreon/core').Ref<HTMLAnchorElement>\n /** Click handler — performs client-side navigation. */\n handleClick: (e: MouseEvent) => void\n /** Mouse enter handler — triggers hover prefetch. */\n handleMouseEnter: () => void\n /** Touch start handler — triggers prefetch on mobile. */\n handleTouchStart: () => void\n /** Whether the link partially matches the current route. */\n isActive: () => boolean\n /** Whether the link exactly matches the current route. */\n isExactActive: () => boolean\n /** Resolved class string including active classes. */\n classes: () => string\n}\n\nconst MAX_PREFETCH_CACHE = 200\nconst prefetched = new Set<string>()\n\nfunction doPrefetch(href: string) {\n if (prefetched.has(href)) return\n // Evict oldest entries when cache is full\n if (prefetched.size >= MAX_PREFETCH_CACHE) {\n const first = prefetched.values().next().value\n if (first) prefetched.delete(first)\n }\n prefetched.add(href)\n\n const docLink = document.createElement('link')\n docLink.rel = 'prefetch'\n docLink.href = href\n docLink.as = 'document'\n document.head.appendChild(docLink)\n\n try {\n const chunkHint = document.createElement('link')\n chunkHint.rel = 'modulepreload'\n chunkHint.href = href\n document.head.appendChild(chunkHint)\n } catch {\n // modulepreload is a hint, not critical\n }\n}\n\n/**\n * Prefetch a route's JS chunk by injecting `<link rel=\"prefetch\">` into the\n * document head. Deduplicates — calling with the same href twice is a no-op.\n *\n * @example\n * prefetchRoute('/about')\n * prefetchRoute('/dashboard')\n */\nexport function prefetchRoute(href: string): void {\n doPrefetch(href)\n}\n\n/**\n * Composable that provides all link behavior — navigation, prefetching,\n * active state, and viewport observation.\n *\n * Use this for full control when `createLink` is too opinionated.\n *\n * @example\n * function MyLink(props: LinkProps) {\n * const link = useLink(props)\n * return (\n * <button ref={link.ref} class={link.classes()} onClick={link.handleClick}>\n * {props.children}\n * </button>\n * )\n * }\n */\nexport function useLink(props: LinkProps): UseLinkReturn {\n const router = useRouter()\n const elementRef = createRef<HTMLAnchorElement>()\n const strategy = props.prefetch ?? 'hover'\n\n function handleClick(e: MouseEvent) {\n // Call user's onClick first — they may call e.preventDefault()\n if (props.onClick) {\n ;(props.onClick as (e: MouseEvent) => void)(e)\n }\n\n if (\n e.defaultPrevented ||\n e.button !== 0 ||\n e.metaKey ||\n e.ctrlKey ||\n e.shiftKey ||\n e.altKey ||\n props.external\n ) {\n return\n }\n e.preventDefault()\n router.push(props.href)\n }\n\n function handleMouseEnter() {\n if (strategy === 'hover') {\n doPrefetch(props.href)\n }\n }\n\n function handleTouchStart() {\n if (strategy === 'hover' || strategy === 'viewport') {\n doPrefetch(props.href)\n }\n }\n\n if (strategy === 'viewport') {\n useIntersectionObserver(\n () => elementRef.current ?? undefined,\n () => doPrefetch(props.href),\n )\n }\n\n const isActive = () => {\n const currentPath = router.currentRoute()?.path\n if (!currentPath || !props.href) return false\n if (props.href === '/') return currentPath === '/'\n return currentPath.startsWith(props.href)\n }\n\n const isExactActive = () => {\n const currentPath = router.currentRoute()?.path\n if (!currentPath) return false\n return currentPath === props.href\n }\n\n const classes = () => {\n const cls: string[] = []\n if (props.class) cls.push(props.class)\n if (props.activeClass && isActive()) cls.push(props.activeClass)\n if (props.exactActiveClass && isExactActive()) cls.push(props.exactActiveClass)\n return cls.join(' ')\n }\n\n return {\n ref: elementRef,\n handleClick,\n handleMouseEnter,\n handleTouchStart,\n isActive,\n isExactActive,\n classes,\n }\n}\n\n/**\n * Higher-order component that wraps any component with link behavior.\n *\n * The wrapped component receives {@link LinkRenderProps} with all handlers,\n * active state, and accessibility attributes pre-wired.\n *\n * @example\n * // Custom button link\n * const ButtonLink = createLink((props) => (\n * <button\n * ref={props.ref}\n * class={props.class}\n * onClick={props.onClick}\n * onMouseEnter={props.onMouseEnter}\n * >\n * {props.children}\n * </button>\n * ))\n *\n * // Custom styled component\n * const CardLink = createLink((props) => (\n * <div\n * ref={props.ref}\n * class={`card ${props.isActive() ? \"card--active\" : \"\"}`}\n * onClick={props.onClick}\n * onMouseEnter={props.onMouseEnter}\n * >\n * {props.children}\n * </div>\n * ))\n *\n * // Usage\n * <ButtonLink href=\"/about\">About</ButtonLink>\n * <CardLink href=\"/posts\" prefetch=\"viewport\">Posts</CardLink>\n */\nexport function createLink(Component: (props: LinkRenderProps) => any): (props: LinkProps) => any {\n return function WrappedLink(props: LinkProps) {\n const link = useLink(props)\n\n return (\n <Component\n href={props.href}\n ref={link.ref}\n onClick={link.handleClick}\n onMouseEnter={link.handleMouseEnter}\n onTouchStart={link.handleTouchStart}\n isActive={link.isActive}\n isExactActive={link.isExactActive}\n class={link.classes}\n {...(props.style ? { style: props.style } : {})}\n {...(props.external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n {...(props['aria-label'] ? { 'aria-label': props['aria-label'] } : {})}\n children={props.children}\n />\n )\n }\n}\n\n/**\n * Default navigation link built on an `<a>` tag.\n *\n * @example\n * <Link href=\"/about\" prefetch=\"viewport\">About</Link>\n * <Link href=\"/posts\" activeClass=\"nav-active\">Posts</Link>\n */\nexport const Link = createLink((props: LinkRenderProps) => (\n <a\n ref={props.ref}\n href={props.href}\n {...(props.class ? { class: props.class } : {})}\n {...(props.style ? { style: props.style } : {})}\n {...(props.target ? { target: props.target } : {})}\n {...(props.rel ? { rel: props.rel } : {})}\n {...(props['aria-label'] ? { 'aria-label': props['aria-label'] } : {})}\n {...(props.isExactActive() ? { 'aria-current': 'page' as const } : {})}\n onClick={props.onClick}\n onMouseEnter={props.onMouseEnter}\n onTouchStart={props.onTouchStart}\n >\n {props.children}\n </a>\n))\n","import type { VNodeChild } from '@pyreon/core'\nimport { createRef, onMount, onUnmount } from '@pyreon/core'\nimport { useIntersectionObserver } from './utils/use-intersection-observer'\n\n// ─── Script optimization component ─────────────────────────────────────────\n//\n// <Script> provides optimized third-party script loading:\n// - Defer loading until after hydration\n// - Load on idle (requestIdleCallback)\n// - Load on interaction (click, scroll, etc.)\n// - Load on viewport entry\n// - Worker offloading for analytics scripts\n\nexport interface ScriptProps {\n /** Script source URL. */\n src: string\n /** Loading strategy. Default: \"afterHydration\" */\n strategy?: ScriptStrategy\n /** Inline script content (alternative to src). */\n children?: string\n /** Script id for deduplication. */\n id?: string\n /** Async attribute. Default: true */\n async?: boolean\n /** onLoad callback. */\n onLoad?: () => void\n /** onError callback. */\n onError?: (error: Error) => void\n}\n\nexport type ScriptStrategy =\n | 'beforeHydration'\n | 'afterHydration'\n | 'onIdle'\n | 'onInteraction'\n | 'onViewport'\n\n/**\n * Optimized script loading component.\n *\n * @example\n * // Load analytics after page is interactive\n * <Script src=\"https://analytics.example.com/script.js\" strategy=\"onIdle\" />\n *\n * // Load chat widget when user scrolls\n * <Script src=\"/chat-widget.js\" strategy=\"onViewport\" />\n *\n * // Inline script with deferred execution\n * <Script strategy=\"afterHydration\">\n * {`console.log(\"App hydrated!\")`}\n * </Script>\n */\nexport function Script(props: ScriptProps): VNodeChild {\n function loadScript() {\n // Deduplication\n if (props.id && document.getElementById(props.id)) return\n\n const script = document.createElement('script')\n if (props.src) script.src = props.src\n if (props.id) script.id = props.id\n script.async = props.async !== false\n\n if (props.onLoad) script.onload = props.onLoad\n if (props.onError) {\n script.onerror = () => props.onError?.(new Error(`Failed to load: ${props.src}`))\n }\n\n if (props.children && !props.src) {\n script.textContent = props.children\n }\n\n document.head.appendChild(script)\n }\n\n onMount(() => {\n const strategy = props.strategy ?? 'afterHydration'\n\n switch (strategy) {\n case 'beforeHydration':\n // Already in HTML — do nothing\n break\n\n case 'afterHydration':\n // Load immediately after mount (hydration is complete)\n loadScript()\n break\n\n case 'onIdle':\n if ('requestIdleCallback' in window) {\n requestIdleCallback(() => loadScript(), { timeout: 5000 })\n } else {\n setTimeout(loadScript, 200)\n }\n break\n\n case 'onInteraction': {\n const events = ['click', 'scroll', 'keydown', 'touchstart']\n function handler() {\n for (const e of events) document.removeEventListener(e, handler)\n loadScript()\n }\n for (const e of events) {\n document.addEventListener(e, handler, { once: true, passive: true })\n }\n onUnmount(() => {\n for (const e of events) document.removeEventListener(e, handler)\n })\n break\n }\n\n case 'onViewport':\n // Handled below via useIntersectionObserver on the sentinel element\n break\n }\n return undefined\n })\n\n const sentinelRef = createRef<HTMLElement>()\n const strategy = props.strategy ?? 'afterHydration'\n\n if (strategy === 'onViewport') {\n useIntersectionObserver(\n () => sentinelRef.current ?? undefined,\n () => loadScript(),\n )\n }\n\n if (strategy === 'onViewport') {\n return <div ref={sentinelRef} style=\"width:0;height:0;overflow:hidden\" />\n }\n\n return null\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Cache control middleware ───────────────────────────────────────────────\n//\n// Smart caching middleware that sets appropriate cache headers based on\n// asset type, URL patterns, and build hashes.\n//\n// Strategies:\n// - Immutable: hashed assets (JS/CSS bundles) — cached forever\n// - Static: images, fonts, media — long cache with revalidation\n// - Dynamic: HTML pages — short or no cache, stale-while-revalidate\n// - API: JSON responses — no cache by default\n\nexport interface CacheConfig {\n /** Cache duration for immutable hashed assets (seconds). Default: 31536000 (1 year) */\n immutable?: number\n /** Cache duration for static assets like images/fonts (seconds). Default: 86400 (1 day) */\n static?: number\n /** Cache duration for pages (seconds). Default: 0 (no cache) */\n pages?: number\n /** Stale-while-revalidate window for pages (seconds). Default: 60 */\n staleWhileRevalidate?: number\n /** Custom rules by URL pattern. */\n rules?: CacheRule[]\n}\n\nexport interface CacheRule {\n /** URL pattern to match (glob-style). e.g. \"/api/*\" */\n match: string\n /** Cache-Control header value. */\n control: string\n}\n\nconst HASHED_ASSET = /\\.[a-f0-9]{8,}\\.\\w+$/\nconst STATIC_EXT = /\\.(png|jpe?g|gif|svg|webp|avif|ico|woff2?|ttf|otf|eot|mp4|webm|ogg|mp3|wav)$/i\nconst SCRIPT_EXT = /\\.(js|css|mjs)$/i\n\n/** @internal Exported for testing */\nexport function matchGlob(pattern: string, path: string): boolean {\n // Escape regex special chars, then convert glob wildcards\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n const regex = escaped.replace(/\\*/g, '.*').replace(/\\?/g, '.')\n return new RegExp(`^${regex}$`).test(path)\n}\n\nfunction resolveControl(\n path: string,\n immutableDuration: number,\n staticDuration: number,\n pageDuration: number,\n swr: number,\n): string {\n if (HASHED_ASSET.test(path)) {\n return `public, max-age=${immutableDuration}, immutable`\n }\n if (SCRIPT_EXT.test(path)) {\n return `public, max-age=3600, stale-while-revalidate=${swr}`\n }\n if (STATIC_EXT.test(path)) {\n return `public, max-age=${staticDuration}, stale-while-revalidate=${swr}`\n }\n if (pageDuration > 0) {\n return `public, max-age=${pageDuration}, stale-while-revalidate=${swr}`\n }\n return 'no-cache'\n}\n\n/**\n * Cache control middleware for Zero.\n * Sets Cache-Control headers on the response based on asset type.\n *\n * @example\n * import { cacheMiddleware } from \"@pyreon/zero/cache\"\n *\n * export default createHandler({\n * routes,\n * middleware: [\n * cacheMiddleware({\n * pages: 60,\n * staleWhileRevalidate: 300,\n * rules: [\n * { match: \"/api/*\", control: \"no-store\" },\n * ],\n * }),\n * ],\n * })\n */\nexport function cacheMiddleware(config: CacheConfig = {}): Middleware {\n const immutableDuration = config.immutable ?? 31536000\n const staticDuration = config.static ?? 86400\n const pageDuration = config.pages ?? 0\n const swr = config.staleWhileRevalidate ?? 60\n const rules = config.rules ?? []\n\n return (ctx: MiddlewareContext) => {\n const path = ctx.url.pathname\n\n for (const rule of rules) {\n if (matchGlob(rule.match, path)) {\n ctx.headers.set('Cache-Control', rule.control)\n return\n }\n }\n\n const control = resolveControl(path, immutableDuration, staticDuration, pageDuration, swr)\n ctx.headers.set('Cache-Control', control)\n }\n}\n\n/**\n * Security headers middleware.\n * Adds common security headers to all responses.\n */\nexport function securityHeaders(): Middleware {\n return (ctx: MiddlewareContext) => {\n ctx.headers.set('X-Content-Type-Options', 'nosniff')\n ctx.headers.set('X-Frame-Options', 'DENY')\n ctx.headers.set('X-XSS-Protection', '1; mode=block')\n ctx.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')\n ctx.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')\n }\n}\n\n/**\n * Compression detection middleware.\n * Sets Vary: Accept-Encoding header so caches can serve compressed variants.\n * Actual compression is handled by the runtime (Bun/Node) or reverse proxy.\n */\nexport function varyEncoding(): Middleware {\n return (ctx: MiddlewareContext) => {\n const existing = ctx.headers.get('Vary')\n if (!existing?.includes('Accept-Encoding')) {\n ctx.headers.set('Vary', existing ? `${existing}, Accept-Encoding` : 'Accept-Encoding')\n }\n }\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Middleware composition ─────────────────────────────────────────────────\n//\n// Chains multiple middleware functions into a single middleware.\n// Each middleware runs in order. If any returns a Response, the chain\n// short-circuits and that Response is returned. If all return void,\n// the composed middleware returns void (continues to rendering).\n\n/**\n * Compose multiple middleware into a single middleware function.\n * Middleware runs sequentially — if any returns a Response, the chain stops.\n *\n * @example\n * import { compose } from \"@pyreon/zero/middleware\"\n * import { corsMiddleware } from \"@pyreon/zero/cors\"\n * import { rateLimitMiddleware } from \"@pyreon/zero/rate-limit\"\n *\n * const combined = compose(\n * corsMiddleware({ origin: \"*\" }),\n * rateLimitMiddleware({ max: 100 }),\n * cacheMiddleware(),\n * )\n */\nexport function compose(...middlewares: Middleware[]): Middleware {\n return async (ctx: MiddlewareContext) => {\n for (const mw of middlewares) {\n const result = await mw(ctx)\n if (result instanceof Response) return result\n }\n }\n}\n\n// ─── Shared request context ─────────────────────────────────────────────────\n//\n// Lightweight context bag attached to MiddlewareContext.locals so middleware\n// can communicate without coupling. Uses a namespaced key to avoid collisions\n// with user-defined locals.\n\nconst ZERO_CTX_KEY = '__zeroCtx'\n\n/**\n * Get the shared Zero context from a middleware context.\n * Creates one if it doesn't exist. Middleware can use this to\n * pass data to downstream middleware without polluting `ctx.locals`.\n *\n * @example\n * const authMiddleware: Middleware = (ctx) => {\n * const zctx = getContext(ctx)\n * zctx.userId = \"user_123\"\n * }\n *\n * const loggingMiddleware: Middleware = (ctx) => {\n * const zctx = getContext(ctx)\n * console.log(\"User:\", zctx.userId)\n * }\n */\nexport function getContext(ctx: MiddlewareContext): Record<string, unknown> {\n let zctx = ctx.locals[ZERO_CTX_KEY] as Record<string, unknown> | undefined\n if (!zctx) {\n zctx = {}\n ctx.locals[ZERO_CTX_KEY] = zctx\n }\n return zctx\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\n\n// ─── Font optimization ──────────────────────────────────────────────────────\n//\n// Zero provides automatic font optimization:\n// - Downloads and self-hosts Google Fonts at build time (privacy + performance)\n// - Falls back to CDN link in dev mode (for fast dev startup)\n// - Injects preconnect/preload hints into the HTML\n// - Sets font-display: swap to prevent FOIT (Flash of Invisible Text)\n// - Generates optimized @font-face declarations\n// - Size-adjusted fallback fonts to reduce CLS\n\nexport interface FontConfig {\n /**\n * Google Fonts families.\n *\n * Accepts both string shorthand and structured objects:\n * - String: \"Inter:wght@400;500;700\" or \"Inter:wght@100..900\"\n * - Object: { family: \"Inter\", weights: [400, 500, 700] }\n * - Variable: { family: \"Inter\", variable: true, weightRange: [100, 900] }\n */\n google?: GoogleFontInput[]\n /** Local font files. */\n local?: LocalFont[]\n /** Default font-display strategy. Default: \"swap\" */\n display?: FontDisplay\n /** Preload critical fonts. Default: true */\n preload?: boolean\n /** Self-host Google Fonts at build time. Default: true */\n selfHost?: boolean\n /** Fallback font metrics for reducing CLS. */\n fallbacks?: Record<string, FallbackMetrics>\n}\n\n/** Static Google Font config. */\nexport interface GoogleFontStatic {\n family: string\n weights: number[]\n italic?: boolean\n variable?: false\n}\n\n/** Variable Google Font config. */\nexport interface GoogleFontVariable {\n family: string\n /** Weight range as [min, max] tuple. e.g. [100, 900] */\n weightRange: [number, number]\n italic?: boolean\n variable: true\n}\n\n/** Google font input: structured object or string shorthand. */\nexport type GoogleFontInput = GoogleFontStatic | GoogleFontVariable | string\n\nexport interface LocalFont {\n family: string\n src: string\n /** Single weight (400) or variable range (\"100 900\"). */\n weight?: number | `${number} ${number}`\n style?: 'normal' | 'italic'\n display?: FontDisplay\n}\n\nexport type FontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional'\n\n/** Metrics for generating size-adjusted fallback fonts to reduce CLS. */\nexport interface FallbackMetrics {\n /** The fallback font to adjust. e.g. \"Arial\", \"Georgia\" */\n fallback: string\n /** Size adjustment factor. e.g. 1.05 */\n sizeAdjust?: number\n /** Ascent override percentage. e.g. 90 */\n ascentOverride?: number\n /** Descent override percentage. e.g. 22 */\n descentOverride?: number\n /** Line gap override percentage. e.g. 0 */\n lineGapOverride?: number\n}\n\ninterface ResolvedFontBase {\n family: string\n italic: boolean\n}\n\ninterface StaticFont extends ResolvedFontBase {\n variable: false\n weights: number[]\n}\n\ninterface VariableFont extends ResolvedFontBase {\n variable: true\n weightRange: [number, number]\n}\n\ntype ResolvedFont = StaticFont | VariableFont\n\n/**\n * Normalize a GoogleFontInput (string or object) into a ResolvedFont.\n */\nexport function resolveGoogleFont(input: GoogleFontInput): ResolvedFont {\n if (typeof input === 'string') {\n return parseGoogleFamily(input)\n }\n\n if (input.variable) {\n return {\n family: input.family,\n italic: input.italic ?? false,\n variable: true,\n weightRange: input.weightRange,\n }\n }\n\n return {\n family: input.family,\n italic: input.italic ?? false,\n variable: false,\n weights: input.weights,\n }\n}\n\n/**\n * Parse Google Fonts family string shorthand.\n *\n * Static weights: \"Inter:wght@400;500;700\"\n * Variable range: \"Inter:wght@100..900\"\n * Variable with italic: \"Inter:ital,wght@100..900\"\n */\nexport function parseGoogleFamily(input: string): ResolvedFont {\n const parts = input.split(':')\n const family = (parts[0] ?? '').trim()\n const spec = parts[1]\n let italic = false\n\n if (spec) {\n italic = spec.includes('ital')\n\n // Variable font range syntax: wght@100..900\n const rangeMatch = spec.match(/wght@(\\d+)\\.\\.(\\d+)/)\n if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {\n return {\n family,\n italic,\n variable: true,\n weightRange: [Number(rangeMatch[1]), Number(rangeMatch[2])],\n }\n }\n\n // Static weights — two formats:\n // Simple: \"wght@400;500;700\"\n // Tuples: \"ital,wght@0,300;0,500;1,300;1,500\" (ital_flag,weight pairs)\n const afterAt = spec.split('@')[1]\n if (afterAt) {\n const entries = afterAt.split(';').filter(Boolean)\n const weights = new Set<number>()\n\n for (const entry of entries) {\n if (entry.includes(',')) {\n // Tuple format: \"0,300\" or \"1,500\" — last value is the weight\n const parts = entry.split(',')\n const weight = Number(parts[parts.length - 1])\n if (weight > 0) weights.add(weight)\n // Detect italic from tuple: \"1,xxx\" means italic\n if (parts[0] === '1') italic = true\n } else if (entry.includes('..')) {\n // Variable range already handled above — skip\n } else {\n // Simple weight: \"400\"\n const weight = Number(entry)\n if (weight > 0) weights.add(weight)\n }\n }\n\n if (weights.size > 0) {\n return {\n family,\n italic,\n variable: false,\n weights: [...weights].sort((a, b) => a - b),\n }\n }\n }\n }\n\n return { family, italic, variable: false, weights: [400] }\n}\n\n/**\n * Generate a Google Fonts CSS URL.\n */\nexport function googleFontsUrl(families: ResolvedFont[], display: FontDisplay = 'swap'): string {\n const params = families\n .map((f) => {\n const axes = f.italic ? 'ital,wght' : 'wght'\n const name = f.family.replace(/ /g, '+')\n\n if (f.variable) {\n const range = `${f.weightRange[0]}..${f.weightRange[1]}`\n const value = f.italic ? `0,${range};1,${range}` : range\n return `family=${name}:${axes}@${value}`\n }\n\n const values = f.weights.map((w) => (f.italic ? `0,${w};1,${w}` : String(w))).join(';')\n return `family=${name}:${axes}@${values}`\n })\n .join('&')\n\n return `https://fonts.googleapis.com/css2?${params}&display=${display}`\n}\n\n/**\n * Generate @font-face CSS for local fonts.\n */\nfunction localFontFaces(fonts: LocalFont[], display: FontDisplay): string {\n return fonts\n .map(\n (f) => `@font-face {\n font-family: \"${f.family}\";\n src: url(\"${f.src}\");\n font-weight: ${f.weight ?? '400'};\n font-style: ${f.style ?? 'normal'};\n font-display: ${f.display ?? display};\n}`,\n )\n .join('\\n\\n')\n}\n\n/**\n * Generate size-adjusted fallback @font-face declarations to reduce CLS.\n */\nfunction fallbackFontFaces(fallbacks: Record<string, FallbackMetrics>): string {\n return Object.entries(fallbacks)\n .map(([family, metrics]) => {\n const overrides: string[] = []\n if (metrics.sizeAdjust != null) overrides.push(` size-adjust: ${metrics.sizeAdjust * 100}%;`)\n if (metrics.ascentOverride != null)\n overrides.push(` ascent-override: ${metrics.ascentOverride}%;`)\n if (metrics.descentOverride != null)\n overrides.push(` descent-override: ${metrics.descentOverride}%;`)\n if (metrics.lineGapOverride != null)\n overrides.push(` line-gap-override: ${metrics.lineGapOverride}%;`)\n\n return `@font-face {\n font-family: \"${family} Fallback\";\n src: local(\"${metrics.fallback}\");\n${overrides.join('\\n')}\n}`\n })\n .join('\\n\\n')\n}\n\n/**\n * Generate preload link tags for critical font files.\n */\nfunction preloadTags(fonts: LocalFont[]): string {\n return fonts\n .map((f) => {\n const ext = f.src.split('.').pop()\n const type =\n ext === 'woff2'\n ? 'font/woff2'\n : ext === 'woff'\n ? 'font/woff'\n : ext === 'ttf'\n ? 'font/ttf'\n : 'font/otf'\n return `<link rel=\"preload\" href=\"${f.src}\" as=\"font\" type=\"${type}\" crossorigin>`\n })\n .join('\\n')\n}\n\n/**\n * Download Google Fonts CSS with woff2 user agent.\n */\nasync function downloadGoogleFontsCSS(url: string): Promise<string> {\n const response = await fetch(url, {\n headers: {\n 'User-Agent':\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\n },\n })\n if (!response.ok) {\n throw new Error(`Failed to fetch Google Fonts CSS: ${response.status}`)\n }\n return response.text()\n}\n\n/**\n * Download a font file.\n */\nasync function downloadFontFile(url: string): Promise<Buffer> {\n const response = await fetch(url)\n if (!response.ok) throw new Error(`Failed to download font: ${url}`)\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n}\n\n/**\n * Extract font file URLs from Google Fonts CSS.\n */\nfunction extractFontUrls(css: string): string[] {\n const urls: string[] = []\n const regex = /url\\((https:\\/\\/fonts\\.gstatic\\.com\\/[^)]+)\\)/g\n for (const match of css.matchAll(regex)) {\n if (match[1]) urls.push(match[1])\n }\n return urls\n}\n\n/**\n * Self-host Google Fonts: download CSS + font files, rewrite URLs to local paths.\n */\nasync function selfHostFonts(\n cssUrl: string,\n fontsSubDir: string,\n root: string,\n): Promise<{\n css: string\n fontFiles: Array<{ name: string; content: Buffer }>\n}> {\n // Cache fonts between builds to avoid re-downloading (~6s penalty)\n const cacheDir = join(root, 'node_modules', '.cache', 'zero-fonts')\n const cacheKey = Buffer.from(cssUrl).toString('base64url')\n const cachePath = join(cacheDir, `${cacheKey}.json`)\n\n try {\n const cached = JSON.parse(await readFile(cachePath, 'utf-8'))\n if (cached.css && cached.fontFiles) {\n return {\n css: cached.css,\n fontFiles: cached.fontFiles.map((f: any) => ({\n name: f.name,\n content: Buffer.from(f.content, 'base64'),\n })),\n }\n }\n } catch {\n // No cache — download fresh\n }\n\n const css = await downloadGoogleFontsCSS(cssUrl)\n const fontUrls = extractFontUrls(css)\n const fontFiles: Array<{ name: string; content: Buffer }> = []\n\n let rewrittenCss = css\n\n for (const url of fontUrls) {\n const urlParts = url.split('/')\n const fileName = urlParts.at(-1)?.split('?')[0] ?? 'font'\n const content = await downloadFontFile(url)\n\n fontFiles.push({ name: fileName, content })\n rewrittenCss = rewrittenCss.replace(url, `/${fontsSubDir}/${fileName}`)\n }\n\n // Write cache\n try {\n await mkdir(cacheDir, { recursive: true })\n await writeFile(cachePath, JSON.stringify({\n css: rewrittenCss,\n fontFiles: fontFiles.map((f) => ({ name: f.name, content: f.content.toString('base64') })),\n }))\n } catch {\n // Cache write failure is non-fatal\n }\n\n return { css: rewrittenCss, fontFiles }\n}\n\n/**\n * Zero font optimization Vite plugin.\n *\n * Dev mode: injects Google Fonts CDN link for fast startup.\n * Build mode: downloads and self-hosts fonts for maximum performance + privacy.\n *\n * @example\n * import { fontPlugin } from \"@pyreon/zero/font\"\n *\n * export default {\n * plugins: [\n * pyreon(),\n * zero(),\n * fontPlugin({\n * google: [\"Inter:wght@400;500;600;700\", \"JetBrains Mono:wght@400\"],\n * fallbacks: {\n * \"Inter\": { fallback: \"Arial\", sizeAdjust: 1.07, ascentOverride: 90 },\n * },\n * }),\n * ],\n * }\n */\nexport function fontPlugin(config: FontConfig = {}): Plugin {\n const display = config.display ?? 'swap'\n const shouldPreload = config.preload !== false\n const shouldSelfHost = config.selfHost !== false\n const googleFamilies = (config.google ?? []).map(resolveGoogleFont)\n\n let isBuild = false\n let root = ''\n let selfHostedCSS = ''\n let selfHostedFontFiles: Array<{ name: string; content: Buffer }> = []\n\n return {\n name: 'pyreon-zero-fonts',\n\n configResolved(resolvedConfig) {\n isBuild = resolvedConfig.command === 'build'\n root = resolvedConfig.root\n },\n\n async buildStart() {\n if (isBuild && shouldSelfHost && googleFamilies.length > 0) {\n const cssUrl = googleFontsUrl(googleFamilies, display)\n try {\n const result = await selfHostFonts(cssUrl, 'assets/fonts', root)\n selfHostedCSS = result.css\n selfHostedFontFiles = result.fontFiles\n } catch {\n // Self-hosting failed — fall back to CDN link\n }\n }\n },\n\n generateBundle() {\n // Emit self-hosted font files as assets\n for (const file of selfHostedFontFiles) {\n this.emitFile({\n type: 'asset',\n fileName: `assets/fonts/${file.name}`,\n source: file.content,\n })\n }\n },\n\n transformIndexHtml(html) {\n const tags: string[] = []\n\n collectGoogleFontTags(tags, {\n isBuild,\n selfHostedCSS,\n selfHostedFontFiles,\n shouldPreload,\n googleFamilies,\n display,\n })\n collectLocalFontTags(tags, config, shouldPreload, display)\n\n if (tags.length === 0) return html\n return html.replace('</head>', `${tags.join('\\n')}\\n</head>`)\n },\n }\n}\n\nfunction collectGoogleFontTags(\n tags: string[],\n opts: {\n isBuild: boolean\n selfHostedCSS: string\n selfHostedFontFiles: Array<{ name: string; content: Buffer }>\n shouldPreload: boolean\n googleFamilies: ResolvedFont[]\n display: FontDisplay\n },\n) {\n if (opts.isBuild && opts.selfHostedCSS) {\n tags.push(`<style>${opts.selfHostedCSS}</style>`)\n if (opts.shouldPreload) {\n for (const file of opts.selfHostedFontFiles.slice(0, opts.googleFamilies.length)) {\n const ext = file.name.split('.').pop()\n const type = ext === 'woff2' ? 'font/woff2' : 'font/woff'\n tags.push(\n `<link rel=\"preload\" href=\"/assets/fonts/${file.name}\" as=\"font\" type=\"${type}\" crossorigin>`,\n )\n }\n }\n } else if (opts.googleFamilies.length > 0) {\n const cssUrl = googleFontsUrl(opts.googleFamilies, opts.display)\n tags.push(`<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">`)\n tags.push(`<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>`)\n tags.push(`<link rel=\"stylesheet\" href=\"${cssUrl}\">`)\n }\n}\n\nfunction collectLocalFontTags(\n tags: string[],\n config: FontConfig,\n shouldPreload: boolean,\n display: FontDisplay,\n) {\n if (shouldPreload && config.local?.length) {\n tags.push(preloadTags(config.local))\n }\n if (config.local?.length) {\n tags.push(`<style>${localFontFaces(config.local, display)}</style>`)\n }\n if (config.fallbacks && Object.keys(config.fallbacks).length > 0) {\n tags.push(`<style>${fallbackFontFaces(config.fallbacks)}</style>`)\n }\n}\n\n/**\n * Generate CSS variables for font families.\n */\nexport function fontVariables(families: Record<string, string>): string {\n const vars = Object.entries(families)\n .map(([key, value]) => ` --font-${key}: ${value};`)\n .join('\\n')\n return `:root {\\n${vars}\\n}`\n}\n","import { existsSync } from 'node:fs'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { basename, extname, join } from 'node:path'\nimport type { Plugin } from 'vite'\n\nlet sharpWarned = false\nfunction warnSharpMissing() {\n if (sharpWarned) return\n sharpWarned = true\n // oxlint-disable-next-line no-console\n console.warn(\n '\\n[zero:image] sharp not installed — images will not be optimized. Install for full support: bun add -D sharp\\n',\n )\n}\n\n// ─── Image processing Vite plugin ──────────────────────────────────────────\n//\n// Processes images at build time:\n// - Generates multiple sizes for responsive srcset\n// - Converts to modern formats (WebP, AVIF)\n// - Creates tiny blur placeholders (base64 inline)\n// - Outputs optimized images to the build directory\n//\n// Usage in code:\n// import heroImg from \"./hero.jpg?optimize\"\n// // → { src, srcset, width, height, placeholder }\n//\n// Or use the component helper:\n// import { Image } from \"@pyreon/zero/image\"\n// <Image src=\"/hero.jpg\" width={1920} height={1080} optimize />\n\nexport interface ImagePluginConfig {\n /** Output directory for processed images. Default: \"assets/img\" */\n outDir?: string\n /** Default widths for responsive images. Default: [640, 1024, 1920] */\n widths?: number[]\n /** Output formats. Default: [\"webp\"] */\n formats?: ImageFormat[]\n /** Quality for lossy formats (1-100). Default: 80 */\n quality?: number\n /** Blur placeholder size in px. Default: 16 */\n placeholderSize?: number\n /** File patterns to process. Default: /\\.(jpe?g|png|webp|avif)$/i */\n include?: RegExp\n}\n\nexport type ImageFormat = 'webp' | 'avif' | 'jpeg' | 'png'\n\n/** Per-format source set for <picture> <source> elements. */\nexport interface FormatSource {\n /** MIME type. e.g. \"image/webp\", \"image/avif\" */\n type: string\n /** srcset string for this format. e.g. \"/img-640.webp 640w, /img-1920.webp 1920w\" */\n srcset: string\n}\n\nexport interface ProcessedImage {\n /** Fallback source path (last format, largest width). */\n src: string\n /** Fallback srcset string (last format). */\n srcset: string\n /** Intrinsic width. */\n width: number\n /** Intrinsic height. */\n height: number\n /** Base64 blur placeholder data URI. */\n placeholder: string\n /** Per-format source sets for <picture> element. Ordered by priority (best format first). */\n formats: FormatSource[]\n /** Flat list of all sources. */\n sources: Array<{ src: string; width: number; format: string }>\n}\n\nconst IMAGE_EXT_RE = /\\.(jpe?g|png|webp|avif)$/i\n\n/**\n * Zero image processing Vite plugin.\n *\n * Transforms image imports with query params into optimized responsive images:\n *\n * @example\n * // vite.config.ts\n * import { imagePlugin } from \"@pyreon/zero/image-plugin\"\n *\n * export default {\n * plugins: [\n * pyreon(),\n * zero(),\n * imagePlugin({ widths: [480, 960, 1440], quality: 85 }),\n * ],\n * }\n *\n * @example\n * // In a component — import with ?optimize\n * import hero from \"./images/hero.jpg?optimize\"\n * // hero = { src, srcset, width, height, placeholder }\n *\n * <Image {...hero} alt=\"Hero\" priority />\n */\nexport function imagePlugin(config: ImagePluginConfig = {}): Plugin {\n const defaultWidths = config.widths ?? [640, 1024, 1920]\n const defaultFormats = config.formats ?? ['webp']\n const quality = config.quality ?? 80\n const placeholderSize = config.placeholderSize ?? 16\n const outSubDir = config.outDir ?? 'assets/img'\n const include = config.include ?? IMAGE_EXT_RE\n\n let root = ''\n let outDir = ''\n let isBuild = false\n\n return {\n name: 'pyreon-zero-images',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n root = resolvedConfig.root\n outDir = resolvedConfig.build.outDir\n isBuild = resolvedConfig.command === 'build'\n },\n\n async resolveId(id) {\n // Handle ?optimize query on image imports\n if (id.includes('?optimize') && include.test(id.split('?')[0]!)) {\n return `\\0virtual:zero-image:${id}`\n }\n return null\n },\n\n async load(id) {\n if (!id.startsWith('\\0virtual:zero-image:')) return null\n\n const rawPath = id.replace('\\0virtual:zero-image:', '').split('?')[0] ?? id\n const absPath = rawPath.startsWith('/') ? join(root, 'public', rawPath) : rawPath\n\n if (!isBuild) {\n const result = await loadDevImage(absPath, rawPath, placeholderSize)\n return `export default ${JSON.stringify(result)}`\n }\n\n const processed = await processImage(absPath, {\n widths: defaultWidths,\n formats: defaultFormats,\n quality,\n placeholderSize,\n outSubDir,\n outDir: join(root, outDir),\n })\n\n await emitProcessedSources(processed, outSubDir, this)\n rebuildFormatSrcsets(processed, absPath)\n\n return `export default ${JSON.stringify(processed)}`\n },\n }\n}\n\nasync function loadDevImage(\n absPath: string,\n rawPath: string,\n placeholderSize: number,\n): Promise<ProcessedImage> {\n const metadata = await getImageMetadata(absPath)\n const publicPath = rawPath.startsWith('/') ? rawPath : `/@fs/${absPath}`\n\n return {\n src: publicPath,\n srcset: '',\n width: metadata.width,\n height: metadata.height,\n placeholder: await generateBlurPlaceholder(absPath, placeholderSize),\n formats: [],\n sources: [{ src: publicPath, width: metadata.width, format: 'original' }],\n }\n}\n\nasync function emitProcessedSources(\n processed: ProcessedImage,\n outSubDir: string,\n ctx: {\n emitFile: (f: { type: 'asset'; fileName: string; source: Uint8Array }) => void\n },\n) {\n for (const source of processed.sources) {\n const fileName = join(outSubDir, basename(source.src))\n const content = await readFile(source.src)\n ctx.emitFile({ type: 'asset', fileName, source: content })\n source.src = `/${fileName}`\n }\n}\n\nfunction rebuildFormatSrcsets(processed: ProcessedImage, fallbackPath: string) {\n const formatGroups = new Map<string, string[]>()\n for (const s of processed.sources) {\n let group = formatGroups.get(s.format)\n if (!group) {\n group = []\n formatGroups.set(s.format, group)\n }\n group.push(`${s.src} ${s.width}w`)\n }\n processed.formats = [...formatGroups.entries()].map(([fmt, entries]) => ({\n type: `image/${fmt}`,\n srcset: entries.join(', '),\n }))\n\n const lastFormat = processed.formats.at(-1)\n processed.srcset = lastFormat?.srcset ?? ''\n processed.src = processed.sources.at(-1)?.src ?? fallbackPath\n}\n\n// ─── Image processing utilities ─────────────────────────────────────────────\n\ninterface ProcessOptions {\n widths: number[]\n formats: ImageFormat[]\n quality: number\n placeholderSize: number\n outSubDir: string\n outDir: string\n}\n\nasync function processImage(absPath: string, opts: ProcessOptions): Promise<ProcessedImage> {\n const metadata = await getImageMetadata(absPath)\n const ext = extname(absPath)\n const name = basename(absPath, ext)\n const sources: Array<{ src: string; width: number; format: string }> = []\n\n // Ensure output directory exists\n const processedDir = join(opts.outDir, opts.outSubDir)\n if (!existsSync(processedDir)) {\n await mkdir(processedDir, { recursive: true })\n }\n\n // Generate resized variants — iterate formats first so sources are grouped by format\n for (const format of opts.formats) {\n for (const targetWidth of opts.widths) {\n // Don't upscale\n const width = Math.min(targetWidth, metadata.width)\n const outName = `${name}-${width}.${format}`\n const outPath = join(processedDir, outName)\n\n await resizeImage(absPath, outPath, width, format, opts.quality)\n sources.push({ src: outPath, width, format })\n }\n }\n\n // Build per-format source sets for <picture>\n const formatGroups = new Map<string, Array<{ src: string; width: number }>>()\n for (const s of sources) {\n let group = formatGroups.get(s.format)\n if (!group) {\n group = []\n formatGroups.set(s.format, group)\n }\n group.push({ src: s.src, width: s.width })\n }\n\n const formats: FormatSource[] = [...formatGroups.entries()].map(([fmt, group]) => ({\n type: `image/${fmt === 'jpeg' ? 'jpeg' : fmt}`,\n srcset: group.map((s) => `${s.src} ${s.width}w`).join(', '),\n }))\n\n // Fallback: last format's srcset\n const fallbackFormat = formats[formats.length - 1]\n const fallbackSources = formatGroups.get([...formatGroups.keys()].pop()!)!\n\n // Generate blur placeholder\n const placeholder = await generateBlurPlaceholder(absPath, opts.placeholderSize)\n\n return {\n src: fallbackSources[fallbackSources.length - 1]?.src ?? absPath,\n srcset: fallbackFormat?.srcset ?? '',\n width: metadata.width,\n height: metadata.height,\n placeholder,\n formats,\n sources,\n }\n}\n\ninterface ImageMetadata {\n width: number\n height: number\n format: string\n}\n\n/**\n * Read basic image metadata.\n * Uses minimal binary header parsing — no external dependencies.\n */\nasync function getImageMetadata(absPath: string): Promise<ImageMetadata> {\n const buffer = await readFile(absPath)\n const ext = extname(absPath).toLowerCase()\n\n if (ext === '.png') {\n // PNG: width at bytes 16-19, height at 20-23 (big-endian)\n const width = buffer.readUInt32BE(16)\n const height = buffer.readUInt32BE(20)\n return { width, height, format: 'png' }\n }\n\n if (ext === '.jpg' || ext === '.jpeg') {\n // JPEG: scan for SOF markers\n const dimensions = parseJpegDimensions(buffer)\n return { ...dimensions, format: 'jpeg' }\n }\n\n if (ext === '.webp') {\n // WebP: VP8 header\n const dimensions = parseWebPDimensions(buffer)\n return { ...dimensions, format: 'webp' }\n }\n\n // Fallback\n return { width: 0, height: 0, format: ext.slice(1) }\n}\n\n/** @internal Exported for testing */\nexport function parseJpegDimensions(buffer: Buffer): {\n width: number\n height: number\n} {\n let offset = 2 // Skip SOI marker\n while (offset < buffer.length) {\n if (buffer[offset] !== 0xff) break\n const marker = buffer[offset + 1]!\n // SOF markers (0xC0-0xCF except 0xC4, 0xC8, 0xCC)\n if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {\n const height = buffer.readUInt16BE(offset + 5)\n const width = buffer.readUInt16BE(offset + 7)\n return { width, height }\n }\n const length = buffer.readUInt16BE(offset + 2)\n offset += 2 + length\n }\n return { width: 0, height: 0 }\n}\n\n/** @internal Exported for testing */\nexport function parseWebPDimensions(buffer: Buffer): {\n width: number\n height: number\n} {\n // RIFF header: bytes 0-3 = \"RIFF\", 8-11 = \"WEBP\"\n const chunk = buffer.toString('ascii', 12, 16)\n if (chunk === 'VP8 ') {\n // Lossy VP8\n const width = buffer.readUInt16LE(26) & 0x3fff\n const height = buffer.readUInt16LE(28) & 0x3fff\n return { width, height }\n }\n if (chunk === 'VP8L') {\n // Lossless VP8L\n const bits = buffer.readUInt32LE(21)\n const width = (bits & 0x3fff) + 1\n const height = ((bits >> 14) & 0x3fff) + 1\n return { width, height }\n }\n if (chunk === 'VP8X') {\n // Extended VP8X\n const width = 1 + ((buffer[24]! | (buffer[25]! << 8) | (buffer[26]! << 16)) & 0xffffff)\n const height = 1 + ((buffer[27]! | (buffer[28]! << 8) | (buffer[29]! << 16)) & 0xffffff)\n return { width, height }\n }\n return { width: 0, height: 0 }\n}\n\n/**\n * Resize an image using native platform capabilities.\n * Uses sharp if available, falls back to canvas API.\n */\nasync function resizeImage(\n input: string,\n output: string,\n width: number,\n format: ImageFormat,\n quality: number,\n): Promise<void> {\n try {\n // Try sharp (the standard Node.js image processing library)\n const sharp = await import('sharp').then((m) => m.default ?? m)\n let pipeline = sharp(input).resize(width)\n\n switch (format) {\n case 'webp':\n pipeline = pipeline.webp({ quality })\n break\n case 'avif':\n pipeline = pipeline.avif({ quality })\n break\n case 'jpeg':\n pipeline = pipeline.jpeg({ quality, mozjpeg: true })\n break\n case 'png':\n pipeline = pipeline.png({ compressionLevel: 9 })\n break\n }\n\n await pipeline.toFile(output)\n } catch {\n // sharp not available — copy original as fallback\n warnSharpMissing()\n const content = await readFile(input)\n await writeFile(output, content)\n }\n}\n\n/**\n * Generate a tiny blur placeholder as a base64 data URI.\n */\nasync function generateBlurPlaceholder(input: string, size: number): Promise<string> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n const buffer = await sharp(input)\n .resize(size, size, { fit: 'inside' })\n .blur(2)\n .webp({ quality: 20 })\n .toBuffer()\n\n return `data:image/webp;base64,${buffer.toString('base64')}`\n } catch {\n // sharp not available — return a transparent placeholder\n return \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'%3E%3C/svg%3E\"\n }\n}\n","import type { VNodeChild } from '@pyreon/core'\nimport { 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/** SSR fallback when system preference can't be detected. Default: 'light'. */\nlet _ssrDefault: 'light' | 'dark' = 'light'\n\n/**\n * Set the default theme for SSR (when `matchMedia` is unavailable).\n * Call once at server startup before rendering.\n */\nexport function setSSRThemeDefault(value: 'light' | 'dark'): void {\n _ssrDefault = value\n}\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 _ssrDefault\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : '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 }): VNodeChild {\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","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 = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n\n/**\n * Generate a sitemap.xml string from route file paths.\n */\nexport function generateSitemap(routeFiles: string[], config: SitemapConfig): 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","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── CORS middleware ────────────────────────────────────────────────────────\n\nexport interface CorsConfig {\n /** Allowed origins. Use `\"*\"` for any origin. Default: `\"*\"` */\n origin?: string | string[] | ((origin: string) => boolean)\n /** Allowed HTTP methods. Default: `[\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"]` */\n methods?: string[]\n /** Allowed request headers. Default: `[\"Content-Type\", \"Authorization\"]` */\n allowedHeaders?: string[]\n /** Headers exposed to the client. Default: `[]` */\n exposedHeaders?: string[]\n /** Allow credentials (cookies, auth headers). Default: `false` */\n credentials?: boolean\n /** Preflight cache duration in seconds. Default: `86400` (24 hours) */\n maxAge?: number\n}\n\nconst DEFAULT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']\nconst DEFAULT_HEADERS = ['Content-Type', 'Authorization']\n\n/**\n * CORS middleware — handles preflight requests and sets appropriate\n * Access-Control headers on all responses.\n *\n * @example\n * import { corsMiddleware } from \"@pyreon/zero/cors\"\n *\n * corsMiddleware({ origin: \"https://example.com\", credentials: true })\n *\n * // Allow any origin\n * corsMiddleware({ origin: \"*\" })\n *\n * // Multiple origins\n * corsMiddleware({ origin: [\"https://app.com\", \"https://admin.com\"] })\n */\nexport function corsMiddleware(config: CorsConfig = {}): Middleware {\n const {\n origin = '*',\n methods = DEFAULT_METHODS,\n allowedHeaders = DEFAULT_HEADERS,\n exposedHeaders = [],\n credentials = false,\n maxAge = 86400,\n } = config\n\n return (ctx: MiddlewareContext) => {\n const requestOrigin = ctx.req.headers.get('origin') ?? ''\n const resolvedOrigin = resolveOrigin(origin, requestOrigin)\n\n if (!resolvedOrigin) return\n\n // Set CORS headers on all responses\n ctx.headers.set('Access-Control-Allow-Origin', resolvedOrigin)\n if (credentials) {\n ctx.headers.set('Access-Control-Allow-Credentials', 'true')\n }\n if (exposedHeaders.length > 0) {\n ctx.headers.set('Access-Control-Expose-Headers', exposedHeaders.join(', '))\n }\n if (resolvedOrigin !== '*') {\n ctx.headers.append('Vary', 'Origin')\n }\n\n // Handle preflight\n if (ctx.req.method === 'OPTIONS') {\n return new Response(null, {\n status: 204,\n headers: {\n 'Access-Control-Allow-Origin': resolvedOrigin,\n 'Access-Control-Allow-Methods': methods.join(', '),\n 'Access-Control-Allow-Headers': allowedHeaders.join(', '),\n 'Access-Control-Max-Age': String(maxAge),\n ...(credentials ? { 'Access-Control-Allow-Credentials': 'true' } : {}),\n },\n })\n }\n }\n}\n\nfunction resolveOrigin(config: CorsConfig['origin'], requestOrigin: string): string | null {\n if (config === '*') return '*'\n if (typeof config === 'string') {\n return config === requestOrigin ? config : null\n }\n if (typeof config === 'function') {\n return config(requestOrigin) ? requestOrigin : null\n }\n if (Array.isArray(config)) {\n return config.includes(requestOrigin) ? requestOrigin : null\n }\n return null\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Rate limiting middleware ───────────────────────────────────────────────\n\nexport interface RateLimitConfig {\n /** Maximum requests per window. Default: `100` */\n max?: number\n /** Time window in seconds. Default: `60` */\n window?: number\n /** Function to extract the client identifier. Default: IP from headers. */\n keyFn?: (ctx: MiddlewareContext) => string\n /** Custom response when rate limited. */\n onLimit?: (ctx: MiddlewareContext) => Response\n /** URL patterns to rate limit (glob-style). Default: all paths. */\n include?: string[]\n /** URL patterns to exclude from rate limiting. */\n exclude?: string[]\n}\n\ninterface RateLimitEntry {\n count: number\n resetAt: number\n}\n\n/**\n * Rate limiting middleware — limits requests per client within a time window.\n * Uses an in-memory store (suitable for single-instance deployments).\n *\n * @example\n * import { rateLimitMiddleware } from \"@pyreon/zero/rate-limit\"\n *\n * // 100 requests per minute (default)\n * rateLimitMiddleware()\n *\n * // Strict API rate limiting\n * rateLimitMiddleware({\n * max: 20,\n * window: 60,\n * include: [\"/api/*\"],\n * })\n */\nexport function rateLimitMiddleware(config: RateLimitConfig = {}): Middleware {\n const {\n max = 100,\n window: windowSec = 60,\n keyFn = defaultKeyFn,\n onLimit,\n include,\n exclude,\n } = config\n\n const windowMs = windowSec * 1000\n const store = new Map<string, RateLimitEntry>()\n const MAX_STORE_SIZE = 10000\n let lastCleanup = Date.now()\n\n // Inline cleanup — runs during request processing, no setInterval needed.\n // Evicts expired entries when store exceeds half capacity or on window boundary.\n function cleanupIfNeeded(now: number) {\n if (store.size < MAX_STORE_SIZE / 2 && now - lastCleanup < windowMs) return\n lastCleanup = now\n for (const [key, entry] of store) {\n if (entry.resetAt <= now) store.delete(key)\n }\n }\n\n return (ctx: MiddlewareContext) => {\n // Check include/exclude patterns\n if (include && !include.some((p) => matchSimpleGlob(p, ctx.path))) return\n if (exclude?.some((p) => matchSimpleGlob(p, ctx.path))) return\n\n const key = keyFn(ctx)\n const now = Date.now()\n\n cleanupIfNeeded(now)\n\n let entry = store.get(key)\n\n if (!entry || entry.resetAt <= now) {\n entry = { count: 0, resetAt: now + windowMs }\n store.set(key, entry)\n }\n\n entry.count++\n const remaining = Math.max(0, max - entry.count)\n const resetSeconds = Math.ceil((entry.resetAt - now) / 1000)\n\n // Set rate limit headers on all responses\n ctx.headers.set('X-RateLimit-Limit', String(max))\n ctx.headers.set('X-RateLimit-Remaining', String(remaining))\n ctx.headers.set('X-RateLimit-Reset', String(resetSeconds))\n\n if (entry.count > max) {\n if (onLimit) return onLimit(ctx)\n\n return new Response(JSON.stringify({ error: 'Too many requests' }), {\n status: 429,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': String(resetSeconds),\n 'X-RateLimit-Limit': String(max),\n 'X-RateLimit-Remaining': '0',\n 'X-RateLimit-Reset': String(resetSeconds),\n },\n })\n }\n }\n}\n\nfunction defaultKeyFn(ctx: MiddlewareContext): string {\n return (\n ctx.req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n ctx.req.headers.get('x-real-ip') ??\n 'unknown'\n )\n}\n\n/** Simple glob matching for path patterns. Supports trailing `*`. */\nfunction matchSimpleGlob(pattern: string, path: string): boolean {\n if (pattern.endsWith('/*')) {\n return path.startsWith(pattern.slice(0, -1))\n }\n return pattern === path\n}\n","import type { Middleware, MiddlewareContext } from '@pyreon/server'\n\n// ─── Compression middleware ─────────────────────────────────────────────────\n\nexport interface CompressionConfig {\n /** Minimum response size in bytes to compress. Default: `1024` (1KB) */\n threshold?: number\n /** Encoding preference order. Default: `[\"gzip\", \"deflate\"]` */\n encodings?: ('gzip' | 'deflate')[]\n}\n\n/**\n * Compression middleware — compresses responses using gzip or deflate\n * based on the client's Accept-Encoding header.\n *\n * Only compresses text-based content types (HTML, JSON, JS, CSS, XML, SVG).\n * Skips responses below the size threshold and already-encoded responses.\n *\n * @example\n * import { compressionMiddleware } from \"@pyreon/zero/compression\"\n *\n * compressionMiddleware() // gzip with 1KB threshold\n * compressionMiddleware({ threshold: 512, encodings: [\"gzip\"] })\n */\nexport function compressionMiddleware(config: CompressionConfig = {}): Middleware {\n const { threshold = 1024, encodings = ['gzip', 'deflate'] } = config\n\n return (ctx: MiddlewareContext) => {\n const acceptEncoding = ctx.req.headers.get('accept-encoding') ?? ''\n\n // Find the best supported encoding\n const encoding = encodings.find((enc) => acceptEncoding.includes(enc))\n if (!encoding) return\n\n // Store the encoding choice for post-processing\n ctx.locals.__compressionEncoding = encoding\n ctx.locals.__compressionThreshold = threshold\n ctx.headers.append('Vary', 'Accept-Encoding')\n }\n}\n\n/**\n * Compress a Response body if it meets the criteria.\n * Use this to post-process responses after the handler runs.\n *\n * @example\n * const response = await handler(request)\n * const compressed = await compressResponse(response, 'gzip', 1024)\n */\nexport async function compressResponse(\n response: Response,\n encoding: 'gzip' | 'deflate',\n threshold: number,\n): Promise<Response> {\n const contentType = response.headers.get('content-type') ?? ''\n\n // Only compress text-based content\n if (!isCompressible(contentType)) return response\n\n // Skip if already encoded\n if (response.headers.get('content-encoding')) return response\n\n const body = await response.arrayBuffer()\n\n // Skip below threshold\n if (body.byteLength < threshold) return response\n\n const compressed = await compress(body, encoding)\n\n const headers = new Headers(response.headers)\n headers.set('Content-Encoding', encoding)\n headers.delete('Content-Length')\n headers.append('Vary', 'Accept-Encoding')\n\n return new Response(compressed, {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n}\n\nconst COMPRESSIBLE_TYPES = [\n 'text/',\n 'application/json',\n 'application/javascript',\n 'application/xml',\n 'application/xhtml+xml',\n 'image/svg+xml',\n]\n\n/** Check if a content type is compressible. Exported for testing. */\nexport function isCompressible(contentType: string): boolean {\n return COMPRESSIBLE_TYPES.some((t) => contentType.includes(t))\n}\n\nasync function compress(data: ArrayBuffer, encoding: 'gzip' | 'deflate'): Promise<ArrayBuffer> {\n // CompressionStream is available in modern browsers and Node 18+/Bun.\n // Fallback: try node:zlib for older runtimes.\n if (typeof CompressionStream !== 'undefined') {\n const format = encoding === 'gzip' ? 'gzip' : 'deflate'\n const stream = new Blob([data]).stream().pipeThrough(new CompressionStream(format))\n return new Response(stream).arrayBuffer()\n }\n\n // Node.js fallback via zlib\n try {\n const zlib = await import('node:zlib')\n const { promisify } = await import('node:util')\n const fn = encoding === 'gzip' ? promisify(zlib.gzip) : promisify(zlib.deflate)\n const result = await fn(Buffer.from(data))\n return result.buffer.slice(result.byteOffset, result.byteOffset + result.byteLength)\n } catch {\n // No compression available — return uncompressed\n return data\n }\n}\n","import type { MiddlewareContext } from '@pyreon/server'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Context passed to server action handlers. */\nexport interface ActionContext {\n /** The original request. */\n request: Request\n /** Parsed form data (for form submissions). */\n formData: FormData | null\n /** Parsed JSON body (for JSON submissions). */\n json: unknown\n /** Request headers. */\n headers: Headers\n}\n\n/** A server action handler function. */\nexport type ActionHandler<T = unknown> = (ctx: ActionContext) => T | Promise<T>\n\n/** A registered action with its ID and handler. */\ninterface RegisteredAction {\n id: string\n handler: ActionHandler\n}\n\n/** Client-side callable action returned by defineAction. */\nexport interface Action<T = unknown> {\n /** Call the action with JSON data. */\n (data?: unknown): Promise<T>\n /** The action's unique ID. */\n actionId: string\n}\n\n// ─── Registry ────────────────────────────────────────────────────────────────\n\nconst actionRegistry = new Map<string, RegisteredAction>()\n\n/**\n * Define a server action. Returns a callable function that:\n * - On the **client**: sends a POST request to `/_zero/actions/<id>`\n * - On the **server** (SSR): executes the handler directly (no fetch)\n *\n * @example\n * // In a route file or module:\n * export const createPost = defineAction(async (ctx) => {\n * const data = ctx.json as { title: string; body: string }\n * // ... save to database\n * return { success: true, id: 123 }\n * })\n *\n * // In a component:\n * const result = await createPost({ title: 'Hello', body: '...' })\n */\nexport function defineAction<T = unknown>(handler: ActionHandler<T>): Action<T> {\n const id = `action_${crypto.randomUUID().slice(0, 8)}`\n\n actionRegistry.set(id, { id, handler: handler as ActionHandler })\n\n const callable = async (data?: unknown): Promise<T> => {\n // Server-side: execute handler directly (no network round-trip)\n if (typeof globalThis.window === 'undefined') {\n return handler({\n request: new Request(`http://localhost/_zero/actions/${id}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data ?? null),\n }),\n formData: null,\n json: data ?? null,\n headers: new Headers({ 'Content-Type': 'application/json' }),\n })\n }\n\n // Client-side: POST to the action endpoint\n const response = await fetch(`/_zero/actions/${id}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data ?? null),\n })\n if (!response.ok) {\n const body = await response.json().catch(() => ({}))\n throw new Error((body as { error?: string }).error ?? `Action failed: ${response.statusText}`)\n }\n return response.json()\n }\n\n callable.actionId = id\n return callable as Action<T>\n}\n\n/** Get all registered actions. Useful for testing. */\nexport function getRegisteredActions(): Map<string, RegisteredAction> {\n return actionRegistry\n}\n\n/**\n * Reset the action registry. Useful for testing.\n * @internal\n */\nexport function _resetActions(): void {\n actionRegistry.clear()\n}\n\n// ─── Server handler ──────────────────────────────────────────────────────────\n\n/**\n * Create a middleware that handles action requests at `/_zero/actions/*`.\n * Mount this before the SSR handler in the server entry.\n */\nexport function createActionMiddleware(): (\n ctx: MiddlewareContext,\n) => Response | undefined | Promise<Response | undefined> {\n return async (ctx: MiddlewareContext) => {\n if (!ctx.path.startsWith('/_zero/actions/')) return\n\n const actionId = ctx.path.slice('/_zero/actions/'.length)\n const action = actionRegistry.get(actionId)\n\n if (!action) {\n return Response.json({ error: 'Action not found' }, { status: 404 })\n }\n\n if (ctx.req.method !== 'POST') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n\n return executeAction(action, ctx.req)\n }\n}\n\nasync function executeAction(action: RegisteredAction, req: Request): Promise<Response> {\n try {\n const contentType = req.headers.get('content-type') ?? ''\n let formData: FormData | null = null\n let json: unknown = null\n\n if (contentType.includes('application/json')) {\n json = await req.json()\n } else if (\n contentType.includes('multipart/form-data') ||\n contentType.includes('application/x-www-form-urlencoded')\n ) {\n formData = await req.formData()\n }\n\n const result = await action.handler({\n request: req,\n formData,\n json,\n headers: req.headers,\n })\n\n return Response.json(result ?? null)\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Internal server error'\n return Response.json({ error: message }, { status: 500 })\n }\n}\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\n\nlet sharpWarned = false\nfunction warnSharpMissing() {\n if (sharpWarned) return\n sharpWarned = true\n // oxlint-disable-next-line no-console\n console.warn(\n '\\n[zero:favicon] sharp not installed — favicons will not be generated. Install for full support: bun add -D sharp\\n',\n )\n}\n\n// ─── Favicon generation plugin ──────────────────────────────────────────────\n//\n// Generates all favicon formats from a single source file (SVG or PNG):\n// - favicon.ico (16x16 + 32x32 combined)\n// - favicon.svg (copied if source is SVG)\n// - apple-touch-icon.png (180x180)\n// - icon-192.png (for web manifest)\n// - icon-512.png (for web manifest)\n// - site.webmanifest\n//\n// Usage:\n// import { faviconPlugin } from \"@pyreon/zero\"\n// export default { plugins: [zero(), faviconPlugin({ source: \"./icon.svg\" })] }\n\nexport interface FaviconLocaleConfig {\n /** Locale-specific source icon (SVG or PNG). */\n source: string\n /** Optional dark mode variant for this locale. */\n darkSource?: string\n}\n\nexport interface FaviconPluginConfig {\n /** Path to the source icon (SVG or PNG, at least 512x512 for PNG). */\n source: string\n /** Theme color for web manifest. Default: \"#ffffff\" */\n themeColor?: string\n /** Background color for web manifest. Default: \"#ffffff\" */\n backgroundColor?: string\n /** App name for web manifest. Uses package.json name if not set. */\n name?: string\n /** Generate web manifest. Default: true */\n manifest?: boolean\n /**\n * Dark mode favicon (SVG only).\n * When provided, the SVG favicon uses prefers-color-scheme media query\n * to switch between light and dark variants.\n */\n darkSource?: string\n /**\n * Locale-specific icon overrides. Each key is a locale code,\n * value is a source icon (and optional dark variant).\n * Locales not in this map use the base `source`.\n *\n * Generated files are placed under `/{locale}/` prefix:\n * /de/favicon.svg, /de/favicon-32x32.png, etc.\n *\n * @example\n * ```ts\n * faviconPlugin({\n * source: \"./icon.svg\",\n * locales: {\n * de: { source: \"./icon-de.svg\" },\n * cs: { source: \"./icon-cs.svg\" },\n * },\n * })\n * ```\n */\n locales?: Record<string, FaviconLocaleConfig>\n}\n\ninterface FaviconSize {\n size: number\n name: string\n}\n\nconst SIZES: FaviconSize[] = [\n { size: 16, name: 'favicon-16x16.png' },\n { size: 32, name: 'favicon-32x32.png' },\n { size: 180, name: 'apple-touch-icon.png' },\n { size: 192, name: 'icon-192.png' },\n { size: 512, name: 'icon-512.png' },\n]\n\n/**\n * Favicon generation Vite plugin.\n *\n * Generates all required favicon formats at build time from a single source.\n * In dev mode, serves the source directly.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { faviconPlugin } from \"@pyreon/zero\"\n *\n * export default {\n * plugins: [faviconPlugin({ source: \"./src/assets/icon.svg\" })],\n * }\n * ```\n */\nexport function faviconPlugin(config: FaviconPluginConfig): Plugin {\n const themeColor = config.themeColor ?? '#ffffff'\n const backgroundColor = config.backgroundColor ?? '#ffffff'\n const generateManifest = config.manifest !== false\n\n let root = ''\n let isBuild = false\n\n return {\n name: 'pyreon-zero-favicon',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n root = resolvedConfig.root\n isBuild = resolvedConfig.command === 'build'\n },\n\n // Dev server: serve generated favicons on-the-fly\n configureServer(server) {\n const sourcePath = join(root, config.source)\n const devCache = new Map<string, Uint8Array>()\n\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? ''\n\n // Resolve locale-specific source: /{locale}/favicon.svg → locale source\n const localeSource = resolveLocaleSource(url, config, root)\n\n // Serve source as favicon.svg in dev\n const svgUrl = localeSource ? localeSource.url : url\n const svgPath = localeSource ? localeSource.sourcePath : sourcePath\n const isSvgSource = localeSource ? localeSource.source.endsWith('.svg') : config.source.endsWith('.svg')\n\n if (svgUrl.endsWith('/favicon.svg') && isSvgSource) {\n try {\n const content = await readFile(svgPath, 'utf-8')\n res.setHeader('Content-Type', 'image/svg+xml')\n res.end(content)\n return\n } catch { /* fall through */ }\n }\n\n // Serve generated PNGs on-demand (supports /{locale}/favicon-32x32.png)\n const baseName = svgUrl.split('/').pop() ?? ''\n const sizeMatch = SIZES.find((s) => s.name === baseName)\n if (sizeMatch) {\n const cacheKey = `${svgPath}:${sizeMatch.size}`\n let png = devCache.get(cacheKey)\n if (!png) {\n const result = await resizeToPng(svgPath, sizeMatch.size)\n if (result) {\n png = result\n devCache.set(cacheKey, result)\n }\n }\n if (png) {\n res.setHeader('Content-Type', 'image/png')\n res.setHeader('Cache-Control', 'no-cache')\n res.end(Buffer.from(png))\n return\n }\n }\n\n // Serve generated ICO on-demand\n if (baseName === 'favicon.ico') {\n const cacheKey = `ico:${svgPath}`\n let ico: Uint8Array | undefined = devCache.get(cacheKey)\n if (!ico) {\n const result = await generateIco(svgPath)\n if (result) {\n ico = result\n devCache.set(cacheKey, result)\n }\n }\n if (ico) {\n res.setHeader('Content-Type', 'image/x-icon')\n res.setHeader('Cache-Control', 'no-cache')\n res.end(Buffer.from(ico))\n return\n }\n }\n\n // Serve manifest (supports /{locale}/site.webmanifest)\n if (baseName === 'site.webmanifest' && generateManifest) {\n const prefix = localeSource ? `/${localeSource.locale}` : ''\n const manifest = {\n name: config.name ?? 'App',\n short_name: config.name ?? 'App',\n icons: [\n { src: `${prefix}/icon-192.png`, sizes: '192x192', type: 'image/png' },\n { src: `${prefix}/icon-512.png`, sizes: '512x512', type: 'image/png' },\n ],\n theme_color: themeColor,\n background_color: backgroundColor,\n display: 'standalone',\n }\n res.setHeader('Content-Type', 'application/manifest+json')\n res.end(JSON.stringify(manifest, null, 2))\n return\n }\n\n next()\n })\n },\n\n // Inject favicon <link> tags into HTML\n transformIndexHtml() {\n const isSvg = config.source.endsWith('.svg')\n const tags: Array<{\n tag: string\n attrs: Record<string, string>\n injectTo: 'head'\n }> = []\n\n if (isSvg) {\n tags.push({\n tag: 'link',\n attrs: { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },\n injectTo: 'head',\n })\n }\n\n tags.push(\n {\n tag: 'link',\n attrs: { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' },\n injectTo: 'head',\n },\n {\n tag: 'link',\n attrs: { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' },\n injectTo: 'head',\n },\n {\n tag: 'link',\n attrs: { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' },\n injectTo: 'head',\n },\n )\n\n if (generateManifest) {\n tags.push({\n tag: 'link',\n attrs: { rel: 'manifest', href: '/site.webmanifest' },\n injectTo: 'head',\n })\n }\n\n tags.push({\n tag: 'meta',\n attrs: { name: 'theme-color', content: themeColor },\n injectTo: 'head',\n })\n\n return tags\n },\n\n async generateBundle() {\n if (!isBuild) return\n\n // Generate favicons for the base (default) source\n await generateFaviconSet.call(this, root, config.source, config.darkSource, '', config, themeColor, backgroundColor, generateManifest)\n\n // Generate locale-specific favicon sets\n if (config.locales) {\n for (const [locale, localeConfig] of Object.entries(config.locales)) {\n await generateFaviconSet.call(this, root, localeConfig.source, localeConfig.darkSource, `${locale}/`, config, themeColor, backgroundColor, generateManifest)\n }\n }\n },\n }\n}\n\n/**\n * Wrap two SVGs into a single SVG that switches based on prefers-color-scheme.\n */\nfunction wrapSvgWithDarkMode(lightSvg: string, darkSvg: string): string {\n // Extract viewBox from light SVG\n const viewBoxMatch = lightSvg.match(/viewBox=\"([^\"]*)\"/)\n const viewBox = viewBoxMatch?.[1] ?? '0 0 32 32'\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\">\n <style>\n :root { color-scheme: light dark; }\n @media (prefers-color-scheme: dark) { .light { display: none; } }\n @media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) { .dark { display: none; } }\n </style>\n <g class=\"light\">${stripSvgWrapper(lightSvg)}</g>\n <g class=\"dark\">${stripSvgWrapper(darkSvg)}</g>\n</svg>`\n}\n\nfunction stripSvgWrapper(svg: string): string {\n return svg\n .replace(/<svg[^>]*>/, '')\n .replace(/<\\/svg>\\s*$/, '')\n .trim()\n}\n\n/**\n * Resolve the source path for a locale-prefixed favicon URL.\n * Returns null if the URL is not locale-prefixed or locale has no override.\n */\nfunction resolveLocaleSource(\n url: string,\n config: FaviconPluginConfig,\n rootDir: string,\n): { locale: string; url: string; source: string; sourcePath: string } | null {\n if (!config.locales) return null\n\n for (const [locale, localeConfig] of Object.entries(config.locales)) {\n const prefix = `/${locale}/`\n if (url.startsWith(prefix)) {\n return {\n locale,\n url,\n source: localeConfig.source,\n sourcePath: join(rootDir, localeConfig.source),\n }\n }\n }\n return null\n}\n\n/**\n * Generate a complete favicon set (SVG, PNGs, ICO, manifest) with a file prefix.\n * Called once for base (prefix = '') and once per locale (prefix = '{locale}/').\n */\nasync function generateFaviconSet(\n this: any,\n rootDir: string,\n source: string,\n darkSource: string | undefined,\n prefix: string,\n config: FaviconPluginConfig,\n themeColor: string,\n backgroundColor: string,\n generateManifest: boolean,\n): Promise<void> {\n const sourcePath = join(rootDir, source)\n if (!existsSync(sourcePath)) {\n // oxlint-disable-next-line no-console\n console.warn(`[zero:favicon] Source not found: ${sourcePath}`)\n return\n }\n\n const isSvg = source.endsWith('.svg')\n\n // Copy SVG as favicon.svg\n if (isSvg) {\n const svgContent = await readFile(sourcePath, 'utf-8')\n let finalSvg = svgContent\n\n if (darkSource) {\n const darkPath = join(rootDir, darkSource)\n if (existsSync(darkPath)) {\n const darkSvg = await readFile(darkPath, 'utf-8')\n finalSvg = wrapSvgWithDarkMode(svgContent, darkSvg)\n }\n }\n\n this.emitFile({\n type: 'asset',\n fileName: `${prefix}favicon.svg`,\n source: finalSvg,\n })\n }\n\n // Generate PNG sizes via sharp\n for (const { size, name } of SIZES) {\n const pngBuffer = await resizeToPng(sourcePath, size)\n if (pngBuffer) {\n this.emitFile({\n type: 'asset',\n fileName: `${prefix}${name}`,\n source: pngBuffer,\n })\n }\n }\n\n // Generate favicon.ico (16 + 32)\n const ico = await generateIco(sourcePath)\n if (ico) {\n this.emitFile({\n type: 'asset',\n fileName: `${prefix}favicon.ico`,\n source: ico,\n })\n }\n\n // Generate web manifest\n if (generateManifest) {\n const manifestPrefix = prefix ? `/${prefix.slice(0, -1)}` : ''\n const manifest = {\n name: config.name ?? 'App',\n short_name: config.name ?? 'App',\n icons: [\n { src: `${manifestPrefix}/icon-192.png`, sizes: '192x192', type: 'image/png' },\n { src: `${manifestPrefix}/icon-512.png`, sizes: '512x512', type: 'image/png' },\n ],\n theme_color: themeColor,\n background_color: backgroundColor,\n display: 'standalone',\n }\n\n this.emitFile({\n type: 'asset',\n fileName: `${prefix}site.webmanifest`,\n source: JSON.stringify(manifest, null, 2),\n })\n }\n}\n\n/**\n * Get favicon link tags for a specific locale.\n * Returns link objects suitable for `useHead()` or direct HTML injection.\n *\n * @example\n * ```ts\n * const links = faviconLinks(\"de\", { source: \"./icon.svg\", locales: { de: { source: \"./icon-de.svg\" } } })\n * // → [{ rel: \"icon\", type: \"image/svg+xml\", href: \"/de/favicon.svg\" }, ...]\n * ```\n */\nexport function faviconLinks(\n locale: string | undefined,\n config: FaviconPluginConfig,\n): Array<{ rel: string; type?: string; sizes?: string; href: string }> {\n const hasLocaleOverride = locale && config.locales?.[locale]\n const prefix = hasLocaleOverride ? `/${locale}` : ''\n const isSvg = (hasLocaleOverride ? config.locales![locale]!.source : config.source).endsWith('.svg')\n\n const links: Array<{ rel: string; type?: string; sizes?: string; href: string }> = []\n\n if (isSvg) {\n links.push({ rel: 'icon', type: 'image/svg+xml', href: `${prefix}/favicon.svg` })\n }\n\n links.push(\n { rel: 'icon', type: 'image/png', sizes: '32x32', href: `${prefix}/favicon-32x32.png` },\n { rel: 'icon', type: 'image/png', sizes: '16x16', href: `${prefix}/favicon-16x16.png` },\n { rel: 'apple-touch-icon', sizes: '180x180', href: `${prefix}/apple-touch-icon.png` },\n )\n\n if (config.manifest !== false) {\n links.push({ rel: 'manifest', href: `${prefix}/site.webmanifest` })\n }\n\n return links\n}\n\nasync function resizeToPng(input: string, size: number): Promise<Uint8Array | null> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n return await sharp(input).resize(size, size, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } } as any).png().toBuffer()\n } catch {\n warnSharpMissing()\n return null\n }\n}\n\nasync function generateIco(input: string): Promise<Uint8Array | null> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n const png16 = await sharp(input).resize(16, 16, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } } as any).png().toBuffer()\n const png32 = await sharp(input).resize(32, 32, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } } as any).png().toBuffer()\n\n // ICO format: header + directory entries + PNG data\n return createIcoFromPngs([\n { buffer: png16, size: 16 },\n { buffer: png32, size: 32 },\n ])\n } catch {\n warnSharpMissing()\n return null\n }\n}\n\nexport interface IcoEntry {\n buffer: Buffer\n size: number\n}\n\n/** @internal Exported for testing */\nexport function createIcoFromPngs(entries: IcoEntry[]): Uint8Array {\n const headerSize = 6\n const dirEntrySize = 16\n const dirSize = dirEntrySize * entries.length\n let dataOffset = headerSize + dirSize\n\n // ICO header\n const header = Buffer.alloc(headerSize)\n header.writeUInt16LE(0, 0) // reserved\n header.writeUInt16LE(1, 2) // type: icon\n header.writeUInt16LE(entries.length, 4) // count\n\n // Directory entries\n const dirEntries = Buffer.alloc(dirSize)\n const dataBuffers: Buffer[] = []\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i]!\n const offset = i * dirEntrySize\n dirEntries.writeUInt8(entry.size === 256 ? 0 : entry.size, offset) // width\n dirEntries.writeUInt8(entry.size === 256 ? 0 : entry.size, offset + 1) // height\n dirEntries.writeUInt8(0, offset + 2) // palette\n dirEntries.writeUInt8(0, offset + 3) // reserved\n dirEntries.writeUInt16LE(1, offset + 4) // color planes\n dirEntries.writeUInt16LE(32, offset + 6) // bits per pixel\n dirEntries.writeUInt32LE(entry.buffer.length, offset + 8) // size\n dirEntries.writeUInt32LE(dataOffset, offset + 12) // offset\n\n dataOffset += entry.buffer.length\n dataBuffers.push(entry.buffer)\n }\n\n return Buffer.concat([header, dirEntries, ...dataBuffers])\n}\n","/**\n * OG Image generation plugin.\n *\n * Generates Open Graph images at build time from templates with\n * text overlays. Supports locale-specific text for i18n apps.\n * Uses sharp for image processing (same optional dep as favicon/image plugins).\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { ogImagePlugin } from \"@pyreon/zero/og-image\"\n *\n * export default {\n * plugins: [\n * ogImagePlugin({\n * locales: [\"en\", \"de\", \"cs\"],\n * templates: [{\n * name: \"default\",\n * background: \"./src/assets/og-bg.jpg\",\n * layers: [{\n * text: { en: \"Build faster\", de: \"Schneller bauen\", cs: \"Stavte rychleji\" },\n * y: \"40%\",\n * fontSize: 72,\n * }],\n * }],\n * }),\n * ],\n * }\n * ```\n */\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Plugin } from 'vite'\n\nlet sharpWarned = false\nfunction warnSharpMissing() {\n if (sharpWarned) return\n sharpWarned = true\n // oxlint-disable-next-line no-console\n console.warn(\n '\\n[zero:og-image] sharp not installed — OG images will not be generated. Install for full support: bun add -D sharp\\n',\n )\n}\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface OgImageLayer {\n /**\n * Text content. Can be:\n * - A string (same for all locales)\n * - A record mapping locale → text\n * - A function receiving locale and returning text\n */\n text: string | Record<string, string> | ((locale: string) => string)\n /** X position — number (px) or string with % (e.g. \"50%\"). Default: \"50%\" */\n x?: number | string\n /** Y position — number (px) or string with % (e.g. \"40%\"). Default: \"50%\" */\n y?: number | string\n /** Font size in px. Default: 64 */\n fontSize?: number\n /** Font family. Default: \"sans-serif\" */\n fontFamily?: string\n /** Font weight. Default: \"bold\" */\n fontWeight?: string\n /** Text color. Default: \"#ffffff\" */\n color?: string\n /** Text anchor (alignment). Default: \"middle\" */\n textAnchor?: 'start' | 'middle' | 'end'\n /** Max width in px before wrapping. Default: 80% of image width. */\n maxWidth?: number\n}\n\nexport interface OgImageTemplate {\n /** Template name — used for output file naming. */\n name: string\n /**\n * Background: path to an image file, or a solid color config.\n *\n * @example \"./src/assets/og-bg.jpg\"\n * @example { color: \"#0066ff\", width: 1200, height: 630 }\n */\n background: string | { color: string; width?: number; height?: number }\n /** Output width. Default: 1200 */\n width?: number\n /** Output height. Default: 630 */\n height?: number\n /** Output format. Default: \"png\" */\n format?: 'png' | 'jpeg'\n /** JPEG quality (1-100). Default: 90 */\n quality?: number\n /** Text layers to overlay on the background. */\n layers?: OgImageLayer[]\n}\n\nexport interface OgImagePluginConfig {\n /** Templates to generate. */\n templates: OgImageTemplate[]\n /** Locales to generate for. When omitted, generates a single image per template. */\n locales?: string[]\n /** Output directory prefix. Default: \"og\" */\n outDir?: string\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction resolvePosition(value: number | string | undefined, dimension: number, fallback = '50%'): number {\n if (value === undefined) value = fallback\n if (typeof value === 'number') return value\n if (value.endsWith('%')) return Math.round((Number.parseFloat(value) / 100) * dimension)\n return Number.parseInt(value, 10) || 0\n}\n\nfunction resolveLayerText(layer: OgImageLayer, locale: string): string {\n if (typeof layer.text === 'string') return layer.text\n if (typeof layer.text === 'function') return layer.text(locale)\n return layer.text[locale] ?? layer.text[Object.keys(layer.text)[0] ?? ''] ?? ''\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/**\n * Build an SVG overlay with text layers.\n * @internal Exported for testing.\n */\nexport function buildTextOverlaySvg(\n layers: OgImageLayer[],\n width: number,\n height: number,\n locale: string,\n): string {\n const textElements = layers.map((layer) => {\n const text = resolveLayerText(layer, locale)\n const x = resolvePosition(layer.x, width, '50%')\n const y = resolvePosition(layer.y, height, '50%')\n const fontSize = layer.fontSize ?? 64\n const fontFamily = layer.fontFamily ?? 'sans-serif'\n const fontWeight = layer.fontWeight ?? 'bold'\n const color = layer.color ?? '#ffffff'\n const anchor = layer.textAnchor ?? 'middle'\n const maxWidth = layer.maxWidth ?? Math.round(width * 0.8)\n\n // Word wrapping via tspan elements.\n // Width estimation: Latin chars ~0.55em, CJK chars ~1.0em, narrow chars ~0.35em.\n const words = text.split(' ')\n const lines: string[] = []\n let currentLine = ''\n\n const estimateWidth = (s: string): number => {\n let width = 0\n for (let i = 0; i < s.length; i++) {\n const code = s.charCodeAt(i)\n if (code >= 0x3000 && code <= 0x9FFF) {\n // CJK characters — full width\n width += fontSize * 1.0\n } else if (code <= 0x7E && 'iljft!|:;.,\\''.includes(s[i]!)) {\n // Narrow Latin characters\n width += fontSize * 0.35\n } else {\n // Regular Latin characters\n width += fontSize * 0.55\n }\n }\n return width\n }\n\n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word\n if (estimateWidth(testLine) > maxWidth && currentLine) {\n lines.push(currentLine)\n currentLine = word\n } else {\n currentLine = testLine\n }\n }\n if (currentLine) lines.push(currentLine)\n\n const tspans = lines\n .map((line, i) => {\n const dy = i === 0 ? '0' : `${fontSize * 1.2}`\n return `<tspan x=\"${x}\" dy=\"${dy}\">${escapeXml(line)}</tspan>`\n })\n .join('')\n\n return `<text x=\"${x}\" y=\"${y}\" font-size=\"${fontSize}\" font-family=\"${escapeXml(fontFamily)}\" font-weight=\"${fontWeight}\" fill=\"${color}\" text-anchor=\"${anchor}\" dominant-baseline=\"middle\">${tspans}</text>`\n })\n\n return `<svg width=\"${width}\" height=\"${height}\" xmlns=\"http://www.w3.org/2000/svg\">${textElements.join('')}</svg>`\n}\n\n/**\n * Render an OG image from a template for a specific locale.\n * @internal Exported for testing.\n */\nexport async function renderOgImage(\n template: OgImageTemplate,\n locale: string,\n rootDir: string,\n): Promise<Uint8Array | null> {\n try {\n const sharp = await import('sharp').then((m) => m.default ?? m)\n const width = template.width ?? 1200\n const height = template.height ?? 630\n\n let pipeline: any\n if (typeof template.background === 'string') {\n const bgPath = join(rootDir, template.background)\n pipeline = sharp(bgPath).resize(width, height, { fit: 'cover' })\n } else {\n pipeline = (sharp as any)({\n create: {\n width,\n height,\n channels: 4,\n background: template.background.color,\n },\n })\n }\n\n // Overlay text layers if any\n if (template.layers && template.layers.length > 0) {\n const svgOverlay = buildTextOverlaySvg(template.layers, width, height, locale)\n pipeline = pipeline.composite([{\n input: Buffer.from(svgOverlay),\n top: 0,\n left: 0,\n }])\n }\n\n if (template.format === 'jpeg') {\n return await pipeline.jpeg({ quality: template.quality ?? 90 }).toBuffer()\n }\n return await pipeline.png().toBuffer()\n } catch {\n warnSharpMissing()\n return null\n }\n}\n\n// ─── Path utility ───────────────────────────────────────────────────────────\n\n/**\n * Compute the OG image path for a template and locale.\n *\n * @example\n * ```ts\n * ogImagePath(\"default\", \"de\") // → \"/og/default-de.png\"\n * ogImagePath(\"default\") // → \"/og/default.png\"\n * ogImagePath(\"hero\", \"en\", \"images\") // → \"/images/hero-en.png\"\n * ```\n */\nexport function ogImagePath(\n templateName: string,\n locale?: string,\n outDir = 'og',\n format: 'png' | 'jpeg' = 'png',\n): string {\n const ext = format === 'jpeg' ? 'jpg' : 'png'\n const suffix = locale ? `-${locale}` : ''\n return `/${outDir}/${templateName}${suffix}.${ext}`\n}\n\n// ─── Vite plugin ────────────────────────────────────────────────────────────\n\n/**\n * OG image generation Vite plugin.\n *\n * Generates Open Graph images at build time. In dev, generates on-demand.\n * Requires `sharp` as an optional dependency.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { ogImagePlugin } from \"@pyreon/zero/og-image\"\n *\n * export default {\n * plugins: [\n * ogImagePlugin({\n * locales: [\"en\", \"de\"],\n * templates: [{\n * name: \"default\",\n * background: { color: \"#0066ff\" },\n * layers: [{ text: { en: \"Hello\", de: \"Hallo\" }, fontSize: 72 }],\n * }],\n * }),\n * ],\n * }\n * ```\n */\nexport function ogImagePlugin(config: OgImagePluginConfig): Plugin {\n const outDir = config.outDir ?? 'og'\n let root = ''\n let isBuild = false\n\n return {\n name: 'pyreon-zero-og-image',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n root = resolvedConfig.root\n isBuild = resolvedConfig.command === 'build'\n },\n\n // Dev: generate on-demand\n configureServer(server) {\n const devCache = new Map<string, Uint8Array>()\n\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith(`/${outDir}/`)) return next()\n\n // Parse: /og/default-en.png → template=default, locale=en\n const fileName = url.slice(outDir.length + 2) // strip /{outDir}/\n const match = fileName.match(/^(.+?)(?:-([a-z]{2,5}))?\\.(png|jpe?g)$/)\n if (!match) return next()\n\n const [, templateName, locale, ext] = match\n const template = config.templates.find((t) => t.name === templateName)\n if (!template) return next()\n\n const resolvedLocale = locale ?? config.locales?.[0] ?? 'en'\n const cacheKey = `${templateName}:${resolvedLocale}`\n\n let buffer = devCache.get(cacheKey)\n if (!buffer) {\n const result = await renderOgImage(template, resolvedLocale, root)\n if (!result) return next()\n buffer = result\n devCache.set(cacheKey, result)\n }\n\n const contentType = ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg' : 'image/png'\n res.setHeader('Content-Type', contentType)\n res.setHeader('Cache-Control', 'no-cache')\n res.end(Buffer.from(buffer))\n })\n },\n\n // Build: generate all variants\n async generateBundle() {\n if (!isBuild) return\n\n for (const template of config.templates) {\n const locales = config.locales ?? [undefined]\n const format = template.format ?? 'png'\n const ext = format === 'jpeg' ? 'jpg' : 'png'\n\n for (const locale of locales) {\n // Validate background exists if it's a file path\n if (typeof template.background === 'string') {\n const bgPath = join(root, template.background)\n if (!existsSync(bgPath)) {\n // oxlint-disable-next-line no-console\n console.warn(`[zero:og-image] Background not found: ${bgPath}`)\n continue\n }\n }\n\n const buffer = await renderOgImage(template, locale ?? 'en', root)\n if (!buffer) continue\n\n const suffix = locale ? `-${locale}` : ''\n this.emitFile({\n type: 'asset',\n fileName: `${outDir}/${template.name}${suffix}.${ext}`,\n source: buffer,\n })\n }\n }\n },\n }\n}\n","import { createContext } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\nimport type { Plugin } from 'vite'\n\n// ─── Localized routing ─────────────────────────────────────────────────────\n//\n// Adds locale-prefixed routes to Zero's file-system router:\n// - /about → /en/about, /de/about, /cs/about\n// - / → /en, /de, /cs (or default locale without prefix)\n// - Automatic locale detection from Accept-Language header\n// - Redirect to preferred locale\n// - hreflang link generation\n//\n// Usage:\n// import { i18nRouting } from \"@pyreon/zero\"\n// export default { plugins: [zero(), i18nRouting({ locales: [\"en\", \"de\"], defaultLocale: \"en\" })] }\n\nexport interface I18nRoutingConfig {\n /** Supported locales. e.g. [\"en\", \"de\", \"cs\"] */\n locales: string[]\n /** Default locale — served without prefix (/ instead of /en/). */\n defaultLocale: string\n /** Redirect root to detected locale. Default: true */\n detectLocale?: boolean\n /** Cookie name to persist locale preference. Default: \"locale\" */\n cookieName?: string\n /** URL strategy. Default: \"prefix-except-default\" */\n strategy?: 'prefix' | 'prefix-except-default'\n}\n\nexport interface LocaleContext {\n /** Current locale code. e.g. \"en\", \"de\" */\n locale: string\n /** All supported locales. */\n locales: string[]\n /** Default locale. */\n defaultLocale: string\n /** Build a localized path. e.g. localePath(\"/about\", \"de\") → \"/de/about\" */\n localePath: (path: string, locale?: string) => string\n /** Get hreflang alternates for the current path. */\n alternates: () => Array<{ locale: string; url: string }>\n}\n\n/**\n * Detect preferred locale from Accept-Language header.\n */\nexport function detectLocaleFromHeader(\n acceptLanguage: string | null | undefined,\n locales: string[],\n defaultLocale: string,\n): string {\n if (!acceptLanguage) return defaultLocale\n\n // Parse Accept-Language: en-US,en;q=0.9,de;q=0.8\n const preferred = acceptLanguage\n .split(',')\n .map((part) => {\n const [lang, q] = part.trim().split(';q=')\n return {\n lang: lang?.split('-')[0]?.toLowerCase() ?? '',\n quality: q ? Number.parseFloat(q) : 1,\n }\n })\n .sort((a, b) => b.quality - a.quality)\n\n for (const { lang } of preferred) {\n if (locales.includes(lang)) return lang\n }\n\n return defaultLocale\n}\n\n/**\n * Extract locale from a URL path.\n * Returns { locale, pathWithoutLocale }.\n */\nexport function extractLocaleFromPath(\n path: string,\n locales: string[],\n defaultLocale: string,\n): { locale: string; pathWithoutLocale: string } {\n const segments = path.split('/').filter(Boolean)\n const firstSegment = segments[0]?.toLowerCase()\n\n if (firstSegment && locales.includes(firstSegment)) {\n return {\n locale: firstSegment,\n pathWithoutLocale: '/' + segments.slice(1).join('/') || '/',\n }\n }\n\n return { locale: defaultLocale, pathWithoutLocale: path }\n}\n\n/**\n * Build a localized path.\n */\nexport function buildLocalePath(\n path: string,\n locale: string,\n defaultLocale: string,\n strategy: 'prefix' | 'prefix-except-default',\n): string {\n const clean = path === '/' ? '' : path\n if (strategy === 'prefix-except-default' && locale === defaultLocale) {\n return path\n }\n return `/${locale}${clean}`\n}\n\n/**\n * Create a LocaleContext for use in components and loaders.\n */\nexport function createLocaleContext(\n locale: string,\n path: string,\n config: I18nRoutingConfig,\n): LocaleContext {\n const strategy = config.strategy ?? 'prefix-except-default'\n\n return {\n locale,\n locales: config.locales,\n defaultLocale: config.defaultLocale,\n\n localePath(targetPath: string, targetLocale?: string) {\n return buildLocalePath(\n targetPath,\n targetLocale ?? locale,\n config.defaultLocale,\n strategy,\n )\n },\n\n alternates() {\n const { pathWithoutLocale } = extractLocaleFromPath(\n path,\n config.locales,\n config.defaultLocale,\n )\n return config.locales.map((loc) => ({\n locale: loc,\n url: buildLocalePath(pathWithoutLocale, loc, config.defaultLocale, strategy),\n }))\n },\n }\n}\n\n/**\n * I18n routing middleware for Zero's server.\n *\n * - Detects locale from URL prefix or Accept-Language header\n * - Redirects root to preferred locale (when detectLocale is true)\n * - Sets locale context for loaders and components\n *\n * @example\n * ```ts\n * // zero.config.ts\n * import { i18nRouting } from \"@pyreon/zero\"\n *\n * export default defineConfig({\n * plugins: [\n * i18nRouting({\n * locales: [\"en\", \"de\", \"cs\"],\n * defaultLocale: \"en\",\n * }),\n * ],\n * })\n * ```\n */\nexport function i18nRouting(config: I18nRoutingConfig): Plugin {\n const strategy = config.strategy ?? 'prefix-except-default'\n const detectEnabled = config.detectLocale !== false\n const cookieName = config.cookieName ?? 'locale'\n\n return {\n name: 'pyreon-zero-i18n-routing',\n\n // Route duplication is NOT handled here. The fs-router's `scanRouteFiles`\n // consumes the i18n config to duplicate routes per locale at build time.\n // This plugin only provides: (1) the server middleware for locale detection\n // and (2) the runtime hooks (useLocale, setLocale) for client-side use.\n configResolved() {},\n\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const url = req.url ?? '/'\n\n // Skip static assets\n if (url.startsWith('/@') || url.startsWith('/__') || url.includes('.')) {\n return next()\n }\n\n const { locale } = extractLocaleFromPath(\n url,\n config.locales,\n config.defaultLocale,\n )\n\n // Redirect root to detected locale\n if (detectEnabled && url === '/') {\n const cookies = parseCookies(req.headers.cookie)\n const preferredFromCookie = cookies[cookieName]\n const preferredFromHeader = detectLocaleFromHeader(\n req.headers['accept-language'],\n config.locales,\n config.defaultLocale,\n )\n const preferred = preferredFromCookie && config.locales.includes(preferredFromCookie)\n ? preferredFromCookie\n : preferredFromHeader\n\n if (strategy === 'prefix' || preferred !== config.defaultLocale) {\n res.writeHead(302, { Location: `/${preferred}/` })\n res.end()\n return\n }\n }\n\n // Attach locale context to request for loaders\n ;(req as any).__locale = locale\n ;(req as any).__localeContext = createLocaleContext(locale, url, config)\n\n // Update the module-level signal so useLocale() returns the correct value\n localeSignal.set(locale)\n\n next()\n })\n },\n }\n}\n\nfunction parseCookies(header: string | undefined): Record<string, string> {\n if (!header) return {}\n const result: Record<string, string> = {}\n for (const pair of header.split(';')) {\n const [key, value] = pair.trim().split('=')\n if (key && value) result[key] = decodeURIComponent(value)\n }\n return result\n}\n\n// ─── Reactive locale hook ───────────────────────────────────────────────────\n\n/** @internal Context for the current locale. */\nexport const LocaleCtx = createContext<string>('en')\n\n/** Current locale signal — set by the server middleware or client-side detection. */\nexport const localeSignal = signal('en')\n\n/**\n * Read the current locale reactively.\n *\n * Returns the locale signal value directly — reactive in both SSR and CSR.\n * The server middleware sets `localeSignal` per-request, and client-side\n * `setLocale()` updates it as well.\n *\n * @example\n * ```tsx\n * const locale = useLocale() // \"en\", \"de\", etc.\n * ```\n */\nexport function useLocale(): string {\n return localeSignal()\n}\n\n/**\n * Set the locale client-side and update the URL.\n *\n * @example\n * ```tsx\n * <button onClick={() => setLocale('de')}>Deutsch</button>\n * ```\n */\nexport function setLocale(\n locale: string,\n config: I18nRoutingConfig,\n): void {\n localeSignal.set(locale)\n\n // Persist to cookie\n if (typeof document !== 'undefined') {\n document.cookie = `${config.cookieName ?? 'locale'}=${locale}; path=/; max-age=31536000`\n }\n\n // Navigate to localized URL — use pushState to avoid full page reload\n if (typeof window !== 'undefined') {\n const strategy = config.strategy ?? 'prefix-except-default'\n const { pathWithoutLocale } = extractLocaleFromPath(\n window.location.pathname,\n config.locales,\n config.defaultLocale,\n )\n const newPath = buildLocalePath(pathWithoutLocale, locale, config.defaultLocale, strategy)\n window.history.pushState(null, '', newPath)\n // Dispatch popstate so @pyreon/router picks up the URL change\n window.dispatchEvent(new PopStateEvent('popstate'))\n }\n}\n","import type { VNodeChild } from '@pyreon/core'\nimport type { UseHeadInput } from '@pyreon/head'\nimport { useHead } from '@pyreon/head'\nimport type { FaviconPluginConfig } from './favicon'\nimport { faviconLinks } from './favicon'\nimport type { I18nRoutingConfig } from './i18n-routing'\nimport { extractLocaleFromPath } from './i18n-routing'\nimport { ogImagePath } from './og-image'\n\n// ─── Meta component ────────────────────────────────────────────────────────\n\nexport interface MetaProps {\n /** Page title. Accepts reactive accessor `() => string`. */\n title?: string | (() => string)\n /** Page description. Accepts reactive accessor. */\n description?: string | (() => string)\n /** Canonical URL. Also sets og:url. */\n canonical?: string\n /** Open Graph image URL. Also sets twitter:image. */\n image?: string\n /** Image alt text for accessibility. */\n imageAlt?: string\n /** Image width in pixels (og:image:width). Helps crawlers layout before loading. */\n imageWidth?: number\n /** Image height in pixels (og:image:height). */\n imageHeight?: number\n /** Open Graph type. Default: \"website\" */\n type?: 'website' | 'article' | 'product' | 'profile'\n /** Site name for og:site_name. */\n siteName?: string\n /** Twitter card type. Default: \"summary_large_image\" */\n twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player'\n /** Twitter @handle. */\n twitterSite?: string\n /** Twitter creator @handle. */\n twitterCreator?: string\n /** Locale. Default: \"en_US\" */\n locale?: string\n /** Alternate locales for hreflang. */\n alternateLocales?: Array<{ locale: string; url: string }>\n /** Robots directives. Default: \"index, follow\" */\n robots?: string\n /** Convenience: set `true` to emit `noindex, nofollow`. Overrides `robots`. */\n noIndex?: boolean\n /** Published time (ISO 8601) for article type. */\n publishedTime?: string\n /** Modified time (ISO 8601) for article type. */\n modifiedTime?: string\n /** Article author. */\n author?: string\n /** Article tags. */\n tags?: string[]\n /** JSON-LD structured data object. */\n jsonLd?: Record<string, unknown>\n /** Additional custom meta tags. */\n extra?: Array<{ name?: string; property?: string; content: string }>\n /**\n * Open Graph video URL. Also sets og:video:type if the URL ends with\n * a known extension (.mp4, .webm).\n */\n video?: string\n /** Video width in pixels. */\n videoWidth?: number\n /** Video height in pixels. */\n videoHeight?: number\n /**\n * Open Graph audio URL.\n */\n audio?: string\n /**\n * I18n routing config — when provided, auto-generates hreflang alternate\n * links for all locales based on the current path.\n * Also sets og:locale and og:locale:alternate.\n */\n i18n?: I18nRoutingConfig\n /** Base URL for building absolute hreflang URLs. e.g. \"https://example.com\" */\n origin?: string\n /**\n * Favicon plugin config — when provided, injects locale-aware favicon\n * `<link>` tags into `<head>`. Uses the current locale to select\n * the correct favicon set.\n */\n favicon?: FaviconPluginConfig\n /**\n * OG image template name — auto-resolves to the correct locale-specific\n * OG image path generated by `ogImagePlugin`.\n * Sets both `og:image` and `twitter:image` unless `image` is also provided.\n */\n ogTemplate?: string\n /** Output directory for OG images. Default: \"og\" */\n ogImageDir?: string\n /** OG image format. Default: \"png\" */\n ogImageFormat?: 'png' | 'jpeg'\n children?: VNodeChild\n}\n\nconst resolveStr = (v: string | (() => string) | undefined): string | undefined =>\n typeof v === 'function' ? v() : v\n\n/**\n * Declarative meta component for SSR-compatible page metadata.\n *\n * Supports reactive title/description — when passed as `() => string` accessors,\n * they are forwarded to `useHead()` as a reactive getter so updates propagate\n * automatically via signal tracking.\n *\n * @example\n * ```tsx\n * <Meta title=\"My Page\" description=\"...\" image=\"/og.jpg\" canonical=\"https://...\" />\n * ```\n *\n * @example Reactive title\n * ```tsx\n * const count = signal(0)\n * <Meta title={() => `${count()} items`} />\n * ```\n */\nexport function Meta(props: MetaProps): VNodeChild {\n const hasReactiveTitle = typeof props.title === 'function'\n const hasReactiveDescription = typeof props.description === 'function'\n\n // If title or description are reactive accessors, pass a getter to useHead\n // so it re-evaluates when the signals change.\n if (hasReactiveTitle || hasReactiveDescription) {\n useHead((): UseHeadInput => {\n const title = resolveStr(props.title)\n const description = resolveStr(props.description)\n const resolved = { ...props, title, description } as Parameters<typeof buildMetaTags>[0]\n const tags = buildMetaTags(resolved)\n const input: UseHeadInput = { meta: tags.meta, link: tags.link, script: tags.script }\n if (title) input.title = title\n return input\n })\n } else {\n const title = resolveStr(props.title)\n const description = resolveStr(props.description)\n const resolved = { ...props, title, description } as Parameters<typeof buildMetaTags>[0]\n const tags = buildMetaTags(resolved)\n const input: UseHeadInput = { meta: tags.meta, link: tags.link, script: tags.script }\n if (title) input.title = title\n useHead(input)\n }\n\n return props.children ?? null\n}\n\ninterface MetaTagEntry {\n name?: string\n property?: string\n content: string\n [key: string]: string | undefined\n}\n\ninterface LinkTagEntry {\n rel: string\n href?: string\n hreflang?: string\n type?: string\n sizes?: string\n [key: string]: string | undefined\n}\n\ninterface ScriptTagEntry {\n type: string\n children: string\n}\n\ninterface MetaTags {\n meta: MetaTagEntry[]\n link: LinkTagEntry[]\n script: ScriptTagEntry[]\n}\n\nexport function buildMetaTags(\n props: Omit<MetaProps, 'title' | 'description' | 'children'> & {\n title?: string\n description?: string\n },\n): MetaTags {\n const meta: MetaTagEntry[] = []\n const link: LinkTagEntry[] = []\n const script: ScriptTagEntry[] = []\n\n const {\n title, description, canonical, imageAlt, imageWidth, imageHeight,\n type = 'website', siteName,\n twitterCard = 'summary_large_image', twitterSite, twitterCreator,\n locale = 'en_US', alternateLocales,\n publishedTime, modifiedTime, author, tags, jsonLd, extra,\n video, videoWidth, videoHeight, audio,\n favicon, ogTemplate, ogImageDir, ogImageFormat,\n } = props\n\n // noIndex convenience overrides robots\n const robots = props.noIndex ? 'noindex, nofollow' : (props.robots ?? 'index, follow')\n\n // Resolve image: explicit `image` prop takes precedence over `ogTemplate`\n const image = props.image ?? (\n ogTemplate\n ? ogImagePath(ogTemplate, locale !== 'en_US' ? locale : undefined, ogImageDir, ogImageFormat)\n : undefined\n )\n\n // Auto-resolve image dimensions for OG template images\n const resolvedImageWidth = imageWidth ?? (ogTemplate && !props.image ? 1200 : undefined)\n const resolvedImageHeight = imageHeight ?? (ogTemplate && !props.image ? 630 : undefined)\n\n if (description) meta.push({ name: 'description', content: description })\n if (robots) meta.push({ name: 'robots', content: robots })\n if (author) meta.push({ name: 'author', content: author })\n\n if (title) meta.push({ property: 'og:title', content: title })\n if (description) meta.push({ property: 'og:description', content: description })\n if (canonical) meta.push({ property: 'og:url', content: canonical })\n if (image) meta.push({ property: 'og:image', content: image })\n if (imageAlt) meta.push({ property: 'og:image:alt', content: imageAlt })\n if (resolvedImageWidth) meta.push({ property: 'og:image:width', content: String(resolvedImageWidth) })\n if (resolvedImageHeight) meta.push({ property: 'og:image:height', content: String(resolvedImageHeight) })\n meta.push({ property: 'og:type', content: type })\n if (siteName) meta.push({ property: 'og:site_name', content: siteName })\n meta.push({ property: 'og:locale', content: locale })\n\n // Video\n if (video) {\n meta.push({ property: 'og:video', content: video })\n if (videoWidth) meta.push({ property: 'og:video:width', content: String(videoWidth) })\n if (videoHeight) meta.push({ property: 'og:video:height', content: String(videoHeight) })\n // Auto-detect video type from extension\n if (video.endsWith('.mp4')) meta.push({ property: 'og:video:type', content: 'video/mp4' })\n else if (video.endsWith('.webm')) meta.push({ property: 'og:video:type', content: 'video/webm' })\n }\n\n // Audio\n if (audio) {\n meta.push({ property: 'og:audio', content: audio })\n }\n\n if (type === 'article') {\n if (publishedTime) meta.push({ property: 'article:published_time', content: publishedTime })\n if (modifiedTime) meta.push({ property: 'article:modified_time', content: modifiedTime })\n if (author) meta.push({ property: 'article:author', content: author })\n if (tags) for (const tag of tags) meta.push({ property: 'article:tag', content: tag })\n }\n\n meta.push({ name: 'twitter:card', content: twitterCard })\n if (title) meta.push({ name: 'twitter:title', content: title })\n if (description) meta.push({ name: 'twitter:description', content: description })\n if (image) meta.push({ name: 'twitter:image', content: image })\n if (imageAlt) meta.push({ name: 'twitter:image:alt', content: imageAlt })\n if (twitterSite) meta.push({ name: 'twitter:site', content: twitterSite })\n if (twitterCreator) meta.push({ name: 'twitter:creator', content: twitterCreator })\n\n if (canonical) link.push({ rel: 'canonical', href: canonical })\n if (alternateLocales) {\n for (const alt of alternateLocales) {\n link.push({ rel: 'alternate', hreflang: alt.locale, href: alt.url })\n }\n }\n\n if (jsonLd) {\n script.push({\n type: 'application/ld+json',\n children: JSON.stringify({ '@context': 'https://schema.org', ...jsonLd }),\n })\n }\n\n if (extra) for (const tag of extra) meta.push(tag)\n\n // I18n: auto-generate hreflang alternates from i18nRouting config\n if (props.i18n) {\n const i18nConfig = props.i18n\n const origin = props.origin ?? ''\n const currentPath = canonical?.replace(origin, '') ?? '/'\n const { pathWithoutLocale } = extractLocaleFromPath(\n currentPath,\n i18nConfig.locales,\n i18nConfig.defaultLocale,\n )\n const strategy = i18nConfig.strategy ?? 'prefix-except-default'\n\n for (const loc of i18nConfig.locales) {\n const localizedPath =\n strategy === 'prefix-except-default' && loc === i18nConfig.defaultLocale\n ? pathWithoutLocale\n : `/${loc}${pathWithoutLocale === '/' ? '' : pathWithoutLocale}`\n\n link.push({\n rel: 'alternate',\n hreflang: loc,\n href: `${origin}${localizedPath}`,\n })\n\n // og:locale:alternate for non-current locales\n if (loc !== locale) {\n meta.push({ property: 'og:locale:alternate', content: loc })\n }\n }\n\n // x-default hreflang pointing to default locale\n link.push({\n rel: 'alternate',\n hreflang: 'x-default',\n href: `${origin}${pathWithoutLocale}`,\n })\n }\n\n // Favicon: inject locale-aware favicon links\n if (favicon) {\n const faviconLocale = locale !== 'en_US' ? locale : undefined\n for (const fl of faviconLinks(faviconLocale, favicon)) {\n link.push(fl as LinkTagEntry)\n }\n // Theme color meta from favicon config\n if (favicon.themeColor) {\n meta.push({ name: 'theme-color', content: favicon.themeColor })\n }\n }\n\n return { meta, link, script }\n}\n","/**\n * Content Security Policy middleware.\n *\n * Generates a CSP header from a typed configuration object.\n * Supports all CSP directives, nonces for inline scripts,\n * and report-only mode for testing.\n *\n * @example\n * ```ts\n * import { cspMiddleware } from \"@pyreon/zero\"\n *\n * const csp = cspMiddleware({\n * directives: {\n * defaultSrc: [\"'self'\"],\n * scriptSrc: [\"'self'\", \"'nonce'\"],\n * styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n * imgSrc: [\"'self'\", \"data:\", \"https:\"],\n * connectSrc: [\"'self'\", \"https://api.example.com\"],\n * },\n * reportOnly: false,\n * })\n * ```\n */\nimport type { Middleware, MiddlewareContext } from '@pyreon/server'\nimport { useRequestLocals } from '@pyreon/server'\n\n/** Client-side fallback nonce (dev server, SPA). */\nlet _clientNonce = ''\n\n/**\n * Read the current CSP nonce in a component.\n *\n * SSR: reads from per-request `ctx.locals.cspNonce` via Pyreon's context\n * system — fully isolated between concurrent requests via AsyncLocalStorage.\n * Client/dev: falls back to module-level variable set by middleware.\n *\n * @example\n * ```tsx\n * import { useNonce } from \"@pyreon/zero/csp\"\n *\n * function InlineScript() {\n * const nonce = useNonce()\n * return <script nonce={nonce}>console.log(\"safe\")</script>\n * }\n * ```\n */\nexport function useNonce(): string {\n const locals = useRequestLocals()\n if (locals.cspNonce) return locals.cspNonce as string\n return _clientNonce\n}\n\nexport interface CspDirectives {\n defaultSrc?: string[]\n scriptSrc?: string[]\n styleSrc?: string[]\n imgSrc?: string[]\n fontSrc?: string[]\n connectSrc?: string[]\n mediaSrc?: string[]\n objectSrc?: string[]\n frameSrc?: string[]\n childSrc?: string[]\n workerSrc?: string[]\n frameAncestors?: string[]\n formAction?: string[]\n baseUri?: string[]\n manifestSrc?: string[]\n /** Reporting endpoint URL. */\n reportUri?: string\n /** Reporting endpoint name (CSP Level 3). */\n reportTo?: string\n /** Upgrade insecure requests. */\n upgradeInsecureRequests?: boolean\n /** Block all mixed content. */\n blockAllMixedContent?: boolean\n}\n\nexport interface CspConfig {\n /** CSP directives. */\n directives: CspDirectives\n /**\n * Report-only mode — logs violations without blocking.\n * Uses Content-Security-Policy-Report-Only header instead.\n * Default: false\n */\n reportOnly?: boolean\n}\n\nconst DIRECTIVE_MAP: Record<string, string> = {\n defaultSrc: 'default-src',\n scriptSrc: 'script-src',\n styleSrc: 'style-src',\n imgSrc: 'img-src',\n fontSrc: 'font-src',\n connectSrc: 'connect-src',\n mediaSrc: 'media-src',\n objectSrc: 'object-src',\n frameSrc: 'frame-src',\n childSrc: 'child-src',\n workerSrc: 'worker-src',\n frameAncestors: 'frame-ancestors',\n formAction: 'form-action',\n baseUri: 'base-uri',\n manifestSrc: 'manifest-src',\n reportUri: 'report-uri',\n reportTo: 'report-to',\n}\n\n/**\n * Build a CSP header string from directives.\n * Exported for testing.\n */\nexport function buildCspHeader(directives: CspDirectives, nonce?: string): string {\n const parts: string[] = []\n\n for (const [key, cssProp] of Object.entries(DIRECTIVE_MAP)) {\n const value = (directives as Record<string, unknown>)[key]\n if (!value) continue\n\n if (Array.isArray(value)) {\n // Replace \"'nonce'\" placeholder with actual nonce\n const resolved = nonce\n ? value.map((v: string) => (v === \"'nonce'\" ? `'nonce-${nonce}'` : v))\n : value.filter((v: string) => v !== \"'nonce'\")\n parts.push(`${cssProp} ${resolved.join(' ')}`)\n } else if (typeof value === 'string') {\n parts.push(`${cssProp} ${value}`)\n }\n }\n\n if (directives.upgradeInsecureRequests) {\n parts.push('upgrade-insecure-requests')\n }\n if (directives.blockAllMixedContent) {\n parts.push('block-all-mixed-content')\n }\n\n return parts.join('; ')\n}\n\n/**\n * Generate a random nonce string (base64, 16 bytes).\n */\nfunction generateNonce(): string {\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n // Convert to base64 using btoa\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return typeof btoa === 'function'\n ? btoa(binary)\n : Buffer.from(bytes).toString('base64')\n }\n // Fallback for environments without crypto\n return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)\n}\n\n/**\n * CSP middleware — sets Content-Security-Policy header.\n *\n * When directives contain `\"'nonce'\"`, a fresh nonce is generated per-request\n * and attached to `ctx.locals.cspNonce` for use in inline script tags.\n *\n * @example\n * ```ts\n * // Apply to all routes\n * export default defineConfig({\n * middleware: [\n * cspMiddleware({\n * directives: {\n * defaultSrc: [\"'self'\"],\n * scriptSrc: [\"'self'\", \"'nonce'\"],\n * styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n * imgSrc: [\"'self'\", \"data:\", \"https:\"],\n * },\n * }),\n * ],\n * })\n * ```\n */\nexport function cspMiddleware(config: CspConfig): Middleware {\n const headerName = config.reportOnly\n ? 'Content-Security-Policy-Report-Only'\n : 'Content-Security-Policy'\n\n // Check if nonce is needed\n const needsNonce = Object.values(config.directives).some(\n (v) => Array.isArray(v) && v.includes(\"'nonce'\"),\n )\n\n // Pre-build header for static case (no nonce)\n const staticHeader = needsNonce ? null : buildCspHeader(config.directives)\n\n return (ctx: MiddlewareContext) => {\n if (staticHeader) {\n _clientNonce = ''\n ctx.headers.set(headerName, staticHeader)\n } else {\n const nonce = generateNonce()\n _clientNonce = nonce\n ;(ctx.locals as Record<string, unknown>).cspNonce = nonce\n ctx.headers.set(headerName, buildCspHeader(config.directives, nonce))\n }\n }\n}\n","/**\n * Request logging middleware.\n *\n * Logs HTTP requests with method, path, status, and duration.\n * Supports custom formatters and log levels.\n *\n * @example\n * ```ts\n * import { loggerMiddleware } from \"@pyreon/zero\"\n *\n * export default defineConfig({\n * middleware: [loggerMiddleware()],\n * })\n * ```\n */\nimport type { Middleware, MiddlewareContext } from '@pyreon/server'\n\nexport interface LoggerConfig {\n /**\n * Log level — controls which requests are logged.\n * - \"all\": log every request\n * - \"none\": disable logging\n * Default: \"all\"\n */\n level?: 'all' | 'none'\n /**\n * Custom log formatter. Receives request details and returns\n * the string to log (or null to skip).\n */\n format?: (entry: LogEntry) => string | null\n /**\n * Skip logging for these path prefixes.\n * Default: [\"/__\", \"/@\", \"/node_modules\"]\n */\n skip?: string[]\n /**\n * Enable colorized output (ANSI codes).\n * Default: true in development, false in production.\n */\n colors?: boolean\n}\n\nexport interface LogEntry {\n method: string\n path: string\n duration: number\n timestamp: Date\n userAgent?: string | undefined\n ip?: string | undefined\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n red: '\\x1b[31m',\n cyan: '\\x1b[36m',\n magenta: '\\x1b[35m',\n}\n\nfunction methodColor(method: string, colors: boolean): string {\n if (!colors) return method.padEnd(7)\n const padded = method.padEnd(7)\n switch (method) {\n case 'GET': return `${COLORS.green}${padded}${COLORS.reset}`\n case 'POST': return `${COLORS.cyan}${padded}${COLORS.reset}`\n case 'PUT': return `${COLORS.yellow}${padded}${COLORS.reset}`\n case 'PATCH': return `${COLORS.yellow}${padded}${COLORS.reset}`\n case 'DELETE': return `${COLORS.red}${padded}${COLORS.reset}`\n default: return `${COLORS.magenta}${padded}${COLORS.reset}`\n }\n}\n\nfunction defaultFormat(entry: LogEntry, colors: boolean): string {\n const dur = entry.duration < 1\n ? '<1ms'\n : entry.duration < 1000\n ? `${Math.round(entry.duration)}ms`\n : `${(entry.duration / 1000).toFixed(2)}s`\n\n const dim = colors ? COLORS.dim : ''\n const reset = colors ? COLORS.reset : ''\n\n return ` ${methodColor(entry.method, colors)} ${entry.path} ${dim}${dur}${reset}`\n}\n\n/**\n * Request logging middleware.\n *\n * Logs incoming requests with method, path, and duration.\n * Runs in middleware phase — logs timing from middleware start to\n * microtask completion (approximate request duration).\n *\n * @example\n * ```ts\n * // Basic usage\n * loggerMiddleware()\n *\n * // Custom format\n * loggerMiddleware({\n * format: (e) => `${e.method} ${e.path} (${e.duration}ms)`,\n * })\n * ```\n */\nexport function loggerMiddleware(config?: LoggerConfig): Middleware {\n const level = config?.level ?? 'all'\n if (level === 'none') return () => {}\n\n const skip = config?.skip ?? ['/__', '/@', '/node_modules']\n const isDev = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\n const colors = config?.colors ?? isDev\n\n return (ctx: MiddlewareContext) => {\n // Skip internal paths\n if (skip.some((p) => ctx.path.startsWith(p))) return\n\n const start = performance.now()\n\n const entry: LogEntry = {\n method: ctx.req.method ?? 'GET',\n path: ctx.path,\n duration: 0,\n timestamp: new Date(),\n userAgent: ctx.req.headers.get('user-agent') ?? undefined,\n }\n\n // Use queueMicrotask to log after the middleware chain completes\n queueMicrotask(() => {\n entry.duration = performance.now() - start\n\n if (config?.format) {\n const line = config.format(entry)\n if (line) {\n // oxlint-disable-next-line no-console\n console.log(line)\n }\n } else {\n // oxlint-disable-next-line no-console\n console.log(defaultFormat(entry, colors))\n }\n })\n }\n}\n","/**\n * Environment variable validation.\n *\n * Infers types from default values — no verbose validator imports needed.\n * Explicit validators (`url()`, `oneOf()`) available for special cases.\n *\n * @example\n * ```ts\n * import { validateEnv, url, oneOf } from \"@pyreon/zero/env\"\n *\n * const env = validateEnv({\n * PORT: 3000, // number, default 3000\n * DEBUG: false, // boolean, default false\n * HOST: \"localhost\", // string, default \"localhost\"\n * DATABASE_URL: url(), // validated URL, required\n * NODE_ENV: oneOf([\"development\", \"production\", \"test\"]),\n * API_KEY: String, // required string, no default\n * MAX_RETRIES: Number, // required number, no default\n * })\n * ```\n */\n\nexport interface EnvValidatorOptions<T = string> {\n /** Whether this variable is required. Default: true */\n required?: boolean\n /** Default value when not set. Makes the variable optional. */\n default?: T\n /** Human-readable description for error messages. */\n description?: string\n}\n\nexport interface EnvValidator<T> {\n __type: 'env-validator'\n parse: (raw: string | undefined, key: string) => T\n required: boolean\n defaultValue?: T | undefined\n}\n\n// ─── Explicit validators (for special cases) ────────────────────────────────\n\n/**\n * String validator — accepts any non-empty string.\n */\nexport function str(options?: EnvValidatorOptions<string>): EnvValidator<string> {\n const required = options?.default === undefined && options?.required !== false\n return {\n __type: 'env-validator',\n required,\n defaultValue: options?.default,\n parse(raw, key) {\n if (raw === undefined || raw === '') {\n if (options?.default !== undefined) return options.default\n throw new EnvError(key, 'is required but not set', options?.description)\n }\n return raw\n },\n }\n}\n\n/**\n * Number validator — parses to a number, rejects NaN.\n */\nexport function num(options?: EnvValidatorOptions<number>): EnvValidator<number> {\n const required = options?.default === undefined && options?.required !== false\n return {\n __type: 'env-validator',\n required,\n defaultValue: options?.default,\n parse(raw, key) {\n if (raw === undefined || raw === '') {\n if (options?.default !== undefined) return options.default\n throw new EnvError(key, 'is required but not set', options?.description)\n }\n const n = Number(raw)\n if (Number.isNaN(n)) {\n throw new EnvError(key, `must be a number, got \"${raw}\"`, options?.description)\n }\n return n\n },\n }\n}\n\n/**\n * Boolean validator — accepts \"true\"/\"1\" as true, \"false\"/\"0\" as false.\n */\nexport function bool(options?: EnvValidatorOptions<boolean>): EnvValidator<boolean> {\n const required = options?.default === undefined && options?.required !== false\n return {\n __type: 'env-validator',\n required,\n defaultValue: options?.default,\n parse(raw, key) {\n if (raw === undefined || raw === '') {\n if (options?.default !== undefined) return options.default\n throw new EnvError(key, 'is required but not set', options?.description)\n }\n const lower = raw.toLowerCase()\n if (lower === 'true' || lower === '1') return true\n if (lower === 'false' || lower === '0') return false\n throw new EnvError(key, `must be \"true\" or \"false\", got \"${raw}\"`, options?.description)\n },\n }\n}\n\n/**\n * URL validator — validates that the value is a valid URL.\n */\nexport function url(options?: EnvValidatorOptions<string>): EnvValidator<string> {\n const required = options?.default === undefined && options?.required !== false\n return {\n __type: 'env-validator',\n required,\n defaultValue: options?.default,\n parse(raw, key) {\n if (raw === undefined || raw === '') {\n if (options?.default !== undefined) return options.default\n throw new EnvError(key, 'is required but not set', options?.description)\n }\n try {\n new URL(raw)\n return raw\n } catch {\n throw new EnvError(key, `must be a valid URL, got \"${raw}\"`, options?.description)\n }\n },\n }\n}\n\n/**\n * Enum validator — value must be one of the allowed values.\n */\nexport function oneOf<T extends string>(\n values: readonly T[],\n options?: EnvValidatorOptions<T>,\n): EnvValidator<T> {\n const required = options?.default === undefined && options?.required !== false\n return {\n __type: 'env-validator',\n required,\n defaultValue: options?.default,\n parse(raw, key) {\n if (raw === undefined || raw === '') {\n if (options?.default !== undefined) return options.default\n throw new EnvError(key, 'is required but not set', options?.description)\n }\n if (!values.includes(raw as T)) {\n throw new EnvError(\n key,\n `must be one of [${values.join(', ')}], got \"${raw}\"`,\n options?.description,\n )\n }\n return raw as T\n },\n }\n}\n\n// ─── Internal helpers ───────────────────────────────────────────────────────\n\nclass EnvError extends Error {\n constructor(key: string, message: string, description?: string) {\n const desc = description ? ` (${description})` : ''\n super(`[zero:env] ${key}${desc}: ${message}`)\n this.name = 'EnvError'\n }\n}\n\nfunction isEnvValidator(v: unknown): v is EnvValidator<unknown> {\n return typeof v === 'object' && v !== null && (v as any).__type === 'env-validator'\n}\n\n/**\n * Convert a plain schema value to an EnvValidator.\n *\n * - `3000` → num({ default: 3000 })\n * - `false` → bool({ default: false })\n * - `\"localhost\"` → str({ default: \"localhost\" })\n * - `String` → str() (required)\n * - `Number` → num() (required)\n * - `Boolean` → bool() (required)\n * - EnvValidator → pass through\n */\nfunction toValidator(value: unknown): EnvValidator<unknown> {\n if (isEnvValidator(value)) return value\n\n // Constructor markers → required, no default\n if (value === String) return str()\n if (value === Number) return num()\n if (value === Boolean) return bool()\n\n // Plain values → infer type + use as default\n if (typeof value === 'number') return num({ default: value })\n if (typeof value === 'boolean') return bool({ default: value })\n if (typeof value === 'string') return str({ default: value })\n\n throw new Error(`[zero:env] Invalid schema value: ${String(value)}. Use a default value, String/Number/Boolean, or a validator like url().`)\n}\n\n// ─── Type inference ─────────────────────────────────────────────────────────\n\n/** Schema entry: plain value, constructor, or explicit validator. */\ntype SchemaEntry =\n | string | number | boolean\n | StringConstructor | NumberConstructor | BooleanConstructor\n | EnvValidator<any>\n\n/** Infer the output type from a schema entry. */\ntype InferEntry<T> =\n T extends EnvValidator<infer V> ? V :\n T extends StringConstructor ? string :\n T extends NumberConstructor ? number :\n T extends BooleanConstructor ? boolean :\n T extends string ? string :\n T extends number ? number :\n T extends boolean ? boolean :\n never\n\ntype InferEnvSchema<T> = {\n [K in keyof T]: InferEntry<T[K]>\n}\n\n// ─── Main API ───────────────────────────────────────────────────────────────\n\n/**\n * Validate environment variables.\n *\n * Schema values can be:\n * - **Default values**: `3000`, `false`, `\"localhost\"` → type inferred, used as default\n * - **Constructors**: `String`, `Number`, `Boolean` → required, no default\n * - **Validators**: `url()`, `oneOf([...])`, `str()`, `num()`, `bool()` → explicit validation\n * - **Custom**: `schema(raw => z.coerce.number().parse(raw))` — bridge to any schema library\n *\n * @example\n * ```ts\n * import { validateEnv, url, oneOf } from \"@pyreon/zero/env\"\n *\n * const env = validateEnv({\n * PORT: 3000, // optional, default 3000\n * DATABASE_URL: url(), // required, validated URL\n * NODE_ENV: oneOf([\"dev\", \"prod\", \"test\"]), // required, must be one of\n * API_KEY: String, // required string\n * DEBUG: false, // optional, default false\n * })\n * ```\n */\nexport function validateEnv<T extends Record<string, SchemaEntry>>(\n schema: T,\n source?: Record<string, string | undefined>,\n): InferEnvSchema<T> {\n const env = source ?? (typeof process !== 'undefined' ? process.env : {})\n const result: Record<string, unknown> = {}\n const errors: string[] = []\n\n for (const [key, entry] of Object.entries(schema)) {\n const validator = toValidator(entry)\n try {\n result[key] = validator.parse(env[key], key)\n } catch (e) {\n errors.push((e as Error).message)\n }\n }\n\n if (errors.length > 0) {\n const header = `\\n[zero:env] Environment validation failed (${errors.length} error${errors.length > 1 ? 's' : ''}):\\n`\n const body = errors.map((e) => ` ✗ ${e.replace('[zero:env] ', '')}`).join('\\n')\n throw new Error(header + body + '\\n')\n }\n\n return result as InferEnvSchema<T>\n}\n\n// ─── Public env (client-safe) ────────────────────────────────────────────────\n\n/**\n * Extract public environment variables (prefixed with `ZERO_PUBLIC_`).\n *\n * @example\n * ```ts\n * const pub = publicEnv()\n * // → { API_URL: \"https://...\", APP_NAME: \"MyApp\" }\n *\n * const pub = publicEnv({ API_URL: url(), APP_NAME: \"Default\" })\n * // → validated against ZERO_PUBLIC_API_URL, ZERO_PUBLIC_APP_NAME\n * ```\n */\nexport function publicEnv(): Record<string, string>\nexport function publicEnv<T extends Record<string, SchemaEntry>>(schema: T): InferEnvSchema<T>\nexport function publicEnv(schema?: Record<string, SchemaEntry>): Record<string, unknown> {\n const prefix = 'ZERO_PUBLIC_'\n const env = typeof process !== 'undefined' ? process.env : {}\n\n if (!schema) {\n const result: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) {\n if (key.startsWith(prefix) && value !== undefined) {\n result[key.slice(prefix.length)] = value\n }\n }\n return result\n }\n\n const prefixedSource: Record<string, string | undefined> = {}\n for (const key of Object.keys(schema)) {\n prefixedSource[key] = env[`${prefix}${key}`]\n }\n return validateEnv(schema, prefixedSource)\n}\n\n// ─── Custom validator escape hatch ──────────────────────────────────────────\n\n/**\n * Create an env validator from a custom parse function.\n * Use this to integrate any schema library (Zod, Valibot, ArkType, etc.).\n *\n * @example\n * ```ts\n * import { z } from \"zod\"\n * import { validateEnv, schema } from \"@pyreon/zero/env\"\n *\n * const env = validateEnv({\n * PORT: schema(raw => z.coerce.number().parse(raw)),\n * DATABASE_URL: schema(raw => z.string().url().parse(raw)),\n * HOST: \"localhost\", // plain defaults still work alongside\n * })\n * ```\n */\nexport function schema<T>(parse: (raw: string) => T): EnvValidator<T> {\n return {\n __type: 'env-validator',\n required: true,\n defaultValue: undefined,\n parse(raw: string | undefined, key: string) {\n if (raw === undefined || raw === '') {\n throw new Error(`[zero:env] ${key}: is required but not set`)\n }\n try {\n return parse(raw)\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e)\n throw new Error(`[zero:env] ${key}: ${msg}`)\n }\n },\n }\n}\n","/**\n * AI integration utilities for Zero.\n *\n * - llms.txt auto-generation from routes and API routes\n * - JSON-LD auto-inference from route meta + Meta props\n * - AI plugin manifest (/.well-known/ai-plugin.json) from API routes\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { aiPlugin } from \"@pyreon/zero/ai\"\n *\n * export default {\n * plugins: [\n * aiPlugin({\n * name: \"My App\",\n * origin: \"https://example.com\",\n * description: \"A modern web application\",\n * }),\n * ],\n * }\n * ```\n */\nimport type { Plugin } from 'vite'\nimport { parseFileRoutes } from './fs-router'\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface AiPluginConfig {\n /** App/API name. */\n name: string\n /** App description for AI agents. */\n description: string\n /** Base URL. e.g. \"https://example.com\" */\n origin: string\n /** Contact email (required by OpenAI plugin spec). */\n contactEmail?: string\n /** Legal info URL. */\n legalUrl?: string\n /** Logo URL for the plugin. */\n logoUrl?: string\n /** Routes directory relative to project root. Default: \"src/routes\" */\n routesDir?: string\n /** API routes directory relative to project root. Default: \"src/api\" */\n apiDir?: string\n /**\n * API route descriptions — map of pattern to description.\n * Used for llms.txt and ai-plugin.json.\n *\n * @example\n * ```ts\n * apiDescriptions: {\n * \"GET /api/posts\": \"List all blog posts, supports ?page=N&limit=N\",\n * \"GET /api/posts/:id\": \"Get a single post by ID\",\n * \"POST /api/posts\": \"Create a new post (requires auth)\",\n * }\n * ```\n */\n apiDescriptions?: Record<string, string>\n /**\n * Page descriptions — map of URL path to description.\n * Used for llms.txt. Falls back to route meta.title/description.\n */\n pageDescriptions?: Record<string, string>\n /**\n * Additional content to append to llms.txt.\n * Useful for authentication instructions, rate limits, etc.\n */\n llmsExtra?: string\n}\n\n// ─── llms.txt generation ────────────────────────────────────────────────────\n\n/**\n * Generate llms.txt content from route files and config.\n *\n * Format follows the llms.txt proposal:\n * ```\n * # {name}\n * > {description}\n *\n * ## Pages\n * - [/about](/about): About page\n *\n * ## API\n * - GET /api/posts: List posts\n * ```\n *\n * @internal Exported for testing.\n */\nexport function generateLlmsTxt(\n routeFiles: string[],\n apiFiles: string[],\n config: AiPluginConfig,\n): string {\n const lines: string[] = []\n\n // Header\n lines.push(`# ${config.name}`)\n lines.push(`> ${config.description}`)\n lines.push('')\n\n // Pages section\n const routes = parseFileRoutes(routeFiles)\n const pages = routes.filter(\n (r) => !r.isLayout && !r.isError && !r.isLoading && !r.isNotFound\n && !r.isCatchAll && !r.urlPath.includes(':'),\n )\n\n if (pages.length > 0) {\n lines.push('## Pages')\n lines.push('')\n for (const page of pages) {\n const desc = config.pageDescriptions?.[page.urlPath]\n const url = `${config.origin}${page.urlPath === '/' ? '' : page.urlPath}`\n if (desc) {\n lines.push(`- [${page.urlPath}](${url}): ${desc}`)\n } else {\n lines.push(`- [${page.urlPath}](${url})`)\n }\n }\n lines.push('')\n }\n\n // Dynamic routes (documented separately — AI needs to know about params)\n const dynamicRoutes = routes.filter(\n (r) => !r.isLayout && !r.isError && !r.isLoading && !r.isNotFound\n && (r.urlPath.includes(':') || r.isCatchAll),\n )\n if (dynamicRoutes.length > 0) {\n lines.push('## Dynamic Pages')\n lines.push('')\n for (const route of dynamicRoutes) {\n const desc = config.pageDescriptions?.[route.urlPath]\n if (desc) {\n lines.push(`- ${route.urlPath}: ${desc}`)\n } else {\n lines.push(`- ${route.urlPath}`)\n }\n }\n lines.push('')\n }\n\n // API section\n const apiPatterns = parseApiFiles(apiFiles)\n if (apiPatterns.length > 0 || config.apiDescriptions) {\n lines.push('## API Endpoints')\n lines.push('')\n\n // From apiDescriptions (most detailed — user-provided)\n if (config.apiDescriptions) {\n for (const [endpoint, desc] of Object.entries(config.apiDescriptions)) {\n lines.push(`- ${endpoint}: ${desc}`)\n }\n }\n\n // From auto-discovered API files (only those not already described)\n const describedPatterns = new Set(\n Object.keys(config.apiDescriptions ?? {}).map((k) => k.replace(/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\s+/, '')),\n )\n for (const pattern of apiPatterns) {\n if (!describedPatterns.has(pattern)) {\n lines.push(`- ${pattern}`)\n }\n }\n lines.push('')\n }\n\n // Extra content\n if (config.llmsExtra) {\n lines.push(config.llmsExtra)\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n\n/**\n * Generate llms-full.txt — expanded version with more detail.\n * Includes all route metadata and API descriptions.\n *\n * @internal Exported for testing.\n */\nexport function generateLlmsFullTxt(\n routeFiles: string[],\n apiFiles: string[],\n config: AiPluginConfig,\n): string {\n const lines: string[] = []\n\n lines.push(`# ${config.name} — Full Reference`)\n lines.push(`> ${config.description}`)\n lines.push('')\n lines.push(`Base URL: ${config.origin}`)\n lines.push('')\n\n // All pages with details\n const routes = parseFileRoutes(routeFiles)\n const pages = routes.filter(\n (r) => !r.isLayout && !r.isError && !r.isLoading && !r.isNotFound,\n )\n\n if (pages.length > 0) {\n lines.push('## All Routes')\n lines.push('')\n for (const page of pages) {\n const desc = config.pageDescriptions?.[page.urlPath] ?? ''\n const dynamic = page.urlPath.includes(':') ? ' (dynamic)' : ''\n const catchAll = page.isCatchAll ? ' (catch-all)' : ''\n lines.push(`### ${page.urlPath}${dynamic}${catchAll}`)\n if (desc) lines.push(desc)\n lines.push(`- File: ${page.filePath}`)\n lines.push(`- Render mode: ${page.renderMode}`)\n lines.push('')\n }\n }\n\n // API endpoints with full detail\n if (config.apiDescriptions) {\n lines.push('## API Reference')\n lines.push('')\n for (const [endpoint, desc] of Object.entries(config.apiDescriptions)) {\n lines.push(`### ${endpoint}`)\n lines.push(desc)\n lines.push('')\n }\n }\n\n if (config.llmsExtra) {\n lines.push('## Additional Information')\n lines.push('')\n lines.push(config.llmsExtra)\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n\n// ─── JSON-LD auto-inference ─────────────────────────────────────────────────\n\nexport interface InferJsonLdOptions {\n /** Page URL. */\n url: string\n /** Page title. */\n title?: string\n /** Page description. */\n description?: string\n /** Page image. */\n image?: string\n /** Site name. */\n siteName?: string\n /** Page type hint. */\n type?: 'website' | 'article' | 'product' | 'profile'\n /** Article metadata. */\n publishedTime?: string\n /** Article author. */\n author?: string\n /** Article tags. */\n tags?: string[]\n /** Breadcrumb path segments. */\n breadcrumbs?: Array<{ name: string; url: string }>\n}\n\n/**\n * Auto-infer JSON-LD structured data from page metadata.\n *\n * Returns an array of JSON-LD objects (multiple schemas can apply to one page).\n * For example, an article page gets both `Article` and `BreadcrumbList`.\n *\n * @example\n * ```tsx\n * const schemas = inferJsonLd({\n * url: \"https://example.com/blog/my-post\",\n * title: \"My Post\",\n * description: \"A great article\",\n * type: \"article\",\n * author: \"Vit Bokisch\",\n * publishedTime: \"2026-03-31\",\n * })\n * // → [Article schema, BreadcrumbList schema]\n * ```\n */\nexport function inferJsonLd(options: InferJsonLdOptions): Record<string, unknown>[] {\n const schemas: Record<string, unknown>[] = []\n\n // Base: WebPage or Article\n if (options.type === 'article') {\n const article: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Article',\n headline: options.title,\n url: options.url,\n }\n if (options.description) article.description = options.description\n if (options.image) article.image = options.image\n if (options.publishedTime) article.datePublished = options.publishedTime\n if (options.author) {\n article.author = { '@type': 'Person', name: options.author }\n }\n if (options.tags && options.tags.length > 0) {\n article.keywords = options.tags.join(', ')\n }\n if (options.siteName) {\n article.publisher = { '@type': 'Organization', name: options.siteName }\n }\n schemas.push(article)\n } else if (options.type === 'product') {\n const product: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Product',\n name: options.title,\n url: options.url,\n }\n if (options.description) product.description = options.description\n if (options.image) product.image = options.image\n schemas.push(product)\n } else {\n const webpage: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'WebPage',\n name: options.title,\n url: options.url,\n }\n if (options.description) webpage.description = options.description\n if (options.image) webpage.thumbnailUrl = options.image\n schemas.push(webpage)\n }\n\n // BreadcrumbList from URL path or explicit breadcrumbs\n if (options.breadcrumbs && options.breadcrumbs.length > 0) {\n schemas.push({\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: options.breadcrumbs.map((bc, i) => ({\n '@type': 'ListItem',\n position: i + 1,\n name: bc.name,\n item: bc.url,\n })),\n })\n } else {\n // Auto-generate breadcrumbs from URL path\n const urlObj = safeParseUrl(options.url)\n if (urlObj) {\n const segments = urlObj.pathname.split('/').filter(Boolean)\n if (segments.length > 0) {\n const items = [\n { '@type': 'ListItem', position: 1, name: 'Home', item: urlObj.origin },\n ]\n let path = ''\n for (let i = 0; i < segments.length; i++) {\n path += `/${segments[i]}`\n items.push({\n '@type': 'ListItem',\n position: i + 2,\n name: capitalize(segments[i]!.replace(/-/g, ' ')),\n item: `${urlObj.origin}${path}`,\n })\n }\n schemas.push({\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items,\n })\n }\n }\n }\n\n return schemas\n}\n\n// ─── AI plugin manifest ─────────────────────────────────────────────────────\n\n/**\n * Generate an OpenAI-compatible AI plugin manifest.\n *\n * Follows the /.well-known/ai-plugin.json spec.\n *\n * @internal Exported for testing.\n */\nexport function generateAiPluginManifest(config: AiPluginConfig): Record<string, unknown> {\n return {\n schema_version: 'v1',\n name_for_human: config.name,\n name_for_model: config.name.toLowerCase().replace(/\\s+/g, '_').replace(/[^a-z0-9_]/g, ''),\n description_for_human: config.description,\n description_for_model: config.description,\n auth: { type: 'none' },\n api: {\n type: 'openapi',\n url: `${config.origin}/.well-known/openapi.yaml`,\n },\n logo_url: config.logoUrl ?? `${config.origin}/favicon.svg`,\n contact_email: config.contactEmail ?? '',\n legal_info_url: config.legalUrl ?? `${config.origin}/legal`,\n }\n}\n\n/**\n * Generate a minimal OpenAPI 3.0 spec from API route descriptions.\n *\n * @internal Exported for testing.\n */\nexport function generateOpenApiSpec(\n apiFiles: string[],\n config: AiPluginConfig,\n): Record<string, unknown> {\n const paths: Record<string, Record<string, unknown>> = {}\n\n // From user-provided descriptions\n if (config.apiDescriptions) {\n for (const [endpoint, desc] of Object.entries(config.apiDescriptions)) {\n const match = endpoint.match(/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\s+(.+)$/)\n if (match) {\n const method = match[1]!.toLowerCase()\n const path = match[2]!\n // Convert :param to {param} for OpenAPI\n const openApiPath = path.replace(/:(\\w+)/g, '{$1}')\n if (!paths[openApiPath]) paths[openApiPath] = {}\n paths[openApiPath][method] = {\n summary: desc,\n responses: { '200': { description: 'Success' } },\n }\n }\n }\n }\n\n // Auto-discovered API files (fill in gaps)\n for (const pattern of parseApiFiles(apiFiles)) {\n const openApiPath = pattern.replace(/:(\\w+)/g, '{$1}')\n if (!paths[openApiPath]) {\n paths[openApiPath] = {\n get: {\n summary: `${openApiPath} endpoint`,\n responses: { '200': { description: 'Success' } },\n },\n }\n }\n }\n\n return {\n openapi: '3.0.0',\n info: {\n title: config.name,\n description: config.description,\n version: '1.0.0',\n },\n servers: [{ url: config.origin }],\n paths,\n }\n}\n\n// ─── Vite plugin ────────────────────────────────────────────────────────────\n\n/**\n * AI integration Vite plugin.\n *\n * Generates at build time:\n * - `/llms.txt` — concise site summary for AI agents\n * - `/llms-full.txt` — detailed reference for AI agents\n * - `/.well-known/ai-plugin.json` — OpenAI plugin manifest\n * - `/.well-known/openapi.yaml` — minimal OpenAPI spec from API routes\n *\n * In dev, serves these files via middleware.\n *\n * @example\n * ```ts\n * import { aiPlugin } from \"@pyreon/zero/ai\"\n *\n * export default {\n * plugins: [\n * aiPlugin({\n * name: \"My App\",\n * origin: \"https://example.com\",\n * description: \"A modern web application\",\n * apiDescriptions: {\n * \"GET /api/posts\": \"List blog posts\",\n * \"GET /api/posts/:id\": \"Get post by ID\",\n * },\n * }),\n * ],\n * }\n * ```\n */\nexport function aiPlugin(config: AiPluginConfig): Plugin {\n let root = ''\n let isBuild = false\n let routeFiles: string[] = []\n let apiFiles: string[] = []\n\n return {\n name: 'pyreon-zero-ai',\n enforce: 'post',\n\n configResolved(resolvedConfig) {\n root = resolvedConfig.root\n isBuild = resolvedConfig.command === 'build'\n },\n\n async buildStart() {\n // Scan for route and API files\n try {\n const { join } = await import('node:path')\n\n const routesDir = join(root, config.routesDir ?? 'src/routes')\n const apiDir = join(root, config.apiDir ?? 'src/api')\n\n routeFiles = await scanDir(routesDir, routesDir)\n apiFiles = await scanDir(apiDir, apiDir)\n } catch {\n // Directories may not exist\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? ''\n\n if (url === '/llms.txt') {\n res.setHeader('Content-Type', 'text/plain; charset=utf-8')\n res.end(generateLlmsTxt(routeFiles, apiFiles, config))\n return\n }\n\n if (url === '/llms-full.txt') {\n res.setHeader('Content-Type', 'text/plain; charset=utf-8')\n res.end(generateLlmsFullTxt(routeFiles, apiFiles, config))\n return\n }\n\n if (url === '/.well-known/ai-plugin.json') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(generateAiPluginManifest(config), null, 2))\n return\n }\n\n if (url === '/.well-known/openapi.yaml' || url === '/.well-known/openapi.json') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(generateOpenApiSpec(apiFiles, config), null, 2))\n return\n }\n\n next()\n })\n },\n\n async generateBundle() {\n if (!isBuild) return\n\n this.emitFile({\n type: 'asset',\n fileName: 'llms.txt',\n source: generateLlmsTxt(routeFiles, apiFiles, config),\n })\n\n this.emitFile({\n type: 'asset',\n fileName: 'llms-full.txt',\n source: generateLlmsFullTxt(routeFiles, apiFiles, config),\n })\n\n this.emitFile({\n type: 'asset',\n fileName: '.well-known/ai-plugin.json',\n source: JSON.stringify(generateAiPluginManifest(config), null, 2),\n })\n\n this.emitFile({\n type: 'asset',\n fileName: '.well-known/openapi.json',\n source: JSON.stringify(generateOpenApiSpec(apiFiles, config), null, 2),\n })\n },\n }\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction parseApiFiles(files: string[]): string[] {\n return files\n .filter((f) => f.endsWith('.ts') || f.endsWith('.js'))\n .map((f) => {\n let path = f.replace(/\\.\\w+$/, '').replace(/\\/index$/, '')\n if (!path.startsWith('/')) path = `/${path}`\n // Convert [param] to :param\n path = path.replace(/\\[\\.\\.\\.(\\w+)\\]/g, ':$1*').replace(/\\[(\\w+)\\]/g, ':$1')\n return `/api${path === '/' ? '' : path}`\n })\n}\n\nasync function scanDir(dir: string, base: string): Promise<string[]> {\n const { readdir, stat } = await import('node:fs/promises')\n const { join, relative } = await import('node:path')\n\n try {\n const entries = await readdir(dir)\n const files: string[] = []\n for (const entry of entries) {\n const full = join(dir, entry)\n const s = await stat(full)\n if (s.isDirectory()) {\n files.push(...(await scanDir(full, base)))\n } else {\n files.push(relative(base, full))\n }\n }\n return files\n } catch {\n return []\n }\n}\n\nfunction safeParseUrl(url: string): URL | null {\n try {\n return new URL(url)\n } catch {\n return null\n }\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n"],"mappings":"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"}