@useavalon/avalon 0.1.46 → 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 (73) hide show
  1. package/README.md +54 -54
  2. package/dist/mod.d.ts +8 -9
  3. package/dist/mod.js +1 -1
  4. package/dist/src/client/adapters/lit-adapter.js +1 -0
  5. package/dist/src/client/adapters/preact-adapter.js +1 -0
  6. package/dist/src/client/adapters/qwik-adapter.js +1 -0
  7. package/dist/src/client/adapters/react-adapter.js +1 -0
  8. package/dist/src/client/adapters/solid-adapter.js +1 -0
  9. package/dist/src/client/adapters/svelte-adapter.js +1 -0
  10. package/dist/src/client/adapters/vue-adapter.js +1 -0
  11. package/dist/src/client/components.d.ts +8 -16
  12. package/dist/src/client/components.js +1 -1
  13. package/dist/src/client/custom-directives.d.ts +25 -0
  14. package/dist/src/client/custom-directives.js +1 -0
  15. package/dist/src/client/main.js +3 -3
  16. package/dist/src/client/types/framework-runtime.d.ts +68 -68
  17. package/dist/src/client/types/vite-hmr.d.ts +46 -46
  18. package/dist/src/client/types/vite-virtual-modules.d.ts +70 -70
  19. package/dist/src/components/IslandErrorBoundary.d.ts +48 -9
  20. package/dist/src/components/IslandErrorBoundary.js +1 -1
  21. package/dist/src/components/LayoutErrorBoundary.d.ts +74 -9
  22. package/dist/src/components/LayoutErrorBoundary.js +1 -1
  23. package/dist/src/islands/builtin-directives.d.ts +15 -0
  24. package/dist/src/islands/builtin-directives.js +1 -0
  25. package/dist/src/islands/hydration-directives.d.ts +89 -0
  26. package/dist/src/islands/hydration-directives.js +1 -0
  27. package/dist/src/islands/island.d.ts +5 -3
  28. package/dist/src/islands/island.js +1 -1
  29. package/dist/src/islands/types.d.ts +4 -2
  30. package/dist/src/layout-system.d.ts +0 -13
  31. package/dist/src/layout-system.js +1 -1
  32. package/dist/src/nitro/config.d.ts +41 -2
  33. package/dist/src/nitro/config.js +1 -1
  34. package/dist/src/nitro/renderer.d.ts +14 -1
  35. package/dist/src/nitro/renderer.js +6 -6
  36. package/dist/src/persistence/island-state-serializer.d.ts +19 -0
  37. package/dist/src/persistence/island-state-serializer.js +1 -0
  38. package/dist/src/persistence/use-persistent-state.d.ts +31 -0
  39. package/dist/src/persistence/use-persistent-state.js +1 -0
  40. package/dist/src/prerender/index.d.ts +53 -0
  41. package/dist/src/prerender/index.js +1 -0
  42. package/dist/src/prerender/prerender.d.ts +20 -0
  43. package/dist/src/prerender/prerender.js +1 -0
  44. package/dist/src/schemas/layout.d.ts +1 -1
  45. package/dist/src/schemas/layout.js +1 -1
  46. package/dist/src/types/image.d.ts +106 -106
  47. package/dist/src/types/index.d.ts +22 -22
  48. package/dist/src/types/island-jsx.d.ts +33 -33
  49. package/dist/src/types/island-prop.d.ts +22 -20
  50. package/dist/src/types/layout.d.ts +0 -8
  51. package/dist/src/types/layout.js +1 -1
  52. package/dist/src/types/mdx.d.ts +6 -6
  53. package/dist/src/types/urlpattern.d.ts +49 -49
  54. package/dist/src/types/vite-env.d.ts +11 -11
  55. package/dist/src/vite-plugin/nitro-integration.d.ts +3 -3
  56. package/dist/src/vite-plugin/nitro-integration.js +14 -14
  57. package/package.json +7 -3
  58. package/dist/src/components/LayoutDataErrorBoundary.d.ts +0 -34
  59. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  60. package/dist/src/components/PersistentIsland.d.ts +0 -36
  61. package/dist/src/components/PersistentIsland.js +0 -1
  62. package/dist/src/components/StreamingErrorBoundary.d.ts +0 -42
  63. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  64. package/dist/src/components/StreamingLayout.d.ts +0 -83
  65. package/dist/src/components/StreamingLayout.js +0 -29
  66. package/dist/src/core/islands/island-persistence.d.ts +0 -74
  67. package/dist/src/core/islands/island-persistence.js +0 -1
  68. package/dist/src/core/islands/island-state-serializer.d.ts +0 -53
  69. package/dist/src/core/islands/island-state-serializer.js +0 -1
  70. package/dist/src/core/islands/persistent-island-context.d.ts +0 -36
  71. package/dist/src/core/islands/persistent-island-context.js +0 -1
  72. package/dist/src/core/islands/use-persistent-state.d.ts +0 -17
  73. package/dist/src/core/islands/use-persistent-state.js +0 -1
