@mandujs/core 0.19.0 → 0.20.0

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 (100) hide show
  1. package/README.ko.md +0 -14
  2. package/package.json +4 -1
  3. package/src/brain/architecture/analyzer.ts +4 -4
  4. package/src/brain/doctor/analyzer.ts +18 -14
  5. package/src/bundler/build.test.ts +127 -0
  6. package/src/bundler/build.ts +385 -115
  7. package/src/bundler/css.ts +20 -5
  8. package/src/bundler/dev.ts +55 -2
  9. package/src/bundler/prerender.ts +195 -0
  10. package/src/bundler/types.ts +20 -0
  11. package/src/change/snapshot.ts +4 -23
  12. package/src/change/types.ts +2 -3
  13. package/src/client/Form.tsx +105 -0
  14. package/src/client/__tests__/use-sse.test.ts +153 -0
  15. package/src/client/hooks.ts +105 -6
  16. package/src/client/index.ts +35 -6
  17. package/src/client/router.ts +670 -433
  18. package/src/client/rpc.ts +140 -0
  19. package/src/client/runtime.ts +24 -21
  20. package/src/client/use-fetch.ts +239 -0
  21. package/src/client/use-head.ts +197 -0
  22. package/src/client/use-sse.ts +378 -0
  23. package/src/components/Image.tsx +162 -0
  24. package/src/config/mandu.ts +5 -0
  25. package/src/config/validate.ts +34 -0
  26. package/src/content/index.ts +5 -1
  27. package/src/devtools/client/catchers/error-catcher.ts +17 -0
  28. package/src/devtools/client/catchers/network-proxy.ts +390 -367
  29. package/src/devtools/client/components/kitchen-root.tsx +479 -467
  30. package/src/devtools/client/components/mandu-character.tsx +77 -53
  31. package/src/devtools/client/components/panel/diff-viewer.tsx +219 -0
  32. package/src/devtools/client/components/panel/errors-panel.tsx +2 -2
  33. package/src/devtools/client/components/panel/guard-panel.tsx +363 -234
  34. package/src/devtools/client/components/panel/index.ts +45 -32
  35. package/src/devtools/client/components/panel/islands-panel.tsx +30 -14
  36. package/src/devtools/client/components/panel/network-panel.tsx +2 -3
  37. package/src/devtools/client/components/panel/panel-container.tsx +273 -100
  38. package/src/devtools/client/components/panel/preview-panel.tsx +212 -0
  39. package/src/devtools/client/state-manager.ts +535 -478
  40. package/src/devtools/design-tokens.ts +265 -264
  41. package/src/devtools/init.ts +1 -1
  42. package/src/devtools/types.ts +321 -295
  43. package/src/filling/filling.ts +328 -6
  44. package/src/filling/index.ts +5 -1
  45. package/src/filling/session.ts +216 -0
  46. package/src/filling/ws.ts +78 -0
  47. package/src/generator/generate.ts +2 -2
  48. package/src/guard/auto-correct.ts +0 -29
  49. package/src/guard/check.ts +14 -31
  50. package/src/guard/presets/index.ts +296 -294
  51. package/src/guard/rules.ts +15 -19
  52. package/src/guard/validator.ts +834 -834
  53. package/src/index.ts +6 -1
  54. package/src/island/index.ts +373 -304
  55. package/src/kitchen/api/contract-api.ts +225 -0
  56. package/src/kitchen/api/diff-parser.ts +108 -0
  57. package/src/kitchen/api/file-api.ts +273 -0
  58. package/src/kitchen/api/guard-api.ts +83 -0
  59. package/src/kitchen/api/guard-decisions.ts +100 -0
  60. package/src/kitchen/api/routes-api.ts +50 -0
  61. package/src/kitchen/index.ts +21 -0
  62. package/src/kitchen/kitchen-handler.ts +335 -0
  63. package/src/kitchen/kitchen-ui.ts +1732 -0
  64. package/src/kitchen/stream/activity-sse.ts +145 -0
  65. package/src/kitchen/stream/file-tailer.ts +99 -0
  66. package/src/middleware/compress.ts +62 -0
  67. package/src/middleware/cors.ts +47 -0
  68. package/src/middleware/index.ts +10 -0
  69. package/src/middleware/jwt.ts +134 -0
  70. package/src/middleware/logger.ts +58 -0
  71. package/src/middleware/timeout.ts +55 -0
  72. package/src/observability/event-bus.ts +79 -0
  73. package/src/observability/index.ts +8 -0
  74. package/src/observability/logger-adapter.ts +36 -0
  75. package/src/paths.ts +0 -4
  76. package/src/plugins/hooks.ts +64 -0
  77. package/src/plugins/index.ts +3 -0
  78. package/src/plugins/types.ts +5 -0
  79. package/src/report/build.ts +0 -6
  80. package/src/resource/__tests__/backward-compat.test.ts +0 -1
  81. package/src/router/fs-patterns.ts +11 -1
  82. package/src/router/fs-routes.ts +78 -14
  83. package/src/router/fs-scanner.ts +2 -2
  84. package/src/router/fs-types.ts +2 -1
  85. package/src/runtime/adapter-bun.ts +62 -0
  86. package/src/runtime/adapter.ts +47 -0
  87. package/src/runtime/cache.ts +310 -0
  88. package/src/runtime/handler.ts +65 -0
  89. package/src/runtime/image-handler.ts +195 -0
  90. package/src/runtime/index.ts +13 -0
  91. package/src/runtime/middleware.ts +263 -0
  92. package/src/runtime/ppr.ts +74 -0
  93. package/src/runtime/server.ts +706 -71
  94. package/src/runtime/ssr.ts +70 -31
  95. package/src/runtime/streaming-ssr.ts +121 -78
  96. package/src/spec/index.ts +0 -1
  97. package/src/spec/schema.ts +1 -0
  98. package/src/testing/index.ts +189 -0
  99. package/src/watcher/watcher.ts +27 -1
  100. package/src/spec/lock.ts +0 -56
