@mandujs/core 0.13.0 → 0.13.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 (157) hide show
  1. package/README.ko.md +4 -4
  2. package/README.md +653 -653
  3. package/package.json +1 -1
  4. package/src/bundler/build.ts +91 -91
  5. package/src/bundler/css.ts +302 -302
  6. package/src/client/Link.tsx +227 -227
  7. package/src/client/globals.ts +44 -44
  8. package/src/client/hooks.ts +267 -267
  9. package/src/client/index.ts +5 -5
  10. package/src/client/island.ts +8 -8
  11. package/src/client/router.ts +435 -435
  12. package/src/client/runtime.ts +23 -23
  13. package/src/client/serialize.ts +404 -404
  14. package/src/client/window-state.ts +101 -101
  15. package/src/config/mandu.ts +9 -0
  16. package/src/config/validate.ts +12 -0
  17. package/src/config/watcher.ts +311 -311
  18. package/src/constants.ts +40 -40
  19. package/src/content/content-layer.ts +314 -314
  20. package/src/content/content.test.ts +433 -433
  21. package/src/content/data-store.ts +245 -245
  22. package/src/content/digest.ts +133 -133
  23. package/src/content/index.ts +164 -164
  24. package/src/content/loader-context.ts +172 -172
  25. package/src/content/loaders/api.ts +216 -216
  26. package/src/content/loaders/file.ts +169 -169
  27. package/src/content/loaders/glob.ts +252 -252
  28. package/src/content/loaders/index.ts +34 -34
  29. package/src/content/loaders/types.ts +137 -137
  30. package/src/content/meta-store.ts +209 -209
  31. package/src/content/types.ts +282 -282
  32. package/src/content/watcher.ts +135 -135
  33. package/src/contract/client-safe.test.ts +42 -42
  34. package/src/contract/client-safe.ts +114 -114
  35. package/src/contract/client.ts +16 -16
  36. package/src/contract/define.ts +459 -459
  37. package/src/contract/handler.ts +10 -10
  38. package/src/contract/normalize.test.ts +276 -276
  39. package/src/contract/normalize.ts +404 -404
  40. package/src/contract/registry.test.ts +206 -206
  41. package/src/contract/registry.ts +568 -568
  42. package/src/contract/schema.ts +48 -48
  43. package/src/contract/types.ts +58 -58
  44. package/src/contract/validator.ts +32 -32
  45. package/src/devtools/ai/context-builder.ts +375 -375
  46. package/src/devtools/ai/index.ts +25 -25
  47. package/src/devtools/ai/mcp-connector.ts +465 -465
  48. package/src/devtools/client/catchers/error-catcher.ts +327 -327
  49. package/src/devtools/client/catchers/index.ts +18 -18
  50. package/src/devtools/client/catchers/network-proxy.ts +363 -363
  51. package/src/devtools/client/components/index.ts +39 -39
  52. package/src/devtools/client/components/kitchen-root.tsx +362 -362
  53. package/src/devtools/client/components/mandu-character.tsx +241 -241
  54. package/src/devtools/client/components/overlay.tsx +368 -368
  55. package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
  56. package/src/devtools/client/components/panel/guard-panel.tsx +244 -244
  57. package/src/devtools/client/components/panel/index.ts +32 -32
  58. package/src/devtools/client/components/panel/islands-panel.tsx +304 -304
  59. package/src/devtools/client/components/panel/network-panel.tsx +292 -292
  60. package/src/devtools/client/components/panel/panel-container.tsx +259 -259
  61. package/src/devtools/client/filters/context-filters.ts +282 -282
  62. package/src/devtools/client/filters/index.ts +16 -16
  63. package/src/devtools/client/index.ts +63 -63
  64. package/src/devtools/client/persistence.ts +335 -335
  65. package/src/devtools/client/state-manager.ts +478 -478
  66. package/src/devtools/design-tokens.ts +263 -263
  67. package/src/devtools/hook/create-hook.ts +207 -207
  68. package/src/devtools/hook/index.ts +13 -13
  69. package/src/devtools/index.ts +439 -439
  70. package/src/devtools/init.ts +266 -266
  71. package/src/devtools/protocol.ts +237 -237
  72. package/src/devtools/server/index.ts +17 -17
  73. package/src/devtools/server/source-context.ts +444 -444
  74. package/src/devtools/types.ts +319 -319
  75. package/src/devtools/worker/index.ts +25 -25
  76. package/src/devtools/worker/redaction-worker.ts +222 -222
  77. package/src/devtools/worker/worker-manager.ts +409 -409
  78. package/src/error/domains.ts +265 -265
  79. package/src/error/result.ts +46 -46
  80. package/src/error/types.ts +6 -6
  81. package/src/errors/extractor.ts +409 -409
  82. package/src/errors/index.ts +19 -19
  83. package/src/filling/auth.ts +308 -308
  84. package/src/filling/context.ts +24 -1
  85. package/src/filling/deps.ts +238 -238
  86. package/src/filling/index.ts +4 -0
  87. package/src/filling/sse-catchup.test.ts +56 -0
  88. package/src/filling/sse-catchup.ts +67 -0
  89. package/src/filling/sse.test.ts +168 -0
  90. package/src/filling/sse.ts +162 -0
  91. package/src/generator/index.ts +3 -3
  92. package/src/guard/analyzer.ts +360 -360
  93. package/src/guard/ast-analyzer.ts +806 -806
  94. package/src/guard/contract-guard.ts +9 -9
  95. package/src/guard/file-type.test.ts +24 -24
  96. package/src/guard/presets/atomic.ts +70 -70
  97. package/src/guard/presets/clean.ts +77 -77
  98. package/src/guard/presets/fsd.ts +79 -79
  99. package/src/guard/presets/hexagonal.ts +68 -68
  100. package/src/guard/presets/index.ts +291 -291
  101. package/src/guard/reporter.ts +445 -445
  102. package/src/guard/rules.ts +12 -12
  103. package/src/guard/statistics.ts +578 -578
  104. package/src/guard/suggestions.ts +358 -358
  105. package/src/guard/types.ts +348 -348
  106. package/src/guard/validator.ts +834 -834
  107. package/src/guard/watcher.ts +404 -404
  108. package/src/index.ts +6 -1
  109. package/src/intent/index.ts +310 -310
  110. package/src/island/index.ts +304 -304
  111. package/src/logging/index.ts +22 -22
  112. package/src/logging/transports.ts +365 -365
  113. package/src/plugins/index.ts +38 -38
  114. package/src/plugins/registry.ts +377 -377
  115. package/src/plugins/types.ts +363 -363
  116. package/src/report/index.ts +1 -1
  117. package/src/router/fs-patterns.ts +387 -387
  118. package/src/router/fs-scanner.ts +497 -497
  119. package/src/runtime/boundary.tsx +232 -232
  120. package/src/runtime/compose.ts +222 -222
  121. package/src/runtime/escape.ts +44 -0
  122. package/src/runtime/lifecycle.ts +381 -381
  123. package/src/runtime/logger.test.ts +345 -345
  124. package/src/runtime/logger.ts +677 -677
  125. package/src/runtime/router.test.ts +476 -476
  126. package/src/runtime/router.ts +105 -105
  127. package/src/runtime/security.ts +155 -155
  128. package/src/runtime/server.ts +257 -0
  129. package/src/runtime/session-key.ts +328 -328
  130. package/src/runtime/ssr.ts +16 -21
  131. package/src/runtime/streaming-ssr.ts +24 -33
  132. package/src/runtime/trace.ts +144 -144
  133. package/src/seo/index.ts +214 -214
  134. package/src/seo/integration/ssr.ts +307 -307
  135. package/src/seo/render/basic.ts +427 -427
  136. package/src/seo/render/index.ts +143 -143
  137. package/src/seo/render/jsonld.ts +539 -539
  138. package/src/seo/render/opengraph.ts +191 -191
  139. package/src/seo/render/robots.ts +116 -116
  140. package/src/seo/render/sitemap.ts +137 -137
  141. package/src/seo/render/twitter.ts +126 -126
  142. package/src/seo/resolve/index.ts +353 -353
  143. package/src/seo/resolve/opengraph.ts +143 -143
  144. package/src/seo/resolve/robots.ts +73 -73
  145. package/src/seo/resolve/title.ts +94 -94
  146. package/src/seo/resolve/twitter.ts +73 -73
  147. package/src/seo/resolve/url.ts +97 -97
  148. package/src/seo/routes/index.ts +290 -290
  149. package/src/seo/types.ts +575 -575
  150. package/src/slot/validator.ts +39 -39
  151. package/src/spec/index.ts +3 -3
  152. package/src/spec/load.ts +76 -76
  153. package/src/spec/lock.ts +56 -56
  154. package/src/utils/bun.ts +8 -8
  155. package/src/utils/lru-cache.ts +75 -75
  156. package/src/utils/safe-io.ts +188 -188
  157. package/src/utils/string-safe.ts +298 -298
