@useavalon/avalon 0.1.47 → 0.1.49
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.
- package/dist/mod.d.ts +48 -49
- package/dist/mod.js +1 -1
- package/dist/src/client/components.d.ts +10 -18
- package/dist/src/client/components.js +1 -1
- package/dist/src/client/custom-directives.d.ts +25 -0
- package/dist/src/client/custom-directives.js +1 -0
- package/dist/src/client/main.js +3 -3
- package/dist/src/components/IslandErrorBoundary.d.ts +48 -9
- package/dist/src/components/IslandErrorBoundary.js +1 -1
- package/dist/src/components/LayoutErrorBoundary.d.ts +75 -10
- package/dist/src/components/LayoutErrorBoundary.js +1 -1
- package/dist/src/islands/builtin-directives.d.ts +15 -0
- package/dist/src/islands/builtin-directives.js +1 -0
- package/dist/src/islands/hydration-directives.d.ts +89 -0
- package/dist/src/islands/hydration-directives.js +1 -0
- package/dist/src/islands/integration-loader.d.ts +2 -2
- package/dist/src/islands/island.d.ts +11 -9
- package/dist/src/islands/island.js +1 -1
- package/dist/src/islands/types.d.ts +4 -2
- package/dist/src/layout-system.d.ts +14 -30
- package/dist/src/layout-system.js +1 -1
- package/dist/src/nitro/config.d.ts +3 -3
- package/dist/src/nitro/renderer.d.ts +18 -5
- package/dist/src/nitro/renderer.js +11 -11
- package/dist/src/persistence/island-state-serializer.d.ts +9 -0
- package/dist/src/persistence/island-state-serializer.js +1 -0
- package/dist/src/persistence/use-persistent-state.d.ts +31 -0
- package/dist/src/persistence/use-persistent-state.js +1 -0
- package/dist/src/prerender/index.d.ts +1 -1
- package/dist/src/prerender/prerender.d.ts +1 -1
- package/dist/src/prerender/prerender.js +1 -1
- package/dist/src/schemas/core.d.ts +2 -2
- package/dist/src/schemas/layout.d.ts +5 -5
- package/dist/src/schemas/layout.js +1 -1
- package/dist/src/schemas/routing/index.d.ts +2 -2
- package/dist/src/schemas/routing.d.ts +4 -4
- package/dist/src/types/island-prop.d.ts +14 -6
- package/dist/src/types/layout.d.ts +11 -19
- package/dist/src/types/layout.js +1 -1
- package/dist/src/vite-plugin/nitro-integration.d.ts +3 -3
- package/dist/src/vite-plugin/nitro-integration.js +14 -14
- package/package.json +2 -2
- package/dist/src/components/LayoutDataErrorBoundary.d.ts +0 -34
- package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
- package/dist/src/components/PersistentIsland.d.ts +0 -36
- package/dist/src/components/PersistentIsland.js +0 -1
- package/dist/src/components/StreamingErrorBoundary.d.ts +0 -42
- package/dist/src/components/StreamingErrorBoundary.js +0 -1
- package/dist/src/components/StreamingLayout.d.ts +0 -83
- package/dist/src/components/StreamingLayout.js +0 -29
- package/dist/src/core/islands/island-persistence.d.ts +0 -74
- package/dist/src/core/islands/island-persistence.js +0 -1
- package/dist/src/core/islands/island-state-serializer.d.ts +0 -53
- package/dist/src/core/islands/island-state-serializer.js +0 -1
- package/dist/src/core/islands/persistent-island-context.d.ts +0 -36
- package/dist/src/core/islands/persistent-island-context.js +0 -1
- package/dist/src/core/islands/use-persistent-state.d.ts +0 -17
- package/dist/src/core/islands/use-persistent-state.js +0 -1
package/dist/mod.d.ts
CHANGED
|
@@ -1,50 +1,49 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
13
|
-
export {
|
|
14
|
-
export type {
|
|
15
|
-
export {
|
|
16
|
-
export
|
|
17
|
-
export {
|
|
18
|
-
export
|
|
19
|
-
export {
|
|
20
|
-
export
|
|
21
|
-
export {
|
|
22
|
-
export type {
|
|
23
|
-
export {
|
|
24
|
-
export type {
|
|
25
|
-
export
|
|
26
|
-
export { asIsland } from
|
|
27
|
-
export {
|
|
28
|
-
export type {
|
|
1
|
+
export type { Integration, IntegrationConfig, RenderParams as IntegrationRenderParams, RenderResult, } from "@useavalon/core";
|
|
2
|
+
export type { ExtendedIslandEntry, ExtendedIslandManifest, IslandEntry, IslandManifest, } from "./src/build/island-manifest.ts";
|
|
3
|
+
export { generateIslandManifest, getIslandBundlePath, loadIslandManifest, } from "./src/build/island-manifest.ts";
|
|
4
|
+
export type { IslandTypeGeneratorOptions, TypeGenerationResult, } from "./src/build/island-types-generator.ts";
|
|
5
|
+
export { generateIslandTypes, watchAndGenerateTypes } from "./src/build/island-types-generator.ts";
|
|
6
|
+
export type { MDXIslandTransformOptions } from "./src/build/mdx-island-transform.ts";
|
|
7
|
+
export { mdxIslandTransform } from "./src/build/mdx-island-transform.ts";
|
|
8
|
+
export type { PageIslandTransformOptions } from "./src/build/page-island-transform.ts";
|
|
9
|
+
export { pageIslandTransform } from "./src/build/page-island-transform.ts";
|
|
10
|
+
export { registry as integrationRegistry } from "./src/core/integrations/registry.ts";
|
|
11
|
+
export { registerBuiltinDirectives } from "./src/islands/builtin-directives.ts";
|
|
12
|
+
export { analyzeComponentFile, renderComponentSSROnly } from "./src/islands/component-analysis.ts";
|
|
13
|
+
export { addSvelteSSRCSS, clearSvelteComponentCSS, generateComponentScopeId, getSvelteComponentCSS, getSvelteSSRCSS, getSvelteSSRCSSForHead, getSvelteSSRCSSStats, } from "./src/islands/css-utils.ts";
|
|
14
|
+
export type { CircularDependency, DiscoveredIsland, ImportPathOptions, IslandChangeCallback, IslandChangeEvent, IslandCollision, IslandDirectory, IslandDiscoveryConfig, IslandFileExtension, IslandWatcherOptions, ResolutionResult, ValidationError, ValidationResult, ValidationWarning, } from "./src/islands/discovery/index.ts";
|
|
15
|
+
export { createIslandRegistry, createIslandResolver, createIslandValidator, createIslandWatcher, DEFAULT_DISCOVERY_CONFIG, discoverAllIslands, discoverIslandDirectories, discoverIslandsInDirectory, formatCircularDependency, formatValidationError, formatValidationResult, formatValidationWarning, getDefaultIslandsPath, getQualifiedIslandName, hasDefaultIslandsDirectory, ISLAND_FILE_EXTENSIONS, IslandRegistry, IslandResolver, IslandValidator, IslandWatcher, isIslandsDirectory, isSupportedIslandExtension, parseQualifiedIslandName, validateAllIslands, } from "./src/islands/discovery/index.ts";
|
|
16
|
+
export { detectFramework, detectFrameworkFromSrc, resolveIslandPath, } from "./src/islands/framework-detection.ts";
|
|
17
|
+
export type { HydrationDirectiveDefinition, HydrationDirectiveFn, } from "./src/islands/hydration-directives.ts";
|
|
18
|
+
export { getDirective, getRegisteredDirectives, isCustomDirective, registerHydrationDirective, unregisterHydrationDirective, } from "./src/islands/hydration-directives.ts";
|
|
19
|
+
export type { PreloadIntegrationsOptions } from "./src/islands/integration-loader.ts";
|
|
20
|
+
export { DEFAULT_PRELOAD_FRAMEWORKS, detectAndLoadIntegration, detectFrameworksFromPageContent, loadIntegration, preloadIntegrations, } from "./src/islands/integration-loader.ts";
|
|
21
|
+
export { default as Island, type IslandProps, renderIsland } from "./src/islands/island.tsx";
|
|
22
|
+
export type { CacheConfig as IslandCacheConfig, CacheStats as IslandCacheStats, } from "./src/islands/render-cache.ts";
|
|
23
|
+
export { clearCache, clearIslandCache, configureCache, getCacheConfig, getCacheStats, invalidateCacheForFile, invalidateCacheForPath, logCacheStats, } from "./src/islands/render-cache.ts";
|
|
24
|
+
export type { Framework, RenderParams, SvelteSSRCSSEntry } from "./src/islands/types.ts";
|
|
25
|
+
export { renderToHtml } from "./src/render/ssr.ts";
|
|
26
|
+
export { asIsland } from "./src/types/as-island.ts";
|
|
27
|
+
export type { IslandDirective } from "./src/types/island-prop.d.ts";
|
|
28
|
+
export type { NitroCoordinationPluginOptions, NitroIntegrationResult, } from "./src/vite-plugin/nitro-integration.ts";
|
|
29
|
+
export { createNitroCoordinationPlugin, createNitroIntegration, createVirtualModulesPlugin, getAvalonConfig, getViteDevServer, isDevelopmentMode, RESOLVED_VIRTUAL_IDS, VIRTUAL_MODULE_IDS, } from "./src/vite-plugin/nitro-integration.ts";
|
|
30
|
+
export { avalon, getLayoutsDir, getNitroConfig, getPagesDir, getResolvedConfig, isNitroEnabled, } from "./src/vite-plugin/plugin.ts";
|
|
31
|
+
export type { AvalonNitroConfig, AvalonPluginConfig, AvalonRuntimeConfig, CacheOptions, IntegrationName, MDXConfig, NitroConfigOutput, ResolvedAvalonConfig, ResolvedMDXConfig, RouteRule, } from "./src/vite-plugin/types.ts";
|
|
29
32
|
export declare function build(_options?: Record<string, unknown>): Promise<void>;
|
|
30
|
-
export {
|
|
31
|
-
export
|
|
32
|
-
export
|
|
33
|
-
export
|
|
34
|
-
export type {
|
|
35
|
-
export type {
|
|
36
|
-
export type { LayoutDataLoadingResult,
|
|
37
|
-
export type {
|
|
38
|
-
export
|
|
39
|
-
export
|
|
40
|
-
export type {
|
|
41
|
-
export {
|
|
42
|
-
export {
|
|
43
|
-
export {
|
|
44
|
-
export {
|
|
45
|
-
export {
|
|
46
|
-
export {
|
|
47
|
-
export { LayoutDataErrorBoundary } from './src/components/LayoutDataErrorBoundary.tsx';
|
|
48
|
-
export { IslandErrorBoundary, withIslandErrorBoundary } from './src/components/IslandErrorBoundary.tsx';
|
|
49
|
-
export { StreamingErrorBoundary, withStreamingErrorBoundary } from './src/components/StreamingErrorBoundary.tsx';
|
|
50
|
-
export type { ILayoutDiscovery, ILayoutMatcher, ILayoutComposer, IIslandPersistence, ILayoutErrorRecovery, ILayoutStreaming, IEnhancedLayoutResolver, ILayoutComponent, IPersistentIslandComponent, ILayoutErrorBoundaryComponent, IStreamingLayoutComponent, LayoutModule, PageModule, LayoutResolutionContext, LayoutPerformanceMetrics, LayoutDebugInfo, LayoutEventType, LayoutEventData, LayoutEventHandler, ILayoutEventEmitter, } from './src/types/layout.ts';
|
|
33
|
+
export type { IslandErrorBoundaryProps } from "./src/components/IslandErrorBoundary.tsx";
|
|
34
|
+
export { IslandErrorBoundary, withIslandErrorBoundary, } from "./src/components/IslandErrorBoundary.tsx";
|
|
35
|
+
export type { LayoutErrorBoundaryProps as LayoutErrorBoundaryComponentProps } from "./src/components/LayoutErrorBoundary.tsx";
|
|
36
|
+
export { LayoutErrorBoundary } from "./src/components/LayoutErrorBoundary.tsx";
|
|
37
|
+
export type { EnhancedLayoutResolverOptions } from "./src/core/layout/enhanced-layout-resolver.ts";
|
|
38
|
+
export type { CacheConfig, CacheEntry, CacheStats, } from "./src/core/layout/layout-cache-manager.ts";
|
|
39
|
+
export type { LayoutDataLoadingOptions, LayoutDataLoadingResult, } from "./src/core/layout/layout-data-loader.ts";
|
|
40
|
+
export type { LayoutCache, LayoutConfig, LayoutContext, LayoutData, LayoutDiscoveryOptions, LayoutErrorInfo, LayoutHandler, LayoutLoader, LayoutProps, LayoutRoute, LayoutRule, ResolvedLayout, RouteInfo, } from "./src/core/layout/layout-types.ts";
|
|
41
|
+
export * from "./src/layout-system.ts";
|
|
42
|
+
export { clearDiscoveryCache, clearMiddlewareCache, discoverScopedMiddleware, executeScopedMiddleware, getContextValue, getMatchingMiddleware, getMiddlewareCacheSize, hasContextValue, invalidateMiddleware, setContextValue, } from "./src/middleware/index.ts";
|
|
43
|
+
export type { MiddlewareDiscoveryOptions, MiddlewareExecutorOptions, MiddlewareFileExport, MiddlewareHandler, MiddlewareRoute, } from "./src/middleware/types.ts";
|
|
44
|
+
export type { MiddlewareContext } from "./src/nitro/middleware-adapter.ts";
|
|
45
|
+
export { usePersistentState } from "./src/persistence/use-persistent-state.ts";
|
|
46
|
+
export type { ApiMethod, ApiRoute } from "./src/schemas/api.ts";
|
|
47
|
+
export type { MetaTag, RenderOptions, ScriptConfig } from "./src/schemas/core.ts";
|
|
48
|
+
export type { EnhancedLayoutContext, ErrorRecoveryStrategy, IslandState, IslandStateClearer, IslandStateLoader, IslandStateSaver, LayoutErrorBoundaryProps, LayoutErrorHandler, LayoutFallbackRenderer, LayoutMatcherFunction, LayoutRetryFunction, PersistentIslandContext, PersistentIslandProps, StreamingComponent, StreamingLayoutProps, StreamingReadyCheck, } from "./src/schemas/layout.ts";
|
|
49
|
+
export type { IEnhancedLayoutResolver, IIslandPersistence, ILayoutComponent, ILayoutComposer, ILayoutDiscovery, ILayoutErrorBoundaryComponent, ILayoutErrorRecovery, ILayoutEventEmitter, ILayoutMatcher, ILayoutStreaming, IPersistentIslandComponent, IStreamingLayoutComponent, LayoutDebugInfo, LayoutEventData, LayoutEventHandler, LayoutEventType, LayoutModule, LayoutPerformanceMetrics, LayoutResolutionContext, PageModule, } from "./src/types/layout.ts";
|
package/dist/mod.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{generateIslandManifest,getIslandBundlePath,loadIslandManifest}from"./src/build/island-manifest.js";export{generateIslandTypes,watchAndGenerateTypes}from"./src/build/island-types-generator.js";export{mdxIslandTransform}from"./src/build/mdx-island-transform.js";export{pageIslandTransform}from"./src/build/page-island-transform.js";export{registry as integrationRegistry}from"./src/core/integrations/registry.js";export{registerBuiltinDirectives}from"./src/islands/builtin-directives.js";export{analyzeComponentFile,renderComponentSSROnly}from"./src/islands/component-analysis.js";export{addSvelteSSRCSS,clearSvelteComponentCSS,generateComponentScopeId,getSvelteComponentCSS,getSvelteSSRCSS,getSvelteSSRCSSForHead,getSvelteSSRCSSStats}from"./src/islands/css-utils.js";export{createIslandRegistry,createIslandResolver,createIslandValidator,createIslandWatcher,DEFAULT_DISCOVERY_CONFIG,discoverAllIslands,discoverIslandDirectories,discoverIslandsInDirectory,formatCircularDependency,formatValidationError,formatValidationResult,formatValidationWarning,getDefaultIslandsPath,getQualifiedIslandName,hasDefaultIslandsDirectory,ISLAND_FILE_EXTENSIONS,IslandRegistry,IslandResolver,IslandValidator,IslandWatcher,isIslandsDirectory,isSupportedIslandExtension,parseQualifiedIslandName,validateAllIslands}from"./src/islands/discovery/index.js";export{detectFramework,detectFrameworkFromSrc,resolveIslandPath}from"./src/islands/framework-detection.js";export{getDirective,getRegisteredDirectives,isCustomDirective,registerHydrationDirective,unregisterHydrationDirective}from"./src/islands/hydration-directives.js";export{DEFAULT_PRELOAD_FRAMEWORKS,detectAndLoadIntegration,detectFrameworksFromPageContent,loadIntegration,preloadIntegrations}from"./src/islands/integration-loader.js";export{default as Island,renderIsland}from"./src/islands/island.js";export{clearCache,clearIslandCache,configureCache,getCacheConfig,getCacheStats,invalidateCacheForFile,invalidateCacheForPath,logCacheStats}from"./src/islands/render-cache.js";export{renderToHtml}from"./src/render/ssr.js";export{asIsland}from"./src/types/as-island.js";export{createNitroCoordinationPlugin,createNitroIntegration,createVirtualModulesPlugin,getAvalonConfig,getViteDevServer,isDevelopmentMode,RESOLVED_VIRTUAL_IDS,VIRTUAL_MODULE_IDS}from"./src/vite-plugin/nitro-integration.js";export{avalon,getLayoutsDir,getNitroConfig,getPagesDir,getResolvedConfig,isNitroEnabled}from"./src/vite-plugin/plugin.js";export async function build(e){throw Error("avalon build() is not available in the published package. Use `vite build` or the Avalon CLI instead.")}export{IslandErrorBoundary,withIslandErrorBoundary}from"./src/components/IslandErrorBoundary.js";export{LayoutErrorBoundary}from"./src/components/LayoutErrorBoundary.js";export*from"./src/layout-system.js";export{clearDiscoveryCache,clearMiddlewareCache,discoverScopedMiddleware,executeScopedMiddleware,getContextValue,getMatchingMiddleware,getMiddlewareCacheSize,hasContextValue,invalidateMiddleware,setContextValue}from"./src/middleware/index.js";export{usePersistentState}from"./src/persistence/use-persistent-state.js";
|
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Client-safe component exports
|
|
2
|
+
* Client-safe component exports.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* you need framework components inside islands. The main entry point
|
|
6
|
-
* re-exports server-only code (nitro, h3, vite plugins) that can't
|
|
7
|
-
* be bundled for the browser.
|
|
4
|
+
* Image optimization, error boundaries, and persistent state for islands.
|
|
8
5
|
*/
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export { StreamingErrorBoundary, withStreamingErrorBoundary } from '../components/StreamingErrorBoundary.tsx';
|
|
18
|
-
export { StreamingLayout, StreamingSuspense, withStreaming, useStreamingState } from '../components/StreamingLayout.tsx';
|
|
19
|
-
export { Image } from '../components/Image.tsx';
|
|
20
|
-
export type { ImageProps } from '../components/Image.tsx';
|
|
21
|
-
export type { IslandState } from '../schemas/layout.ts';
|
|
6
|
+
export type { ImageProps } from "../components/Image.tsx";
|
|
7
|
+
export { Image } from "../components/Image.tsx";
|
|
8
|
+
export type { IslandErrorBoundaryProps } from "../components/IslandErrorBoundary.tsx";
|
|
9
|
+
export { IslandErrorBoundary, withIslandErrorBoundary, } from "../components/IslandErrorBoundary.tsx";
|
|
10
|
+
export type { LayoutErrorBoundaryProps } from "../components/LayoutErrorBoundary.tsx";
|
|
11
|
+
export { LayoutErrorBoundary } from "../components/LayoutErrorBoundary.tsx";
|
|
12
|
+
export { usePersistentState } from "../persistence/use-persistent-state.ts";
|
|
13
|
+
export { registerClientDirective } from "./custom-directives.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{Image}from"../components/Image.tsx";export{IslandErrorBoundary,withIslandErrorBoundary}from"../components/IslandErrorBoundary.tsx";export{LayoutDataErrorBoundary}from"../components/LayoutDataErrorBoundary.tsx";export{LayoutErrorBoundary}from"../components/LayoutErrorBoundary.tsx";export{PersistentIsland}from"../components/PersistentIsland.tsx";export{StreamingErrorBoundary,withStreamingErrorBoundary}from"../components/StreamingErrorBoundary.tsx";export{StreamingLayout,StreamingSuspense,useStreamingState,withStreaming}from"../components/StreamingLayout.tsx";export{defaultIslandPersistence,IslandPersistence}from"../core/islands/island-persistence.js";export{IslandStateSerializer}from"../core/islands/island-state-serializer.js";export{createPersistentIslandContext,PersistentIslandProvider,usePersistentIslandContext}from"../core/islands/persistent-island-context.tsx";export{usePersistentState}from"../core/islands/use-persistent-state.js";export{registerClientDirective}from"./custom-directives.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for the client-side custom directives module.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Register a client-side directive function at runtime.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerClientDirective(
|
|
9
|
+
name: string,
|
|
10
|
+
fn: (el: HTMLElement, hydrate: () => void, arg?: string) => void,
|
|
11
|
+
): void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if a directive is registered on the client.
|
|
15
|
+
*/
|
|
16
|
+
export declare function hasClientDirective(name: string): boolean;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Execute a custom directive for an island element.
|
|
20
|
+
*/
|
|
21
|
+
export declare function executeCustomDirective(
|
|
22
|
+
island: HTMLElement,
|
|
23
|
+
directiveName: string,
|
|
24
|
+
hydrateFn: () => void,
|
|
25
|
+
): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=new Map;export function registerClientDirective(t,n){e.set(t,n)}export function hasClientDirective(t){return e.has(t)}export function executeCustomDirective(t,n,r){let i=t.dataset.conditionArg||void 0;if(e.has(n)){let a=e.get(n);try{a(t,r,i)}catch(e){console.error(`[avalon] Custom directive "${n}" threw:`,e),r()}return!0}let a=t.dataset.directiveScript;if(a){try{let o=Function(`return (`+a+`)`)();e.set(n,o),o(t,r,i)}catch(e){console.error(`[avalon] Failed to execute inline directive "${n}":`,e),r()}return!0}return!1}typeof globalThis<`u`&&(globalThis.__avalon_registerDirective=registerClientDirective);
|
package/dist/src/client/main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,
|
|
1
|
+
import{executeCustomDirective as e,hasClientDirective as t}from"./custom-directives.js";document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,n):n();function n(){let n=document.querySelectorAll(`[data-framework]`);n.length!==0&&n.forEach(n=>{try{let c=n.dataset.framework,l=n.dataset.condition||`on:client`;if(n.dataset.renderStrategy===`ssr-only`||!r(n,l))return;l===`on:client`?u(n,c):l===`on:visible`?i(n,c):l===`on:interaction`?a(n,c):l===`on:idle`?o(n,c):l.startsWith(`media:`)?s(n,c,l.slice(6)):n.dataset.customDirective||t(l)?e(n,l,()=>{u(n,c)})||(console.warn(`[avalon] Unknown hydration condition: "${l}". Hydrating immediately.`),u(n,c)):u(n,c)}catch(e){console.error(`Error processing island:`,e),d(n,n.dataset.framework||`unknown`,n.dataset.src||`unknown`,e)}})}function r(e,t){if(!t||t===`on:client`)return!0;if(t.startsWith(`media:`)){let e=t.slice(6);try{return globalThis.matchMedia(e).matches}catch(t){return console.error(`Invalid media query:`,e,t),!0}}return t===`on:visible`||t===`on:interaction`||t===`on:idle`||console.warn(`Unknown hydration condition:`,t),!0}function i(e,t){try{let n=new IntersectionObserver(r=>{r[0].isIntersecting&&(u(e,t),n.disconnect())},{rootMargin:`50px`,threshold:0});n.observe(e)}catch(n){console.error(`Failed to setup intersection observer:`,n),u(e,t)}}function a(e,t){let n=[`click`,`touchstart`,`mouseenter`,`focusin`],r=!1,i=()=>{r||(r=!0,n.forEach(t=>{e.removeEventListener(t,i)}),u(e,t))};try{n.forEach(t=>{e.addEventListener(t,i,{once:!0,passive:!0})})}catch(n){console.error(`Failed to setup interaction observer:`,n),u(e,t)}}function o(e,t){try{`requestIdleCallback`in globalThis?globalThis.requestIdleCallback(()=>{u(e,t)},{timeout:5e3}):document.readyState===`complete`?setTimeout(()=>{u(e,t)},200):globalThis.addEventListener(`load`,()=>{setTimeout(()=>{u(e,t)},200)},{once:!0})}catch(n){console.error(`Failed to setup idle callback:`,n),u(e,t)}}function s(e,t,n){try{let r=globalThis.matchMedia(n);if(r.matches){u(e,t);return}let i=n=>{n.matches&&(u(e,t),r.removeEventListener(`change`,i))};r.addEventListener(`change`,i)}catch(r){console.error(`Failed to setup media query:`,n,r),u(e,t)}}async function c(e){if(![`preact`,`react`,`vue`,`svelte`,`solid`,`lit`,`qwik`].includes(e))throw Error(`Unknown framework: ${e}`);if(import.meta.env?.DEV)return import(`/@useavalon/${e}/client`);switch(e){case`preact`:case`react`:return import(`@useavalon/preact/client`);case`vue`:return import(`@useavalon/vue/client`);case`svelte`:return import(`@useavalon/svelte/client`);case`solid`:return import(`@useavalon/solid/client`);case`lit`:return import(`@useavalon/lit/client`);case`qwik`:return import(`@useavalon/qwik/client`);default:throw Error(`Unknown framework: ${e}`)}}function l(e,t){let n=e.default;if(!n){let t=Object.keys(e).filter(e=>e!==`default`);for(let r of t){let t=e[r];if(typeof t==`function`&&t.prototype){n=t;break}}n||=e}if(!n)throw Error(`Component ${t} has no default export`);return n}async function u(e,t){if(e.dataset.hydrated)return;let n=e.dataset.src,r=e.dataset.props;if(!n){console.warn(`Island missing data-src attribute`);return}try{let i=r?JSON.parse(r):{};t===`lit`&&(import.meta.env?.DEV?await import(`/@useavalon/lit/client`):await import(`@useavalon/lit/client`));let a=l(await import(n),n);try{let n=await c(t);if(!n.hydrate||typeof n.hydrate!=`function`)throw Error(`Integration ${t} does not export a hydrate function`);await n.hydrate(e,a,i),e.dataset.hydrated=`true`}catch(r){import.meta.env?.DEV&&console.error(`Integration hydration failed for ${t}: ${n}`,r),e.dataset.hydrationStatus=`failed`,e.dataset.hydrationError=r.message,e.dispatchEvent(new CustomEvent(`hydration-error`,{detail:{framework:t,src:n,error:r.message,timestamp:Date.now(),hydrationType:`integration-level`},bubbles:!0}))}}catch(r){console.error(`❌ Critical error hydrating ${t} island ${n}:`,r),d(e,t,n,r)}}function d(e,t,n,r){console.error(`Hydration error for ${t} island:`,{src:n,error:r.message,stack:r.stack}),e.dataset.hydrationStatus=`failed`,e.dataset.renderStrategy=`ssr-only`,e.classList.add(`hydration-failed`),e.dispatchEvent(new CustomEvent(`hydration-error`,{detail:{framework:t,src:n,error:r.message,timestamp:Date.now()},bubbles:!0})),p()&&f(e,t,n,r)}function f(e,t,n,r){let i=document.createElement(`div`);i.className=`hydration-error-indicator`,i.style.cssText=`
|
|
2
2
|
position: absolute;
|
|
3
3
|
top: 0;
|
|
4
4
|
right: 0;
|
|
@@ -11,7 +11,7 @@ document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,e):
|
|
|
11
11
|
z-index: 9999;
|
|
12
12
|
cursor: pointer;
|
|
13
13
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
14
|
-
`,i.textContent=`❌ ${t}`,i.title=`Hydration failed: ${n}\n${r.message}\nClick for details`,i.addEventListener(`click`,()=>{alert(`Hydration Error\n\nFramework: ${t}\nComponent: ${n}\n\nError: ${r.message}\n\nStack:\n${r.stack}`)}),globalThis.getComputedStyle(e).position===`static`&&(e.style.position=`relative`),e.appendChild(i)}function
|
|
14
|
+
`,i.textContent=`❌ ${t}`,i.title=`Hydration failed: ${n}\n${r.message}\nClick for details`,i.addEventListener(`click`,()=>{alert(`Hydration Error\n\nFramework: ${t}\nComponent: ${n}\n\nError: ${r.message}\n\nStack:\n${r.stack}`)}),globalThis.getComputedStyle(e).position===`static`&&(e.style.position=`relative`),e.appendChild(i)}function p(){return import.meta.env?.DEV||import.meta.env?.MODE===`development`||globalThis.location?.hostname===`localhost`||globalThis.location?.hostname===`127.0.0.1`}function m(e){let t=e.dataset.framework,n=e.dataset.src;if(!n)return null;let r={framework:t,src:n,props:e.dataset.props,scrollPosition:{x:globalThis.scrollX,y:globalThis.scrollY},focusedElement:document.activeElement?.id||null};try{t===`vue`&&e.__vue__?r.vueData=structuredClone(e.__vue__.$data||{}):t===`svelte`&&e.__svelte__?r.svelteState=e.__svelte__:t===`lit`&&e.tagName?.includes(`-`)&&(e.querySelector(`[data-lit-element]`)||e)._$litElement$&&(r.litProperties={})}catch(e){console.warn(`Failed to preserve island state:`,e)}return r}function h(e,t){if(t)try{if(t.scrollPosition&&globalThis.scrollTo(t.scrollPosition.x,t.scrollPosition.y),t.focusedElement){let e=document.getElementById(t.focusedElement);e&&e.focus()}e.dataset.framework===`vue`&&t.vueData&&e.__vue__&&Object.assign(e.__vue__.$data,t.vueData)}catch(e){console.warn(`Failed to restore island state:`,e)}}async function g(e,t,n,r){let i=e.dataset.props,a=i?JSON.parse(i):{};t===`lit`&&(import.meta.env?.DEV?await import(`/@useavalon/lit/client`):await import(`@useavalon/lit/client`));let o=l(await import(n),r),s=await c(t);if(!s.hydrate||typeof s.hydrate!=`function`)throw Error(`Integration ${t} does not export a hydrate function`);s.hydrate(e,o,a),e.dataset.hydrated=`true`}function _(e,t,n,r){let i=e.querySelector(`.hmr-error-indicator`);i&&i.remove();let a=document.createElement(`div`);a.className=`hmr-error-indicator`,a.style.cssText=`
|
|
15
15
|
position: absolute;
|
|
16
16
|
top: 0;
|
|
17
17
|
left: 0;
|
|
@@ -36,4 +36,4 @@ document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,e):
|
|
|
36
36
|
cursor: pointer;
|
|
37
37
|
font-size: 14px;
|
|
38
38
|
line-height: 1;
|
|
39
|
-
`,c.onclick=()=>a.remove(),a.appendChild(o),a.appendChild(s),a.appendChild(c),globalThis.getComputedStyle(e).position===`static`&&(e.style.position=`relative`),e.insertBefore(a,e.firstChild)}import.meta.hot&&(import.meta.hot.accept(),import(`./hmr-coordinator.js`).then(async({initializeHMR:e,getHMRCoordinator:t})=>{e();let n=t(),r=new Set;document.querySelectorAll(`[data-framework]`).forEach(e=>{let t=e.dataset.framework;t&&r.add(t)});let i=e=>import(`/@useavalon/${e}/client/hmr`).then(t=>t[`${e}Adapter`]),a={react:()=>i(`react`),preact:()=>i(`preact`),vue:()=>i(`vue`),svelte:()=>i(`svelte`),solid:()=>i(`solid`),lit:()=>i(`lit`),qwik:()=>i(`qwik`)};for(let e of r){let t=a[e];if(t)try{let r=await t();n.registerAdapter(e,r)}catch(t){console.warn(`[HMR] Failed to load adapter for ${e}:`,t)}}}).catch(e=>{console.error(`[HMR] Failed to initialize:`,e)}),
|
|
39
|
+
`,c.onclick=()=>a.remove(),a.appendChild(o),a.appendChild(s),a.appendChild(c),globalThis.getComputedStyle(e).position===`static`&&(e.style.position=`relative`),e.insertBefore(a,e.firstChild)}import.meta.hot&&(import.meta.hot.accept(),import(`./hmr-coordinator.js`).then(async({initializeHMR:e,getHMRCoordinator:t})=>{e();let n=t(),r=new Set;document.querySelectorAll(`[data-framework]`).forEach(e=>{let t=e.dataset.framework;t&&r.add(t)});let i=e=>import(`/@useavalon/${e}/client/hmr`).then(t=>t[`${e}Adapter`]),a={react:()=>i(`react`),preact:()=>i(`preact`),vue:()=>i(`vue`),svelte:()=>i(`svelte`),solid:()=>i(`solid`),lit:()=>i(`lit`),qwik:()=>i(`qwik`)};for(let e of r){let t=a[e];if(t)try{let r=await t();n.registerAdapter(e,r)}catch(t){console.warn(`[HMR] Failed to load adapter for ${e}:`,t)}}}).catch(e=>{console.error(`[HMR] Failed to initialize:`,e)}),y());async function v(e,t,n,r){try{let{showHMRErrorOverlay:e}=await import(`./hmr-error-overlay.js`);e({framework:t,src:n,error:r,filePath:n})}catch{_(e,t,n,r)}}function y(){if(!import.meta.hot)return;let e=new Map;async function t(e){let t=e.replaceAll(`\\`,`/`),r=document.querySelectorAll(`[data-src*="${t}"], [data-src$="${t}"]`);if(r.length===0){let e=document.querySelectorAll(`[data-src]`);for(let r of e){let e=r.dataset.src;e&&(e.includes(t)||t.includes(e.replace(/^\//,``)))&&await n(r)}return}for(let e of r)await n(e)}async function n(t){let n=t.dataset.framework,r=t.dataset.src;if(!(!r||!n))try{let i=m(t);e.set(r,i),delete t.dataset.hydrated,delete t.dataset.hydrationStatus;let a=t.querySelector(`.hydration-error-indicator`);a&&a.remove();let o=Date.now();await g(t,n,r.includes(`?`)?`${r}&t=${o}`:`${r}?t=${o}`,r);let s=e.get(r);s&&(h(t,s),e.delete(r)),t.dispatchEvent(new CustomEvent(`hmr-update`,{detail:{framework:n,src:r,timestamp:Date.now(),success:!0},bubbles:!0}))}catch(e){console.error(`[HMR] Failed for ${n} island ${r}:`,e),t.dispatchEvent(new CustomEvent(`hmr-error`,{detail:{framework:n,src:r,error:e.message,timestamp:Date.now()},bubbles:!0})),p()&&v(t,n,r,e)}}import.meta.hot.on(`vite:beforeUpdate`,e=>{for(let n of e.updates||[]){let e=n.path||n.acceptedPath;e&&(e.includes(`/islands/`)||e.includes(`\\islands\\`))&&t(e)}}),import.meta.hot.on(`vite:beforeFullReload`,()=>{let e=document.querySelectorAll(`[data-hydrated="true"]`),t={};for(let n of e){let e=n.dataset.src;e&&(t[e]=m(n))}try{sessionStorage.setItem(`__avalon_hmr_states__`,JSON.stringify(t))}catch{}});try{let e=sessionStorage.getItem(`__avalon_hmr_states__`);if(e){let t=JSON.parse(e);sessionStorage.removeItem(`__avalon_hmr_states__`),setTimeout(()=>{for(let[e,n]of Object.entries(t)){let t=document.querySelector(`[data-src="${e}"]`);t&&n&&h(t,n)}},100)}}catch{}}
|
|
@@ -1,20 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
import type
|
|
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
|
-
|
|
31
|
+
interface IslandErrorBoundaryState {
|
|
11
32
|
hasError: boolean;
|
|
12
33
|
error: Error | null;
|
|
13
34
|
errorInfo: LayoutErrorInfo | null;
|
|
14
35
|
}
|
|
15
36
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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`,{type:`button`,onClick:this.handleReloadIsland,className:`island-reload-button`,children:`Reload Island`}),t(`button`,{type:`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
|
-
|
|
2
|
-
import
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
import { Component, type ComponentChildren } from "preact";
|
|
3
|
+
import type { LayoutData, LayoutErrorInfo } 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
45
|
+
/** Categorises the error for structured logging. Defaults to `'component'`. */
|
|
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
|
-
|
|
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:
|
|
22
|
-
|
|
23
|
-
|
|
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{
|
|
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`,{type:`button`,onClick:this.handleRetry,disabled:i,className:`retry-button`,children:i?`Retrying...`:`Try Again (${this.maxRetries-r} left)`}),a!==null&&t(`button`,{type:`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;
|