rasengan 1.2.1 → 1.2.2

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 (34) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/lib/esm/core/plugins/index.js +10 -13
  3. package/lib/esm/entries/client/render.js +24 -7
  4. package/lib/esm/entries/server/entry.server.js +1 -1
  5. package/lib/esm/entries/server/error-template.js +14 -0
  6. package/lib/esm/entries/server/index.js +2 -1
  7. package/lib/esm/routing/components/index.js +12 -69
  8. package/lib/esm/routing/error-overlay/ErrorBoundaryFallback.js +42 -0
  9. package/lib/esm/routing/error-overlay/ErrorOverlay.js +60 -0
  10. package/lib/esm/routing/error-overlay/ErrorOverlayProvider.js +39 -0
  11. package/lib/esm/routing/error-overlay/error-overlay.css +472 -0
  12. package/lib/esm/routing/error-overlay/error-store.js +44 -0
  13. package/lib/esm/routing/error-overlay/index.js +2 -0
  14. package/lib/esm/routing/error-overlay/stack-utils.js +72 -0
  15. package/lib/esm/routing/utils/define-router.js +2 -2
  16. package/lib/esm/routing/utils/flat-routes.js +10 -5
  17. package/lib/esm/routing/utils/generate-routes.js +3 -2
  18. package/lib/esm/server/dev/handlers.js +9 -5
  19. package/lib/esm/server/dev/server.js +8 -1
  20. package/lib/esm/server/node/index.js +2 -4
  21. package/lib/esm/server/node/rendering.js +6 -4
  22. package/lib/tsconfig.esm.tsbuildinfo +1 -1
  23. package/lib/tsconfig.types.tsbuildinfo +1 -1
  24. package/lib/types/entries/server/entry.server.d.ts +2 -0
  25. package/lib/types/entries/server/error-template.d.ts +1 -0
  26. package/lib/types/routing/error-overlay/ErrorBoundaryFallback.d.ts +14 -0
  27. package/lib/types/routing/error-overlay/ErrorOverlay.d.ts +2 -0
  28. package/lib/types/routing/error-overlay/ErrorOverlayProvider.d.ts +7 -0
  29. package/lib/types/routing/error-overlay/error-store.d.ts +24 -0
  30. package/lib/types/routing/error-overlay/index.d.ts +3 -0
  31. package/lib/types/routing/error-overlay/stack-utils.d.ts +31 -0
  32. package/lib/types/server/node/rendering.d.ts +7 -1
  33. package/package.json +12 -11
  34. package/types/client.d.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 1.2.2 (2026-06-01)
4
+
5
+ ### Features
6
+
7
+ - add custom ErrorBoundary d419e67
8
+
9
+ ### Bug Fixes
10
+
11
+ - add error templates and fix flat-routes error regarding the default layout 333414d
12
+ - catch error occuring in component definition 62d6d51
13
+
3
14
  ## 1.2.1 (2026-04-03)
4
15
 
5
16
  ## 1.2.1-beta.4 (2026-01-11)
