@pyreon/zero 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/lib/api-routes-Ci0kVmM4.js +146 -0
  2. package/lib/client.js +7 -2
  3. package/lib/csp.js +19 -9
  4. package/lib/env.js +6 -6
  5. package/lib/font.js +3 -3
  6. package/lib/{fs-router-CQ7Zxeca.js → fs-router-MewHc5SB.js} +56 -24
  7. package/lib/i18n-routing.js +112 -1
  8. package/lib/image-plugin.js +4 -0
  9. package/lib/image.js +141 -108
  10. package/lib/index.js +253 -132
  11. package/lib/link.js +1 -49
  12. package/lib/og-image.js +5 -5
  13. package/lib/rolldown-runtime-CjeV3_4I.js +18 -0
  14. package/lib/script.js +115 -74
  15. package/lib/seo.js +186 -15
  16. package/lib/server.js +275 -1247
  17. package/lib/theme.js +1 -50
  18. package/lib/types/config.d.ts +275 -3
  19. package/lib/types/env.d.ts +2 -2
  20. package/lib/types/i18n-routing.d.ts +197 -6
  21. package/lib/types/image.d.ts +105 -5
  22. package/lib/types/index.d.ts +640 -178
  23. package/lib/types/link.d.ts +3 -3
  24. package/lib/types/script.d.ts +78 -6
  25. package/lib/types/seo.d.ts +128 -4
  26. package/lib/types/server.d.ts +603 -77
  27. package/lib/types/theme.d.ts +2 -2
  28. package/lib/vite-plugin-xjWZwudX.js +2454 -0
  29. package/package.json +16 -13
  30. package/src/adapters/bun.ts +20 -1
  31. package/src/adapters/cloudflare.ts +78 -1
  32. package/src/adapters/index.ts +25 -3
  33. package/src/adapters/netlify.ts +63 -1
  34. package/src/adapters/node.ts +25 -1
  35. package/src/adapters/static.ts +26 -1
  36. package/src/adapters/validate.ts +8 -1
  37. package/src/adapters/vercel.ts +76 -1
  38. package/src/adapters/warn-missing-env.ts +49 -0
  39. package/src/app.ts +35 -1
  40. package/src/client.ts +18 -0
  41. package/src/csp.ts +28 -12
  42. package/src/entry-server.ts +55 -5
  43. package/src/env.ts +7 -7
  44. package/src/font.ts +3 -3
  45. package/src/fs-router.ts +123 -4
  46. package/src/i18n-routing.ts +246 -12
  47. package/src/image.tsx +242 -91
  48. package/src/index.ts +4 -4
  49. package/src/isr.ts +24 -6
  50. package/src/manifest.ts +675 -0
  51. package/src/og-image.ts +5 -5
  52. package/src/script.tsx +159 -36
  53. package/src/seo.ts +346 -15
  54. package/src/server.ts +10 -2
  55. package/src/ssg-plugin.ts +1523 -0
  56. package/src/types.ts +329 -19
  57. package/src/vercel-revalidate-handler.ts +204 -0
  58. package/src/vite-plugin.ts +326 -68
  59. package/lib/actions.js.map +0 -1
  60. package/lib/ai.js.map +0 -1
  61. package/lib/api-routes.js.map +0 -1
  62. package/lib/cache.js.map +0 -1
  63. package/lib/client.js.map +0 -1
  64. package/lib/compression.js.map +0 -1
  65. package/lib/config.js.map +0 -1
  66. package/lib/cors.js.map +0 -1
  67. package/lib/csp.js.map +0 -1
  68. package/lib/env.js.map +0 -1
  69. package/lib/favicon.js.map +0 -1
  70. package/lib/font.js.map +0 -1
  71. package/lib/fs-router-3xzp-4Wj.js.map +0 -1
  72. package/lib/fs-router-CQ7Zxeca.js.map +0 -1
  73. package/lib/i18n-routing.js.map +0 -1
  74. package/lib/image-plugin.js.map +0 -1
  75. package/lib/image.js.map +0 -1
  76. package/lib/index.js.map +0 -1
  77. package/lib/link.js.map +0 -1
  78. package/lib/logger.js.map +0 -1
  79. package/lib/meta.js.map +0 -1
  80. package/lib/middleware.js.map +0 -1
  81. package/lib/og-image.js.map +0 -1
  82. package/lib/rate-limit.js.map +0 -1
  83. package/lib/script.js.map +0 -1
  84. package/lib/seo.js.map +0 -1
  85. package/lib/server.js.map +0 -1
  86. package/lib/testing.js.map +0 -1
  87. package/lib/theme.js.map +0 -1
  88. package/lib/types/actions.d.ts.map +0 -1
  89. package/lib/types/ai.d.ts.map +0 -1
  90. package/lib/types/api-routes.d.ts.map +0 -1
  91. package/lib/types/cache.d.ts.map +0 -1
  92. package/lib/types/client.d.ts.map +0 -1
  93. package/lib/types/compression.d.ts.map +0 -1
  94. package/lib/types/config.d.ts.map +0 -1
  95. package/lib/types/cors.d.ts.map +0 -1
  96. package/lib/types/csp.d.ts.map +0 -1
  97. package/lib/types/env.d.ts.map +0 -1
  98. package/lib/types/favicon.d.ts.map +0 -1
  99. package/lib/types/font.d.ts.map +0 -1
  100. package/lib/types/i18n-routing.d.ts.map +0 -1
  101. package/lib/types/image-plugin.d.ts.map +0 -1
  102. package/lib/types/image.d.ts.map +0 -1
  103. package/lib/types/index.d.ts.map +0 -1
  104. package/lib/types/link.d.ts.map +0 -1
  105. package/lib/types/logger.d.ts.map +0 -1
  106. package/lib/types/meta.d.ts.map +0 -1
  107. package/lib/types/middleware.d.ts.map +0 -1
  108. package/lib/types/og-image.d.ts.map +0 -1
  109. package/lib/types/rate-limit.d.ts.map +0 -1
  110. package/lib/types/script.d.ts.map +0 -1
  111. package/lib/types/seo.d.ts.map +0 -1
  112. package/lib/types/server.d.ts.map +0 -1
  113. package/lib/types/testing.d.ts.map +0 -1
  114. package/lib/types/theme.d.ts.map +0 -1