@@ -1,259 +1,259 @@
1
- /**
2
- * Mandu Kitchen DevTools - Errors Panel
3
- * @version 1.0.3
4
- */
5
-
6
- import React, { useCallback } from 'react';
7
- import type { NormalizedError } from '../../../types';
8
- import { colors, typography, spacing, borderRadius, animation, testIds } from '../../../design-tokens';
9
- import { ManduCharacter } from '../mandu-character';
10
- import { sanitizeErrorMessage } from '../../filters';
11
-
12
- // ============================================================================
13
- // Styles
14
- // ============================================================================
15
-
16
- const styles = {
17
- container: {
18
- padding: spacing.md,
19
- height: '100%',
20
- display: 'flex',
21
- flexDirection: 'column' as const,
22
- gap: spacing.md,
23
- },
24
- header: {
25
- display: 'flex',
26
- alignItems: 'center',
27
- justifyContent: 'space-between',
28
- },
29
- clearButton: {
30
- padding: `${spacing.xs} ${spacing.sm}`,
31
- borderRadius: borderRadius.sm,
32
- backgroundColor: colors.background.light,
33
- border: 'none',
34
- color: colors.text.secondary,
35
- fontSize: typography.fontSize.xs,
36
- cursor: 'pointer',
37
- transition: `all ${animation.duration.fast}`,
38
- },
39
- list: {
40
- flex: 1,
41
- overflow: 'auto',
42
- display: 'flex',
43
- flexDirection: 'column' as const,
44
- gap: spacing.sm,
45
- },
46
- emptyState: {
47
- flex: 1,
48
- display: 'flex',
49
- flexDirection: 'column' as const,
50
- alignItems: 'center',
51
- justifyContent: 'center',
52
- gap: spacing.md,
53
- padding: spacing.xl,
54
- color: colors.text.muted,
55
- },
56
- emptyMessage: {
57
- fontSize: typography.fontSize.sm,
58
- textAlign: 'center' as const,
59
- },
60
- errorItem: {
61
- padding: spacing.md,
62
- borderRadius: borderRadius.md,
63
- backgroundColor: colors.background.medium,
64
- cursor: 'pointer',
65
- transition: `all ${animation.duration.fast}`,
66
- borderLeft: '3px solid',
67
- },
68
- errorHeader: {
69
- display: 'flex',
70
- alignItems: 'flex-start',
71
- justifyContent: 'space-between',
72
- gap: spacing.sm,
73
- marginBottom: spacing.xs,
74
- },
75
- errorType: {
76
- display: 'flex',
77
- alignItems: 'center',
78
- gap: spacing.xs,
79
- },
80
- errorBadge: {
81
- padding: `2px ${spacing.xs}`,
82
- borderRadius: borderRadius.sm,
83
- fontSize: typography.fontSize.xs,
84
- fontWeight: typography.fontWeight.medium,
85
- textTransform: 'uppercase' as const,
86
- },
87
- errorTime: {
88
- fontSize: typography.fontSize.xs,
89
- color: colors.text.muted,
90
- whiteSpace: 'nowrap' as const,
91
- },
92
- errorMessage: {
93
- fontSize: typography.fontSize.sm,
94
- color: colors.text.primary,
95
- lineHeight: typography.lineHeight.normal,
96
- wordBreak: 'break-word' as const,
97
- display: '-webkit-box',
98
- WebkitLineClamp: 2,
99
- WebkitBoxOrient: 'vertical' as const,
100
- overflow: 'hidden',
101
- },
102
- errorSource: {
103
- marginTop: spacing.xs,
104
- fontSize: typography.fontSize.xs,
105
- color: colors.text.muted,
106
- fontFamily: typography.fontFamily.mono,
107
- },
108
- actionButton: {
109
- padding: spacing.xs,
110
- borderRadius: borderRadius.sm,
111
- backgroundColor: 'transparent',
112
- border: 'none',
113
- color: colors.text.muted,
114
- fontSize: typography.fontSize.sm,
115
- cursor: 'pointer',
116
- opacity: 0,
117
- transition: `opacity ${animation.duration.fast}`,
118
- },
119
- };
120
-
121
- const severityColors: Record<string, { bg: string; border: string; text: string }> = {
122
- critical: {
123
- bg: `${colors.semantic.error}15`,
124
- border: colors.semantic.error,
125
- text: colors.semantic.error,
126
- },
127
- error: {
128
- bg: `${colors.semantic.error}15`,
129
- border: colors.semantic.error,
130
- text: colors.semantic.error,
131
- },
132
- warning: {
133
- bg: `${colors.semantic.warning}15`,
134
- border: colors.semantic.warning,
135
- text: colors.semantic.warning,
136
- },
137
- info: {
138
- bg: `${colors.semantic.info}15`,
139
- border: colors.semantic.info,
140
- text: colors.semantic.info,
141
- },
142
- };
143
-
144
- // ============================================================================
145
- // Props
146
- // ============================================================================
147
-
148
- export interface ErrorsPanelProps {
149
- errors: NormalizedError[];
150
- onErrorClick: (error: NormalizedError) => void;
151
- onErrorIgnore: (id: string) => void;
152
- onClearAll: () => void;
153
- }
154
-
155
- // ============================================================================
156
- // Component
157
- // ============================================================================
158
-
159
- export function ErrorsPanel({
160
- errors,
161
- onErrorClick,
162
- onErrorIgnore,
163
- onClearAll,
164
- }: ErrorsPanelProps): React.ReactElement {
165
- const formatTime = useCallback((timestamp: number): string => {
166
- return new Date(timestamp).toLocaleTimeString('ko-KR', {
167
- hour: '2-digit',
168
- minute: '2-digit',
169
- second: '2-digit',
170
- });
171
- }, []);
172
-
173
- if (errors.length === 0) {
174
- return (
175
- <div style={styles.container}>
176
- <div style={styles.emptyState}>
177
- <ManduCharacter state="normal" compact />
178
- <p style={styles.emptyMessage}>
179
- 에러가 없어요!<br />
180
- 만두가 잘 익고 있어요 🥟
181
- </p>
182
- </div>
183
- </div>
184
- );
185
- }
186
-
187
- return (
188
- <div style={styles.container}>
189
- {/* Header */}
190
- <div style={styles.header}>
191
- <span style={{ fontSize: typography.fontSize.sm, color: colors.text.secondary }}>
192
- {errors.length}개의 이슈
193
- </span>
194
- <button style={styles.clearButton} onClick={onClearAll}>
195
- 모두 지우기
196
- </button>
197
- </div>
198
-
199
- {/* Error List */}
200
- <div data-testid={testIds.errorList} style={styles.list}>
201
- {errors.map((error) => {
202
- const severity = severityColors[error.severity] ?? severityColors.error;
203
-
204
- return (
205
- <div
206
- key={error.id}
207
- style={{
208
- ...styles.errorItem,
209
- borderLeftColor: severity.border,
210
- }}
211
- onClick={() => onErrorClick(error)}
212
- onKeyDown={(e) => {
213
- if (e.key === 'Enter') onErrorClick(error);
214
- }}
215
- tabIndex={0}
216
- role="button"
217
- >
218
- <div style={styles.errorHeader}>
219
- <div style={styles.errorType}>
220
- <span
221
- style={{
222
- ...styles.errorBadge,
223
- backgroundColor: severity.bg,
224
- color: severity.text,
225
- }}
226
- >
227
- {error.type}
228
- </span>
229
- <span
230
- style={{
231
- ...styles.errorBadge,
232
- backgroundColor: severity.bg,
233
- color: severity.text,
234
- }}
235
- >
236
- {error.severity}
237
- </span>
238
- </div>
239
- <span style={styles.errorTime}>{formatTime(error.timestamp)}</span>
240
- </div>
241
-
242
- <p style={styles.errorMessage}>
243
- {sanitizeErrorMessage(error.message)}
244
- </p>
245
-
246
- {error.source && (
247
- <p style={styles.errorSource}>
248
- {error.source}
249
- {error.line && `:${error.line}`}
250
- {error.column && `:${error.column}`}
251
- </p>
252
- )}
253
- </div>
254
- );
255
- })}
256
- </div>
257
- </div>
258
- );
259
- }
1
+ /**
2
+ * Mandu Kitchen DevTools - Errors Panel
3
+ * @version 1.0.3
4
+ */
5
+
6
+ import React, { useCallback } from 'react';
7
+ import type { NormalizedError } from '../../../types';
8
+ import { colors, typography, spacing, borderRadius, animation, testIds } from '../../../design-tokens';
9
+ import { ManduCharacter } from '../mandu-character';
10
+ import { sanitizeErrorMessage } from '../../filters';
11
+
12
+ // ============================================================================
13
+ // Styles
14
+ // ============================================================================
15
+
16
+ const styles = {
17
+ container: {
18
+ padding: spacing.md,
19
+ height: '100%',
20
+ display: 'flex',
21
+ flexDirection: 'column' as const,
22
+ gap: spacing.md,
23
+ },
24
+ header: {
25
+ display: 'flex',
26
+ alignItems: 'center',
27
+ justifyContent: 'space-between',
28
+ },
29
+ clearButton: {
30
+ padding: `${spacing.xs} ${spacing.sm}`,
31
+ borderRadius: borderRadius.sm,
32
+ backgroundColor: colors.background.light,
33
+ border: 'none',
34
+ color: colors.text.secondary,
35
+ fontSize: typography.fontSize.xs,
36
+ cursor: 'pointer',
37
+ transition: `all ${animation.duration.fast}`,
38
+ },
39
+ list: {
40
+ flex: 1,
41
+ overflow: 'auto',
42
+ display: 'flex',
43
+ flexDirection: 'column' as const,
44
+ gap: spacing.sm,
45
+ },
46
+ emptyState: {
47
+ flex: 1,
48
+ display: 'flex',
49
+ flexDirection: 'column' as const,
50
+ alignItems: 'center',
51
+ justifyContent: 'center',
52
+ gap: spacing.md,
53
+ padding: spacing.xl,
54
+ color: colors.text.muted,
55
+ },
56
+ emptyMessage: {
57
+ fontSize: typography.fontSize.sm,
58
+ textAlign: 'center' as const,
59
+ },
60
+ errorItem: {
61
+ padding: spacing.md,
62
+ borderRadius: borderRadius.md,
63
+ backgroundColor: colors.background.medium,
64
+ cursor: 'pointer',
65
+ transition: `all ${animation.duration.fast}`,
66
+ borderLeft: '3px solid',
67
+ },
68
+ errorHeader: {
69
+ display: 'flex',
70
+ alignItems: 'flex-start',
71
+ justifyContent: 'space-between',
72
+ gap: spacing.sm,
73
+ marginBottom: spacing.xs,
74
+ },
75
+ errorType: {
76
+ display: 'flex',
77
+ alignItems: 'center',
78
+ gap: spacing.xs,
79
+ },
80
+ errorBadge: {
81
+ padding: `2px ${spacing.xs}`,
82
+ borderRadius: borderRadius.sm,
83
+ fontSize: typography.fontSize.xs,
84
+ fontWeight: typography.fontWeight.medium,
85
+ textTransform: 'uppercase' as const,
86
+ },
87
+ errorTime: {
88
+ fontSize: typography.fontSize.xs,
89
+ color: colors.text.muted,
90
+ whiteSpace: 'nowrap' as const,
91
+ },
92
+ errorMessage: {
93
+ fontSize: typography.fontSize.sm,
94
+ color: colors.text.primary,
95
+ lineHeight: typography.lineHeight.normal,
96
+ wordBreak: 'break-word' as const,
97
+ display: '-webkit-box',
98
+ WebkitLineClamp: 2,
99
+ WebkitBoxOrient: 'vertical' as const,
100
+ overflow: 'hidden',
101
+ },
102
+ errorSource: {
103
+ marginTop: spacing.xs,
104
+ fontSize: typography.fontSize.xs,
105
+ color: colors.text.muted,
106
+ fontFamily: typography.fontFamily.mono,
107
+ },
108
+ actionButton: {
109
+ padding: spacing.xs,
110
+ borderRadius: borderRadius.sm,
111
+ backgroundColor: 'transparent',
112
+ border: 'none',
113
+ color: colors.text.muted,
114
+ fontSize: typography.fontSize.sm,
115
+ cursor: 'pointer',
116
+ opacity: 0,
117
+ transition: `opacity ${animation.duration.fast}`,
118
+ },
119
+ };
120
+
121
+ const severityColors: Record<string, { bg: string; border: string; text: string }> = {
122
+ critical: {
123
+ bg: `${colors.semantic.error}15`,
124
+ border: colors.semantic.error,
125
+ text: colors.semantic.error,
126
+ },
127
+ error: {
128
+ bg: `${colors.semantic.error}15`,
129
+ border: colors.semantic.error,
130
+ text: colors.semantic.error,
131
+ },
132
+ warning: {
133
+ bg: `${colors.semantic.warning}15`,
134
+ border: colors.semantic.warning,
135
+ text: colors.semantic.warning,
136
+ },
137
+ info: {
138
+ bg: `${colors.semantic.info}15`,
139
+ border: colors.semantic.info,
140
+ text: colors.semantic.info,
141
+ },
142
+ };
143
+
144
+ // ============================================================================
145
+ // Props
146
+ // ============================================================================
147
+
148
+ export interface ErrorsPanelProps {
149
+ errors: NormalizedError[];
150
+ onErrorClick: (error: NormalizedError) => void;
151
+ onErrorIgnore: (id: string) => void;
152
+ onClearAll: () => void;
153
+ }
154
+
155
+ // ============================================================================
156
+ // Component
157
+ // ============================================================================
158
+
159
+ export function ErrorsPanel({
160
+ errors,
161
+ onErrorClick,
162
+ onErrorIgnore,
163
+ onClearAll,
164
+ }: ErrorsPanelProps): React.ReactElement {
165
+ const formatTime = useCallback((timestamp: number): string => {
166
+ return new Date(timestamp).toLocaleTimeString('ko-KR', {
167
+ hour: '2-digit',
168
+ minute: '2-digit',
169
+ second: '2-digit',
170
+ });
171
+ }, []);
172
+
173
+ if (errors.length === 0) {
174
+ return (
175
+ <div style={styles.container}>
176
+ <div style={styles.emptyState}>
177
+ <ManduCharacter state="normal" compact />
178
+ <p style={styles.emptyMessage}>
179
+ 에러가 없어요!<br />
180
+ 만두가 잘 익고 있어요 🥟
181
+ </p>
182
+ </div>
183
+ </div>
184
+ );
185
+ }
186
+
187
+ return (
188
+ <div style={styles.container}>
189
+ {/* Header */}
190
+ <div style={styles.header}>
191
+ <span style={{ fontSize: typography.fontSize.sm, color: colors.text.secondary }}>
192
+ {errors.length}개의 이슈
193
+ </span>
194
+ <button style={styles.clearButton} onClick={onClearAll}>
195
+ 모두 지우기
196
+ </button>
197
+ </div>
198
+
199
+ {/* Error List */}
200
+ <div data-testid={testIds.errorList} style={styles.list}>
201
+ {errors.map((error) => {
202
+ const severity = severityColors[error.severity] ?? severityColors.error;
203
+
204
+ return (
205
+ <div
206
+ key={error.id}
207
+ style={{
208
+ ...styles.errorItem,
209
+ borderLeftColor: severity.border,
210
+ }}
211
+ onClick={() => onErrorClick(error)}
212
+ onKeyDown={(e) => {
213
+ if (e.key === 'Enter') onErrorClick(error);
214
+ }}
215
+ tabIndex={0}
216
+ role="button"
217
+ >
218
+ <div style={styles.errorHeader}>
219
+ <div style={styles.errorType}>
220
+ <span
221
+ style={{
222
+ ...styles.errorBadge,
223
+ backgroundColor: severity.bg,
224
+ color: severity.text,
225
+ }}
226
+ >
227
+ {error.type}
228
+ </span>
229
+ <span
230
+ style={{
231
+ ...styles.errorBadge,
232
+ backgroundColor: severity.bg,
233
+ color: severity.text,
234
+ }}
235
+ >
236
+ {error.severity}
237
+ </span>
238
+ </div>
239
+ <span style={styles.errorTime}>{formatTime(error.timestamp)}</span>
240
+ </div>
241
+
242
+ <p style={styles.errorMessage}>
243
+ {sanitizeErrorMessage(error.message)}
244
+ </p>
245
+
246
+ {error.source && (
247
+ <p style={styles.errorSource}>
248
+ {error.source}
249
+ {error.line && `:${error.line}`}
250
+ {error.column && `:${error.column}`}
251
+ </p>
252
+ )}
253
+ </div>
254
+ );
255
+ })}
256
+ </div>
257
+ </div>
258
+ );
259
+ }