@@ -9,29 +9,26 @@ import { preRenderApp } from '../../server/node/index.js';
9
9
  function loadRasenganGlobal() {
10
10
  return {
11
11
  name: 'vite-plugin-rasengan-config',
12
- async config(_, { command }) {
13
- if (command !== 'build')
14
- return;
15
- const packageJsonPath = resolve(process.cwd(), 'package.json');
16
- if (!fs.existsSync(packageJsonPath)) {
17
- throw new Error(`Package.json file not found at: ${packageJsonPath}`);
12
+ async config() {
13
+ let version = '';
14
+ try {
15
+ const rasenganPkgPath = resolve(process.cwd(), 'node_modules/rasengan/package.json');
16
+ if (fs.existsSync(rasenganPkgPath)) {
17
+ const raw = fs.readFileSync(rasenganPkgPath, { encoding: 'utf-8' });
18
+ version = JSON.parse(raw).version || '';
19
+ }
18
20
  }
19
- const packageJsonRaw = fs.readFileSync(packageJsonPath, {
20
- encoding: 'utf-8',
21
- });
22
- const packageJson = JSON.parse(packageJsonRaw);
21
+ catch { }
23
22
  const rasenganConfig = {
24
- version: packageJson.version,
23
+ version,
25
24
  ssr: true,
26
25
  };
27
- // Inject the configuration as a global constant
28
26
  return {
29
27
  define: {
30
28
  ['Rasengan']: JSON.stringify(rasenganConfig),
31
29
  },
32
30
  };
33
31
  },
34
- apply: 'build',
35
32
  };
36
33
  }
37
34
  function rasenganConfigPlugin() {
@@ -4,11 +4,18 @@ import { StrictMode } from 'react';
4
4
  import { RootComponent } from '../../routing/components/template.js';
5
5
  import { generateRoutes, preloadMatches, } from '../../routing/utils/generate-routes.js';
6
6
  import { createBrowserRouter, RouterProvider } from 'react-router';
7
+ import { ErrorOverlayProvider, errorStore, } from '../../routing/error-overlay/index.js';
7
8
  const isSpaMode = Boolean(window.__RASENGAN_SPA_MODE__);
8
9
  export default async function renderApp(App, AppRouter, options) {
10
+ // Forward SSR errors to the error overlay
11
+ const ssrError = window.__RASENGAN_SSR_ERROR__;
12
+ if (ssrError) {
13
+ const err = new Error(ssrError.message);
14
+ err.name = ssrError.name;
15
+ err.stack = ssrError.stack;
16
+ errorStore.addError(err, 'global');
17
+ }
9
18
  // Get root element
10
- // We need to get the root element to render the app - (SPA mode)
11
- // or to hydrate the app - (SSR mode)
12
19
  const root = document.getElementById('root');
13
20
  if (!root) {
14
21
  throw new Error('#root element not found in the DOM');
@@ -17,18 +24,24 @@ export default async function renderApp(App, AppRouter, options) {
17
24
  const RasenganRouter = await AppRouter;
18
25
  // Generate routes for the browser routing
19
26
  const routes = generateRoutes(RasenganRouter);
20
- // Preload lazy routes
21
- // We have to only preload the routes that are matched for the current URL
22
- // The remaining routing will be lazy loaded on the client after routes change
23
- await preloadMatches(window.location, routes);
27
+ if (!ssrError) {
28
+ // Preload lazy routes
29
+ // We have to only preload the routes that are matched for the current URL
30
+ // The remaining routing will be lazy loaded on the client after routes change
31
+ await preloadMatches(window.location, routes);
32
+ }
24
33
  // Create router
25
34
  let router = createBrowserRouter(routes, {
26
35
  hydrationData: window.__staticRouterHydrationData,
27
36
  });
28
37
  // Generate client router
29
38
  const ClientRouter = () => _jsx(RouterProvider, { router: router });
39
+ // Detect dev mode
40
+ const devMode = typeof import.meta !== 'undefined' &&
41
+ import.meta.env?.DEV === true;
30
42
  // Generate app tree
31
- const appTree = options.reactStrictMode ? (_jsx(StrictMode, { children: _jsx(App, { Component: (props) => (_jsx(RootComponent, { ...props, Router: ClientRouter })) }) })) : (_jsx(App, { Component: (props) => _jsx(RootComponent, { ...props, Router: ClientRouter }) }));
43
+ const appContent = options.reactStrictMode ? (_jsx(StrictMode, { children: _jsx(App, { Component: (props) => (_jsx(RootComponent, { ...props, Router: ClientRouter })) }) })) : (_jsx(App, { Component: (props) => _jsx(RootComponent, { ...props, Router: ClientRouter }) }));
44
+ const appTree = (_jsx(ErrorOverlayProvider, { devMode: devMode, children: appContent }));
32
45
  // Render app
33
46
  if (isSpaMode) {
34
47
  // No SSR markup, so start fresh
@@ -36,6 +49,10 @@ export default async function renderApp(App, AppRouter, options) {
36
49
  onCaughtError: (error) => {
37
50
  console.error(error);
38
51
  },
52
+ onUncaughtError(error, errorInfo) {
53
+ console.error(error);
54
+ console.error(errorInfo);
55
+ },
39
56
  onRecoverableError(error, errorInfo) {
40
57
  console.error(error);
41
58
  console.error(errorInfo);
@@ -28,7 +28,7 @@ export const render = async (StaticRouterComponent, res, options, stream = true)
28
28
  Template = (await loadModuleSSR(`${rootPath}/src/template`)).default;
29
29
  }
30
30
  if (stream) {
31
- await renderToStream(_jsx(TemplateLayout, { StaticRouterComponent: StaticRouterComponent, metadata: metadata, assets: assets, App: App, Template: Template }), res);
31
+ await renderToStream(_jsx(TemplateLayout, { StaticRouterComponent: StaticRouterComponent, metadata: metadata, assets: assets, App: App, Template: Template }), res, { statusCode: options.statusCode, responseHeaders: options.responseHeaders });
32
32
  }
33
33
  else {
34
34
  const html = renderToString(_jsx(TemplateLayout, { StaticRouterComponent: StaticRouterComponent, metadata: metadata, assets: assets, App: App, Template: Template }));
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TemplateLayout } from './index.js';
3
+ import { renderToString } from '../../server/node/rendering.js';
4
+ export function renderErrorPage(error) {
5
+ const serialized = JSON.stringify({
6
+ message: error instanceof Error ? error.message : String(error),
7
+ stack: error instanceof Error ? error.stack : undefined,
8
+ name: error instanceof Error ? error.name : 'Error',
9
+ });
10
+ const ErrorTemplate = ({ Head, Body, Script, }) => (_jsxs("html", { lang: "en", children: [_jsx(Head, { children: _jsx("script", { type: "module", dangerouslySetInnerHTML: {
11
+ __html: `window.__RASENGAN_SSR_ERROR__ = ${serialized};`,
12
+ } }) }), _jsx(Body, { children: _jsx(Script, { children: _jsx("script", { type: "module", src: "/src/index" }) }) })] }));
13
+ return renderToString(_jsx(TemplateLayout, { Template: ErrorTemplate, isSpaMode: false }));
14
+ }
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { BodyComponent, HeadComponent, RootComponent, ScriptComponent, } from '../../routing/components/template.js';
4
4
  import { isServerMode, ServerMode } from '../../server/runtime/mode.js';
5
+ import { ErrorBoundaryFallback } from '../../routing/error-overlay/ErrorBoundaryFallback.js';
5
6
  export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, Template, isSpaMode = false, }) => {
6
7
  // inject vite refresh script to avoid "React refresh preamble was not loaded"
7
8
  let viteScripts = _jsx(React.Fragment, {});
@@ -27,5 +28,5 @@ export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, T
27
28
  __html: `window.__RASENGAN_SPA_MODE__=false;`,
28
29
  } }) }));
29
30
  }
