@pyreon/zero 0.11.8 → 0.11.10

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 (95) hide show
  1. package/lib/font.js +20 -7
  2. package/lib/font.js.map +1 -1
  3. package/lib/fs-router-BkbIWqek.js.map +1 -1
  4. package/lib/{fs-router-n4VA4lxu.js → fs-router-Dil4IKZR.js} +23 -19
  5. package/lib/fs-router-Dil4IKZR.js.map +1 -0
  6. package/lib/image-plugin.js.map +1 -1
  7. package/lib/index.js +893 -24
  8. package/lib/index.js.map +1 -1
  9. package/lib/link.js +13 -1
  10. package/lib/link.js.map +1 -1
  11. package/lib/types/actions.d.ts +57 -0
  12. package/lib/types/actions.d.ts.map +1 -0
  13. package/lib/types/adapters/bun.d.ts +6 -0
  14. package/lib/types/adapters/bun.d.ts.map +1 -0
  15. package/lib/types/adapters/index.d.ts +10 -0
  16. package/lib/types/adapters/index.d.ts.map +1 -0
  17. package/lib/types/adapters/node.d.ts +6 -0
  18. package/lib/types/adapters/node.d.ts.map +1 -0
  19. package/lib/types/adapters/static.d.ts +7 -0
  20. package/lib/types/adapters/static.d.ts.map +1 -0
  21. package/lib/types/api-routes.d.ts +66 -0
  22. package/lib/types/api-routes.d.ts.map +1 -0
  23. package/lib/types/app.d.ts +24 -0
  24. package/lib/types/app.d.ts.map +1 -0
  25. package/lib/types/cache.d.ts +54 -0
  26. package/lib/types/cache.d.ts.map +1 -0
  27. package/lib/types/client.d.ts +19 -0
  28. package/lib/types/client.d.ts.map +1 -0
  29. package/lib/types/compression.d.ts +33 -0
  30. package/lib/types/compression.d.ts.map +1 -0
  31. package/lib/types/config.d.ts +18 -0
  32. package/lib/types/config.d.ts.map +1 -0
  33. package/lib/types/cors.d.ts +32 -0
  34. package/lib/types/cors.d.ts.map +1 -0
  35. package/lib/types/entry-server.d.ts +37 -0
  36. package/lib/types/entry-server.d.ts.map +1 -0
  37. package/lib/types/error-overlay.d.ts +6 -0
  38. package/lib/types/error-overlay.d.ts.map +1 -0
  39. package/lib/types/favicon.d.ts +43 -0
  40. package/lib/types/favicon.d.ts.map +1 -0
  41. package/lib/types/font.d.ts +119 -0
  42. package/lib/types/font.d.ts.map +1 -0
  43. package/lib/types/fs-router.d.ts +47 -0
  44. package/lib/types/fs-router.d.ts.map +1 -0
  45. package/lib/types/i18n-routing.d.ts +98 -0
  46. package/lib/types/i18n-routing.d.ts.map +1 -0
  47. package/lib/types/image-plugin.d.ts +79 -0
  48. package/lib/types/image-plugin.d.ts.map +1 -0
  49. package/lib/types/image.d.ts +51 -0
  50. package/lib/types/image.d.ts.map +1 -0
  51. package/lib/types/index.d.ts +46 -0
  52. package/lib/types/index.d.ts.map +1 -0
  53. package/lib/types/isr.d.ts +9 -0
  54. package/lib/types/isr.d.ts.map +1 -0
  55. package/lib/types/link.d.ts +127 -0
  56. package/lib/types/link.d.ts.map +1 -0
  57. package/lib/types/meta.d.ts +91 -0
  58. package/lib/types/meta.d.ts.map +1 -0
  59. package/lib/types/middleware.d.ts +35 -0
  60. package/lib/types/middleware.d.ts.map +1 -0
  61. package/lib/types/not-found.d.ts +7 -0
  62. package/lib/types/not-found.d.ts.map +1 -0
  63. package/lib/types/rate-limit.d.ts +34 -0
  64. package/lib/types/rate-limit.d.ts.map +1 -0
  65. package/lib/types/script.d.ts +35 -0
  66. package/lib/types/script.d.ts.map +1 -0
  67. package/lib/types/seo.d.ts +88 -0
  68. package/lib/types/seo.d.ts.map +1 -0
  69. package/lib/types/testing.d.ts +85 -0
  70. package/lib/types/testing.d.ts.map +1 -0
  71. package/lib/types/theme.d.ts +39 -0
  72. package/lib/types/theme.d.ts.map +1 -0
  73. package/lib/types/types.d.ts +111 -0
  74. package/lib/types/types.d.ts.map +1 -0
  75. package/lib/types/utils/use-intersection-observer.d.ts +10 -0
  76. package/lib/types/utils/use-intersection-observer.d.ts.map +1 -0
  77. package/lib/types/utils/with-headers.d.ts +6 -0
  78. package/lib/types/utils/with-headers.d.ts.map +1 -0
  79. package/lib/types/vite-plugin.d.ts +17 -0
  80. package/lib/types/vite-plugin.d.ts.map +1 -0
  81. package/package.json +10 -10
  82. package/src/entry-server.ts +124 -76
  83. package/src/favicon.ts +380 -0
  84. package/src/font.ts +32 -8
  85. package/src/fs-router.ts +54 -13
  86. package/src/i18n-routing.ts +299 -0
  87. package/src/image-plugin.ts +1 -1
  88. package/src/index.ts +125 -76
  89. package/src/link.tsx +19 -0
  90. package/src/meta.tsx +210 -0
  91. package/src/middleware.ts +65 -0
  92. package/src/not-found.ts +44 -0
  93. package/src/types.ts +2 -0
  94. package/src/vite-plugin.ts +258 -127
  95. package/lib/fs-router-n4VA4lxu.js.map +0 -1