package/lib/image.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRef, onMount, onUnmount } from "@pyreon/core";
2
2
  import { signal } from "@pyreon/reactivity";
3
+ import { jsx, jsxs } from "@pyreon/core/jsx-runtime";
3
4
 
4
5
  //#region src/utils/use-intersection-observer.ts
5
6
  /**
@@ -25,83 +26,37 @@ function useIntersectionObserver(getElement, onIntersect, rootMargin = "200px")
25
26
  });
26
27
  }
27
28
 
28
- //#endregion
29
- //#region ../../core/core/lib/jsx-runtime.js
30
- /**
31
- * Hyperscript function — the compiled output of JSX.
32
- * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
33
- *
34
- * Generic on P so TypeScript validates props match the component's signature
35
- * at the call site, then stores the result in the loosely-typed VNode.
36
- */
37
- /** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */
38
- const EMPTY_PROPS = {};
39
- function h(type, props, ...children) {
40
- return {
41
- type,
42
- props: props ?? EMPTY_PROPS,
43
- children: normalizeChildren(children),
44
- key: props?.key ?? null
45
- };
46
- }
47
- function normalizeChildren(children) {
48
- for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
49
- return children;
50
- }
51
- function flattenChildren(children) {
52
- const result = [];
53
- for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));
54
- else result.push(child);
55
- return result;
56
- }
57
- /**
58
- * JSX automatic runtime.
59
- *
60
- * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
61
- * rewrites JSX to imports from this file automatically:
62
- * <div class="x" /> → jsx("div", { class: "x" })
63
- */
64
- function jsx(type, props, key) {
65
- const { children, ...rest } = props;
66
- const propsWithKey = key != null ? {
67
- ...rest,
68
- key
69
- } : rest;
70
- if (typeof type === "function") return h(type, children !== void 0 ? {
71
- ...propsWithKey,
72
- children
73
- } : propsWithKey);
74
- return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
75
- }
76
- const jsxs = jsx;
77
-
78
29
  //#endregion
79
30
  //#region src/image.tsx
80
31
  /**
81
- * Optimized image component with lazy loading, responsive images,
82
- * multi-format <picture> support, and blur-up placeholders.
32
+ * Composable that provides all image optimization behavior lazy loading,
33
+ * srcset/sizes resolution, format selection, blur-placeholder state,
34
+ * load tracking.
83
35
  *
84
- * @example
85
- * // With imagePlugin spread the import directly
86
- * import hero from "./hero.jpg?optimize"
87
- * <Image {...hero} alt="Hero" priority />
36
+ * Use this for full control when `createImage` is too opinionated about
37
+ * the surrounding markup (e.g. custom container layouts, non-`<div>`
38
+ * wrappers, additional overlay elements).
88
39
  *
89
40
  * @example
90
- * // Manual usage
91
- * <Image src="/hero.jpg" alt="Hero" width={1200} height={630} />
41
+ * function MyImage(props: ImageProps) {
42
+ * const img = useImage(props)
43
+ * return (
44
+ * <figure ref={img.containerRef} style={img.containerStyle}>
45
+ * <img
46
+ * src={img.src}
47
+ * srcSet={img.srcSet}
48
+ * sizes={img.sizes}
49
+ * alt={props.alt}
50
+ * loading={img.loading}
51
+ * onLoad={img.handleLoad}
52
+ * style={img.imageStyle}
53
+ * />
54
+ * <figcaption>{props.alt}</figcaption>
55
+ * </figure>
56
+ * )
57
+ * }
92
58
  */