@@ -1,319 +1,345 @@
1
- /**
2
- * Mandu Kitchen DevTools - Type Definitions
3
- * @version 1.0.3
4
- */
5
-
6
- // ============================================================================
7
- // Core Event Types
8
- // ============================================================================
9
-
10
- export interface KitchenEvent<T extends string = string, D = unknown> {
11
- type: T;
12
- timestamp: number;
13
- data: D;
14
- }
15
-
16
- // ============================================================================
17
- // Error Types
18
- // ============================================================================
19
-
20
- export type ErrorType = 'runtime' | 'unhandled' | 'react' | 'network' | 'hmr' | 'guard';
21
- export type DevToolsSeverity = 'critical' | 'error' | 'warning' | 'info';
22
-
23
- export interface NormalizedError {
24
- id: string;
25
- type: ErrorType;
26
- severity: DevToolsSeverity;
27
- message: string;
28
- stack?: string;
29
- source?: string;
30
- line?: number;
31
- column?: number;
32
- componentStack?: string;
33
- islandId?: string;
34
- timestamp: number;
35
- url: string;
36
- }
37
-
38
- // ============================================================================
39
- // Island Types
40
- // ============================================================================
41
-
42
- export type DevToolsHydrationStrategy = 'load' | 'idle' | 'visible' | 'media' | 'never';
43
- export type IslandStatus = 'ssr' | 'pending' | 'hydrating' | 'hydrated' | 'error';
44
-
45
- export interface IslandSnapshot {
46
- id: string;
47
- name: string;
48
- strategy: DevToolsHydrationStrategy;
49
- status: IslandStatus;
50
- ssrRenderTime?: number;
51
- hydrateStartTime?: number;
52
- hydrateEndTime?: number;
53
- bundleSize?: number;
54
- }
55
-
56
- // ============================================================================
57
- // Network Types
58
- // ============================================================================
59
-
60
- export interface NetworkRequest {
61
- id: string;
62
- method: string;
63
- url: string;
64
- safeHeaders: Record<string, string>;
65
- redactedHeaders: string[];
66
- body?: {
67
- available: boolean;
68
- size: number;
69
- content?: unknown;
70
- };
71
- status?: number;
72
- startTime: number;
73
- endTime?: number;
74
- isStreaming: boolean;
75
- chunkCount?: number;
76
- }
77
-
78
- export interface NetworkBodyPolicy {
79
- collectBody: boolean;
80
- optInPolicy?: {
81
- maxBytes: number;
82
- applyPIIFilter: boolean;
83
- applySecretFilter: boolean;
84
- allowedContentTypes: string[];
85
- };
86
- }
87
-
88
- // ============================================================================
89
- // Guard Types
90
- // ============================================================================
91
-
92
- export interface DevToolsGuardViolation {
93
- id: string;
94
- ruleId: string;
95
- ruleName: string;
96
- severity: 'error' | 'warning';
97
- message: string;
98
- source: {
99
- file: string;
100
- line?: number;
101
- column?: number;
102
- };
103
- target?: {
104
- file: string;
105
- line?: number;
106
- };
107
- suggestion?: string;
108
- timestamp: number;
109
- }
110
-
111
- // ============================================================================
112
- // AI Context Types
113
- // ============================================================================
114
-
115
- export interface CodeContextInfo {
116
- filePath: string;
117
- line: number;
118
- column?: number;
119
- sourcemapUrl?: string;
120
- snippet?: {
121
- content: string;
122
- lineRange: [number, number];
123
- source: 'dev-server' | 'sourcemap-inline' | 'unavailable';
124
- };
125
- }
126
-
127
- export interface AIContextPayload {
128
- error: NormalizedError;
129
- island?: IslandSnapshot;
130
- framework: { name: 'mandu'; version: string };
131
- devtools: { version: string };
132
- recentErrors?: Array<{
133
- id: string;
134
- message: string;
135
- timestamp: number;
136
- isCausedBy?: string;
137
- }>;
138
- userActions?: Array<{
139
- type: 'navigation' | 'interaction' | 'reload';
140
- targetHint?: string;
141
- timestamp: number;
142
- }>;
143
- codeContext?: CodeContextInfo;
144
- }
145
-
146
- // ============================================================================
147
- // Redaction Types
148
- // ============================================================================
149
-
150
- export interface RedactPattern {
151
- source: string;
152
- flags?: string;
153
- replacement?: string;
154
- label?: string;
155
- }
156
-
157
- // ============================================================================
158
- // Persistence Types
159
- // ============================================================================
160
-
161
- export interface PreserveLogConfig {
162
- enabled: boolean;
163
- maxPersistEvents: number;
164
- maxPersistBytes: number;
165
- priority: 'errors-first' | 'recent-first';
166
- incremental?: {
167
- enabled: boolean;
168
- idleSyncMs: number;
169
- };
170
- }
171
-
172
- // ============================================================================
173
- // Worker Types
174
- // ============================================================================
175
-
176
- export interface WorkerPolicy {
177
- timeout: number;
178
- onTimeout: 'fallback-main' | 'skip';
179
- onError: 'disable-worker' | 'retry-once';
180
- maxConsecutiveFailures: number;
181
- }
182
-
183
- export interface WorkerTask {
184
- id: string;
185
- type: 'redact' | 'truncate';
186
- data: {
187
- text: string;
188
- patterns?: RedactPattern[];
189
- maxBytes?: number;
190
- };
191
- }
192
-
193
- // ============================================================================
194
- // Config Types
195
- // ============================================================================
196
-
197
- export type Position = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
198
- export type Theme = 'light' | 'dark' | 'auto';
199
-
200
- export interface DevToolsConfig {
201
- enabled?: boolean;
202
- position?: Position;
203
- defaultOpen?: boolean;
204
- theme?: Theme;
205
-
206
- features?: {
207
- errorOverlay?: boolean;
208
- islandsInspector?: boolean;
209
- networkMonitor?: boolean;
210
- guardViewer?: boolean;
211
- };
212
-
213
- dataSafety?: {
214
- stringMode?: 'smart' | 'strip';
215
- collectUserActions?: boolean;
216
- collectCodeContext?: boolean;
217
- customRedactPatterns?: RedactPattern[];
218
- };
219
-
220
- network?: {
221
- collectBody?: boolean;
222
- bodyMaxBytes?: number;
223
- };
224
-
225
- persistence?: PreserveLogConfig;
226
- plugins?: KitchenPanelPlugin[];
227
- }
228
-
229
- // ============================================================================
230
- // Plugin Types
231
- // ============================================================================
232
-
233
- export interface KitchenAPI {
234
- subscribe(type: string, callback: (event: KitchenEvent) => void): () => void;
235
- getErrors(): NormalizedError[];
236
- getIslands(): IslandSnapshot[];
237
- getNetworkRequests(): NetworkRequest[];
238
- clearErrors(): void;
239
- getConfig(): DevToolsConfig;
240
- copyToClipboard(text: string): Promise<void>;
241
- openInEditor(file: string, line?: number): void;
242
- }
243
-
244
- export interface KitchenPanelPlugin {
245
- id: string;
246
- name: string;
247
- icon: string;
248
- order: number;
249
-
250
- init(api: KitchenAPI): void;
251
- destroy?(): void;
252
- render(container: HTMLElement): void;
253
- onEvent?(event: KitchenEvent): void;
254
- }
255
-
256
- // ============================================================================
257
- // Meta Log Types
258
- // ============================================================================
259
-
260
- export type MetaLogType =
261
- | 'init'
262
- | 'hook_fail'
263
- | 'render_fail'
264
- | 'persist_fail'
265
- | 'worker_timeout'
266
- | 'worker_error'
267
- | 'worker_disabled'
268
- | 'recovered';
269
-
270
- export interface KitchenMetaLog {
271
- timestamp: number;
272
- type: MetaLogType;
273
- error?: string;
274
- context: {
275
- eventCount: number;
276
- activeTab: string;
277
- memoryInfo?: { usedJSHeapSize?: number };
278
- };
279
- }
280
-
281
- // ============================================================================
282
- // Mandu Character Types
283
- // ============================================================================
284
-
1
+ /**
2
+ * Mandu Kitchen DevTools - Type Definitions
3
+ * @version 1.0.3
4
+ */
5
+
6
+ // ============================================================================
7
+ // Core Event Types
8
+ // ============================================================================
9
+
10
+ export interface KitchenEvent<T extends string = string, D = unknown> {
11
+ type: T;
12
+ timestamp: number;
13
+ data: D;
14
+ }
15
+
16
+ // ============================================================================
17
+ // Error Types
18
+ // ============================================================================
19
+
20
+ export type ErrorType = 'runtime' | 'unhandled' | 'react' | 'network' | 'hmr' | 'guard';
21
+ export type DevToolsSeverity = 'critical' | 'error' | 'warning' | 'info';
22
+
23
+ export interface NormalizedError {
24
+ id: string;
25
+ type: ErrorType;
26
+ severity: DevToolsSeverity;
27
+ message: string;
28
+ stack?: string;
29
+ source?: string;
30
+ line?: number;
31
+ column?: number;
32
+ componentStack?: string;
33
+ islandId?: string;
34
+ timestamp: number;
35
+ url: string;
36
+ }
37
+
38
+ // ============================================================================
39
+ // Island Types
40
+ // ============================================================================
41
+
42
+ export type DevToolsHydrationStrategy = 'load' | 'idle' | 'visible' | 'media' | 'never';
43
+ export type IslandStatus = 'ssr' | 'pending' | 'hydrating' | 'hydrated' | 'error';
44
+
45
+ export interface IslandSnapshot {
46
+ id: string;
47
+ name: string;
48
+ strategy: DevToolsHydrationStrategy;
49
+ status: IslandStatus;
50
+ ssrRenderTime?: number;
51
+ hydrateStartTime?: number;
52
+ hydrateEndTime?: number;
53
+ bundleSize?: number;
54
+ }
55
+
56
+ // ============================================================================
57
+ // Network Types
58
+ // ============================================================================
59
+
60
+ export interface NetworkRequest {
61
+ id: string;
62
+ method: string;
63
+ url: string;
64
+ safeHeaders: Record<string, string>;
65
+ redactedHeaders: string[];
66
+ body?: {
67
+ available: boolean;
68
+ size: number;
69
+ content?: unknown;
70
+ };
71
+ status?: number;
72
+ startTime: number;
73
+ endTime?: number;
74
+ isStreaming: boolean;
75
+ chunkCount?: number;
76
+ }
77
+
78
+ export interface NetworkBodyPolicy {
79
+ collectBody: boolean;
80
+ optInPolicy?: {
81
+ maxBytes: number;
82
+ applyPIIFilter: boolean;
83
+ applySecretFilter: boolean;
84
+ allowedContentTypes: string[];
85
+ };
86
+ }
87
+
88
+ // ============================================================================
89
+ // Guard Types
90
+ // ============================================================================
91
+
92
+ export interface DevToolsGuardViolation {
93
+ id: string;
94
+ ruleId: string;
95
+ ruleName: string;
96
+ severity: 'error' | 'warning';
97
+ message: string;
98
+ source: {
99
+ file: string;
100
+ line?: number;
101
+ column?: number;
102
+ };
103
+ target?: {
104
+ file: string;
105
+ line?: number;
106
+ };
107
+ suggestion?: string;
108
+ timestamp: number;
109
+ }
110
+
111
+ // ============================================================================
112
+ // Preview Types
113
+ // ============================================================================
114
+
115
+ export interface RecentChange {
116
+ filePath: string;
117
+ timestamp: number;
118
+ type: 'add' | 'change' | 'delete';
119
+ additions: number;
120
+ deletions: number;
121
+ }
122
+
123
+ // ============================================================================
124
+ // Guard Decision Types
125
+ // ============================================================================
126
+
127
+ export interface GuardDecision {
128
+ id: string;
129
+ violationKey: string; // "${ruleId}::${filePath}"
130
+ action: 'approve' | 'reject';
131
+ ruleId: string;
132
+ filePath: string;
133
+ reason?: string;
134
+ decidedAt: string;
135
+ }
136
+
137
+ // ============================================================================
138
+ // AI Context Types
139
+ // ============================================================================
140
+
141
+ export interface CodeContextInfo {
142
+ filePath: string;
143
+ line: number;
144
+ column?: number;
145
+ sourcemapUrl?: string;
146
+ snippet?: {
147
+ content: string;
148
+ lineRange: [number, number];
149
+ source: 'dev-server' | 'sourcemap-inline' | 'unavailable';
150
+ };
151
+ }
152
+
153
+ export interface AIContextPayload {
154
+ error: NormalizedError;
155
+ island?: IslandSnapshot;
156
+ framework: { name: 'mandu'; version: string };
157
+ devtools: { version: string };
158
+ recentErrors?: Array<{
159
+ id: string;
160
+ message: string;
161
+ timestamp: number;
162
+ isCausedBy?: string;
163
+ }>;
164
+ userActions?: Array<{
165
+ type: 'navigation' | 'interaction' | 'reload';
166
+ targetHint?: string;
167
+ timestamp: number;
168
+ }>;
169
+ codeContext?: CodeContextInfo;
170
+ }
171
+
172
+ // ============================================================================
173
+ // Redaction Types
174
+ // ============================================================================
175
+
176
+ export interface RedactPattern {
177
+ source: string;
178
+ flags?: string;
179
+ replacement?: string;
180
+ label?: string;
181
+ }
182
+
183
+ // ============================================================================
184
+ // Persistence Types
185
+ // ============================================================================
186
+
187
+ export interface PreserveLogConfig {
188
+ enabled: boolean;
189
+ maxPersistEvents: number;
190
+ maxPersistBytes: number;
191
+ priority: 'errors-first' | 'recent-first';
192
+ incremental?: {
193
+ enabled: boolean;
194
+ idleSyncMs: number;
195
+ };
196
+ }
197
+
198
+ // ============================================================================
199
+ // Worker Types
200
+ // ============================================================================
201
+
202
+ export interface WorkerPolicy {
203
+ timeout: number;
204
+ onTimeout: 'fallback-main' | 'skip';
205
+ onError: 'disable-worker' | 'retry-once';
206
+ maxConsecutiveFailures: number;
207
+ }
208
+
209
+ export interface WorkerTask {
210
+ id: string;
211
+ type: 'redact' | 'truncate';
212
+ data: {
213
+ text: string;
214
+ patterns?: RedactPattern[];
215
+ maxBytes?: number;
216
+ };
217
+ }
218
+
219
+ // ============================================================================
220
+ // Config Types
221
+ // ============================================================================
222
+
223
+ export type Position = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
224
+ export type Theme = 'light' | 'dark' | 'auto';
225
+
226
+ export interface DevToolsConfig {
227
+ enabled?: boolean;
228
+ position?: Position;
229
+ defaultOpen?: boolean;
230
+ theme?: Theme;
231
+
232
+ features?: {
233
+ errorOverlay?: boolean;
234
+ islandsInspector?: boolean;
235
+ networkMonitor?: boolean;
236
+ guardViewer?: boolean;
237
+ };
238
+
239
+ dataSafety?: {
240
+ stringMode?: 'smart' | 'strip';
241
+ collectUserActions?: boolean;
242
+ collectCodeContext?: boolean;
243
+ customRedactPatterns?: RedactPattern[];
244
+ };
245
+
246
+ network?: {
247
+ collectBody?: boolean;
248
+ bodyMaxBytes?: number;
249
+ };
250
+
251
+ persistence?: PreserveLogConfig;
252
+ plugins?: KitchenPanelPlugin[];
253
+ }
254
+
255
+ // ============================================================================
256
+ // Plugin Types
257
+ // ============================================================================
258
+
259
+ export interface KitchenAPI {
260
+ subscribe(type: string, callback: (event: KitchenEvent) => void): () => void;
261
+ getErrors(): NormalizedError[];
262
+ getIslands(): IslandSnapshot[];
263
+ getNetworkRequests(): NetworkRequest[];
264
+ clearErrors(): void;
265
+ getConfig(): DevToolsConfig;
266
+ copyToClipboard(text: string): Promise<void>;
267
+ openInEditor(file: string, line?: number): void;
268
+ }
269
+
270
+ export interface KitchenPanelPlugin {
271
+ id: string;
272
+ name: string;
273
+ icon: string;
274
+ order: number;
275
+
276
+ init(api: KitchenAPI): void;
277
+ destroy?(): void;
278
+ render(container: HTMLElement): void;
279
+ onEvent?(event: KitchenEvent): void;
280
+ }
281
+
282
+ // ============================================================================
283
+ // Meta Log Types
284
+ // ============================================================================
285
+
286
+ export type MetaLogType =
287
+ | 'init'
288
+ | 'hook_fail'
289
+ | 'render_fail'
290
+ | 'persist_fail'
291
+ | 'worker_timeout'
292
+ | 'worker_error'
293
+ | 'worker_disabled'
294
+ | 'recovered';
295
+
296
+ export interface KitchenMetaLog {
297
+ timestamp: number;
298
+ type: MetaLogType;
299
+ error?: string;
300
+ context: {
301
+ eventCount: number;
302
+ activeTab: string;
303
+ memoryInfo?: { usedJSHeapSize?: number };
304
+ };
305
+ }
306
+
307
+ // ============================================================================
308
+ // Mandu Character Types
309
+ // ============================================================================
310
+
285
311
  export type ManduState = 'normal' | 'warning' | 'error' | 'loading' | 'hmr';