30
- return (_jsx(Template, { Head: ({ children }) => (_jsxs(HeadComponent, { metadata: metadata, assets: assets, children: [viteScripts, otherScripts, children] })), Body: ({ children }) => (_jsx(BodyComponent, { asChild: App ? true : false, AppContent: App && (_jsx(App, { Component: (props) => _jsx(RootComponent, { ...props }), children: StaticRouterComponent })), children: children })), Script: ({ children }) => _jsx(ScriptComponent, { children: children }) }));
31
+ return (_jsx(Template, { Head: ({ children }) => (_jsxs(HeadComponent, { metadata: metadata, assets: assets, children: [viteScripts, otherScripts, children] })), Body: ({ children }) => (_jsx(BodyComponent, { asChild: App ? true : false, AppContent: App && (_jsx(ErrorBoundaryFallback, { children: _jsx(App, { Component: (props) => _jsx(RootComponent, { ...props }), children: StaticRouterComponent }) })), children: children })), Script: ({ children }) => _jsx(ScriptComponent, { children: children }) }));
31
32
  };
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { isRouteErrorResponse, Link, useLocation, useParams, useRouteError, } from 'react-router';
3
3
  import { useEffect, useRef } from 'react';
4
+ import { errorStore } from '../error-overlay/error-store.js';
4
5
  // Extract the environment variables
5
6
  const extractEnv = () => {
6
7
  try {
@@ -55,75 +56,17 @@ export function ErrorBoundary() {
55
56
  }, children: _jsx("p", { style: {
56
57
  fontSize: '18px',
57
58
  }, children: "Application Error" }) }));