93
- function Image(props) {
94
- if (props.raw) return /* @__PURE__ */ jsx("img", {
95
- src: props.src,
96
- alt: props.alt,
97
- width: props.width,
98
- height: props.height,
99
- class: props.class,
100
- style: props.style,
101
- decoding: props.decoding ?? "async",
102
- loading: props.loading ?? "lazy",
103
- fetchPriority: props.priority ? "high" : void 0
104
- });
59
+ function useImage(props) {
105
60
  const isEager = props.priority || props.loading === "eager";
106
61
  const loaded = signal(isEager);
107
62
  const inView = signal(isEager);
@@ -109,7 +64,7 @@ function Image(props) {
109
64
  const resolvedSrcset = typeof props.srcset === "string" ? props.srcset : props.srcset?.map((s) => `${s.src} ${s.width}w`).join(", ");
110
65
  const sizes = props.sizes ?? "100vw";
111
66
  const fit = props.fit ?? "cover";
112
- const hasFormats = props.formats && props.formats.length > 0;
67
+ const hasFormats = !!(props.formats && props.formats.length > 0);
113
68
  const aspectRatio = `${props.width} / ${props.height}`;
114
69
  if (!isEager) useIntersectionObserver(() => containerRef.current ?? void 0, () => inView.set(true));
115
70
  const containerStyle = [
@@ -120,54 +75,132 @@ function Image(props) {
120
75
  "width: 100%",
121
76
  props.style
122
77
  ].filter(Boolean).join("; ");
123
- const imgEl = /* @__PURE__ */ jsx("img", {
78
+ const imageStyle = () => [
79
+ "display: block",
80
+ "width: 100%",
81
+ "height: 100%",
82
+ `object-fit: ${fit}`,
83
+ "transition: opacity 0.3s ease",
84
+ props.placeholder && !loaded() ? "opacity: 0" : "opacity: 1"
85
+ ].join("; ");
86
+ const placeholderStyle = () => [
87
+ "position: absolute",
88
+ "inset: 0",
89
+ "width: 100%",
90
+ "height: 100%",
91
+ "object-fit: cover",
92
+ "filter: blur(20px)",
93
+ "transform: scale(1.1)",
94
+ "transition: opacity 0.4s ease",
95
+ loaded() ? "opacity: 0; pointer-events: none" : "opacity: 1"
96
+ ].join("; ");
97
+ return {
98
+ containerRef,
99
+ inView,
100
+ loaded,
124
101
  src: () => inView() ? props.src : "",
125
102
  srcSet: () => !hasFormats && inView() && resolvedSrcset ? resolvedSrcset : "",
126
103
  sizes: resolvedSrcset ? sizes : void 0,
127
- alt: props.alt,
128
- width: props.width,
129
- height: props.height,
104
+ aspectRatio,
105
+ containerStyle,
106
+ imageStyle,
107
+ placeholderStyle,
130
108
  loading: isEager ? "eager" : "lazy",
131
- decoding: props.decoding ?? "async",
132
109
  fetchPriority: props.priority ? "high" : void 0,
133
- onLoad: () => loaded.set(true),
134
- style: () => [
135
- "display: block",
136
- "width: 100%",
137
- "height: 100%",
138
- `object-fit: ${fit}`,
139
- "transition: opacity 0.3s ease",
140
- props.placeholder && !loaded() ? "opacity: 0" : "opacity: 1"
141
- ].join("; ")
142
- });
143
- return /* @__PURE__ */ jsxs("div", {
144
- ref: containerRef,
145
- class: props.class,
146
- style: containerStyle,
147
- children: [props.placeholder && /* @__PURE__ */ jsx("img", {
110
+ handleLoad: () => loaded.set(true),
111
+ formats: props.formats,
112
+ hasFormats
113
+ };
114
+ }
115
+ /**
116
+ * Higher-order component that wraps any component with image optimization.
117
+ *
118
+ * The wrapped component receives {@link ImageRenderProps} with the pre-rendered
119
+ * `image` JSX (bare `<img>` OR `<picture>` tree depending on formats), the
120
+ * pre-rendered `placeholder` JSX, and the container ref + styles. Consumers
121
+ * compose those pieces with whatever wrapper element / layout they want.
122
+ *
123
+ * @example
124
+ * // Custom figure-based image with caption
125
+ * const FigureImage = createImage((props) => (
126
+ * <figure ref={props.containerRef} class={props.class} style={props.containerStyle}>
127
+ * {props.placeholder}
128
+ * {props.image}
129
+ * <figcaption>Caption goes here</figcaption>
130
+ * </figure>
131
+ * ))
132
+ *
133
+ * // Usage — identical to default <Image>
134
+ * <FigureImage src="/hero.jpg" alt="Hero" width={1200} height={630} />
135
+ */
136
+ function createImage(Component) {
137
+ return function WrappedImage(props) {
138
+ if (props.raw) return /* @__PURE__ */ jsx("img", {
139
+ src: props.src,
140
+ alt: props.alt,
141
+ width: props.width,
142
+ height: props.height,
143
+ class: props.class,
144
+ style: props.style,
145
+ decoding: props.decoding ?? "async",
146
+ loading: props.loading ?? "lazy",
147
+ fetchPriority: props.priority ? "high" : void 0
148
+ });
149
+ const img = useImage(props);
150
+ const imgEl = /* @__PURE__ */ jsx("img", {
151
+ src: img.src,
152
+ srcSet: img.srcSet,
153
+ sizes: img.sizes,
154
+ alt: props.alt,
155
+ width: props.width,
156
+ height: props.height,
157
+ loading: img.loading,
158
+ decoding: props.decoding ?? "async",
159
+ fetchPriority: img.fetchPriority,
160
+ onLoad: img.handleLoad,
161
+ style: img.imageStyle
162
+ });
163
+ const placeholderEl = props.placeholder ? /* @__PURE__ */ jsx("img", {
148
164
  src: props.placeholder,
149
165
  alt: "",
150
166
  "aria-hidden": "true",
151
167
  loading: "eager",
152
- style: () => [
153
- "position: absolute",
154
- "inset: 0",
155
- "width: 100%",
156
- "height: 100%",
157
- "object-fit: cover",
158
- "filter: blur(20px)",
159
- "transform: scale(1.1)",
160
- "transition: opacity 0.4s ease",
161
- loaded() ? "opacity: 0; pointer-events: none" : "opacity: 1"
162
- ].join("; ")
163
- }), hasFormats ? /* @__PURE__ */ jsxs("picture", { children: [props.formats?.map((fmt) => /* @__PURE__ */ jsx("source", {
168
+ style: img.placeholderStyle
169
+ }) : null;
170
+ const imageEl = img.hasFormats ? /* @__PURE__ */ jsxs("picture", { children: [img.formats?.map((fmt) => /* @__PURE__ */ jsx("source", {
164
171
  type: fmt.type,
165
- srcSet: () => inView() ? fmt.srcset ?? "" : "",
166
- sizes
167
- })), imgEl] }) : imgEl]
168
- });
172
+ srcSet: () => img.inView() ? fmt.srcset ?? "" : "",
173
+ sizes: img.sizes
174
+ })), imgEl] }) : imgEl;
175
+ return /* @__PURE__ */ jsx(Component, {
176
+ containerRef: img.containerRef,
177
+ class: props.class,
178
+ containerStyle: img.containerStyle,
179
+ placeholder: placeholderEl,
180
+ image: imageEl
181
+ });
182
+ };
169
183
  }
184
+ /**
185
+ * Default optimized image component with lazy loading, responsive srcset,
186
+ * `<picture>` multi-format support, and blur-up placeholders.
187
+ *
188
+ * @example
189
+ * // With imagePlugin — spread the import directly
190
+ * import hero from "./hero.jpg?optimize"
191
+ * <Image {...hero} alt="Hero" priority />
192
+ *
193
+ * @example
194
+ * // Manual usage
195
+ * <Image src="/hero.jpg" alt="Hero" width={1200} height={630} />
196
+ */
197
+ const Image = createImage((props) => /* @__PURE__ */ jsxs("div", {
198
+ ref: props.containerRef,
199
+ class: props.class,
200
+ style: props.containerStyle,
201
+ children: [props.placeholder, props.image]
202
+ }));
170
203
 
171
204
  //#endregion
172
- export { Image };
205
+ export { Image, createImage, useImage };
173
206
  //# sourceMappingURL=image.js.map