@useavalon/avalon 0.1.47 → 0.1.48

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/dist/mod.d.ts +8 -9
  2. package/dist/mod.js +1 -1
  3. package/dist/src/client/components.d.ts +8 -16
  4. package/dist/src/client/components.js +1 -1
  5. package/dist/src/client/custom-directives.d.ts +25 -0
  6. package/dist/src/client/custom-directives.js +1 -0
  7. package/dist/src/client/main.js +3 -3
  8. package/dist/src/components/IslandErrorBoundary.d.ts +48 -9
  9. package/dist/src/components/IslandErrorBoundary.js +1 -1
  10. package/dist/src/components/LayoutErrorBoundary.d.ts +74 -9
  11. package/dist/src/components/LayoutErrorBoundary.js +1 -1
  12. package/dist/src/islands/builtin-directives.d.ts +15 -0
  13. package/dist/src/islands/builtin-directives.js +1 -0
  14. package/dist/src/islands/hydration-directives.d.ts +89 -0
  15. package/dist/src/islands/hydration-directives.js +1 -0
  16. package/dist/src/islands/island.d.ts +5 -3
  17. package/dist/src/islands/island.js +1 -1
  18. package/dist/src/islands/types.d.ts +4 -2
  19. package/dist/src/layout-system.d.ts +0 -13
  20. package/dist/src/layout-system.js +1 -1
  21. package/dist/src/nitro/config.d.ts +3 -3
  22. package/dist/src/nitro/renderer.d.ts +14 -1
  23. package/dist/src/nitro/renderer.js +6 -6
  24. package/dist/src/persistence/island-state-serializer.d.ts +19 -0
  25. package/dist/src/persistence/island-state-serializer.js +1 -0
  26. package/dist/src/persistence/use-persistent-state.d.ts +31 -0
  27. package/dist/src/persistence/use-persistent-state.js +1 -0
  28. package/dist/src/prerender/index.d.ts +1 -1
  29. package/dist/src/prerender/prerender.d.ts +1 -1
  30. package/dist/src/prerender/prerender.js +1 -1
  31. package/dist/src/schemas/layout.d.ts +1 -1
  32. package/dist/src/schemas/layout.js +1 -1
  33. package/dist/src/types/island-prop.d.ts +4 -2
  34. package/dist/src/types/layout.d.ts +0 -8
  35. package/dist/src/types/layout.js +1 -1
  36. package/dist/src/vite-plugin/nitro-integration.d.ts +3 -3
  37. package/dist/src/vite-plugin/nitro-integration.js +14 -14
  38. package/package.json +2 -2
  39. package/dist/src/components/LayoutDataErrorBoundary.d.ts +0 -34
  40. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  41. package/dist/src/components/PersistentIsland.d.ts +0 -36
  42. package/dist/src/components/PersistentIsland.js +0 -1
  43. package/dist/src/components/StreamingErrorBoundary.d.ts +0 -42
  44. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  45. package/dist/src/components/StreamingLayout.d.ts +0 -83
  46. package/dist/src/components/StreamingLayout.js +0 -29
  47. package/dist/src/core/islands/island-persistence.d.ts +0 -74
  48. package/dist/src/core/islands/island-persistence.js +0 -1
  49. package/dist/src/core/islands/island-state-serializer.d.ts +0 -53
  50. package/dist/src/core/islands/island-state-serializer.js +0 -1
  51. package/dist/src/core/islands/persistent-island-context.d.ts +0 -36
  52. package/dist/src/core/islands/persistent-island-context.js +0 -1
  53. package/dist/src/core/islands/use-persistent-state.d.ts +0 -17
  54. package/dist/src/core/islands/use-persistent-state.js +0 -1