package/lib/link.js CHANGED
@@ -93,6 +93,17 @@ function doPrefetch(href) {
93
93
  } catch {}
94
94
  }
95
95
  /**
96
+ * Prefetch a route's JS chunk by injecting `<link rel="prefetch">` into the
97
+ * document head. Deduplicates — calling with the same href twice is a no-op.
98
+ *
99
+ * @example
100
+ * prefetchRoute('/about')
101
+ * prefetchRoute('/dashboard')
102
+ */
103
+ function prefetchRoute(href) {
104
+ doPrefetch(href);
105
+ }
106
+ /**
96
107
  * Composable that provides all link behavior — navigation, prefetching,
97
108
  * active state, and viewport observation.
98
109
  *
@@ -113,6 +124,7 @@ function useLink(props) {
113
124
  const elementRef = createRef();
114
125
  const strategy = props.prefetch ?? "hover";
115
126
  function handleClick(e) {
127
+ if (props.onClick) props.onClick(e);
116
128
  if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || props.external) return;
117
129
  e.preventDefault();
118
130
  router.push(props.href);
@@ -232,5 +244,5 @@ const Link = createLink((props) => /* @__PURE__ */ jsx("a", {
232
244
  }));
233
245
 
234
246
  //#endregion
235
- export { Link, createLink, useLink };
247
+ export { Link, createLink, prefetchRoute, useLink };
236
248
  //# sourceMappingURL=link.js.map
