@genesislcap/blank-app-seed 5.11.0-prerelease.2 → 5.11.0-prerelease.4
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/.genx/package.json +1 -1
- package/.genx/templates/react/route.hbs +9 -1
- package/.genx/versions.json +3 -3
- package/CHANGELOG.md +18 -0
- package/client-tmp/react/src/components/error-boundary/ErrorBoundary.tsx +250 -0
- package/client-tmp/react/src/components/error-boundary/ErrorBoundaryStyles.ts +161 -0
- package/client-tmp/react/src/main.tsx +4 -1
- package/package.json +1 -1
package/.genx/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useRef } from 'react';
|
|
2
2
|
import { Layout, Model, TabNode } from 'flexlayout-react';
|
|
3
3
|
import { getFlexLayoutStorageKey } from '../../utils/layout';
|
|
4
|
+
import { TileErrorBoundary } from '../../components/error-boundary/ErrorBoundary';
|
|
4
5
|
{{#each route.tiles}}
|
|
5
6
|
import { {{pascalCase this.componentName}} } from './{{pascalCase this.title}}{{pascalCase this.componentType}}';
|
|
6
7
|
{{/each}}
|
|
@@ -31,7 +32,14 @@ const {{pascalCase route.name}} = () => {
|
|
|
31
32
|
|
|
32
33
|
const factory = (node: TabNode) => {
|
|
33
34
|
const Component = componentMap[node.getComponent() ?? ''];
|
|
34
|
-
return Component ?
|
|
35
|
+
return Component ? (
|
|
36
|
+
<TileErrorBoundary
|
|
37
|
+
title={node.getName() ?? node.getComponent() ?? ''}
|
|
38
|
+
tileRegistration={node.getComponent() ?? ''}
|
|
39
|
+
>
|
|
40
|
+
<Component />
|
|
41
|
+
</TileErrorBoundary>
|
|
42
|
+
) : null;
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
const onModelChange = (model: Model) => {
|
package/.genx/versions.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.11.0-prerelease.4](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0-prerelease.3...v5.11.0-prerelease.4) (2026-05-04)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add default error boundary for components [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 0353f8d
|
|
9
|
+
* add default error boundary for React generated apps (#567) fd39752
|
|
10
|
+
|
|
11
|
+
## [5.11.0-prerelease.3](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0-prerelease.2...v5.11.0-prerelease.3) (2026-04-29)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* FUI update [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 8a984be
|
|
17
|
+
* GSF update [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) 4ffdbe6
|
|
18
|
+
* update GSF versions [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) c771f3f
|
|
19
|
+
* update GSF versions [FUI-0](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/0) (#564) def3ad3
|
|
20
|
+
|
|
3
21
|
## [5.11.0-prerelease.2](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.11.0-prerelease.1...v5.11.0-prerelease.2) (2026-04-28)
|
|
4
22
|
|
|
5
23
|
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { errorBoundaryStyles } from './ErrorBoundaryStyles';
|
|
3
|
+
|
|
4
|
+
type ErrorBoundaryScope = 'application' | 'tile';
|
|
5
|
+
|
|
6
|
+
type ErrorBoundaryFallbackProps = {
|
|
7
|
+
scope: ErrorBoundaryScope;
|
|
8
|
+
referenceId: string;
|
|
9
|
+
title: string;
|
|
10
|
+
subtitle: string;
|
|
11
|
+
details: string;
|
|
12
|
+
onRetry: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type BaseErrorBoundaryProps = {
|
|
16
|
+
scope: ErrorBoundaryScope;
|
|
17
|
+
title: string;
|
|
18
|
+
tileRegistration?: string;
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type BaseErrorBoundaryState = {
|
|
23
|
+
hasError: boolean;
|
|
24
|
+
error: Error | null;
|
|
25
|
+
componentStack: string;
|
|
26
|
+
capturedAt: string;
|
|
27
|
+
referenceId: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const initialBoundaryState: BaseErrorBoundaryState = {
|
|
31
|
+
hasError: false,
|
|
32
|
+
error: null,
|
|
33
|
+
componentStack: '',
|
|
34
|
+
capturedAt: '',
|
|
35
|
+
referenceId: '',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const toError = (value: unknown): Error => {
|
|
39
|
+
if (value instanceof Error) {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof value === 'string') {
|
|
44
|
+
return new Error(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
return new Error(JSON.stringify(value));
|
|
49
|
+
} catch {
|
|
50
|
+
return new Error('Unknown non-serializable error');
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const createReferenceId = (): string => {
|
|
55
|
+
const timestamp = new Date().toISOString().split(':').join('-');
|
|
56
|
+
const random = Math.random().toString(36).slice(2, 8).toUpperCase();
|
|
57
|
+
return `APP-${timestamp}-${random}`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const ErrorBoundaryFallback: React.FC<ErrorBoundaryFallbackProps> = ({
|
|
61
|
+
scope,
|
|
62
|
+
referenceId,
|
|
63
|
+
title,
|
|
64
|
+
subtitle,
|
|
65
|
+
details,
|
|
66
|
+
onRetry,
|
|
67
|
+
}) => {
|
|
68
|
+
const [copied, setCopied] = useState(false);
|
|
69
|
+
const [copyHelpVisible, setCopyHelpVisible] = useState(false);
|
|
70
|
+
|
|
71
|
+
const handleCopy = async () => {
|
|
72
|
+
try {
|
|
73
|
+
await navigator.clipboard.writeText(details);
|
|
74
|
+
setCopied(true);
|
|
75
|
+
setCopyHelpVisible(false);
|
|
76
|
+
window.setTimeout(() => {
|
|
77
|
+
setCopied(false);
|
|
78
|
+
}, 2_000);
|
|
79
|
+
} catch {
|
|
80
|
+
setCopyHelpVisible(true);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<section className="error-boundary" role="alert">
|
|
86
|
+
<style>{errorBoundaryStyles}</style>
|
|
87
|
+
<div className="error-boundary__top">
|
|
88
|
+
<span className="error-boundary__status">
|
|
89
|
+
{scope === 'application' ? 'Application incident' : 'Tile incident'}
|
|
90
|
+
</span>
|
|
91
|
+
<span className="error-boundary__reference">Ref: {referenceId}</span>
|
|
92
|
+
</div>
|
|
93
|
+
<h2 className="error-boundary__title">{title}</h2>
|
|
94
|
+
<p className="error-boundary__subtitle">{subtitle}</p>
|
|
95
|
+
<p className="error-boundary__hint">
|
|
96
|
+
Tip: copy diagnostics and paste directly into Cursor to speed up debugging.
|
|
97
|
+
</p>
|
|
98
|
+
<textarea
|
|
99
|
+
className="error-boundary__details"
|
|
100
|
+
readOnly
|
|
101
|
+
value={details}
|
|
102
|
+
aria-label="Error diagnostics details"
|
|
103
|
+
/>
|
|
104
|
+
<div className="error-boundary__actions">
|
|
105
|
+
<button type="button" className="error-boundary__button" onClick={onRetry}>
|
|
106
|
+
Retry
|
|
107
|
+
</button>
|
|
108
|
+
<button
|
|
109
|
+
type="button"
|
|
110
|
+
className="error-boundary__button error-boundary__button--secondary"
|
|
111
|
+
onClick={handleCopy}
|
|
112
|
+
>
|
|
113
|
+
{copied ? 'Copied' : 'Copy diagnostics'}
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
{copyHelpVisible ? (
|
|
117
|
+
<p className="error-boundary__manual-copy-help">
|
|
118
|
+
Clipboard access is blocked in this browser context. Please select the diagnostics text
|
|
119
|
+
and copy it manually.
|
|
120
|
+
</p>
|
|
121
|
+
) : null}
|
|
122
|
+
</section>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
class BaseErrorBoundary extends React.Component<BaseErrorBoundaryProps, BaseErrorBoundaryState> {
|
|
127
|
+
public constructor(props: BaseErrorBoundaryProps) {
|
|
128
|
+
super(props);
|
|
129
|
+
this.state = initialBoundaryState;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public static getDerivedStateFromError(error: unknown): Partial<BaseErrorBoundaryState> {
|
|
133
|
+
const normalized = toError(error);
|
|
134
|
+
return {
|
|
135
|
+
hasError: true,
|
|
136
|
+
error: normalized,
|
|
137
|
+
capturedAt: new Date().toISOString(),
|
|
138
|
+
referenceId: createReferenceId(),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void {
|
|
143
|
+
const normalized = toError(error);
|
|
144
|
+
this.setState({
|
|
145
|
+
componentStack: errorInfo.componentStack ?? '',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
console.error(`[${this.props.scope}] error boundary captured an error`, {
|
|
149
|
+
referenceId: this.state.referenceId,
|
|
150
|
+
title: this.props.title,
|
|
151
|
+
tileRegistration: this.props.tileRegistration,
|
|
152
|
+
error: normalized,
|
|
153
|
+
componentStack: errorInfo.componentStack,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private readonly handleRetry = (): void => {
|
|
158
|
+
this.setState(initialBoundaryState);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
public render(): React.ReactNode {
|
|
162
|
+
if (!this.state.hasError || !this.state.error) {
|
|
163
|
+
return this.props.children;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { scope, title, tileRegistration } = this.props;
|
|
167
|
+
const diagnostics = [
|
|
168
|
+
`Reference: ${this.state.referenceId}`,
|
|
169
|
+
`Scope: ${scope}`,
|
|
170
|
+
`Title: ${title}`,
|
|
171
|
+
`Tile registration: ${tileRegistration ?? 'N/A'}`,
|
|
172
|
+
`Captured at: ${this.state.capturedAt}`,
|
|
173
|
+
`URL: ${window.location.href}`,
|
|
174
|
+
`User agent: ${window.navigator.userAgent}`,
|
|
175
|
+
`Error name: ${this.state.error.name}`,
|
|
176
|
+
`Error message: ${this.state.error.message}`,
|
|
177
|
+
'Error stack:',
|
|
178
|
+
this.state.error.stack ?? 'No stack available',
|
|
179
|
+
'Component stack:',
|
|
180
|
+
this.state.componentStack || 'No component stack available',
|
|
181
|
+
].join('\n');
|
|
182
|
+
|
|
183
|
+
const fallbackTitle =
|
|
184
|
+
scope === 'application'
|
|
185
|
+
? 'Something went wrong'
|
|
186
|
+
: `Something went wrong in "${title}"`;
|
|
187
|
+
const fallbackSubtitle =
|
|
188
|
+
scope === 'application'
|
|
189
|
+
? 'The app hit an unexpected error. You can retry or copy diagnostics for a fast fix.'
|
|
190
|
+
: 'This tile crashed, but the rest of the application keeps running. Retry or copy diagnostics.';
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<ErrorBoundaryFallback
|
|
194
|
+
scope={scope}
|
|
195
|
+
referenceId={this.state.referenceId}
|
|
196
|
+
title={fallbackTitle}
|
|
197
|
+
subtitle={fallbackSubtitle}
|
|
198
|
+
details={diagnostics}
|
|
199
|
+
onRetry={this.handleRetry}
|
|
200
|
+
/>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
type AppErrorBoundaryProps = {
|
|
206
|
+
children: React.ReactNode;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export const AppErrorBoundary: React.FC<AppErrorBoundaryProps> = ({ children }) => {
|
|
210
|
+
return (
|
|
211
|
+
<BaseErrorBoundary scope="application" title="Application">
|
|
212
|
+
{children}
|
|
213
|
+
</BaseErrorBoundary>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
type TileErrorBoundaryProps = {
|
|
218
|
+
title: string;
|
|
219
|
+
tileRegistration: string;
|
|
220
|
+
children: React.ReactNode;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const TileErrorBoundary: React.FC<TileErrorBoundaryProps> = ({
|
|
224
|
+
title,
|
|
225
|
+
tileRegistration,
|
|
226
|
+
children,
|
|
227
|
+
}) => {
|
|
228
|
+
return (
|
|
229
|
+
<BaseErrorBoundary scope="tile" title={title} tileRegistration={tileRegistration}>
|
|
230
|
+
{children}
|
|
231
|
+
</BaseErrorBoundary>
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const withTileErrorBoundary = (
|
|
236
|
+
Component: React.ComponentType,
|
|
237
|
+
title: string,
|
|
238
|
+
tileRegistration: string,
|
|
239
|
+
): React.FC => {
|
|
240
|
+
const WrappedWithErrorBoundary: React.FC = () => {
|
|
241
|
+
return (
|
|
242
|
+
<TileErrorBoundary title={title} tileRegistration={tileRegistration}>
|
|
243
|
+
<Component />
|
|
244
|
+
</TileErrorBoundary>
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
WrappedWithErrorBoundary.displayName = `WithTileErrorBoundary(${title}:${tileRegistration}:${Component.displayName || Component.name || 'TileComponent'})`;
|
|
249
|
+
return WrappedWithErrorBoundary;
|
|
250
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export const errorBoundaryStyles = `
|
|
2
|
+
.error-boundary,
|
|
3
|
+
.error-boundary__tile-wrap {
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.error-boundary {
|
|
9
|
+
--eb-bg: #f6f8fb;
|
|
10
|
+
--eb-surface: #ffffff;
|
|
11
|
+
--eb-text: #18212f;
|
|
12
|
+
--eb-text-muted: #5f6f84;
|
|
13
|
+
--eb-border: #d3dae6;
|
|
14
|
+
--eb-accent: #2f6fed;
|
|
15
|
+
--eb-accent-hover: #255dcc;
|
|
16
|
+
--eb-accent-foreground: #ffffff;
|
|
17
|
+
--eb-error: #c53d3d;
|
|
18
|
+
--eb-focus: #2f6fed;
|
|
19
|
+
--eb-font: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
20
|
+
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 12px;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
padding: 16px;
|
|
27
|
+
background: var(--eb-bg);
|
|
28
|
+
color: var(--eb-text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.error-boundary__top,
|
|
32
|
+
.error-boundary__title,
|
|
33
|
+
.error-boundary__subtitle,
|
|
34
|
+
.error-boundary__hint,
|
|
35
|
+
.error-boundary__details,
|
|
36
|
+
.error-boundary__actions,
|
|
37
|
+
.error-boundary__manual-copy-help {
|
|
38
|
+
width: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.error-boundary__top {
|
|
42
|
+
display: flex;
|
|
43
|
+
width: 100%;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
gap: 10px;
|
|
47
|
+
flex-wrap: wrap;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.error-boundary__status {
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
border: 1px solid var(--eb-error);
|
|
54
|
+
border-radius: 999px;
|
|
55
|
+
padding: 5px 10px;
|
|
56
|
+
font-family: var(--eb-font);
|
|
57
|
+
text-transform: uppercase;
|
|
58
|
+
letter-spacing: 0.13em;
|
|
59
|
+
font-size: 11px;
|
|
60
|
+
font-weight: 700;
|
|
61
|
+
color: var(--eb-error);
|
|
62
|
+
background: #fff4f4;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.error-boundary__reference {
|
|
66
|
+
font-family: 'Menlo', 'Consolas', 'Liberation Mono', monospace;
|
|
67
|
+
font-size: 11px;
|
|
68
|
+
letter-spacing: 0.08em;
|
|
69
|
+
color: var(--eb-text-muted);
|
|
70
|
+
text-transform: uppercase;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.error-boundary__title {
|
|
74
|
+
margin: 0;
|
|
75
|
+
font-family: var(--eb-font);
|
|
76
|
+
font-size: clamp(22px, 3vw, 30px);
|
|
77
|
+
line-height: 1.15;
|
|
78
|
+
font-weight: 700;
|
|
79
|
+
color: var(--eb-text);
|
|
80
|
+
text-wrap: balance;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.error-boundary__subtitle {
|
|
84
|
+
margin: 0;
|
|
85
|
+
max-width: 80ch;
|
|
86
|
+
font-family: var(--eb-font);
|
|
87
|
+
color: var(--eb-text);
|
|
88
|
+
line-height: 1.45;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.error-boundary__hint {
|
|
92
|
+
margin: -2px 0 2px;
|
|
93
|
+
font-family: var(--eb-font);
|
|
94
|
+
color: var(--eb-text-muted);
|
|
95
|
+
font-size: 13px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.error-boundary__details {
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
width: 100%;
|
|
101
|
+
min-height: 190px;
|
|
102
|
+
max-height: 360px;
|
|
103
|
+
padding: 12px;
|
|
104
|
+
border: 1px solid var(--eb-border);
|
|
105
|
+
border-radius: 10px;
|
|
106
|
+
background: var(--eb-surface);
|
|
107
|
+
color: var(--eb-text);
|
|
108
|
+
font-family: 'Menlo', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
|
|
109
|
+
font-size: 12px;
|
|
110
|
+
line-height: 1.5;
|
|
111
|
+
resize: vertical;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.error-boundary__actions {
|
|
115
|
+
display: flex;
|
|
116
|
+
gap: 10px;
|
|
117
|
+
flex-wrap: wrap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.error-boundary__button {
|
|
121
|
+
border: 1px solid var(--eb-accent);
|
|
122
|
+
border-radius: 999px;
|
|
123
|
+
background: var(--eb-accent);
|
|
124
|
+
color: var(--eb-accent-foreground);
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
padding: 9px 16px;
|
|
127
|
+
font-family: var(--eb-font);
|
|
128
|
+
font-size: 12px;
|
|
129
|
+
font-weight: 700;
|
|
130
|
+
letter-spacing: 0.04em;
|
|
131
|
+
text-transform: uppercase;
|
|
132
|
+
transition: transform 180ms ease, background-color 180ms ease;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.error-boundary__button--secondary {
|
|
136
|
+
border-color: var(--eb-border);
|
|
137
|
+
background: var(--eb-surface);
|
|
138
|
+
color: var(--eb-text);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.error-boundary__button:hover {
|
|
142
|
+
background: var(--eb-accent-hover);
|
|
143
|
+
transform: translateY(-1px);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.error-boundary__button--secondary:hover {
|
|
147
|
+
background: #eef2f8;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.error-boundary__button:focus-visible {
|
|
151
|
+
outline: 2px solid var(--eb-focus);
|
|
152
|
+
outline-offset: 2px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.error-boundary__manual-copy-help {
|
|
156
|
+
margin: 2px 0 0;
|
|
157
|
+
font-size: 13px;
|
|
158
|
+
color: var(--eb-text-muted);
|
|
159
|
+
line-height: 1.4;
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom/client'
|
|
3
3
|
import App from './App.tsx'
|
|
4
|
+
import { AppErrorBoundary } from './components/error-boundary/ErrorBoundary'
|
|
4
5
|
|
|
5
6
|
import { registerPBCs } from './pbc/utils';
|
|
6
7
|
import { createLogger } from '@genesislcap/foundation-logger';
|
|
@@ -16,7 +17,9 @@ function bootstrapApp() {
|
|
|
16
17
|
if (rootEelement) {
|
|
17
18
|
ReactDOM.createRoot(rootEelement!).render(
|
|
18
19
|
<React.StrictMode>
|
|
19
|
-
<
|
|
20
|
+
<AppErrorBoundary>
|
|
21
|
+
<App rootElement={rootEelement} />
|
|
22
|
+
</AppErrorBoundary>
|
|
20
23
|
</React.StrictMode>,
|
|
21
24
|
)
|
|
22
25
|
}
|