@@ -1,36 +0,0 @@
1
- /** @jsxImportSource preact */
2
- import type { ComponentChildren } from 'preact';
3
- import { type IslandPersistence } from '../core/islands/island-persistence.ts';
4
- interface PersistentIslandProps {
5
- /** Unique ID used as the storage key for this island's state */
6
- persistentId: string;
7
- /** The island component(s) to wrap with persistence context */
8
- children: ComponentChildren;
9
- /** Custom persistence instance (defaults to sessionStorage-backed) */
10
- persistence?: IslandPersistence;
11
- }
12
- /**
13
- * PersistentIsland — provides automatic state persistence context to child islands.
14
- *
15
- * Wrap any island with this component and use `usePersistentIslandContext()` inside
16
- * the island to get `saveState`, `loadState`, and `clearState` functions.
17
- *
18
- * Usage in a page:
19
- * ```tsx
20
- * <PersistentIsland persistentId="my-counter" island={{ condition: 'on:client' }}>
21
- * <MyCounter />
22
- * </PersistentIsland>
23
- * ```
24
- *
25
- * Usage inside the island:
26
- * ```tsx
27
- * import { usePersistentIslandContext } from '@useavalon/avalon';
28
- *
29
- * function MyCounter() {
30
- * const { saveState, loadState, clearState } = usePersistentIslandContext();
31
- * // ...
32
- * }
33
- * ```
34
- */
35
- export declare function PersistentIsland({ persistentId, children, persistence, }: Readonly<PersistentIslandProps>): import("preact").JSX.Element;
36
- export default PersistentIsland;
@@ -1 +0,0 @@
1
- import{PersistentIslandProvider as e}from"../core/islands/persistent-island-context.js";import{defaultIslandPersistence as t}from"../core/islands/island-persistence.js";import{jsx as n}from"preact/jsx-runtime";export function PersistentIsland({persistentId:r,children:i,persistence:a=t}){return n(e,{persistentId:r,persistence:a,children:n(`div`,{"data-persistent-id":r,children:i})})}export default PersistentIsland;
@@ -1,42 +0,0 @@
1
- /**
2
- * StreamingErrorBoundary - Error boundary component for streaming contexts
3
- *
4
- * This component provides error isolation for Suspense boundaries in streaming SSR.
5
- * It ensures that errors in one component don't break the entire page.
6
- */
7
- import { Component, type ComponentChildren } from 'preact';
8
- export interface StreamingErrorBoundaryProps {
9
- children: ComponentChildren;
10
- fallback?: (error: Error, retry: () => void) => ComponentChildren;
11
- onError?: (error: Error, errorInfo: any) => void;
12
- componentId?: string;
13
- isolateError?: boolean;
14
- }
15
- export interface StreamingErrorBoundaryState {
16
- hasError: boolean;
17
- error: Error | null;
18
- errorInfo: any;
19
- }
20
- /**
21
- * Error boundary component for streaming contexts
22
- *
23
- * Wraps Suspense boundaries to provide error isolation and recovery.
24
- * Prevents errors in one component from breaking the entire page.
25
- */
26
- export declare class StreamingErrorBoundary extends Component<StreamingErrorBoundaryProps, StreamingErrorBoundaryState> {
27
- constructor(props: StreamingErrorBoundaryProps);
28
- static getDerivedStateFromError(error: Error): Partial<StreamingErrorBoundaryState>;
29
- componentDidCatch(error: Error, errorInfo: any): void;
30
- private handleRetry;
31
- private renderFallback;
32
- render(): ComponentChildren;
33
- }
34
- /**
35
- * Higher-order component to wrap components with streaming error boundaries
36
- */
37
- export declare function withStreamingErrorBoundary<P extends object>(WrappedComponent: (props: P) => ComponentChildren, options?: {
38
- fallback?: (error: Error, retry: () => void) => ComponentChildren;
39
- componentId?: string;
40
- isolateError?: boolean;
41
- onError?: (error: Error, errorInfo: any) => void;
42
- }): (props: P) => import("preact").JSX.Element;
@@ -1 +0,0 @@
1
- import{Component as e}from"preact";import{jsx as t,jsxs as n}from"preact/jsx-runtime";export class StreamingErrorBoundary extends e{constructor(e){super(e),this.state={hasError:!1,error:null,errorInfo:null}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){if(this.setState({errorInfo:t}),console.error(`[StreamingErrorBoundary] Caught error:`,{componentId:this.props.componentId,error:e.message,stack:e.stack,componentStack:t.componentStack}),this.props.onError&&this.props.onError(e,t),!this.props.isolateError)throw e}handleRetry=()=>{this.setState({hasError:!1,error:null,errorInfo:null})};renderFallback(){let{error:e}=this.state,{fallback:r,componentId:i}=this.props;if(r&&e)return r(e,this.handleRetry);let a=typeof process<`u`&&process.env?.NODE_ENV!==`production`;return n(`div`,{class:`streaming-error-boundary`,"data-error-boundary":`true`,"data-component-id":i,style:{background:`#fff3cd`,border:`2px solid #ffc107`,borderRadius:`8px`,padding:`20px`,margin:`20px 0`,fontFamily:`system-ui, -apple-system, sans-serif`},children:[n(`div`,{class:`error-boundary-header`,style:{display:`flex`,alignItems:`center`,gap:`10px`,marginBottom:`10px`},children:[t(`span`,{style:{fontSize:`24px`},children:`⚠️`}),t(`h3`,{style:{margin:0,color:`#856404`},children:`Component Error`})]}),t(`p`,{style:{margin:`10px 0`,color:`#856404`},children:`An error occurred while rendering this component. The rest of the page should work normally.`}),t(`button`,{onClick:this.handleRetry,style:{background:`#ffc107`,border:`none`,borderRadius:`4px`,padding:`8px 16px`,cursor:`pointer`,fontWeight:`bold`,color:`#856404`,marginTop:`10px`},children:`Retry`}),a&&e&&n(`details`,{style:{marginTop:`15px`},children:[t(`summary`,{style:{cursor:`pointer`,color:`#856404`,fontWeight:`bold`},children:`Error Details (Development Mode)`}),n(`div`,{style:{marginTop:`10px`},children:[i&&n(`p`,{children:[t(`strong`,{children:`Component ID:`}),` `,i]}),n(`p`,{children:[t(`strong`,{children:`Error:`}),` `,e.message]}),e.stack&&t(`pre`,{style:{background:`#f5f5f5`,padding:`10px`,borderRadius:`4px`,overflowX:`auto`,fontSize:`12px`,marginTop:`10px`},children:e.stack}),this.state.errorInfo?.componentStack&&n(`div`,{children:[t(`p`,{children:t(`strong`,{children:`Component Stack:`})}),t(`pre`,{style:{background:`#f5f5f5`,padding:`10px`,borderRadius:`4px`,overflowX:`auto`,fontSize:`12px`,marginTop:`10px`},children:this.state.errorInfo.componentStack})]})]})]})]})}render(){return this.state.hasError?this.renderFallback():this.props.children}}export function withStreamingErrorBoundary(e,n){return function(i){return t(StreamingErrorBoundary,{componentId:n?.componentId,fallback:n?.fallback,isolateError:n?.isolateError??!0,onError:n?.onError,children:t(e,{...i})})}}
@@ -1,83 +0,0 @@
1
- import { ComponentChildren, ComponentType } from 'preact';
2
- import type { StreamingLayoutProps } from '../types/layout.ts';
3
- /**
4
- * Streaming Layout Component Props
5
- */
6
- export interface StreamingLayoutComponentProps extends StreamingLayoutProps {
7
- /**
8
- * Component to render when ready
9
- */
10
- component: ComponentType<any>;
11
- /**
12
- * Props to pass to the component
13
- */
14
- componentProps?: any;
15
- /**
16
- * Function to check if component is ready
17
- */
18
- isReady?: () => Promise<boolean>;
19
- /**
20
- * Timeout for loading (ms)
21
- */
22
- timeout?: number;
23
- /**
24
- * Error boundary fallback
25
- */
26
- onError?: (error: Error) => ComponentChildren;
27
- /**
28
- * Loading state callback
29
- */
30
- onLoadingChange?: (isLoading: boolean) => void;
31
- }
32
- /**
33
- * Streaming Layout Component with Suspense-like behavior
34
- */
35
- export declare function StreamingLayout(props: StreamingLayoutComponentProps): ComponentChildren;
36
- /**
37
- * Suspense-like boundary for streaming components
38
- */
39
- export interface StreamingSuspenseProps {
40
- /**
41
- * Fallback to show while loading
42
- */
43
- fallback?: ComponentChildren;
44
- /**
45
- * Children components
46
- */
47
- children: ComponentChildren;
48
- /**
49
- * Priority for this suspense boundary
50
- */
51
- priority?: 'high' | 'medium' | 'low';
52
- /**
53
- * Timeout for all children (ms)
54
- */
55
- timeout?: number;
56
- /**
57
- * Error boundary for failed components
58
- */
59
- onError?: (error: Error) => ComponentChildren;
60
- }
61
- /**
62
- * Streaming Suspense Boundary Component
63
- */
64
- export declare function StreamingSuspense(props: StreamingSuspenseProps): ComponentChildren;
65
- /**
66
- * Higher-order component to add streaming capabilities
67
- */
68
- export declare function withStreaming<P extends object>(WrappedComponent: ComponentType<P>, streamingOptions?: {
69
- fallback?: ComponentChildren;
70
- priority?: 'high' | 'medium' | 'low';
71
- isReady?: () => Promise<boolean>;
72
- timeout?: number;
73
- }): (props: P) => import("preact").JSX.Element;
74
- /**
75
- * Hook for streaming component state
76
- */
77
- export declare function useStreamingState(isReady?: () => Promise<boolean>, timeout?: number): {
78
- retry: () => void;
79
- isReady: boolean;
80
- isLoading: boolean;
81
- error: Error | null;
82
- hasTimedOut: boolean;
83
- };
@@ -1,29 +0,0 @@
1
- import{Component as e}from"preact";import{useState as t,useEffect as n,useRef as r}from"preact/hooks";import{jsxs as i,jsx as a}from"preact/jsx-runtime";export function StreamingLayout(e){let{component:o,componentProps:c={},children:l,fallback:u,priority:d=`medium`,isReady:f,timeout:p=5e3,onError:m,onLoadingChange:h}=e;if(typeof window>`u`)return l;let[g,_]=t({isReady:!1,isLoading:!0,error:null,hasTimedOut:!1}),v=r(),y=r(!0);return n(()=>()=>{y.current=!1},[]),n(()=>{if(!f){_(e=>({...e,isReady:!0,isLoading:!1})),h?.(!1);return}let e=!1;return(async()=>{try{p>0&&(v.current=setTimeout(()=>{!e&&y.current&&(_(e=>({...e,hasTimedOut:!0,isLoading:!1})),h?.(!1))},p));let t=await f();if(e||!y.current)return;v.current&&clearTimeout(v.current),_(e=>({...e,isReady:t,isLoading:!1,hasTimedOut:!1})),h?.(!1)}catch(t){if(e||!y.current)return;v.current&&clearTimeout(v.current),_(e=>({...e,error:t,isLoading:!1})),h?.(!1)}})(),()=>{e=!0,v.current&&clearTimeout(v.current)}},[f,p,h]),g.error?m?m(g.error):i(`div`,{class:`streaming-error`,"data-priority":d,children:[i(`p`,{children:[`Failed to load component: `,g.error.message]}),a(`button`,{onClick:()=>{_({isReady:!1,isLoading:!0,error:null,hasTimedOut:!1}),h?.(!0)},children:`Retry`})]}):g.hasTimedOut?a(`div`,{class:`streaming-timeout`,"data-priority":d,children:u||i(`div`,{class:`timeout-message`,children:[a(`p`,{children:`Component loading timed out`}),a(`button`,{onClick:()=>{_({isReady:!1,isLoading:!0,error:null,hasTimedOut:!1}),h?.(!0)},children:`Retry`})]})}):g.isLoading||!g.isReady?a(`div`,{class:`streaming-loading`,"data-priority":d,children:u||a(s,{priority:d})}):a(`div`,{class:`streaming-ready`,"data-priority":d,children:a(o,{...c,children:l})})}function s({priority:e}){return i(`div`,{class:`streaming-skeleton ${`skeleton-${e}`}`,children:[i(`div`,{class:`skeleton-content`,children:[a(`div`,{class:`skeleton-line skeleton-line-1`}),a(`div`,{class:`skeleton-line skeleton-line-2`}),a(`div`,{class:`skeleton-line skeleton-line-3`})]}),a(`style`,{children:`
2
- .streaming-skeleton {
3
- padding: 1rem;
4
- border-radius: 0.5rem;
5
- background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
6
- background-size: 200% 100%;
7
- animation: skeleton-loading 1.5s infinite;
8
- }
9
- .skeleton-content {
10
- display: flex;
11
- flex-direction: column;
12
- gap: 0.5rem;
13
- }
14
- .skeleton-line {
15
- height: 1rem;
16
- background: rgba(255, 255, 255, 0.8);
17
- border-radius: 0.25rem;
18
- }
19
- .skeleton-line-1 { width: 100%; }
20
- .skeleton-line-2 { width: 75%; }
21
- .skeleton-line-3 { width: 50%; }
22
- .skeleton-high { border-left: 4px solid #ef4444; }
23
- .skeleton-medium { border-left: 4px solid #f59e0b; }
24
- .skeleton-low { border-left: 4px solid #10b981; }
25
- @keyframes skeleton-loading {
26
- 0% { background-position: 200% 0; }
27
- 100% { background-position: -200% 0; }
28
- }
29
- `})]})}export function StreamingSuspense(e){let{fallback:o,children:c,priority:u=`medium`,timeout:d=5e3,onError:f}=e,[p,m]=t(!0),[h,g]=t(null),[_,v]=t(!1),y=r(),b=r(!0);n(()=>()=>{b.current=!1},[]),n(()=>(d>0&&(y.current=setTimeout(()=>{b.current&&p&&(v(!0),m(!1))},d)),()=>{y.current&&clearTimeout(y.current)}),[d,p]);let x=e=>{g(e),m(!1),y.current&&clearTimeout(y.current)};return h?f?f(h):i(`div`,{class:`streaming-suspense-error`,"data-priority":u,children:[i(`p`,{children:[`Suspense boundary error: `,h.message]}),a(`button`,{onClick:()=>{g(null),m(!0),v(!1)},children:`Retry`})]}):_?a(`div`,{class:`streaming-suspense-timeout`,"data-priority":u,children:o||i(`div`,{class:`suspense-timeout-message`,children:[a(`p`,{children:`Suspense boundary timed out`}),a(`button`,{onClick:()=>{v(!1),m(!0)},children:`Retry`})]})}):p?a(`div`,{class:`streaming-suspense-loading`,"data-priority":u,children:o||a(s,{priority:u})}):a(`div`,{class:`streaming-suspense-ready`,"data-priority":u,children:a(l,{onError:x,children:c})})}class l extends e{constructor(e){super(e),this.state={hasError:!1}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){console.error(`StreamingErrorBoundary caught an error:`,e,t),this.props.onError?.(e)}render(){return this.state.hasError?i(`div`,{class:`streaming-error-boundary`,children:[a(`p`,{children:`Something went wrong in streaming component.`}),a(`button`,{onClick:()=>this.setState({hasError:!1,error:void 0}),children:`Retry`})]}):this.props.children}}export function withStreaming(e,t={}){return function(n){return a(StreamingLayout,{component:e,componentProps:n,priority:`medium`,...t,children:null})}}export function useStreamingState(e,r=5e3){let[i,a]=t({isReady:!1,isLoading:!0,error:null,hasTimedOut:!1});n(()=>{if(!e){a(e=>({...e,isReady:!0,isLoading:!1}));return}let t=!1,n;return(async()=>{try{r>0&&(n=setTimeout(()=>{t||a(e=>({...e,hasTimedOut:!0,isLoading:!1}))},r));let i=await e();if(t)return;n&&clearTimeout(n),a(e=>({...e,isReady:i,isLoading:!1,hasTimedOut:!1}))}catch(e){if(t)return;n&&clearTimeout(n),a(t=>({...t,error:e,isLoading:!1}))}})(),()=>{t=!0,n&&clearTimeout(n)}},[e,r]);let o=()=>{a({isReady:!1,isLoading:!0,error:null,hasTimedOut:!1})};return{...i,retry:o}}
@@ -1,74 +0,0 @@
1
- import type { IslandState } from '../../schemas/layout.ts';
2
- import type { IIslandPersistence } from '../../types/layout.ts';
3
- /**
4
- * IslandPersistence class for state management across navigation
5
- *
6
- * Handles saving, loading, and clearing island state using browser storage
7
- * with support for both sessionStorage and localStorage persistence strategies.
8
- */
9
- export declare class IslandPersistence implements IIslandPersistence {
10
- private storageType;
11
- private keyPrefix;
12
- private storage;
13
- constructor(options?: {
14
- storageType?: 'session' | 'local';
15
- keyPrefix?: string;
16
- });
17
- /**
18
- * Save island state to browser storage
19
- */
20
- saveState(id: string, state: IslandState): void;
21
- /**
22
- * Load island state from browser storage
23
- */
24
- loadState(id: string): IslandState | null;
25
- /**
26
- * Clear island state from browser storage
27
- */
28
- clearState(id: string): void;
29
- /**
30
- * Check if state exists for an island
31
- */
32
- hasState(id: string): boolean;
33
- /**
34
- * Get all stored island IDs
35
- */
36
- getStoredIds(): string[];
37
- /**
38
- * Clear all stored states
39
- */
40
- clearAllStates(): void;
41
- /**
42
- * Get the storage key for an island ID
43
- */
44
- private getStorageKey;
45
- /**
46
- * Get current storage configuration
47
- */
48
- getConfig(): {
49
- storageType: string;
50
- keyPrefix: string;
51
- available: boolean;
52
- };
53
- /**
54
- * Get storage usage statistics
55
- */
56
- getStorageStats(): {
57
- totalKeys: number;
58
- islandKeys: number;
59
- estimatedSize: number;
60
- };
61
- /**
62
- * JSON.stringify replacer function to handle special types
63
- */
64
- private replacer;
65
- /**
66
- * JSON.parse reviver function to restore special types
67
- */
68
- private reviver;
69
- }
70
- /**
71
- * Default island persistence instance
72
- * Uses sessionStorage by default for better privacy and performance
73
- */
74
- export declare const defaultIslandPersistence: IslandPersistence;
@@ -1 +0,0 @@
1
- import{IslandStateSerializer as e}from"./island-state-serializer.js";export class IslandPersistence{storageType;keyPrefix;storage=null;constructor(e={}){this.storageType=e.storageType||`session`,this.keyPrefix=e.keyPrefix||`island-state`,typeof window<`u`&&(this.storage=this.storageType===`session`?sessionStorage:localStorage)}saveState(t,n){if(!this.storage){console.warn(`Island persistence: Storage not available (server-side or unsupported browser)`);return}try{let r=this.getStorageKey(t),i=e.serialize({state:n,timestamp:Date.now(),version:`1.0`});this.storage.setItem(r,i),console.log(`Island state saved for ${t}`)}catch(e){console.error(`Failed to save island state for ${t}:`,e)}}loadState(t){if(!this.storage)return console.warn(`Island persistence: Storage not available (server-side or unsupported browser)`),null;try{let n=this.getStorageKey(t),r=this.storage.getItem(n);if(!r)return null;let i=e.deserialize(r);return!i.state||!i.timestamp||!i.version?(console.warn(`Invalid island state format for ${t}, clearing...`),this.clearState(t),null):(console.log(`Island state loaded for ${t}`),i.state)}catch(e){return console.error(`Failed to load island state for ${t}:`,e),this.clearState(t),null}}clearState(e){if(this.storage)try{let t=this.getStorageKey(e);this.storage.removeItem(t),console.log(`Island state cleared for ${e}`)}catch(t){console.error(`Failed to clear island state for ${e}:`,t)}}hasState(e){if(!this.storage)return!1;try{let t=this.getStorageKey(e);return this.storage.getItem(t)!==null}catch(t){return console.error(`Failed to check island state for ${e}:`,t),!1}}getStoredIds(){if(!this.storage)return[];try{let e=[],t=this.keyPrefix.length+1;for(let n=0;n<this.storage.length;n++){let r=this.storage.key(n);r&&r.startsWith(this.keyPrefix+`:`)&&e.push(r.substring(t))}return e}catch(e){return console.error(`Failed to get stored island IDs:`,e),[]}}clearAllStates(){if(this.storage)try{let e=[];for(let t=0;t<this.storage.length;t++){let n=this.storage.key(t);n&&n.startsWith(this.keyPrefix+`:`)&&e.push(n)}e.forEach(e=>this.storage.removeItem(e)),console.log(`Cleared ${e.length} island states`)}catch(e){console.error(`Failed to clear all island states:`,e)}}getStorageKey(e){return`${this.keyPrefix}:${e}`}getConfig(){return{storageType:this.storageType,keyPrefix:this.keyPrefix,available:this.storage!==null}}getStorageStats(){if(!this.storage)return{totalKeys:0,islandKeys:0,estimatedSize:0};try{let e=0,t=0;for(let n=0;n<this.storage.length;n++){let r=this.storage.key(n);if(r&&r.startsWith(this.keyPrefix+`:`)){e++;let n=this.storage.getItem(r);n&&(t+=r.length+n.length)}}return{totalKeys:this.storage.length,islandKeys:e,estimatedSize:t}}catch(e){return console.error(`Failed to get storage stats:`,e),{totalKeys:0,islandKeys:0,estimatedSize:0}}}replacer(e,t){return t instanceof Date?{__type:`Date`,__value:t.toISOString()}:t instanceof RegExp?{__type:`RegExp`,__value:{source:t.source,flags:t.flags}}:t instanceof Map?{__type:`Map`,__value:Array.from(t.entries())}:t instanceof Set?{__type:`Set`,__value:Array.from(t.values())}:typeof t==`function`?(console.warn(`Function found in island state at key "${e}", converting to null`),null):t===void 0?null:t}reviver(e,t){if(t&&typeof t==`object`){let e=t;if(e.__type&&e.__value!==void 0)switch(e.__type){case`Date`:return new Date(e.__value);case`RegExp`:{let t=e.__value;return new RegExp(t.source,t.flags)}case`Map`:return new Map(e.__value);case`Set`:return new Set(e.__value);default:return console.warn(`Unknown special type "${e.__type}" in serialized state`),e.__value}}return t}}export const defaultIslandPersistence=new IslandPersistence({storageType:`session`,keyPrefix:`island-state`});
@@ -1,53 +0,0 @@
1
- import type { IslandState } from '../../schemas/layout.ts';
2
- /**
3
- * Island State Serializer
4
- *
5
- * Handles serialization and deserialization of island state for browser storage.
6
- * Supports complex data types including Dates, RegExp, and circular references.
7
- */
8
- export declare class IslandStateSerializer {
9
- /**
10
- * Serialize island state to JSON string
11
- */
12
- static serialize(state: IslandState): string;
13
- /**
14
- * Deserialize JSON string to island state
15
- */
16
- static deserialize(serializedState: string): IslandState;
17
- /**
18
- * Transform state for serialization, handling special types
19
- */
20
- private static transformForSerialization;
21
- /**
22
- * JSON.parse reviver function to restore special types
23
- */
24
- private static reviver;
25
- /**
26
- * Validate that state can be safely serialized
27
- */
28
- static validate(state: IslandState): {
29
- valid: boolean;
30
- errors: string[];
31
- };
32
- /**
33
- * Get size information for serialized state
34
- */
35
- static getSize(state: IslandState): {
36
- bytes: number;
37
- kilobytes: number;
38
- megabytes: number;
39
- readable: string;
40
- };
41
- /**
42
- * Deep clone state object (useful for preventing mutations)
43
- */
44
- static clone(state: IslandState): IslandState;
45
- /**
46
- * Compare two states for equality
47
- */
48
- static equals(state1: IslandState, state2: IslandState): boolean;
49
- /**
50
- * Sanitize state by removing non-serializable values
51
- */
52
- static sanitize(state: IslandState): IslandState;
53
- }
@@ -1 +0,0 @@
1
- export class IslandStateSerializer{static serialize(t){try{let n=IslandStateSerializer.transformForSerialization(t);return JSON.stringify(n)}catch(e){throw console.error(`Failed to serialize island state:`,e),Error(`State serialization failed: ${e instanceof Error?e.message:String(e)}`)}}static deserialize(t){try{return JSON.parse(t,IslandStateSerializer.reviver)}catch(e){throw console.error(`Failed to deserialize island state:`,e),Error(`State deserialization failed: ${e instanceof Error?e.message:String(e)}`)}}static transformForSerialization(t){if(t===null)return t;if(t===void 0)return null;if(t instanceof Date)return{__type:`Date`,__value:t.toISOString()};if(t instanceof RegExp)return{__type:`RegExp`,__value:{source:t.source,flags:t.flags}};if(t instanceof Map)return{__type:`Map`,__value:Array.from(t.entries()).map(([t,n])=>[IslandStateSerializer.transformForSerialization(t),IslandStateSerializer.transformForSerialization(n)])};if(t instanceof Set)return{__type:`Set`,__value:Array.from(t.values()).map(t=>IslandStateSerializer.transformForSerialization(t))};if(typeof t==`function`)return console.warn(`Function found in island state, converting to null`),null;if(Array.isArray(t))return t.map(t=>IslandStateSerializer.transformForSerialization(t));if(typeof t==`object`){let n={},r=t;for(let t in r)Object.hasOwn(r,t)&&(n[t]=IslandStateSerializer.transformForSerialization(r[t]));return n}return t}static reviver(e,t){if(t&&typeof t==`object`){let e=t;if(e.__type&&e.__value!==void 0)switch(e.__type){case`Date`:return new Date(e.__value);case`RegExp`:{let t=e.__value;return new RegExp(t.source,t.flags)}case`Map`:return new Map(e.__value);case`Set`:return new Set(e.__value);case`undefined`:return null;default:return console.warn(`Unknown special type "${typeof e.__type==`string`?e.__type:`non-string`}" in serialized state`),e.__value}}return t}static validate(e){let t=[];try{let n=this.serialize(e),r=new Blob([n]).size,i=5*1024*1024;r>i&&t.push(`Serialized state size (${Math.round(r/1024)}KB) exceeds recommended limit (${Math.round(i/1024)}KB)`),this.deserialize(n)}catch(e){t.push(e instanceof Error?e.message:String(e))}return{valid:t.length===0,errors:t}}static getSize(e){try{let t=this.serialize(e),n=new Blob([t]).size,r=n/1024,i=r/1024,a;return a=i>=1?`${i.toFixed(2)} MB`:r>=1?`${r.toFixed(2)} KB`:`${n} bytes`,{bytes:n,kilobytes:r,megabytes:i,readable:a}}catch(e){return console.error(`Failed to calculate state size:`,e),{bytes:0,kilobytes:0,megabytes:0,readable:`0 bytes`}}}static clone(e){try{let t=this.serialize(e);return this.deserialize(t)}catch(t){return console.error(`Failed to clone island state:`,t),{...e}}}static equals(e,t){try{return this.serialize(e)===this.serialize(t)}catch(e){return console.error(`Failed to compare island states:`,e),!1}}static sanitize(e){try{let t=this.serialize(e);return this.deserialize(t)}catch(e){return console.error(`Failed to sanitize island state:`,e),{}}}}
@@ -1,36 +0,0 @@
1
- import type { ComponentChildren } from 'preact';
2
- import type { IslandState } from '../../schemas/layout.ts';
3
- import { defaultIslandPersistence } from './island-persistence.ts';
4
- /**
5
- * Explicit interface for persistent island context
6
- * (Zod z.any() inference for function types is unreliable)
7
- */
8
- export interface PersistentIslandContextType {
9
- saveState: (state: IslandState) => void;
10
- loadState: () => IslandState | null;
11
- clearState: () => void;
12
- }
13
- /**
14
- * Context for persistent island state operations
15
- *
16
- * Provides save, load, and clear operations for island state management
17
- * across navigation and browser sessions.
18
- */
19
- export declare const PersistentIslandContextProvider: import("preact").Context<PersistentIslandContextType>;
20
- /**
21
- * Create a persistent island context for a specific island ID
22
- */
23
- export declare function createPersistentIslandContext(persistentId: string, persistence?: import("./island-persistence.ts").IslandPersistence): PersistentIslandContextType;
24
- /**
25
- * Hook to use persistent island context
26
- * Must be used within a PersistentIsland component
27
- */
28
- export declare function usePersistentIslandContext(): PersistentIslandContextType;
29
- /**
30
- * Provider component for persistent island context
31
- */
32
- export declare function PersistentIslandProvider({ persistentId, children, persistence, }: Readonly<{
33
- persistentId: string;
34
- children: ComponentChildren;
35
- persistence?: typeof defaultIslandPersistence;
36
- }>): import("preact").JSX.Element;
@@ -1 +0,0 @@
1
- import{createContext as e}from"preact";import{useContext as t}from"preact/hooks";import{defaultIslandPersistence as n}from"./island-persistence.js";import{jsx as r}from"preact/jsx-runtime";export const PersistentIslandContextProvider=e(null);export function createPersistentIslandContext(e,t=n){return{saveState:n=>{t.saveState(e,n)},loadState:()=>t.loadState(e),clearState:()=>{t.clearState(e)}}}export function usePersistentIslandContext(){let e=t(PersistentIslandContextProvider);if(!e)throw Error(`usePersistentIslandContext must be used within a PersistentIsland component`);return e}export function PersistentIslandProvider({persistentId:e,children:t,persistence:i=n}){let a=createPersistentIslandContext(e,i);return r(PersistentIslandContextProvider.Provider,{value:a,children:t})}
@@ -1,17 +0,0 @@
1
- /**
2
- * Hook for persistent island state — like useState but survives navigations.
3
- *
4
- * State is serialized to sessionStorage (supports Date, Map, Set, RegExp).
5
- * On the server or when storage is unavailable, falls back to in-memory state.
6
- *
7
- * ```tsx
8
- * const [count, setCount, clearCount] = usePersistentState('my-counter', 0);
9
- * ```
10
- *
11
- * @param id - Unique storage key for this piece of state
12
- * @param initialValue - Default value when no persisted state exists
13
- * @param options - Optional: `storage` ('session' | 'local'), defaults to 'session'
14
- */
15
- export declare function usePersistentState<T>(id: string, initialValue: T, options?: {
16
- storage?: 'session' | 'local';
17
- }): [T, (value: T | ((prev: T) => T)) => void, () => void];
@@ -1 +0,0 @@
1
- import{useState as e,useEffect as t,useCallback as n}from"preact/hooks";import{IslandStateSerializer as r}from"./island-state-serializer.js";export function usePersistentState(i,a,o){let s=o?.storage??`session`,c=`avalon-island:${i}`,[l,u]=e(()=>{if(typeof window>`u`)return a;try{let e=(s===`local`?localStorage:sessionStorage).getItem(c);return e===null?a:r.deserialize(e).v??a}catch{return a}});return t(()=>{if(!(typeof window>`u`))try{(s===`local`?localStorage:sessionStorage).setItem(c,r.serialize({v:l}))}catch{}},[l,c,s]),[l,n(e=>{u(t=>typeof e==`function`?e(t):e)},[]),n(()=>{if(u(a),!(typeof window>`u`))try{(s===`local`?localStorage:sessionStorage).removeItem(c)}catch{}},[a,c,s])]}