package/lib/link.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"link.js","names":[],"sources":["../src/utils/use-intersection-observer.ts","../../../core/core/lib/jsx-runtime.js","../src/link.tsx"],"sourcesContent":["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 { 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}\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 * 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 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"],"mappings":"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"}
1
+ {"version":3,"file":"link.js","names":[],"sources":["../src/utils/use-intersection-observer.ts","../../../core/core/lib/jsx-runtime.js","../src/link.tsx"],"sourcesContent":["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 { 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"],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { MiddlewareContext } from '@pyreon/server';
2
+ /** Context passed to server action handlers. */
3
+ export interface ActionContext {
4
+ /** The original request. */
5
+ request: Request;
6
+ /** Parsed form data (for form submissions). */
7
+ formData: FormData | null;
8
+ /** Parsed JSON body (for JSON submissions). */
9
+ json: unknown;
10
+ /** Request headers. */
11
+ headers: Headers;
12
+ }
13
+ /** A server action handler function. */
14
+ export type ActionHandler<T = unknown> = (ctx: ActionContext) => T | Promise<T>;
15
+ /** A registered action with its ID and handler. */
16
+ interface RegisteredAction {
17
+ id: string;
18
+ handler: ActionHandler;
19
+ }
20
+ /** Client-side callable action returned by defineAction. */
21
+ export interface Action<T = unknown> {
22
+ /** Call the action with JSON data. */
23
+ (data?: unknown): Promise<T>;
24
+ /** The action's unique ID. */
25
+ actionId: string;
26
+ }
27
+ /**
28
+ * Define a server action. Returns a callable function that:
29
+ * - On the **client**: sends a POST request to `/_zero/actions/<id>`
30
+ * - On the **server** (SSR): executes the handler directly (no fetch)
31
+ *
32
+ * @example
33
+ * // In a route file or module:
34
+ * export const createPost = defineAction(async (ctx) => {
35
+ * const data = ctx.json as { title: string; body: string }
36
+ * // ... save to database
37
+ * return { success: true, id: 123 }
38
+ * })
39
+ *
40
+ * // In a component:
41
+ * const result = await createPost({ title: 'Hello', body: '...' })
42
+ */
43
+ export declare function defineAction<T = unknown>(handler: ActionHandler<T>): Action<T>;
44
+ /** Get all registered actions. Useful for testing. */
45
+ export declare function getRegisteredActions(): Map<string, RegisteredAction>;
46
+ /**
47
+ * Reset the action registry. Useful for testing.
48
+ * @internal
49
+ */
50
+ export declare function _resetActions(): void;
51
+ /**
52
+ * Create a middleware that handles action requests at `/_zero/actions/*`.
53
+ * Mount this before the SSR handler in the server entry.
54
+ */
55
+ export declare function createActionMiddleware(): (ctx: MiddlewareContext) => Response | undefined | Promise<Response | undefined>;
56
+ export {};
57
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAIvD,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,+CAA+C;IAC/C,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAA;IACzB,+CAA+C;IAC/C,IAAI,EAAE,OAAO,CAAA;IACb,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,wCAAwC;AACxC,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,aAAa,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAE/E,mDAAmD;AACnD,UAAU,gBAAgB;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,aAAa,CAAA;CACvB;AAED,4DAA4D;AAC5D,MAAM,WAAW,MAAM,CAAC,CAAC,GAAG,OAAO;IACjC,sCAAsC;IACtC,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC5B,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAmC9E;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAEpE;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAID;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,CACxC,GAAG,EAAE,iBAAiB,KACnB,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAiBxD"}
@@ -0,0 +1,6 @@
1
+ import type { Adapter } from '../types';
2
+ /**
3
+ * Bun adapter — generates a standalone Bun.serve() entry.
4
+ */
5
+ export declare function bunAdapter(): Adapter;
6
+ //# sourceMappingURL=bun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../../../src/adapters/bun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAuB,MAAM,UAAU,CAAA;AAE5D;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CA2DpC"}
@@ -0,0 +1,10 @@
1
+ export { bunAdapter } from './bun';
2
+ export { nodeAdapter } from './node';
3
+ export { staticAdapter } from './static';
4
+ import type { Adapter, ZeroConfig } from '../types';
5
+ /**
6
+ * Resolve the adapter from config.
7
+ * Returns a built-in adapter or throws if unknown.
8
+ */
9
+ export declare function resolveAdapter(config: ZeroConfig): Adapter;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAKnD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAa1D"}
@@ -0,0 +1,6 @@
1
+ import type { Adapter } from '../types';
2
+ /**
3
+ * Node.js adapter — generates a standalone server entry using node:http.
4
+ */
5
+ export declare function nodeAdapter(): Adapter;
6
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/adapters/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAuB,MAAM,UAAU,CAAA;AAE5D;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAwGrC"}
@@ -0,0 +1,7 @@
1
+ import type { Adapter } from '../types';
2
+ /**
3
+ * Static adapter — just copies the client build output.
4
+ * Used with SSG mode where all pages are pre-rendered at build time.
5
+ */
6
+ export declare function staticAdapter(): Adapter;
7
+ //# sourceMappingURL=static.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../../../src/adapters/static.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAuB,MAAM,UAAU,CAAA;AAE5D;;;GAGG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAUvC"}
@@ -0,0 +1,66 @@
1
+ import type { Middleware } from '@pyreon/server';
2
+ /** HTTP methods supported by API routes. */
3
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
4
+ /** Context passed to API route handlers. */
5
+ export interface ApiContext {
6
+ /** The incoming request. */
7
+ request: Request;
8
+ /** Parsed URL. */
9
+ url: URL;
10
+ /** URL path. */
11
+ path: string;
12
+ /** Dynamic route parameters (e.g., { id: "123" }). */
13
+ params: Record<string, string>;
14
+ /** Request headers. */
15
+ headers: Headers;
16
+ }
17
+ /** An API route handler function. */
18
+ export type ApiHandler = (ctx: ApiContext) => Response | Promise<Response>;
19
+ /** An API route module — exports named HTTP method handlers. */
20
+ export interface ApiRouteModule {
21
+ GET?: ApiHandler;
22
+ POST?: ApiHandler;
23
+ PUT?: ApiHandler;
24
+ PATCH?: ApiHandler;
25
+ DELETE?: ApiHandler;
26
+ HEAD?: ApiHandler;
27
+ OPTIONS?: ApiHandler;
28
+ }
29
+ /** A registered API route entry. */
30
+ export interface ApiRouteEntry {
31
+ /** URL pattern (e.g., "/api/posts/:id"). */
32
+ pattern: string;
33
+ /** The route module with method handlers. */
34
+ module: ApiRouteModule;
35
+ }
36
+ /**
37
+ * Match a URL path against an API route pattern.
38
+ * Returns extracted params or null if no match.
39
+ */
40
+ export declare function matchApiRoute(pattern: string, path: string): Record<string, string> | null;
41
+ /**
42
+ * Create a middleware that dispatches API route requests.
43
+ * API routes are matched by URL pattern and HTTP method.
44
+ */
45
+ export declare function createApiMiddleware(routes: ApiRouteEntry[]): Middleware;
46
+ /**
47
+ * Detect whether a route file is an API route.
48
+ * API routes are `.ts` or `.js` files inside an `api/` directory.
49
+ */
50
+ export declare function isApiRoute(filePath: string): boolean;
51
+ /**
52
+ * Convert an API route file path to a URL pattern.
53
+ *
54
+ * Examples:
55
+ * "api/posts.ts" → "/api/posts"
56
+ * "api/posts/index.ts" → "/api/posts"
57
+ * "api/posts/[id].ts" → "/api/posts/:id"
58
+ * "api/[...path].ts" → "/api/:path*"
59
+ */
60
+ export declare function apiFilePathToPattern(filePath: string): string;
61
+ /**
62
+ * Generate a virtual module that exports API route entries.
63
+ * Each entry maps a URL pattern to a module with HTTP method handlers.
64
+ */
65
+ export declare function generateApiRouteModule(files: string[], routesDir: string): string;
66
+ //# sourceMappingURL=api-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-routes.d.ts","sourceRoot":"","sources":["../../src/api-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAA;AAInE,4CAA4C;AAC5C,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;AAEzF,4CAA4C;AAC5C,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,kBAAkB;IAClB,GAAG,EAAE,GAAG,CAAA;IACR,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,qCAAqC;AACrC,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE1E,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,OAAO,CAAC,EAAE,UAAU,CAAA;CACrB;AAED,oCAAoC;AACpC,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAA;IACf,6CAA6C;IAC7C,MAAM,EAAE,cAAc,CAAA;CACvB;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CA8B1F;AAMD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CA8BvE;AAID;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAkC7D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAsBjF"}
@@ -0,0 +1,24 @@
1
+ import type { ComponentFn } from '@pyreon/core';
2
+ import type { RouteRecord } from '@pyreon/router';
3
+ export interface CreateAppOptions {
4
+ /** Route definitions (from file-based routing or manual). */
5
+ routes: RouteRecord[];
6
+ /** Router mode. Default: "history" for SSR, "hash" for SPA. */
7
+ routerMode?: 'hash' | 'history';
8
+ /** Initial URL for SSR. */
9
+ url?: string;
10
+ /** Root layout component wrapping all routes. */
11
+ layout?: ComponentFn;
12
+ /** Global error component. */
13
+ errorComponent?: ComponentFn;
14
+ }
15
+ /**
16
+ * Create a full Zero app — assembles router, head provider, and root layout.
17
+ *
18
+ * Used internally by entry-server and entry-client.
19
+ */
20
+ export declare function createApp(options: CreateAppOptions): {
21
+ App: () => import("@pyreon/core").VNode;
22
+ router: import("@pyreon/router").Router;
23
+ };
24
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAS,MAAM,cAAc,CAAA;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAKjD,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,MAAM,EAAE,WAAW,EAAE,CAAA;IAErB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/B,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,iDAAiD;IACjD,MAAM,CAAC,EAAE,WAAW,CAAA;IAEpB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,WAAW,CAAA;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB;;;EAuBlD"}
@@ -0,0 +1,54 @@
1
+ import type { Middleware } from '@pyreon/server';
2
+ export interface CacheConfig {
3
+ /** Cache duration for immutable hashed assets (seconds). Default: 31536000 (1 year) */
4
+ immutable?: number;
5
+ /** Cache duration for static assets like images/fonts (seconds). Default: 86400 (1 day) */
6
+ static?: number;
7
+ /** Cache duration for pages (seconds). Default: 0 (no cache) */
8
+ pages?: number;
9
+ /** Stale-while-revalidate window for pages (seconds). Default: 60 */
10
+ staleWhileRevalidate?: number;
11
+ /** Custom rules by URL pattern. */
12
+ rules?: CacheRule[];
13
+ }
14
+ export interface CacheRule {
15
+ /** URL pattern to match (glob-style). e.g. "/api/*" */
16
+ match: string;
17
+ /** Cache-Control header value. */
18
+ control: string;
19
+ }
20
+ /** @internal Exported for testing */
21
+ export declare function matchGlob(pattern: string, path: string): boolean;
22
+ /**
23
+ * Cache control middleware for Zero.
24
+ * Sets Cache-Control headers on the response based on asset type.
25
+ *
26
+ * @example
27
+ * import { cacheMiddleware } from "@pyreon/zero/cache"
28
+ *
29
+ * export default createHandler({
30
+ * routes,
31
+ * middleware: [
32
+ * cacheMiddleware({
33
+ * pages: 60,
34
+ * staleWhileRevalidate: 300,
35
+ * rules: [
36
+ * { match: "/api/*", control: "no-store" },
37
+ * ],
38
+ * }),
39
+ * ],
40
+ * })
41
+ */
42
+ export declare function cacheMiddleware(config?: CacheConfig): Middleware;
43
+ /**
44
+ * Security headers middleware.
45
+ * Adds common security headers to all responses.
46
+ */
47
+ export declare function securityHeaders(): Middleware;
48
+ /**
49
+ * Compression detection middleware.
50
+ * Sets Vary: Accept-Encoding header so caches can serve compressed variants.
51
+ * Actual compression is handled by the runtime (Bun/Node) or reverse proxy.
52
+ */
53
+ export declare function varyEncoding(): Middleware;
54
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAA;AAanE,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,mCAAmC;IACnC,KAAK,CAAC,EAAE,SAAS,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAA;CAChB;AAMD,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKhE;AAwBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,MAAM,GAAE,WAAgB,GAAG,UAAU,CAoBpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAQ5C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,UAAU,CAOzC"}
@@ -0,0 +1,19 @@
1
+ import type { ComponentFn } from '@pyreon/core';
2
+ import type { RouteRecord } from '@pyreon/router';
3
+ export interface StartClientOptions {
4
+ /** Route definitions. */
5
+ routes: RouteRecord[];
6
+ /** Root layout component. */
7
+ layout?: ComponentFn;
8
+ }
9
+ /**
10
+ * Start the client-side app — hydrates SSR content or mounts fresh for SPA.
11
+ *
12
+ * @example
13
+ * import { routes } from "virtual:zero/routes"
14
+ * import { startClient } from "@pyreon/zero/client"
15
+ *
16
+ * startClient({ routes })
17
+ */
18
+ export declare function startClient(options: StartClientOptions): () => void;
19
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAMjD,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,cAkBtD"}
@@ -0,0 +1,33 @@
1
+ import type { Middleware } from '@pyreon/server';
2
+ export interface CompressionConfig {
3
+ /** Minimum response size in bytes to compress. Default: `1024` (1KB) */
4
+ threshold?: number;
5
+ /** Encoding preference order. Default: `["gzip", "deflate"]` */
6
+ encodings?: ('gzip' | 'deflate')[];
7
+ }
8
+ /**
9
+ * Compression middleware — compresses responses using gzip or deflate
10
+ * based on the client's Accept-Encoding header.
11
+ *
12
+ * Only compresses text-based content types (HTML, JSON, JS, CSS, XML, SVG).
13
+ * Skips responses below the size threshold and already-encoded responses.
14
+ *
15
+ * @example
16
+ * import { compressionMiddleware } from "@pyreon/zero/compression"
17
+ *
18
+ * compressionMiddleware() // gzip with 1KB threshold
19
+ * compressionMiddleware({ threshold: 512, encodings: ["gzip"] })
20
+ */
21
+ export declare function compressionMiddleware(config?: CompressionConfig): Middleware;
22
+ /**
23
+ * Compress a Response body if it meets the criteria.
24
+ * Use this to post-process responses after the handler runs.
25
+ *
26
+ * @example
27
+ * const response = await handler(request)
28
+ * const compressed = await compressResponse(response, 'gzip', 1024)
29
+ */
30
+ export declare function compressResponse(response: Response, encoding: 'gzip' | 'deflate', threshold: number): Promise<Response>;
31
+ /** Check if a content type is compressible. Exported for testing. */
32
+ export declare function isCompressible(contentType: string): boolean;
33
+ //# sourceMappingURL=compression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../../src/compression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAA;AAInE,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAA;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,iBAAsB,GAAG,UAAU,CAehF;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,CAAC,CA0BnB;AAWD,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE3D"}
@@ -0,0 +1,18 @@
1
+ import type { ZeroConfig } from './types';
2
+ /**
3
+ * Define a Zero configuration.
4
+ * Used in `zero.config.ts` at the project root.
5
+ *
6
+ * @example
7
+ * import { defineConfig } from "@pyreon/zero/config"
8
+ *
9
+ * export default defineConfig({
10
+ * mode: "ssr",
11
+ * ssr: { mode: "stream" },
12
+ * port: 3000,
13
+ * })
14
+ */
15
+ export declare function defineConfig(config: ZeroConfig): ZeroConfig;
16
+ /** Merge user config with defaults. */
17
+ export declare function resolveConfig(userConfig?: ZeroConfig): Required<Pick<ZeroConfig, 'mode' | 'base' | 'port' | 'adapter'>> & ZeroConfig;
18
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAE3D;AAED,uCAAuC;AACvC,wBAAgB,aAAa,CAC3B,UAAU,GAAE,UAAe,GAC1B,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,UAAU,CAY/E"}
@@ -0,0 +1,32 @@
1
+ import type { Middleware } from '@pyreon/server';
2
+ export interface CorsConfig {
3
+ /** Allowed origins. Use `"*"` for any origin. Default: `"*"` */
4
+ origin?: string | string[] | ((origin: string) => boolean);
5
+ /** Allowed HTTP methods. Default: `["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]` */
6
+ methods?: string[];
7
+ /** Allowed request headers. Default: `["Content-Type", "Authorization"]` */
8
+ allowedHeaders?: string[];
9
+ /** Headers exposed to the client. Default: `[]` */
10
+ exposedHeaders?: string[];
11
+ /** Allow credentials (cookies, auth headers). Default: `false` */
12
+ credentials?: boolean;
13
+ /** Preflight cache duration in seconds. Default: `86400` (24 hours) */
14
+ maxAge?: number;
15
+ }
16
+ /**
17
+ * CORS middleware — handles preflight requests and sets appropriate
18
+ * Access-Control headers on all responses.
19
+ *
20
+ * @example
21
+ * import { corsMiddleware } from "@pyreon/zero/cors"
22
+ *
23
+ * corsMiddleware({ origin: "https://example.com", credentials: true })
24
+ *
25
+ * // Allow any origin
26
+ * corsMiddleware({ origin: "*" })
27
+ *
28
+ * // Multiple origins
29
+ * corsMiddleware({ origin: ["https://app.com", "https://admin.com"] })
30
+ */
31
+ export declare function corsMiddleware(config?: CorsConfig): Middleware;
32
+ //# sourceMappingURL=cors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAA;AAInE,MAAM,WAAW,UAAU;IACzB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;IAC1D,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,UAAe,GAAG,UAAU,CA0ClE"}
@@ -0,0 +1,37 @@
1
+ import type { ComponentFn } from "@pyreon/core";
2
+ import type { RouteRecord } from "@pyreon/router";
3
+ import type { Middleware } from "@pyreon/server";
4
+ import type { ApiRouteEntry } from "./api-routes";
5
+ import type { RouteMiddlewareEntry, ZeroConfig } from "./types";
6
+ export interface CreateServerOptions {
7
+ /** Route definitions. */
8
+ routes: RouteRecord[];
9
+ /** Zero config. */
10
+ config?: ZeroConfig;
11
+ /** Additional middleware. */
12
+ middleware?: Middleware[];
13
+ /** Per-route middleware from virtual:zero/route-middleware. */
14
+ routeMiddleware?: RouteMiddlewareEntry[];
15
+ /** API route entries from virtual:zero/api-routes. */
16
+ apiRoutes?: ApiRouteEntry[];
17
+ /** HTML template override. */
18
+ template?: string;
19
+ /** Client entry path. */
20
+ clientEntry?: string;
21
+ /** Component to render when no route matches (from _404.tsx). */
22
+ notFoundComponent?: ComponentFn;
23
+ }
24
+ /** Simple URL pattern matcher supporting :param and :param* segments. */
25
+ export declare function matchPattern(pattern: string, path: string): boolean;
26
+ /**
27
+ * Create the SSR request handler for production.
28
+ *
29
+ * @example
30
+ * import { routes } from "virtual:zero/routes"
31
+ * import { routeMiddleware } from "virtual:zero/route-middleware"
32
+ * import { createServer } from "@pyreon/zero"
33
+ *
34
+ * export default createServer({ routes, routeMiddleware, apiRoutes })
35
+ */
36
+ export declare function createServer(options: CreateServerOptions): (req: Request) => Promise<Response>;
37
+ //# sourceMappingURL=entry-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-server.d.ts","sourceRoot":"","sources":["../../src/entry-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,gBAAgB,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAIhE,MAAM,WAAW,mBAAmB;IACnC,yBAAyB;IACzB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,mBAAmB;IACnB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1B,+DAA+D;IAC/D,eAAe,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACzC,sDAAsD;IACtD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,WAAW,CAAC;CAChC;AAuBD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAanE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,uCAwDxD"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Dev-only error overlay for SSR/loader errors.
3
+ * Renders a styled HTML page with the error stack trace.
4
+ */
5
+ export declare function renderErrorOverlay(error: Error): string;
6
+ //# sourceMappingURL=error-overlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-overlay.d.ts","sourceRoot":"","sources":["../../src/error-overlay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CA0FvD"}
@@ -0,0 +1,43 @@
1
+ import type { Plugin } from 'vite';
2
+ export interface FaviconPluginConfig {
3
+ /** Path to the source icon (SVG or PNG, at least 512x512 for PNG). */
4
+ source: string;
5
+ /** Theme color for web manifest. Default: "#ffffff" */
6
+ themeColor?: string;
7
+ /** Background color for web manifest. Default: "#ffffff" */
8
+ backgroundColor?: string;
9
+ /** App name for web manifest. Uses package.json name if not set. */
10
+ name?: string;
11
+ /** Generate web manifest. Default: true */
12
+ manifest?: boolean;
13
+ /**
14
+ * Dark mode favicon (SVG only).
15
+ * When provided, the SVG favicon uses prefers-color-scheme media query
16
+ * to switch between light and dark variants.
17
+ */
18
+ darkSource?: string;
19
+ }
20
+ /**
21
+ * Favicon generation Vite plugin.
22
+ *
23
+ * Generates all required favicon formats at build time from a single source.
24
+ * In dev mode, serves the source directly.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * // vite.config.ts
29
+ * import { faviconPlugin } from "@pyreon/zero"
30
+ *
31
+ * export default {
32
+ * plugins: [faviconPlugin({ source: "./src/assets/icon.svg" })],
33
+ * }
34
+ * ```
35
+ */
36
+ export declare function faviconPlugin(config: FaviconPluginConfig): Plugin;
37
+ export interface IcoEntry {
38
+ buffer: Buffer;
39
+ size: number;
40
+ }
41
+ /** @internal Exported for testing */
42
+ export declare function createIcoFromPngs(entries: IcoEntry[]): Uint8Array;
43
+ //# sourceMappingURL=favicon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"favicon.d.ts","sourceRoot":"","sources":["../../src/favicon.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AA0BlC,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAeD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAgNjE;AAuDD,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qCAAqC;AACrC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,UAAU,CAiCjE"}