58
- if (isRouteErrorResponse(error)) {
59
- return (_jsxs("section", { style: {
60
- position: 'fixed',
61
- top: 0,
62
- left: 0,
63
- right: 0,
64
- bottom: 0,
65
- zIndex: 100,
66
- display: 'flex',
67
- flexDirection: 'row',
68
- justifyContent: 'center',
69
- alignItems: 'center',
70
- height: '100vh',
71
- width: '100vw',
72
- gap: 10,
73
- backgroundColor: '#fff',
74
- }, children: [_jsx("p", { style: {
75
- fontSize: '18px',
76
- }, children: "Application Error" }), _jsxs("h1", { style: {
77
- fontSize: '18px',
78
- }, children: [error.status, " ", error.statusText] }), _jsx("p", { style: {
79
- fontSize: '18px',
80
- }, children: error.data })] }));
81
- }
82
- else if (error instanceof Error) {
83
- return (_jsxs("section", { style: {
84
- position: 'fixed',
85
- top: 0,
86
- left: 0,
87
- right: 0,
88
- bottom: 0,
89
- zIndex: 100,
90
- display: 'flex',
91
- flexDirection: 'column',
92
- justifyContent: 'start',
93
- alignItems: 'start',
94
- height: '100vh',
95
- width: '100vw',
96
- padding: 20,
97
- backgroundColor: '#fff',
98
- }, children: [_jsx("h1", { style: {
99
- fontSize: '1.4rem',
100
- fontWeight: 'bold',
101
- marginBottom: 5,
102
- }, children: "Application Error" }), _jsx("p", { style: {
103
- fontSize: '1rem',
104
- marginBottom: 10,
105
- }, children: error.message }), _jsxs("div", { style: {
106
- marginTop: 20,
107
- overflow: 'auto',
108
- width: '100%',
109
- maxHeight: '100vh',
110
- padding: '10px 20px',
111
- borderRadius: 10,
112
- backgroundColor: '#f5f5f5',
113
- }, children: [_jsx("p", { style: {
114
- fontWeight: 'bold',
115
- fontSize: '1.2rem',
116
- color: '#000',
117
- }, children: "The stack trace is:" }), _jsx("pre", { style: {
118
- whiteSpace: 'pre-wrap',
119
- wordWrap: 'break-word',
120
- fontSize: '1rem',
121
- color: '#ff0000aa',
122
- }, children: error.stack })] })] }));
123
- }
124
- else {
125
- return _jsx("h1", { children: "Unknown Error" });
126
- }
59
+ useEffect(() => {
60
+ if (!DEV)
61
+ return;
62
+ const err = error instanceof Error
63
+ ? error
64
+ : isRouteErrorResponse(error)
65
+ ? new Error(`${error.status} ${error.statusText}: ${error.data}`)
66
+ : new Error(String(error));
67
+ errorStore.addError(err, 'route');
68
+ }, [error, DEV]);
69
+ return null;
127
70
  }