286
312
 
287
313
  export interface ManduCharacterData {
288
314
  state: ManduState;
289
- emoji: string;
315
+ mark: string;
290
316
  message: string;
291
317
  }
292
318
 
293
319
  export const MANDU_CHARACTERS: Record<ManduState, ManduCharacterData> = {
294
320
  normal: {
295
321
  state: 'normal',
296
- emoji: '(◕‿◕)',
297
- message: '모든 만두가 익고 있어요~',
322
+ mark: 'OK',
323
+ message: '시스템이 안정적으로 동작 중입니다.',
298
324
  },
299
325
  warning: {
300
326
  state: 'warning',
301
- emoji: '(◕_◕)',
302
- message: '뭔가 이상해요...',
327
+ mark: 'WARN',
328
+ message: '확인이 필요한 항목이 있습니다.',
303
329
  },
304
330
  error: {
305
331
  state: 'error',
306
- emoji: '(ノಠ益ಠ)ノ彡┻━┻',
307
- message: '만두가 타버렸어요!',
332
+ mark: 'ERR',
333
+ message: '즉시 확인이 필요한 오류가 있습니다.',
308
334
  },
309
335
  loading: {
310
336
  state: 'loading',
311
- emoji: '(◕‿◕)💨',
312
- message: '만두 찌는 중...',
337
+ mark: 'SYNC',
338
+ message: '상태를 새로 불러오는 중입니다.',
313
339
  },
314
340
  hmr: {
315
341
  state: 'hmr',
316
- emoji: '(◕‿◕)✨',
317
- message: '레시피 업데이트됨!',
342
+ mark: 'HMR',
343
+ message: '코드 변경을 반영했습니다.',
318
344
  },
319
345
  };