@pyreon/zero 0.24.4 → 0.24.6

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 (54) hide show
  1. package/package.json +10 -39
  2. package/src/actions.ts +0 -196
  3. package/src/adapters/bun.ts +0 -114
  4. package/src/adapters/cloudflare.ts +0 -166
  5. package/src/adapters/index.ts +0 -61
  6. package/src/adapters/netlify.ts +0 -154
  7. package/src/adapters/node.ts +0 -163
  8. package/src/adapters/static.ts +0 -42
  9. package/src/adapters/validate.ts +0 -23
  10. package/src/adapters/vercel.ts +0 -182
  11. package/src/adapters/warn-missing-env.ts +0 -49
  12. package/src/ai.ts +0 -623
  13. package/src/api-routes.ts +0 -219
  14. package/src/app.ts +0 -92
  15. package/src/cache.ts +0 -136
  16. package/src/client.ts +0 -143
  17. package/src/compression.ts +0 -116
  18. package/src/config.ts +0 -35
  19. package/src/cors.ts +0 -94
  20. package/src/csp.ts +0 -226
  21. package/src/entry-server.ts +0 -224
  22. package/src/env.ts +0 -344
  23. package/src/error-overlay.ts +0 -118
  24. package/src/favicon.ts +0 -841
  25. package/src/font.ts +0 -511
  26. package/src/fs-router.ts +0 -1519
  27. package/src/i18n-routing.ts +0 -533
  28. package/src/icon.tsx +0 -182
  29. package/src/icons-plugin.ts +0 -296
  30. package/src/image-plugin.ts +0 -751
  31. package/src/image-types.ts +0 -60
  32. package/src/image.tsx +0 -340
  33. package/src/index.ts +0 -92
  34. package/src/isr.ts +0 -394
  35. package/src/link.tsx +0 -304
  36. package/src/logger.ts +0 -144
  37. package/src/manifest.ts +0 -787
  38. package/src/meta.tsx +0 -354
  39. package/src/middleware.ts +0 -65
  40. package/src/not-found.ts +0 -44
  41. package/src/og-image.ts +0 -378
  42. package/src/rate-limit.ts +0 -140
  43. package/src/script.tsx +0 -260
  44. package/src/seo.ts +0 -617
  45. package/src/server.ts +0 -89
  46. package/src/sharp.d.ts +0 -22
  47. package/src/ssg-plugin.ts +0 -1582
  48. package/src/testing.ts +0 -146
  49. package/src/theme.tsx +0 -257
  50. package/src/types.ts +0 -624
  51. package/src/utils/use-intersection-observer.ts +0 -36
  52. package/src/utils/with-headers.ts +0 -13
  53. package/src/vercel-revalidate-handler.ts +0 -204
  54. package/src/vite-plugin.ts +0 -848