@@ -1,70 +1,70 @@
1
- /**
2
- * Type declarations for Vite virtual modules
3
- *
4
- * These modules are resolved by Vite at runtime in the browser.
5
- * They don't exist as actual files but are provided by Vite's plugin system.
6
- */
7
-
8
- declare module '/@useavalon/preact/client' {
9
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
10
- export function getHydrationScript(): string;
11
- }
12
-
13
- declare module '/@useavalon/react/client' {
14
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
15
- export function getHydrationScript(): string;
16
- }
17
-
18
- declare module '/@useavalon/vue/client' {
19
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
20
- export function getHydrationScript(): string;
21
- }
22
-
23
- declare module '/@useavalon/svelte/client' {
24
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
25
- export function getHydrationScript(): string;
26
- }
27
-
28
- declare module '/@useavalon/solid/client' {
29
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
30
- export function getHydrationScript(): string;
31
- }
32
-
33
- declare module '/@useavalon/lit/client' {
34
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
35
- export function getHydrationScript(): string;
36
- }
37
-
38
- declare module '/@useavalon/qwik/client' {
39
- export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
40
- export function getHydrationScript(): string;
41
- }
42
-
43
- // HMR adapter virtual modules
44
- declare module '/@useavalon/react/client/hmr' {
45
- export { reactAdapter } from '@useavalon/react/client/hmr';
46
- }
47
-
48
- declare module '/@useavalon/preact/client/hmr' {
49
- export { preactAdapter } from '@useavalon/preact/client/hmr';
50
- }
51
-
52
- declare module '/@useavalon/vue/client/hmr' {
53
- export { vueAdapter } from '@useavalon/vue/client/hmr';
54
- }
55
-
56
- declare module '/@useavalon/svelte/client/hmr' {
57
- export { svelteAdapter } from '@useavalon/svelte/client/hmr';
58
- }
59
-
60
- declare module '/@useavalon/solid/client/hmr' {
61
- export { solidAdapter } from '@useavalon/solid/client/hmr';
62
- }
63
-
64
- declare module '/@useavalon/lit/client/hmr' {
65
- export { litAdapter } from '@useavalon/lit/client/hmr';
66
- }
67
-
68
- declare module '/@useavalon/qwik/client/hmr' {
69
- export { qwikAdapter } from '@useavalon/qwik/client/hmr';
70
- }
1
+ /**
2
+ * Type declarations for Vite virtual modules
3
+ *
4
+ * These modules are resolved by Vite at runtime in the browser.
5
+ * They don't exist as actual files but are provided by Vite's plugin system.
6
+ */
7
+
8
+ declare module '/@useavalon/preact/client' {
9
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
10
+ export function getHydrationScript(): string;
11
+ }
12
+
13
+ declare module '/@useavalon/react/client' {
14
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
15
+ export function getHydrationScript(): string;
16
+ }
17
+
18
+ declare module '/@useavalon/vue/client' {
19
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
20
+ export function getHydrationScript(): string;
21
+ }
22
+
23
+ declare module '/@useavalon/svelte/client' {
24
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
25
+ export function getHydrationScript(): string;
26
+ }
27
+
28
+ declare module '/@useavalon/solid/client' {
29
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
30
+ export function getHydrationScript(): string;
31
+ }
32
+
33
+ declare module '/@useavalon/lit/client' {
34
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
35
+ export function getHydrationScript(): string;
36
+ }
37
+
38
+ declare module '/@useavalon/qwik/client' {
39
+ export function hydrate(container: Element, component: unknown, props?: Record<string, unknown>): void;
40
+ export function getHydrationScript(): string;
41
+ }
42
+
43
+ // HMR adapter virtual modules
44
+ declare module '/@useavalon/react/client/hmr' {
45
+ export { reactAdapter } from '@useavalon/react/client/hmr';
46
+ }
47
+
48
+ declare module '/@useavalon/preact/client/hmr' {
49
+ export { preactAdapter } from '@useavalon/preact/client/hmr';
50
+ }
51
+
52
+ declare module '/@useavalon/vue/client/hmr' {
53
+ export { vueAdapter } from '@useavalon/vue/client/hmr';
54
+ }
55
+
56
+ declare module '/@useavalon/svelte/client/hmr' {
57
+ export { svelteAdapter } from '@useavalon/svelte/client/hmr';
58
+ }
59
+
60
+ declare module '/@useavalon/solid/client/hmr' {
61
+ export { solidAdapter } from '@useavalon/solid/client/hmr';
62
+ }
63
+
64
+ declare module '/@useavalon/lit/client/hmr' {
65
+ export { litAdapter } from '@useavalon/lit/client/hmr';
66
+ }
67
+
68
+ declare module '/@useavalon/qwik/client/hmr' {
69
+ export { qwikAdapter } from '@useavalon/qwik/client/hmr';
70
+ }
@@ -1,20 +1,51 @@
1
- import { Component, ComponentChildren, ComponentType } from 'preact';
2
- import type { LayoutErrorInfo } from '../types/layout.ts';
1
+ /** @jsxImportSource preact */
2
+ import { Component, type ComponentChildren, type ComponentType } from 'preact';
3
+ import type { LayoutErrorInfo } from '../schemas/layout.ts';
4
+ /**
5
+ * Props for {@link IslandErrorBoundary}.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <IslandErrorBoundary
10
+ * islandId="counter"
11
+ * isolateError
12
+ * onError={(err, info) => console.error(info.errorBoundary, err)}
13
+ * fallback={(err, id) => <p>Island "{id}" failed: {err.message}</p>}
14
+ * >
15
+ * <Counter />
16
+ * </IslandErrorBoundary>
17
+ * ```
18
+ */
3
19
  export interface IslandErrorBoundaryProps {
20
+ /** The content to render inside the error boundary. */
4
21
  children: ComponentChildren;
22
+ /** Unique identifier for the island — used in error reports and DOM attributes. */
5
23
  islandId: string;
24
+ /** Called when the island throws. Receives the error and structured error info. */
6
25
  onError?: (error: Error, errorInfo: LayoutErrorInfo) => void;
26
+ /** Custom fallback UI. When omitted a default error card is shown. */
7
27
  fallback?: (error: Error, islandId: string) => ComponentChildren;
28
+ /** When `true` (default for HOC), the error is caught and isolated. When `false`, it re-throws to parent boundaries. */
8
29
  isolateError?: boolean;
9
30
  }
10
- export interface IslandErrorBoundaryState {
31
+ interface IslandErrorBoundaryState {
11
32
  hasError: boolean;
12
33
  error: Error | null;
13
34
  errorInfo: LayoutErrorInfo | null;
14
35
  }
15
36
  /**
16
- * Specialized error boundary for island components
17
- * Provides error isolation to prevent island errors from affecting the main layout
37
+ * Error boundary that wraps individual islands to prevent one broken island
38
+ * from taking down the entire page.
39
+ *
40
+ * Renders a default error card with "Reload" / "Remove" actions, or your
41
+ * custom `fallback` if provided. In development mode, a stack trace is shown.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * <IslandErrorBoundary islandId="cart" isolateError>
46
+ * <CartIsland />
47
+ * </IslandErrorBoundary>
48
+ * ```
18
49
  */
19
50
  export declare class IslandErrorBoundary extends Component<IslandErrorBoundaryProps, IslandErrorBoundaryState> {
20
51
  constructor(props: IslandErrorBoundaryProps);
@@ -22,16 +53,24 @@ export declare class IslandErrorBoundary extends Component<IslandErrorBoundaryPr
22
53
  componentDidCatch(error: Error, errorInfo: {
23
54
  componentStack?: string;
24
55
  }): void;
25
- private handleRemoveIsland;
26
- private handleReloadIsland;
27
- private renderFallback;
56
+ private readonly handleRemoveIsland;
57
+ private readonly handleReloadIsland;
28
58
  render(): ComponentChildren;
29
59
  }
30
60
  /**
31
- * Higher-order component to wrap islands with error boundaries
61
+ * HOC that wraps a component with {@link IslandErrorBoundary}.
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const SafeCounter = withIslandErrorBoundary(Counter, 'counter', {
66
+ * isolateError: true,
67
+ * fallback: (err) => <p>Oops: {err.message}</p>,
68
+ * });
69
+ * ```
32
70
  */
33
71
  export declare function withIslandErrorBoundary<P extends object>(WrappedComponent: ComponentType<P>, islandId: string, options?: {
34
72
  fallback?: (error: Error, islandId: string) => ComponentChildren;
35
73
  isolateError?: boolean;
36
74
  onError?: (error: Error, errorInfo: LayoutErrorInfo) => void;
37
75
  }): (props: P) => import("preact").JSX.Element;
76
+ export {};
@@ -1 +1 @@
1
- import{Component as e}from"preact";import{jsx as t,jsxs as n}from"preact/jsx-runtime";export class IslandErrorBoundary 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){let n={layoutPath:`island:${this.props.islandId}`,errorType:`island`,timestamp:Date.now(),componentStack:t.componentStack,errorBoundary:`IslandErrorBoundary`};if(this.setState({errorInfo:n}),this.props.onError&&this.props.onError(e,n),typeof process<`u`&&process.env?.NODE_ENV===`development`&&console.error(`Island Error [${this.props.islandId}]:`,e),!this.props.isolateError)throw e}handleRemoveIsland=()=>{let e=document.querySelector(`[data-island-id="${this.props.islandId}"]`);e&&e.remove()};handleReloadIsland=()=>{this.setState({hasError:!1,error:null,errorInfo:null})};renderFallback(){let{error:e}=this.state,{fallback:r,islandId:i}=this.props;if(r&&e)return r(e,i);let a=typeof process<`u`&&process.env?.NODE_ENV===`development`;return t(`div`,{class:`island-error-boundary`,"data-island-error":i,children:n(`div`,{class:`island-error-container`,children:[n(`div`,{class:`island-error-header`,children:[t(`span`,{class:`island-error-icon`,children:`⚠️`}),t(`span`,{class:`island-error-title`,children:`Island Error`})]}),n(`p`,{class:`island-error-message`,children:[`An error occurred in island "`,i,`". The rest of the page should work normally.`]}),n(`div`,{class:`island-error-actions`,children:[t(`button`,{onClick:this.handleReloadIsland,class:`island-reload-button`,children:`Reload Island`}),t(`button`,{onClick:this.handleRemoveIsland,class:`island-remove-button`,children:`Remove Island`})]}),a&&e&&n(`details`,{class:`island-error-details`,children:[t(`summary`,{children:`Error Details (Development)`}),n(`div`,{class:`island-error-info`,children:[n(`p`,{children:[t(`strong`,{children:`Island ID:`}),` `,i]}),n(`p`,{children:[t(`strong`,{children:`Error:`}),` `,e.message]})]}),t(`pre`,{class:`island-error-stack`,children:e.stack})]})]})})}render(){return this.state.hasError?this.renderFallback():this.props.children}}export function withIslandErrorBoundary(e,n,i){return function(a){return t(IslandErrorBoundary,{islandId:n,fallback:i?.fallback,isolateError:i?.isolateError??!0,onError:i?.onError,children:t(e,{...a})})}}
1
+ import{Component as e}from"preact";import{jsx as t,jsxs as n}from"preact/jsx-runtime";export class IslandErrorBoundary 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){let n={layoutPath:`island:${this.props.islandId}`,errorType:`island`,timestamp:Date.now(),componentStack:t.componentStack,errorBoundary:`IslandErrorBoundary`};if(this.setState({errorInfo:n}),this.props.onError?.(e,n),!this.props.isolateError)throw e}handleRemoveIsland=()=>{document.querySelector(`[data-island-id="${this.props.islandId}"]`)?.remove()};handleReloadIsland=()=>{this.setState({hasError:!1,error:null,errorInfo:null})};render(){if(!this.state.hasError)return this.props.children;let{error:e}=this.state,{fallback:r,islandId:i}=this.props;if(r&&e)return r(e,i);let a=typeof process<`u`&&process.env?.NODE_ENV===`development`;return t(`div`,{className:`island-error-boundary`,"data-island-error":i,children:n(`div`,{className:`island-error-container`,children:[n(`div`,{className:`island-error-header`,children:[t(`span`,{className:`island-error-icon`,children:`⚠️`}),t(`span`,{className:`island-error-title`,children:`Island Error`})]}),n(`p`,{className:`island-error-message`,children:[`An error occurred in island "`,i,`".`]}),n(`div`,{className:`island-error-actions`,children:[t(`button`,{onClick:this.handleReloadIsland,className:`island-reload-button`,children:`Reload Island`}),t(`button`,{onClick:this.handleRemoveIsland,className:`island-remove-button`,children:`Remove Island`})]}),a&&e&&n(`details`,{className:`island-error-details`,children:[t(`summary`,{children:`Error Details (Development)`}),n(`p`,{children:[t(`strong`,{children:`Island ID:`}),` `,i]}),n(`p`,{children:[t(`strong`,{children:`Error:`}),` `,e.message]}),t(`pre`,{className:`island-error-stack`,children:e.stack})]})]})})}}export function withIslandErrorBoundary(e,n,i){return function(a){return t(IslandErrorBoundary,{islandId:n,fallback:i?.fallback,isolateError:i?.isolateError??!0,onError:i?.onError,children:t(e,{...a})})}}
@@ -1,25 +1,90 @@
1
- import { Component, ComponentChildren } from 'preact';
2
- import type { LayoutErrorInfo, ErrorRecoveryStrategy } from '../types/layout.ts';
1
+ /** @jsxImportSource preact */
2
+ import { Component, type ComponentChildren } from 'preact';
3
+ import type { LayoutErrorInfo, LayoutData } from '../schemas/layout.ts';
4
+ /**
5
+ * Props for {@link LayoutErrorBoundary}.
6
+ *
7
+ * @example Basic usage
8
+ * ```tsx
9
+ * <LayoutErrorBoundary onError={(err) => logToSentry(err)}>
10
+ * <DashboardLayout />
11
+ * </LayoutErrorBoundary>
12
+ * ```
13
+ *
14
+ * @example Custom fallback with retry
15
+ * ```tsx
16
+ * <LayoutErrorBoundary
17
+ * fallback={(err, retry) => (
18
+ * <div>
19
+ * <p>Layout crashed: {err.message}</p>
20
+ * <button onClick={retry}>Retry</button>
21
+ * </div>
22
+ * )}
23
+ * >
24
+ * <DashboardLayout />
25
+ * </LayoutErrorBoundary>
26
+ * ```
27
+ *
28
+ * @example Data loading with async retry and cached fallback
29
+ * ```tsx
30
+ * <LayoutErrorBoundary
31
+ * retryLoader={() => fetch('/api/blog').then(r => r.json())}
32
+ * fallbackData={{ posts: [] }}
33
+ * >
34
+ * <BlogLayout />
35
+ * </LayoutErrorBoundary>
36
+ * ```
37
+ */
3
38
  export interface LayoutErrorBoundaryProps {
39
+ /** The layout tree to protect. */
4
40
  children: ComponentChildren;
41
+ /** Custom fallback UI. Receives the error and a retry callback. */
5
42
  fallback?: (error: Error, retry: () => void) => ComponentChildren;
43
+ /** Called when the layout throws. */
6
44
  onError?: (error: Error, errorInfo: LayoutErrorInfo) => void;
7
- recoveryStrategy?: ErrorRecoveryStrategy;
8
- layoutPath?: string;
45
+ /** Categorises the error for structured logging. Defaults to `'component'`. */
9
46
  errorType?: 'component' | 'loader' | 'rendering' | 'island';
47
+ /** Async function to re-attempt data loading. Enables the async retry button (up to 3 attempts). */
48
+ retryLoader?: () => Promise<LayoutData>;
49
+ /** Static data to offer as a "Use Cached Data" option when the loader fails. */
50
+ fallbackData?: LayoutData;
10
51
  }