128
71
  /**
129
72
  * Page component that defines title and description to a page
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Component } from 'react';
3
+ import { errorStore } from './error-store.js';
4
+ export class ErrorBoundaryFallback extends Component {
5
+ constructor(props) {
6
+ super(props);
7
+ this.state = { hasError: false };
8
+ }
9
+ static getDerivedStateFromError() {
10
+ return { hasError: true };
11
+ }
12
+ componentDidCatch(error, info) {
13
+ errorStore.addError(error, 'react', info.componentStack);
14
+ }
15
+ render() {
16
+ if (this.state.hasError) {
17
+ if (typeof process !== 'undefined' &&
18
+ process.env?.NODE_ENV === 'production') {
19
+ return (_jsx("section", { style: {
20
+ position: 'fixed',
21
+ top: 0,
22
+ left: 0,
23
+ right: 0,
24
+ bottom: 0,
25
+ zIndex: 100,
26
+ display: 'flex',
27
+ flexDirection: 'row',
28
+ justifyContent: 'center',
29
+ alignItems: 'center',
30
+ height: '100vh',
31
+ width: '100vw',
32
+ gap: 10,
33
+ backgroundColor: '#fff',
34
+ }, children: _jsx("p", { style: {
35
+ fontSize: '18px',
36
+ }, children: "Application Error" }) }));
37
+ }
38
+ return null;
39
+ }
40
+ return this.props.children;
41
+ }
42
+ }
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState, useSyncExternalStore } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { errorStore } from './error-store.js';
5
+ import { parseStackFrame, fetchSourceSnippet } from './stack-utils.js';
6
+ import './error-overlay.css';
7
+ export function ErrorOverlay() {
8
+ const errors = useSyncExternalStore(errorStore.subscribe.bind(errorStore), () => errorStore.getErrors(), () => errorStore.getErrors());
9
+ const minimized = useSyncExternalStore(errorStore.subscribe.bind(errorStore), () => errorStore.isMinimized(), () => errorStore.isMinimized());
10
+ const [activeTab, setActiveTab] = useState(0);
11
+ const [codePreview, setCodePreview] = useState(null);
12
+ useEffect(() => {
13
+ if (errors.length > 0 && activeTab >= errors.length) {
14
+ setActiveTab(errors.length - 1);
15
+ }
16
+ }, [errors.length, activeTab]);
17
+ useEffect(() => {
18
+ const error = errors[activeTab];
19
+ if (!error || !error.error.stack) {
20
+ setCodePreview(null);
21
+ return;
22
+ }
23
+ const frame = parseStackFrame(error.error.stack);
24
+ if (!frame) {
25
+ setCodePreview(null);
26
+ return;
27
+ }
28
+ fetchSourceSnippet(frame.file, frame.line).then((result) => {
29
+ if (result) {
30
+ setCodePreview({
31
+ snippet: result.snippet,
32
+ errorLineIndex: result.errorLineIndex,
33
+ file: frame.file,
34
+ line: frame.line,
35
+ });
36
+ }
37
+ else {
38
+ setCodePreview(null);
39
+ }
40
+ });
41
+ }, [activeTab, errors]);
42
+ if (errors.length === 0)
43
+ return null;
44
+ if (minimized)
45
+ return createPortal(_jsxs("button", { className: "rasengan-error-fab", onClick: () => errorStore.toggleMinimize(), title: `${errors.length} error${errors.length > 1 ? 's' : ''} — click to view`, children: [_jsx("svg", { className: "rasengan-error-fab-icon", viewBox: "0 0 16 16", width: "18", height: "18", fill: "currentColor", children: _jsx("path", { d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l5.782 10.832c.659 1.234-.165 2.771-1.543 2.771H2.218c-1.378 0-2.202-1.537-1.543-2.771L6.457 1.047zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 7a1 1 0 100-2 1 1 0 000 2z" }) }), _jsx("span", { className: "rasengan-error-fab-count", children: errors.length })] }), document.body);
46
+ const activeError = errors[activeTab];
47
+ if (!activeError)
48
+ return null;
49
+ const totalErrors = errors.length;
50
+ const isFirst = activeTab === 0;
51
+ const isLast = activeTab === totalErrors - 1;
52
+ const snippetLines = codePreview ? codePreview.snippet.split('\n') : [];
53
+ const relativeLineStart = codePreview
54
+ ? codePreview.line - codePreview.errorLineIndex
55
+ : 0;
56
+ return createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: "rasengan-error-backdrop" }), _jsxs("div", { className: "rasengan-error-overlay", children: [_jsxs("div", { className: "rasengan-error-overlay-header", children: [_jsxs("div", { className: "rasengan-error-pagination", children: [_jsx("button", { className: "rasengan-error-pagination-btn", disabled: isFirst, onClick: () => setActiveTab(activeTab - 1), title: "Previous error", children: "\u2039" }), _jsxs("span", { className: "rasengan-error-pagination-text", children: [activeTab + 1, " of ", totalErrors] }), _jsx("button", { className: "rasengan-error-pagination-btn", disabled: isLast, onClick: () => setActiveTab(activeTab + 1), title: "Next error", children: "\u203A" })] }), _jsx("div", { className: "rasengan-error-overlay-actions", children: _jsx("button", { className: "rasengan-error-overlay-close", onClick: () => errorStore.toggleMinimize(), title: "Minimize", children: "\u00D7" }) })] }), _jsxs("div", { className: "rasengan-error-overlay-body", children: [_jsxs("div", { className: "rasengan-error-overlay-source", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", style: { marginRight: 4, verticalAlign: -2 }, children: _jsx("path", { d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l5.782 10.832c.659 1.234-.165 2.771-1.543 2.771H2.218c-1.378 0-2.202-1.537-1.543-2.771L6.457 1.047zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 7a1 1 0 100-2 1 1 0 000 2z" }) }), _jsx("p", { children: activeError.source })] }), _jsx("div", { className: "rasengan-error-overlay-message", children: activeError.error.message || 'Unknown error' }), codePreview && (_jsxs("div", { className: "rasengan-error-code-preview", children: [_jsxs("div", { className: "rasengan-error-code-preview-header", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", style: { marginRight: 4, flexShrink: 0 }, children: _jsx("path", { d: "M2 1.75C2 .784 2.784 0 3.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0112.25 16h-8.5A1.75 1.75 0 012 14.25V1.75zM3.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h8.5a.25.25 0 00.25-.25V5.5h-2.75A1.75 1.75 0 018 3.75V1.5H3.75zm5.75.56v2.19c0 .138.112.25.25.25h2.19L9.5 2.06z" }) }), _jsx("span", { className: "rasengan-error-code-preview-file", children: codePreview.file }), _jsxs("span", { className: "rasengan-error-code-preview-line", children: ["line ", codePreview.line] })] }), _jsx("div", { className: "rasengan-error-code-preview-content", children: snippetLines.map((lineContent, idx) => {
57
+ const isErrorLine = idx === codePreview.errorLineIndex;
58
+ return (_jsxs("div", { className: `rasengan-error-code-line${isErrorLine ? ' error' : ''}`, children: [_jsx("span", { className: "rasengan-error-code-line-number", children: relativeLineStart + idx }), isErrorLine && (_jsx("span", { className: "rasengan-error-code-line-arrow", children: "\u25B8" })), !isErrorLine && (_jsx("span", { className: "rasengan-error-code-line-arrow" })), _jsx("span", { className: "rasengan-error-code-line-content", children: lineContent })] }, idx));
59
+ }) })] })), activeError.componentStack && (_jsxs("details", { className: "rasengan-error-overlay-section", open: true, children: [_jsx("summary", { className: "rasengan-error-overlay-section-title", children: "Component Stack" }), _jsx("pre", { className: "rasengan-error-overlay-stack", children: activeError.componentStack })] })), activeError.error.stack && (_jsxs("details", { className: "rasengan-error-overlay-section", open: true, children: [_jsx("summary", { className: "rasengan-error-overlay-section-title", children: "Stack Trace" }), _jsx("pre", { className: "rasengan-error-overlay-stack", children: activeError.error.stack })] }))] })] })] }), document.body);
60
+ }
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect } from 'react';
3
+ import { ErrorBoundaryFallback } from './ErrorBoundaryFallback.js';
4
+ import { errorStore } from './error-store.js';
5
+ import { ErrorOverlay } from './ErrorOverlay.js';
6
+ export function ErrorOverlayProvider({ children, devMode }) {
7
+ useEffect(() => {
8
+ if (!devMode)
9
+ return;
10
+ const onError = (event) => {
11
+ errorStore.addError(event.error || new Error(event.message), 'global');
12
+ };
13
+ const onRejection = (event) => {
14
+ const error = event.reason instanceof Error
15
+ ? event.reason
16
+ : new Error(String(event.reason));
17
+ errorStore.addError(error, 'global');
18
+ };
19
+ window.addEventListener('error', onError);
20
+ window.addEventListener('unhandledrejection', onRejection);
21
+ const hot = import.meta.hot;
22
+ const handleViteError = (data) => {
23
+ if (data?.err) {
24
+ errorStore.addError(data.err, 'vite');
25
+ }
26
+ };
27
+ if (typeof hot?.on === 'function') {
28
+ hot.on('vite:error', handleViteError);
29
+ }
30
+ return () => {
31
+ window.removeEventListener('error', onError);
32
+ window.removeEventListener('unhandledrejection', onRejection);
33
+ if (typeof hot?.off === 'function') {
34
+ hot.off('vite:error', handleViteError);
35
+ }
36
+ };
37
+ }, [devMode]);
38
+ return (_jsxs(_Fragment, { children: [_jsx(ErrorBoundaryFallback, { children: children }), devMode && _jsx(ErrorOverlay, {})] }));
39
+ }