package/src/script.tsx DELETED
@@ -1,260 +0,0 @@
1
- import type { Ref, VNodeChild } from '@pyreon/core'
2
- import { createRef, onMount, onUnmount } from '@pyreon/core'
3
- import { signal } from '@pyreon/reactivity'
4
- import { useIntersectionObserver } from './utils/use-intersection-observer'
5
-
6
- // ─── Script optimization component ─────────────────────────────────────────
7
- //
8
- // <Script> provides optimized third-party script loading:
9
- // - Defer loading until after hydration
10
- // - Load on idle (requestIdleCallback)
11
- // - Load on interaction (click, scroll, etc.)
12
- // - Load on viewport entry
13
- //
14
- // Three levels of API (mirrors @pyreon/zero/link and @pyreon/zero/image):
15
- //
16
- // 1. useScript(props) — composable returning load-state signals + sentinel ref
17
- // 2. createScript(Comp) — HOC wrapping any component with script load behavior
18
- // 3. Script — default sentinel-or-null component (built on createScript)
19
-
20
- export interface ScriptProps {
21
- /** Script source URL. */
22
- src: string
23
- /** Loading strategy. Default: "afterHydration" */
24
- strategy?: ScriptStrategy
25
- /** Inline script content (alternative to src). */
26
- children?: string
27
- /** Script id for deduplication. */
28
- id?: string
29
- /** Async attribute. Default: true */
30
- async?: boolean
31
- /** onLoad callback — fires when the `<script>` finishes loading. */
32
- onLoad?: () => void
33
- /** onError callback — fires when the `<script>` fails to load. */
34
- onError?: (error: Error) => void
35
- }
36
-
37
- export type ScriptStrategy =
38
- | 'beforeHydration'
39
- | 'afterHydration'
40
- | 'onIdle'
41
- | 'onInteraction'
42
- | 'onViewport'
43
-
44
- /** Return type of {@link useScript}. */
45
- export interface UseScriptReturn {
46
- /** Ref — attach to the sentinel element for `onViewport` strategy. Undefined for other strategies. */
47
- sentinelRef: Ref<HTMLElement> | undefined
48
- /** Whether the script has finished loading (onLoad fired). */
49
- loaded: () => boolean
50
- /** Whether the script load failed (onError fired). */
51
- errored: () => boolean
52
- /** Whether the script is in the strategy state machine awaiting a trigger (idle/interaction/viewport). */
53
- pending: () => boolean
54
- /** Whether the consumer needs to render a sentinel element (only true for `onViewport`). */
55
- needsSentinel: boolean
56
- /** Imperatively trigger the script load. Already invoked automatically by the strategy. */
57
- load: () => void
58
- }
59
-
60
- /** Props passed to a custom component via {@link createScript}. */
61
- export interface ScriptRenderProps {
62
- /** Ref — attach to whatever sentinel element you render (only matters for `onViewport`). */
63
- sentinelRef: Ref<HTMLElement> | undefined
64
- /** Whether the script is in viewport-wait mode (true → render a sentinel; false → render null). */
65
- needsSentinel: boolean
66
- /** Whether the script has finished loading (onLoad fired). */
67
- loaded: () => boolean
68
- /** Whether the script load failed (onError fired). */
69
- errored: () => boolean
70
- /** Whether the script is in the strategy state machine awaiting a trigger. */
71
- pending: () => boolean
72
- }
73
-
74
- /**
75
- * Composable that provides all script loading behavior — strategy state
76
- * machine (afterHydration / onIdle / onInteraction / onViewport),
77
- * deduplication, load/error tracking.
78
- *
79
- * Returns reactive signals (`loaded`, `errored`, `pending`) so consumers
80
- * can render loading indicators, retry buttons, or analytics-readiness
81
- * gates without re-implementing the strategy machine.
82
- *
83
- * @example
84
- * function MyScript(props: ScriptProps) {
85
- * const s = useScript(props)
86
- * return (
87
- * <>
88
- * {() => s.loaded() ? <Analytics /> : <Skeleton />}
89
- * {() => s.needsSentinel && <div ref={s.sentinelRef} style="width:0;height:0" />}
90
- * </>
91
- * )
92
- * }
93
- */
94
- export function useScript(props: ScriptProps): UseScriptReturn {
95
- const strategy = props.strategy ?? 'afterHydration'
96
- const loaded = signal(false)
97
- const errored = signal(false)
98
- const pending = signal(strategy !== 'beforeHydration' && strategy !== 'afterHydration')
99
- const sentinelRef = strategy === 'onViewport' ? createRef<HTMLElement>() : undefined
100
-
101
- function loadScript() {
102
- // Only invoked from `onMount` or strategy triggers — explicit guard
103
- // documents the SSR-safety contract at the callsite (the rule can't
104
- // AST-trace the indirect call).
105
- if (typeof document === 'undefined') return
106
- // Deduplication — short-circuit if a script with the same id exists.
107
- if (props.id && document.getElementById(props.id)) {
108
- loaded.set(true)
109
- pending.set(false)
110
- return
111
- }
112
-
113
- const script = document.createElement('script')
114
- if (props.src) script.src = props.src
115
- if (props.id) script.id = props.id
116
- script.async = props.async !== false
117
-
118
- script.onload = () => {
119
- loaded.set(true)
120
- pending.set(false)
121
- props.onLoad?.()
122
- }
123
- script.onerror = () => {
124
- errored.set(true)
125
- pending.set(false)
126
- props.onError?.(new Error(`Failed to load: ${props.src}`))
127
- }
128
-
129
- if (props.children && !props.src) {
130
- script.textContent = props.children
131
- // Inline scripts have no async load event — mark loaded synchronously
132
- // post-append so consumers can react. setTimeout 0 keeps the order
133
- // (DOM append → script body executes → next microtask → signals update).
134
- setTimeout(() => {
135
- loaded.set(true)
136
- pending.set(false)
137
- }, 0)
138
- }
139
-
140
- document.head.appendChild(script)
141
- }
142
-
143
- onMount(() => {
144
- switch (strategy) {
145
- case 'beforeHydration':
146
- // Already in HTML — do nothing.
147
- loaded.set(true)
148
- pending.set(false)
149
- break
150
-
151
- case 'afterHydration':
152
- // Load immediately after mount (hydration is complete).
153
- loadScript()
154
- break
155
-
156
- case 'onIdle':
157
- if ('requestIdleCallback' in window) {
158
- requestIdleCallback(() => loadScript(), { timeout: 5000 })
159
- } else {
160
- setTimeout(loadScript, 200)
161
- }
162
- break
163
-
164
- case 'onInteraction': {
165
- const events = ['click', 'scroll', 'keydown', 'touchstart']
166
- function handler() {
167
- for (const e of events) document.removeEventListener(e, handler)
168
- loadScript()
169
- }
170
- for (const e of events) {
171
- document.addEventListener(e, handler, { once: true, passive: true })
172
- }
173
- onUnmount(() => {
174
- for (const e of events) document.removeEventListener(e, handler)
175
- })
176
- break
177
- }
178
-
179
- case 'onViewport':
180
- // Handled below via useIntersectionObserver on the sentinel ref.
181
- break
182
- }
183
- return undefined
184
- })
185
-
186
- if (strategy === 'onViewport') {
187
- useIntersectionObserver(
188
- () => sentinelRef!.current ?? undefined,
189
- () => loadScript(),
190
- )
191
- }
192
-
193
- return {
194
- sentinelRef,
195
- loaded,
196
- errored,
197
- pending,
198
- needsSentinel: strategy === 'onViewport',
199
- load: loadScript,
200
- }
201
- }
202
-
203
- /**
204
- * Higher-order component that wraps any component with script load behavior.
205
- *
206
- * The wrapped component receives {@link ScriptRenderProps} with the sentinel
207
- * ref, load-state signals, and a `needsSentinel` flag. Use this when you want
208
- * to render a loading indicator, retry button, or custom analytics-readiness
209
- * gate around the script load.
210
- *
211
- * @example
212
- * // Script with a loading indicator
213
- * const TrackedScript = createScript((props) => (
214
- * <>
215
- * {() => props.pending() && <Spinner />}
216
- * {() => props.errored() && <button onClick={() => location.reload()}>Retry</button>}
217
- * {props.needsSentinel && <div ref={props.sentinelRef} style="width:0;height:0" />}
218
- * </>
219
- * ))
220
- *
221
- * <TrackedScript src="/analytics.js" strategy="onIdle" />
222
- */
223
- export function createScript(
224
- Component: (p: ScriptRenderProps) => any,
225
- ): (props: ScriptProps) => any {
226
- return function WrappedScript(props: ScriptProps) {
227
- const s = useScript(props)
228
- return (
229
- <Component
230
- sentinelRef={s.sentinelRef}
231
- needsSentinel={s.needsSentinel}
232
- loaded={s.loaded}
233
- errored={s.errored}
234
- pending={s.pending}
235
- />
236
- )
237
- }
238
- }
239
-
240
- /**
241
- * Default optimized script component. Renders a 0×0 sentinel `<div>` for the
242
- * `onViewport` strategy (so IntersectionObserver has an element to observe),
243
- * `null` for every other strategy.
244
- *
245
- * @example
246
- * // Load analytics after page is interactive
247
- * <Script src="https://analytics.example.com/script.js" strategy="onIdle" />
248
- *
249
- * // Load chat widget when user scrolls
250
- * <Script src="/chat-widget.js" strategy="onViewport" />
251
- *
252
- * // Inline script with deferred execution
253
- * <Script strategy="afterHydration">
254
- * {`console.log("App hydrated!")`}
255
- * </Script>
256
- */
257
- export const Script: (props: ScriptProps) => VNodeChild = createScript((props) => {
258
- if (!props.needsSentinel) return null
259
- return <div ref={props.sentinelRef} style="width:0;height:0;overflow:hidden" />
260
- })