11
- export interface LayoutErrorBoundaryState {
52
+ interface LayoutErrorBoundaryState {
12
53
  hasError: boolean;
13
54
  error: Error | null;
14
55
  errorInfo: LayoutErrorInfo | null;
15
56
  retryCount: number;
57
+ isRetrying: boolean;
58
+ fallbackData: LayoutData | null;
16
59
  }
60
+ /**
61
+ * Error boundary for Avalon layouts.
62
+ *
63
+ * Catches render and data-loading errors in the wrapped layout tree.
64
+ * Shows a default error card with up to 3 retries, or your custom
65
+ * `fallback`. When `retryLoader` is provided, retry is async. When
66
+ * `fallbackData` is provided, a "Use Cached Data" button appears.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * <LayoutErrorBoundary
71
+ * retryLoader={() => fetchDashboardData()}
72
+ * fallbackData={{ widgets: [] }}
73
+ * onError={(err) => logToSentry(err)}
74
+ * >
75
+ * <DashboardLayout />
76
+ * </LayoutErrorBoundary>
77
+ * ```
78
+ */
17
79
  export declare class LayoutErrorBoundary extends Component<LayoutErrorBoundaryProps, LayoutErrorBoundaryState> {
18
- private maxRetries;
80
+ private readonly maxRetries;
19
81
  constructor(props: LayoutErrorBoundaryProps);
20
82
  static getDerivedStateFromError(error: Error): Partial<LayoutErrorBoundaryState>;
21
- componentDidCatch(error: Error, errorInfo: any): void;
22
- private handleRetry;
23
- private renderFallback;
83
+ componentDidCatch(error: Error, errorInfo: {
84
+ componentStack?: string;
85
+ }): void;
86
+ private readonly handleRetry;
87
+ private readonly handleUseFallback;
24
88
  render(): ComponentChildren;
25
89
  }
90
+ export {};
@@ -1 +1 @@
1
- import{Component as e}from"preact";import{jsxs as t,jsx as n}from"preact/jsx-runtime";export class LayoutErrorBoundary extends e{maxRetries=3;constructor(e){super(e),this.state={hasError:!1,error:null,errorInfo:null,retryCount:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){let n={layoutPath:this.props.layoutPath||`unknown`,errorType:this.props.errorType||`component`,timestamp:Date.now(),componentStack:t.componentStack,errorBoundary:this.constructor.name};this.setState({errorInfo:n}),this.props.onError&&this.props.onError(e,n),typeof process<`u`&&process.env?.NODE_ENV===`development`&&(console.error(`Layout Error Boundary caught an error:`,e),console.error(`Error Info:`,n),console.error(`Component Stack:`,t.componentStack))}handleRetry=()=>{this.state.retryCount<this.maxRetries&&this.setState({hasError:!1,error:null,errorInfo:null,retryCount:this.state.retryCount+1})};renderFallback(){let{error:e}=this.state,{fallback:r}=this.props;return r&&e?r(e,this.handleRetry):n(`div`,{class:`layout-error-boundary`,children:t(`div`,{class:`error-container`,children:[n(`h2`,{children:`Something went wrong`}),n(`p`,{children:`An error occurred while rendering this layout.`}),this.state.retryCount<this.maxRetries&&t(`button`,{onClick:this.handleRetry,class:`retry-button`,children:[`Try Again (`,this.maxRetries-this.state.retryCount,` attempts left)`]}),typeof process<`u`&&process.env?.NODE_ENV===`development`&&t(`details`,{class:`error-details`,children:[n(`summary`,{children:`Error Details (Development)`}),n(`pre`,{children:e?.stack}),this.state.errorInfo&&t(`div`,{children:[t(`p`,{children:[n(`strong`,{children:`Layout Path:`}),` `,this.state.errorInfo.layoutPath]}),t(`p`,{children:[n(`strong`,{children:`Error Type:`}),` `,this.state.errorInfo.errorType]}),t(`p`,{children:[n(`strong`,{children:`Timestamp:`}),` `,new Date(this.state.errorInfo.timestamp).toISOString()]})]})]})]})})}render(){return this.state.hasError?this.renderFallback():this.props.children}}
1
+ import{Component as e}from"preact";import{jsx as t,jsxs as n}from"preact/jsx-runtime";export class LayoutErrorBoundary extends e{maxRetries=3;constructor(e){super(e),this.state={hasError:!1,error:null,errorInfo:null,retryCount:0,isRetrying:!1,fallbackData:e.fallbackData??null}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){let n={errorType:this.props.errorType??`component`,timestamp:Date.now(),componentStack:t.componentStack,errorBoundary:`LayoutErrorBoundary`,layoutPath:void 0};this.setState({errorInfo:n}),this.props.onError?.(e,n)}handleRetry=async()=>{if(!(this.state.retryCount>=this.maxRetries))if(this.props.retryLoader){this.setState({isRetrying:!0});try{let e=await this.props.retryLoader();this.setState(t=>({hasError:!1,error:null,errorInfo:null,retryCount:t.retryCount+1,isRetrying:!1,fallbackData:e}))}catch(e){this.setState(t=>({retryCount:t.retryCount+1,isRetrying:!1,error:e instanceof Error?e:Error(String(e))}))}}else this.setState(e=>({hasError:!1,error:null,errorInfo:null,retryCount:e.retryCount+1}))};handleUseFallback=()=>{this.state.fallbackData&&this.setState({hasError:!1,error:null,errorInfo:null})};render(){if(!this.state.hasError)return this.props.children;let{error:e,retryCount:r,isRetrying:i,fallbackData:a}=this.state;if(this.props.fallback&&e)return this.props.fallback(e,this.handleRetry);let o=r<this.maxRetries,s=typeof process<`u`&&process.env?.NODE_ENV===`development`;return t(`div`,{className:`layout-error-boundary`,children:n(`div`,{className:`error-container`,children:[t(`h2`,{children:`Something went wrong`}),t(`p`,{children:`An error occurred while rendering this layout.`}),n(`div`,{className:`error-actions`,children:[o&&t(`button`,{onClick:this.handleRetry,disabled:i,className:`retry-button`,children:i?`Retrying...`:`Try Again (${this.maxRetries-r} left)`}),a!==null&&t(`button`,{onClick:this.handleUseFallback,className:`fallback-button`,children:`Use Cached Data`})]}),s&&e&&n(`details`,{className:`error-details`,children:[t(`summary`,{children:`Error Details (Development)`}),n(`p`,{children:[t(`strong`,{children:`Error:`}),` `,e.message]}),this.state.errorInfo&&n(`p`,{children:[t(`strong`,{children:`Error Type:`}),` `,this.state.errorInfo.errorType]}),t(`pre`,{className:`error-stack`,children:e.stack})]})]})})}}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Built-in custom hydration directives.
3
+ *
4
+ * These extend the core set (on:client, on:visible, on:interaction, on:idle, media:*)
5
+ * with additional strategies that users can opt into.
6
+ *
7
+ * Import and call `registerBuiltinDirectives()` in your server entry
8
+ * to make them available.
9
+ *
10
+ * @module islands/builtin-directives
11
+ */
12
+ /**
13
+ * Register all built-in custom directives.
14
+ */
15
+ export declare function registerBuiltinDirectives(): void;
@@ -0,0 +1 @@
1
+ import{registerHydrationDirective as e}from"./hydration-directives.js";export function registerBuiltinDirectives(){e(`on:delay`,{name:`on:delay`,script:(e,t,n)=>{setTimeout(t,Number.parseInt(n||`1000`,10))}}),e(`on:event`,{name:`on:event`,script:(e,t,n)=>{if(!n){t();return}let r=()=>{document.removeEventListener(n,r),t()};document.addEventListener(n,r,{once:!0})}}),e(`on:scroll`,{name:`on:scroll`,script:(e,t,n)=>{let r=Number.parseInt(n||`100`,10),i=()=>{globalThis.scrollY>=r&&(globalThis.removeEventListener(`scroll`,i),t())};globalThis.addEventListener(`scroll`,i,{passive:!0}),globalThis.scrollY>=r&&(globalThis.removeEventListener(`scroll`,i),t())}}),e(`on:match`,{name:`on:match`,script:(e,t,n)=>{if(!n){t();return}let r=globalThis.matchMedia(n);if(r.matches){t();return}let i=e=>{e.matches&&(r.removeEventListener(`change`,i),t())};r.addEventListener(`change`,i)}})}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Custom Hydration Directives
3
+ *
4
+ * Allows users to define custom hydration strategies for islands.
5
+ * A directive controls _when_ an island hydrates on the client.
6
+ *
7
+ * Built-in directives (on:client, on:visible, on:interaction, on:idle, media:*)
8
+ * are handled natively in main.js. This module enables user-defined directives
9
+ * that extend the system with arbitrary trigger logic.
10
+ *
11
+ * @example
12
+ * @example
13
+ * ```ts
14
+ * // Register a directive that hydrates after a delay
15
+ * registerHydrationDirective('on:delay', {
16
+ * name: 'on:delay',
17
+ * // Runs on the client — receives the island element and a hydrate callback
18
+ * script: (el, hydrate, arg) => {
19
+ * const ms = parseInt(arg || '1000', 10);
20
+ * setTimeout(hydrate, ms);
21
+ * },
22
+ * });
23
+ *
24
+ * // Use in a page via the island prop
25
+ * <Counter island={{ condition: 'on:delay', conditionArg: '2000' }} />
26
+ * ```
27
+ * @module islands/hydration-directives
28
+ */
29
+ /**
30
+ * A client-side hydration directive function.
31
+ *
32
+ * Called once per island element when the page initializes.
33
+ * The function must call `hydrate()` exactly once when the island
34
+ * should become interactive.
35
+ *
36
+ * @param el - The `<avalon-island>` DOM element
37
+ * @param hydrate - Callback that triggers hydration. Call it once.
38
+ * @param arg - Optional argument string from `conditionArg` prop
39
+ */
40
+ export type HydrationDirectiveFn = (el: HTMLElement, hydrate: () => void, arg?: string) => void | (() => void);
41
+ /**
42
+ * Definition of a custom hydration directive.
43
+ */
44
+ export interface HydrationDirectiveDefinition {
45
+ /** Directive name — must match the `condition` prop value (e.g. "on:delay") */
46
+ name: string;
47
+ /**
48
+ * Client-side script that controls when hydration fires.
49
+ *
50
+ * Can be either:
51
+ * - A function (will be serialized to the client)
52
+ * - A string of JavaScript (inlined directly)
53
+ */
54
+ script: HydrationDirectiveFn | string;
55
+ }
56
+ /**
57
+ * Register a custom hydration directive.
58
+ *
59
+ * @param name - Directive name (e.g. "on:delay", "on:event")
60
+ * @param definition - The directive definition
61
+ */
62
+ export declare function registerHydrationDirective(name: string, definition: HydrationDirectiveDefinition): void;
63
+ /**
64
+ * Unregister a custom hydration directive.
65
+ */
66
+ export declare function unregisterHydrationDirective(name: string): boolean;
67
+ /**
68
+ * Check whether a condition string maps to a registered custom directive.
69
+ */
70
+ export declare function isCustomDirective(condition: string): boolean;
71
+ /**
72
+ * Get a registered directive definition by name.
73
+ */
74
+ export declare function getDirective(name: string): HydrationDirectiveDefinition | undefined;
75
+ /**
76
+ * Get all registered custom directive names.
77
+ */
78
+ export declare function getRegisteredDirectives(): string[];
79
+ /**
80
+ * Serialize a directive's script to an inline-safe string.
81
+ * Used by the SSR renderer to embed the directive logic in the HTML
82
+ * so the client can execute it without an extra network request.
83
+ */
84
+ export declare function serializeDirectiveScript(name: string): string | null;
85
+ /**
86
+ * Clear all registered directives. Useful for testing.
87
+ * @internal
88
+ */
89
+ export declare function clearDirectives(): void;
@@ -0,0 +1 @@
1
+ const e=new Map;export function registerHydrationDirective(t,n){e.has(t)&&console.warn(`[avalon] Hydration directive "${t}" is already registered. Overwriting.`),e.set(t,n)}export function unregisterHydrationDirective(t){return e.delete(t)}export function isCustomDirective(t){return e.has(t)}export function getDirective(t){return e.get(t)}export function getRegisteredDirectives(){return[...e.keys()]}export function serializeDirectiveScript(t){let n=e.get(t);return n?typeof n.script==`string`?n.script:n.script.toString():null}export function clearDirectives(){e.clear()}
@@ -6,7 +6,7 @@ declare global {
6
6
  var __viteDevServer: ViteDevServer | undefined;
7
7
  }
8
8
  /** Supported hydration conditions for island components */
9
- export type HydrationCondition = 'on:visible' | 'on:interaction' | 'on:idle' | 'on:client' | `media:${string}`;
9
+ export type HydrationCondition = 'on:visible' | 'on:interaction' | 'on:idle' | 'on:client' | `media:${string}` | `on:${string}`;
10
10
  /** Supported framework identifiers (without "unknown") */
11
11
  export type FrameworkId = Exclude<Framework, 'unknown'>;
12
12
  export interface IslandProps {
@@ -14,6 +14,8 @@ export interface IslandProps {
14
14
  src: string;
15
15
  /** Hydration condition */
16
16
  condition?: HydrationCondition;
17
+ /** Optional argument passed to custom hydration directives */
18
+ conditionArg?: string;
17
19
  /** Props to pass to the island component */
18
20
  props?: Record<string, unknown>;
19
21
  /** Children to render inside the island (for SSR) */
@@ -37,7 +39,7 @@ export interface IslandProps {
37
39
  * Uses custom elements instead of div wrappers for cleaner, more semantic markup.
38
40
  * Supports intelligent rendering strategy detection to skip hydration for SSR-only components.
39
41
  */
40
- export default function Island({ src, condition, props, children, ssr, framework, ssrOnly, renderOptions, hydrationData, }: IslandProps): JSX.Element;
42
+ export default function Island({ src, condition, conditionArg, props, children, ssr, framework, ssrOnly, renderOptions, hydrationData, }: IslandProps): JSX.Element;
41
43
  /**
42
44
  * Universal renderIsland function – auto-detects framework and handles SSR + hydration.
43
45
  *
@@ -54,4 +56,4 @@ export default function Island({ src, condition, props, children, ssr, framework
54
56
  * Error isolation: If SSR fails, returns an error placeholder instead of throwing,
55
57
  * allowing the page to continue rendering other islands.
56
58
  */
57
- export declare function renderIsland({ src, condition, props, children, ssr, framework, ssrOnly, renderOptions, component: preloadedComponent, }: IslandProps): Promise<JSX.Element>;
59
+ export declare function renderIsland({ src, condition, conditionArg, props, children, ssr, framework, ssrOnly, renderOptions, component: preloadedComponent, }: IslandProps): Promise<JSX.Element>;
@@ -1 +1 @@
1
- import{h as e}from"preact";import{detectFramework as t}from"./framework-detection.js";import{analyzeComponentFile as n,renderComponentSSROnly as r}from"./component-analysis.js";import{loadIntegration as i,detectFrameworkFromPath as a}from"./integration-loader.js";import{addUniversalCSS as o}from"./universal-css-collector.js";import{addUniversalHead as s}from"./universal-head-collector.js";import{getIslandBundlePath as c}from"../build/island-manifest.js";import{isDev as l,devLog as u,devWarn as d,devError as f,logRenderTiming as p}from"../utils/dev-logger.js";function m(e){return`island-${e.replaceAll(/[^a-zA-Z0-9]/g,`-`)}`}function h(e){let t={};e.renderId&&(t[`data-solid-render-id`]=e.renderId);let n=e.metadata;return n?.tagName&&(t[`data-tag-name`]=n.tagName),t}function g(e,t,n,r){return{"data-condition":t,"data-src":c(e),"data-props":JSON.stringify(n),"data-render-strategy":`hydrate`,...h(r)}}function _(e){return e.startsWith(`<script`)?`script`:e.startsWith(`<style`)?`style`:e.startsWith(`<meta`)?`meta`:e.startsWith(`<link`)?`link`:e.includes(`window._$HY`)||e.includes(`_$HY=`)?`script`:`other`}function v(e){let t=e.match(/<style[^>]*>([\s\S]*?)<\/style>/i);return t?t[1].trim():null}function y(e,t,n,r){if(e.css&&o(e.css,t,n,e.scopeId),e.head){let i=e.head.trim(),a=_(i);if(a===`style`){let a=v(i);a&&(u(`${r} Extracting CSS from head <style> tag`),o(a,t,n,e.scopeId));return}s(e.head,t,n,a)}}function b(t){let{islandId:n,detectedFramework:r,shouldSkipHydration:i,src:a,condition:o,props:s,hydrationData:c,children:l}=t,d={id:n,"data-framework":r},f=i?{"data-render-strategy":`ssr-only`}:g(a,o,s,c);r===`lit`&&u(`🔍 [Island Component] ${a} - Lit hydration data:`,{hydrationDataKeys:Object.keys(c),metadata:c.metadata});let p={...d,...f};return typeof l==`string`?e(`avalon-island`,{...p,dangerouslySetInnerHTML:{__html:l}}):e(`avalon-island`,p,l)}function x(t,n,r,i,a,o,s){return r?e(`avalon-island`,{id:t,"data-render-strategy":`ssr-only`,"data-framework":n}):e(`avalon-island`,{id:t,"data-condition":a,"data-src":c(i),"data-props":JSON.stringify(o),"data-render-strategy":`hydrate`,"data-framework":n,...h(s)})}export default function S({src:e,condition:t=`on:client`,props:n={},children:r,ssr:i=t!==`on:client`,framework:o,ssrOnly:s=!1,renderOptions:c={},hydrationData:l={}}){let f=m(e),p=s||!!c.forceSSROnly,h=o||a(e),g=r!=null&&r!==``;return u(`🔍 [Island Component] ${e}`,{ssr:i,ssrOnly:s,hasChildren:g,framework:o,condition:t}),i&&g?b({islandId:f,detectedFramework:h,shouldSkipHydration:p,src:e,condition:t,props:n,hydrationData:l,children:r}):(i&&!g&&p&&d(`${e}: SSR-only component has no rendered content. This may indicate a rendering error.`),x(f,h,p,e,t,n,l))}function C(t,n){let r=n instanceof Error?n.message:String(n);return f(`🚨 Island SSR failed for ${t}:`,n),n instanceof Error&&n.stack&&f(`Stack trace:`,n.stack),e(`avalon-island`,{id:m(t),"data-src":c(t),"data-ssr-error":r,"data-render-strategy":`client-only`})}async function w({src:e,condition:t,props:n,children:r,ssr:a,framework:o,ssrOnly:s,renderOptions:c,component:u}){let d=`🏝️ [${e}]`;if(!a||r)return S({src:e,condition:t,props:n,children:r,ssr:a,framework:o,ssrOnly:s,renderOptions:c});let p;try{p=await i(o)}catch(r){return f(`${d} Failed to load ${o} integration:`,r),S({src:e,condition:t,props:n,ssr:!1,framework:o,ssrOnly:s,renderOptions:c})}try{let r=await p.render({component:u??null,props:n,src:e,condition:t,ssrOnly:s,viteServer:globalThis.__viteDevServer,isDev:l()});return y(r,e,o,d),S({src:e,condition:t,props:n,children:r.html,ssr:!0,framework:o,ssrOnly:s,renderOptions:c,hydrationData:s?void 0:r.hydrationData})}catch(r){return f(`${d} Fast path SSR failed:`,r),S({src:e,condition:t,props:n,ssr:!1,framework:o,ssrOnly:s,renderOptions:c})}}async function T(e,t,r,i){if(t||r.detectScripts===!1)return t;try{let t=await n(e,r);if(t.decision.warnings?.length)for(let e of t.decision.warnings)d(`${i} Analysis warning: ${e}`);return!t.decision.shouldHydrate}catch(e){return d(`${i} Component analysis failed:`,e),t}}async function E(e){return e.endsWith(`.vue`)?`vue`:e.endsWith(`.svelte`)?`svelte`:e.endsWith(`.tsx`)||e.endsWith(`.jsx`)||e.endsWith(`.ts`)||e.endsWith(`.js`)?t(e):`unknown`}async function D(e,t,n,r,i,a,o){let s=await E(e),c=s,u=await(await O(s,a)).render({component:o??null,props:n,src:e,condition:t,ssrOnly:r,viteServer:globalThis.__viteDevServer,isDev:l()});return y(u,e,s,a),S({src:e,condition:t,props:n,children:u.html,ssr:!0,framework:c,ssrOnly:r,renderOptions:i,hydrationData:r?void 0:u.hydrationData})}async function O(e,t){try{u(`${t} Loading integration for framework: ${e}`);let n=await i(e);return u(`${t} ✅ Integration loaded successfully`),n}catch(n){throw f(`${t} Failed to load ${e} integration:`,n),Error(`Failed to load integration for framework '${e}'. Make sure @useavalon/${e} is installed.\nInstall it with: deno add @useavalon/${e}`,{cause:n})}}export async function renderIsland({src:e,condition:t=`on:client`,props:n={},children:r,ssr:i=t!==`on:client`,framework:a,ssrOnly:o=!1,renderOptions:s={},component:c}){let u=l()?performance.now():0,d=`🏝️ [${e}]`;try{return o&&!i&&(i=!0),a?await w({src:e,condition:t,props:n,children:r,ssr:i,framework:a,ssrOnly:o,renderOptions:s,component:c}):await k({src:e,condition:t,props:n,children:r,ssr:i,ssrOnly:o,renderOptions:s,logPrefix:d,component:c})}catch(t){return C(e,t)}finally{l()&&p(e,performance.now()-u)}}async function k(e){let{src:t,condition:n,props:r,children:i,ssr:a,ssrOnly:o,renderOptions:s,logPrefix:c,component:l}=e;if(u(`🔍 [renderIsland] ${t} - Starting render (slow path)`,{ssr:a,ssrOnly:o,hasChildren:!!i,condition:n}),await T(t,o,s,c))return A(t,n,r,i,a,s,c);if(!a||i)return S({src:t,condition:n,props:r,children:i,ssr:a,renderOptions:s});try{return await D(t,n,r,o,s,c,l)}catch(e){let i=await E(t);return f(`${c} Framework rendering failed:`,e),S({src:t,condition:n,props:r,ssr:!1,framework:i,renderOptions:s})}}function A(e,t,n,i,a,o,s){return a&&!i?r({src:e,condition:t,props:n,renderOptions:o}).catch(r=>(f(`${s} SSR failed for SSR-only component:`,r),S({src:e,condition:t,props:n,ssr:!1,ssrOnly:!0,renderOptions:o}))):S({src:e,condition:t,props:n,children:i,ssr:a,ssrOnly:!0,renderOptions:o})}
1
+ import{h as e}from"preact";import{detectFramework as t}from"./framework-detection.js";import{analyzeComponentFile as n,renderComponentSSROnly as r}from"./component-analysis.js";import{loadIntegration as i,detectFrameworkFromPath as a}from"./integration-loader.js";import{addUniversalCSS as o}from"./universal-css-collector.js";import{addUniversalHead as s}from"./universal-head-collector.js";import{getIslandBundlePath as c}from"../build/island-manifest.js";import{isDev as l,devLog as u,devWarn as d,devError as f,logRenderTiming as p}from"../utils/dev-logger.js";import{isCustomDirective as m,serializeDirectiveScript as h}from"./hydration-directives.js";function g(e){return`island-${e.replaceAll(/[^a-zA-Z0-9]/g,`-`)}`}function _(e){let t={};e.renderId&&(t[`data-solid-render-id`]=e.renderId);let n=e.metadata;return n?.tagName&&(t[`data-tag-name`]=n.tagName),t}function v(e,t,n,r,i){let a={"data-condition":t,"data-src":c(e),"data-props":JSON.stringify(n),"data-render-strategy":`hydrate`,..._(r)};if(m(t)){a[`data-custom-directive`]=t;let e=h(t);e&&(a[`data-directive-script`]=e)}return i&&(a[`data-condition-arg`]=i),a}function y(e){return e.startsWith(`<script`)?`script`:e.startsWith(`<style`)?`style`:e.startsWith(`<meta`)?`meta`:e.startsWith(`<link`)?`link`:e.includes(`window._$HY`)||e.includes(`_$HY=`)?`script`:`other`}function b(e){let t=e.match(/<style[^>]*>([\s\S]*?)<\/style>/i);return t?t[1].trim():null}function x(e,t,n,r){if(e.css&&o(e.css,t,n,e.scopeId),e.head){let i=e.head.trim(),a=y(i);if(a===`style`){let a=b(i);a&&(u(`${r} Extracting CSS from head <style> tag`),o(a,t,n,e.scopeId));return}s(e.head,t,n,a)}}function S(t){let{islandId:n,detectedFramework:r,shouldSkipHydration:i,src:a,condition:o,conditionArg:s,props:c,hydrationData:l,children:d}=t,f={id:n,"data-framework":r},p=i?{"data-render-strategy":`ssr-only`}:v(a,o,c,l,s);r===`lit`&&u(`🔍 [Island Component] ${a} - Lit hydration data:`,{hydrationDataKeys:Object.keys(l),metadata:l.metadata});let m={...f,...p};return typeof d==`string`?e(`avalon-island`,{...m,dangerouslySetInnerHTML:{__html:d}}):e(`avalon-island`,m,d)}function C(t){let{islandId:n,detectedFramework:r,shouldSkipHydration:i,src:a,condition:o,props:s,hydrationData:l,conditionArg:u}=t;if(i)return e(`avalon-island`,{id:n,"data-render-strategy":`ssr-only`,"data-framework":r});let d={id:n,"data-condition":o,"data-src":c(a),"data-props":JSON.stringify(s),"data-render-strategy":`hydrate`,"data-framework":r,..._(l)};if(m(o)){d[`data-custom-directive`]=o;let e=h(o);e&&(d[`data-directive-script`]=e)}return u&&(d[`data-condition-arg`]=u),e(`avalon-island`,d)}export default function w({src:e,condition:t=`on:client`,conditionArg:n,props:r={},children:i,ssr:o=t!==`on:client`,framework:s,ssrOnly:c=!1,renderOptions:l={},hydrationData:f={}}){let p=g(e),m=c||!!l.forceSSROnly,h=s||a(e),_=i!=null&&i!==``;return u(`🔍 [Island Component] ${e}`,{ssr:o,ssrOnly:c,hasChildren:_,framework:s,condition:t}),o&&_?S({islandId:p,detectedFramework:h,shouldSkipHydration:m,src:e,condition:t,conditionArg:n,props:r,hydrationData:f,children:i}):(o&&!_&&m&&d(`${e}: SSR-only component has no rendered content. This may indicate a rendering error.`),C({islandId:p,detectedFramework:h,shouldSkipHydration:m,src:e,condition:t,props:r,hydrationData:f,conditionArg:n}))}function T(t,n){let r=n instanceof Error?n.message:String(n);return f(`🚨 Island SSR failed for ${t}:`,n),n instanceof Error&&n.stack&&f(`Stack trace:`,n.stack),e(`avalon-island`,{id:g(t),"data-src":c(t),"data-ssr-error":r,"data-render-strategy":`client-only`})}async function E({src:e,condition:t,conditionArg:n,props:r,children:a,ssr:o,framework:s,ssrOnly:c,renderOptions:u,component:d}){let p=`🏝️ [${e}]`;if(!o||a)return w({src:e,condition:t,conditionArg:n,props:r,children:a,ssr:o,framework:s,ssrOnly:c,renderOptions:u});let m;try{m=await i(s)}catch(i){return f(`${p} Failed to load ${s} integration:`,i),w({src:e,condition:t,conditionArg:n,props:r,ssr:!1,framework:s,ssrOnly:c,renderOptions:u})}try{let i=await m.render({component:d??null,props:r,src:e,condition:t,ssrOnly:c,viteServer:globalThis.__viteDevServer,isDev:l()});return x(i,e,s,p),w({src:e,condition:t,conditionArg:n,props:r,children:i.html,ssr:!0,framework:s,ssrOnly:c,renderOptions:u,hydrationData:c?void 0:i.hydrationData})}catch(i){return f(`${p} Fast path SSR failed:`,i),w({src:e,condition:t,conditionArg:n,props:r,ssr:!1,framework:s,ssrOnly:c,renderOptions:u})}}async function D(e,t,r,i){if(t||r.detectScripts===!1)return t;try{let t=await n(e,r);if(t.decision.warnings?.length)for(let e of t.decision.warnings)d(`${i} Analysis warning: ${e}`);return!t.decision.shouldHydrate}catch(e){return d(`${i} Component analysis failed:`,e),t}}async function O(e){return e.endsWith(`.vue`)?`vue`:e.endsWith(`.svelte`)?`svelte`:e.endsWith(`.tsx`)||e.endsWith(`.jsx`)||e.endsWith(`.ts`)||e.endsWith(`.js`)?t(e):`unknown`}async function k(e,t,n,r,i,a,o){let s=await O(e),c=s,u=await(await A(s,a)).render({component:o??null,props:n,src:e,condition:t,ssrOnly:r,viteServer:globalThis.__viteDevServer,isDev:l()});return x(u,e,s,a),w({src:e,condition:t,props:n,children:u.html,ssr:!0,framework:c,ssrOnly:r,renderOptions:i,hydrationData:r?void 0:u.hydrationData})}async function A(e,t){try{u(`${t} Loading integration for framework: ${e}`);let n=await i(e);return u(`${t} ✅ Integration loaded successfully`),n}catch(n){throw f(`${t} Failed to load ${e} integration:`,n),Error(`Failed to load integration for framework '${e}'. Make sure @useavalon/${e} is installed.\nInstall it with: deno add @useavalon/${e}`,{cause:n})}}export async function renderIsland({src:e,condition:t=`on:client`,conditionArg:n,props:r={},children:i,ssr:a=t!==`on:client`,framework:o,ssrOnly:s=!1,renderOptions:c={},component:u}){let d=l()?performance.now():0,f=`🏝️ [${e}]`;try{return s&&!a&&(a=!0),o?await E({src:e,condition:t,conditionArg:n,props:r,children:i,ssr:a,framework:o,ssrOnly:s,renderOptions:c,component:u}):await j({src:e,condition:t,conditionArg:n,props:r,children:i,ssr:a,ssrOnly:s,renderOptions:c,logPrefix:f,component:u})}catch(t){return T(e,t)}finally{l()&&p(e,performance.now()-d)}}async function j(e){let{src:t,condition:n,conditionArg:r,props:i,children:a,ssr:o,ssrOnly:s,renderOptions:c,logPrefix:l,component:d}=e;if(u(`🔍 [renderIsland] ${t} - Starting render (slow path)`,{ssr:o,ssrOnly:s,hasChildren:!!a,condition:n}),await D(t,s,c,l))return M(t,n,i,a,o,c,l);if(!o||a)return w({src:t,condition:n,conditionArg:r,props:i,children:a,ssr:o,renderOptions:c});try{return await k(t,n,i,s,c,l,d)}catch(e){let a=await O(t);return f(`${l} Framework rendering failed:`,e),w({src:t,condition:n,conditionArg:r,props:i,ssr:!1,framework:a,renderOptions:c})}}function M(e,t,n,i,a,o,s){return a&&!i?r({src:e,condition:t,props:n,renderOptions:o}).catch(r=>(f(`${s} SSR failed for SSR-only component:`,r),w({src:e,condition:t,props:n,ssr:!1,ssrOnly:!0,renderOptions:o}))):w({src:e,condition:t,props:n,children:i,ssr:a,ssrOnly:!0,renderOptions:o})}
@@ -12,8 +12,10 @@ export type Framework = "solid" | "vue" | "svelte" | "preact" | "react" | "lit"
12
12
  export interface IslandProps {
13
13
  /** Path to the island component (e.g., "/islands/Counter.tsx") */
14
14
  src: string;
15
- /** Hydration condition */
16
- condition?: "on:visible" | "on:interaction" | "on:idle" | "on:client" | `media:${string}`;
15
+ /** Hydration condition (built-in or custom directive name) */
16
+ condition?: "on:visible" | "on:interaction" | "on:idle" | "on:client" | `media:${string}` | `on:${string}`;
17
+ /** Optional argument passed to custom hydration directives */
18
+ conditionArg?: string;
17
19
  /** Props to pass to the island component */
18
20
  props?: Record<string, unknown>;
19
21
  /** Children to render inside the island (for SSR) */
@@ -14,25 +14,12 @@ export { LayoutComposer } from './core/layout/layout-composer.ts';
14
14
  export type { LayoutConfig } from './schemas/layout.ts';
15
15
  export { EnhancedLayoutResolver, createEnhancedLayoutResolver, EnhancedLayoutResolverUtils, } from './core/layout/enhanced-layout-resolver.ts';
16
16
  export type { EnhancedLayoutResolverOptions } from './core/layout/enhanced-layout-resolver.ts';
17
- export { IslandPersistence, defaultIslandPersistence } from './core/islands/island-persistence.ts';
18
- export { IslandStateSerializer } from './core/islands/island-state-serializer.ts';
19
- export { createPersistentIslandContext, usePersistentIslandContext, PersistentIslandProvider, } from './core/islands/persistent-island-context.tsx';
20
- export { PersistentIsland } from './components/PersistentIsland.tsx';
21
- export type { IslandState, PersistentIslandProps, PersistentIslandContext } from './schemas/layout.ts';
22
- export { LayoutErrorBoundary } from './components/LayoutErrorBoundary.tsx';
23
- export { LayoutDataErrorBoundary } from './components/LayoutDataErrorBoundary.tsx';
24
- export { IslandErrorBoundary, withIslandErrorBoundary } from './components/IslandErrorBoundary.tsx';
25
- export { StreamingErrorBoundary, withStreamingErrorBoundary } from './components/StreamingErrorBoundary.tsx';
26
- export type { LayoutErrorInfo, LayoutErrorBoundaryProps, ErrorRecoveryStrategy } from './schemas/layout.ts';
27
- export { StreamingLayout, StreamingSuspense, withStreaming, useStreamingState } from './components/StreamingLayout.tsx';
28
- export type { StreamingLayoutProps, StreamingComponent } from './schemas/layout.ts';
29
17
  export { LayoutCacheManager } from './core/layout/layout-cache-manager.ts';
30
18
  export type { CacheEntry, CacheStats, CacheConfig, } from './core/layout/layout-cache-manager.ts';
31
19
  export type { LayoutContext, LayoutData, LayoutHandler, LayoutProps, LayoutLoader, ResolvedLayout, LayoutCache, EnhancedLayoutContext, LayoutMatcherFunction, LayoutErrorHandler, LayoutRetryFunction, LayoutFallbackRenderer, IslandStateSaver, IslandStateLoader, IslandStateClearer, StreamingReadyCheck, } from './schemas/layout.ts';
32
20
  export type { ILayoutDiscovery, ILayoutMatcher, ILayoutComposer, IIslandPersistence, ILayoutErrorRecovery, ILayoutStreaming, IEnhancedLayoutResolver, ILayoutComponent, IPersistentIslandComponent, ILayoutErrorBoundaryComponent, IStreamingLayoutComponent, LayoutModule, PageModule, LayoutResolutionContext, LayoutPerformanceMetrics, LayoutDebugInfo, LayoutEventType, LayoutEventData, LayoutEventHandler, ILayoutEventEmitter, } from './types/layout.ts';
33
21
  export { LayoutContextSchema, LayoutDataSchema, LayoutRouteSchema, LayoutHandlerSchema, LayoutPropsSchema, LayoutDiscoveryOptionsSchema, RouteInfoSchema, LayoutRuleSchema, LayoutConfigSchema, IslandStateSchema, PersistentIslandPropsSchema, PersistentIslandContextSchema, LayoutErrorInfoSchema, LayoutErrorBoundaryPropsSchema, ErrorRecoveryStrategySchema, StreamingLayoutPropsSchema, StreamingComponentSchema, ResolvedLayoutSchema, LayoutCacheSchema, EnhancedLayoutContextSchema, } from './schemas/layout.ts';
34
22
  export { EnhancedLayoutResolver as LayoutSystem } from './core/layout/enhanced-layout-resolver.ts';
35
- export { defaultIslandPersistence as defaultPersistence } from './core/islands/island-persistence.ts';
36
23
  export { createEnhancedLayoutResolver as createLayoutSystem } from './core/layout/enhanced-layout-resolver.ts';
37
24
  /**
38
25
  * Layout System Version Information
@@ -1 +1 @@
1
- export{LayoutDiscovery}from"./core/layout/layout-discovery.js";export{LayoutDataLoader,LayoutDataLoadingError,loadSingleLayoutData,mergeLayoutData,getParentLayoutData,defaultLayoutDataLoader}from"./core/layout/layout-data-loader.js";export{LayoutMatcher as LayoutMatcherClass,BuiltInLayoutRules}from"./core/layout/layout-matcher.js";export{LayoutComposer}from"./core/layout/layout-composer.js";export{EnhancedLayoutResolver,createEnhancedLayoutResolver,EnhancedLayoutResolverUtils}from"./core/layout/enhanced-layout-resolver.js";export{IslandPersistence,defaultIslandPersistence}from"./core/islands/island-persistence.js";export{IslandStateSerializer}from"./core/islands/island-state-serializer.js";export{createPersistentIslandContext,usePersistentIslandContext,PersistentIslandProvider}from"./core/islands/persistent-island-context.js";export{PersistentIsland}from"./components/PersistentIsland.js";export{LayoutErrorBoundary}from"./components/LayoutErrorBoundary.js";export{LayoutDataErrorBoundary}from"./components/LayoutDataErrorBoundary.js";export{IslandErrorBoundary,withIslandErrorBoundary}from"./components/IslandErrorBoundary.js";export{StreamingErrorBoundary,withStreamingErrorBoundary}from"./components/StreamingErrorBoundary.js";export{StreamingLayout,StreamingSuspense,withStreaming,useStreamingState}from"./components/StreamingLayout.js";export{LayoutCacheManager}from"./core/layout/layout-cache-manager.js";export{LayoutContextSchema,LayoutDataSchema,LayoutRouteSchema,LayoutHandlerSchema,LayoutPropsSchema,LayoutDiscoveryOptionsSchema,RouteInfoSchema,LayoutRuleSchema,LayoutConfigSchema,IslandStateSchema,PersistentIslandPropsSchema,PersistentIslandContextSchema,LayoutErrorInfoSchema,LayoutErrorBoundaryPropsSchema,ErrorRecoveryStrategySchema,StreamingLayoutPropsSchema,StreamingComponentSchema,ResolvedLayoutSchema,LayoutCacheSchema,EnhancedLayoutContextSchema}from"./schemas/layout.js";export{EnhancedLayoutResolver as LayoutSystem}from"./core/layout/enhanced-layout-resolver.js";export{defaultIslandPersistence as defaultPersistence}from"./core/islands/island-persistence.js";export{createEnhancedLayoutResolver as createLayoutSystem}from"./core/layout/enhanced-layout-resolver.js";export const LAYOUT_SYSTEM_VERSION=`1.0.0`;export const LAYOUT_SYSTEM_FEATURES={DISCOVERY:!0,DATA_LOADING:!0,CONDITIONAL_RENDERING:!0,COMPOSITION_CONTROL:!0,PERSISTENT_ISLANDS:!0,ERROR_BOUNDARIES:!0,STREAMING:!0,CACHING:!0,PERFORMANCE_MONITORING:!0,DEBUG_UTILITIES:!0};export const LAYOUT_SYSTEM_DEFAULTS={DISCOVERY:{baseDirectory:`src/pages`,filePattern:`_layout.tsx`,excludeDirectories:[`node_modules`,`.git`,`dist`],enableWatching:!1,developmentMode:!1},CACHING:{enabled:!0,ttl:3e5,maxSize:100,cleanupInterval:6e4},STREAMING:{enabled:!0,priority:`medium`,timeout:5e3},ERROR_BOUNDARIES:{enabled:!0,maxRetries:3,fallbackStrategy:`component`},PERFORMANCE:{monitoring:!0,thresholds:{discoveryTime:100,dataLoadingTime:500,renderingTime:200,totalTime:1e3}}};
1
+ export{LayoutDiscovery}from"./core/layout/layout-discovery.js";export{LayoutDataLoader,LayoutDataLoadingError,loadSingleLayoutData,mergeLayoutData,getParentLayoutData,defaultLayoutDataLoader}from"./core/layout/layout-data-loader.js";export{LayoutMatcher as LayoutMatcherClass,BuiltInLayoutRules}from"./core/layout/layout-matcher.js";export{LayoutComposer}from"./core/layout/layout-composer.js";export{EnhancedLayoutResolver,createEnhancedLayoutResolver,EnhancedLayoutResolverUtils}from"./core/layout/enhanced-layout-resolver.js";export{LayoutCacheManager}from"./core/layout/layout-cache-manager.js";export{LayoutContextSchema,LayoutDataSchema,LayoutRouteSchema,LayoutHandlerSchema,LayoutPropsSchema,LayoutDiscoveryOptionsSchema,RouteInfoSchema,LayoutRuleSchema,LayoutConfigSchema,IslandStateSchema,PersistentIslandPropsSchema,PersistentIslandContextSchema,LayoutErrorInfoSchema,LayoutErrorBoundaryPropsSchema,ErrorRecoveryStrategySchema,StreamingLayoutPropsSchema,StreamingComponentSchema,ResolvedLayoutSchema,LayoutCacheSchema,EnhancedLayoutContextSchema}from"./schemas/layout.js";export{EnhancedLayoutResolver as LayoutSystem}from"./core/layout/enhanced-layout-resolver.js";export{createEnhancedLayoutResolver as createLayoutSystem}from"./core/layout/enhanced-layout-resolver.js";export const LAYOUT_SYSTEM_VERSION=`1.0.0`;export const LAYOUT_SYSTEM_FEATURES={DISCOVERY:!0,DATA_LOADING:!0,CONDITIONAL_RENDERING:!0,COMPOSITION_CONTROL:!0,PERSISTENT_ISLANDS:!0,ERROR_BOUNDARIES:!0,STREAMING:!0,CACHING:!0,PERFORMANCE_MONITORING:!0,DEBUG_UTILITIES:!0};export const LAYOUT_SYSTEM_DEFAULTS={DISCOVERY:{baseDirectory:`src/pages`,filePattern:`_layout.tsx`,excludeDirectories:[`node_modules`,`.git`,`dist`],enableWatching:!1,developmentMode:!1},CACHING:{enabled:!0,ttl:3e5,maxSize:100,cleanupInterval:6e4},STREAMING:{enabled:!0,priority:`medium`,timeout:5e3},ERROR_BOUNDARIES:{enabled:!0,maxRetries:3,fallbackStrategy:`component`},PERFORMANCE:{monitoring:!0,thresholds:{discoveryTime:100,dataLoadingTime:500,renderingTime:200,totalTime:1e3}}};