@mandujs/core 0.9.41 → 0.9.43

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 (71) hide show
  1. package/README.ko.md +1 -1
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/bundler/build.ts +91 -73
  5. package/src/bundler/css.ts +283 -0
  6. package/src/bundler/dev.ts +31 -6
  7. package/src/bundler/index.ts +1 -0
  8. package/src/client/globals.ts +44 -0
  9. package/src/client/index.ts +5 -4
  10. package/src/client/island.ts +8 -13
  11. package/src/client/router.ts +33 -41
  12. package/src/client/runtime.ts +23 -51
  13. package/src/client/window-state.ts +101 -0
  14. package/src/config/index.ts +1 -0
  15. package/src/config/mandu.ts +45 -9
  16. package/src/config/validate.ts +158 -0
  17. package/src/constants.ts +25 -0
  18. package/src/contract/client.ts +4 -3
  19. package/src/contract/define.ts +459 -0
  20. package/src/devtools/ai/context-builder.ts +375 -0
  21. package/src/devtools/ai/index.ts +25 -0
  22. package/src/devtools/ai/mcp-connector.ts +465 -0
  23. package/src/devtools/client/catchers/error-catcher.ts +327 -0
  24. package/src/devtools/client/catchers/index.ts +18 -0
  25. package/src/devtools/client/catchers/network-proxy.ts +363 -0
  26. package/src/devtools/client/components/index.ts +39 -0
  27. package/src/devtools/client/components/kitchen-root.tsx +362 -0
  28. package/src/devtools/client/components/mandu-character.tsx +241 -0
  29. package/src/devtools/client/components/overlay.tsx +368 -0
  30. package/src/devtools/client/components/panel/errors-panel.tsx +259 -0
  31. package/src/devtools/client/components/panel/guard-panel.tsx +244 -0
  32. package/src/devtools/client/components/panel/index.ts +32 -0
  33. package/src/devtools/client/components/panel/islands-panel.tsx +304 -0
  34. package/src/devtools/client/components/panel/network-panel.tsx +292 -0
  35. package/src/devtools/client/components/panel/panel-container.tsx +259 -0
  36. package/src/devtools/client/filters/context-filters.ts +282 -0
  37. package/src/devtools/client/filters/index.ts +16 -0
  38. package/src/devtools/client/index.ts +63 -0
  39. package/src/devtools/client/persistence.ts +335 -0
  40. package/src/devtools/client/state-manager.ts +478 -0
  41. package/src/devtools/design-tokens.ts +263 -0
  42. package/src/devtools/hook/create-hook.ts +207 -0
  43. package/src/devtools/hook/index.ts +13 -0
  44. package/src/devtools/index.ts +439 -0
  45. package/src/devtools/init.ts +266 -0
  46. package/src/devtools/protocol.ts +237 -0
  47. package/src/devtools/server/index.ts +17 -0
  48. package/src/devtools/server/source-context.ts +444 -0
  49. package/src/devtools/types.ts +319 -0
  50. package/src/devtools/worker/index.ts +25 -0
  51. package/src/devtools/worker/redaction-worker.ts +222 -0
  52. package/src/devtools/worker/worker-manager.ts +409 -0
  53. package/src/error/formatter.ts +28 -24
  54. package/src/error/index.ts +13 -9
  55. package/src/error/result.ts +46 -0
  56. package/src/error/types.ts +6 -4
  57. package/src/filling/filling.ts +6 -5
  58. package/src/guard/check.ts +60 -56
  59. package/src/guard/types.ts +3 -1
  60. package/src/guard/watcher.ts +10 -1
  61. package/src/index.ts +81 -0
  62. package/src/intent/index.ts +310 -0
  63. package/src/island/index.ts +304 -0
  64. package/src/router/fs-patterns.ts +7 -0
  65. package/src/router/fs-routes.ts +20 -8
  66. package/src/router/fs-scanner.ts +117 -133
  67. package/src/runtime/server.ts +189 -61
  68. package/src/runtime/ssr.ts +14 -4
  69. package/src/runtime/streaming-ssr.ts +15 -4
  70. package/src/utils/bun.ts +8 -0
  71. package/src/utils/lru-cache.ts +75 -0
@@ -0,0 +1,439 @@
1
+ /**
2
+ * Mandu Kitchen DevTools
3
+ * AI-Native Developer Tools for Mandu Framework
4
+ *
5
+ * @version 1.1.0
6
+ * @description "만두를 찌듯 편안하게 디버깅한다"
7
+ */
8
+
9
+ // ============================================================================
10
+ // Types
11
+ // ============================================================================
12
+
13
+ export type {
14
+ // Core
15
+ KitchenEvent,
16
+
17
+ // Error
18
+ ErrorType,
19
+ Severity,
20
+ NormalizedError,
21
+
22
+ // Island
23
+ HydrationStrategy,
24
+ IslandStatus,
25
+ IslandSnapshot,
26
+
27
+ // Network
28
+ NetworkRequest,
29
+ NetworkBodyPolicy,
30
+
31
+ // Guard
32
+ GuardViolation,
33
+
34
+ // AI Context
35
+ CodeContextInfo,
36
+ AIContextPayload,
37
+
38
+ // Redaction
39
+ RedactPattern,
40
+
41
+ // Persistence
42
+ PreserveLogConfig,
43
+
44
+ // Worker
45
+ WorkerPolicy,
46
+ WorkerTask,
47
+
48
+ // Config
49
+ Position,
50
+ Theme,
51
+ DevToolsConfig,
52
+
53
+ // Plugin
54
+ KitchenAPI,
55
+ KitchenPanelPlugin,
56
+
57
+ // Meta
58
+ MetaLogType,
59
+ KitchenMetaLog,
60
+
61
+ // Character
62
+ ManduState,
63
+ ManduCharacterData,
64
+ } from './types';
65
+
66
+ export { MANDU_CHARACTERS } from './types';
67
+
68
+ // ============================================================================
69
+ // Design Tokens
70
+ // ============================================================================
71
+
72
+ export {
73
+ colors,
74
+ typography,
75
+ spacing,
76
+ borderRadius,
77
+ borderWidth,
78
+ shadows,
79
+ animation,
80
+ zIndex,
81
+ breakpoints,
82
+ ManduDesignTokens,
83
+ generateCSSVariables,
84
+ testIds,
85
+ type TestId,
86
+ } from './design-tokens';
87
+
88
+ // ============================================================================
89
+ // Client
90
+ // ============================================================================
91
+
92
+ export {
93
+ // State
94
+ KitchenStateManager,
95
+ getStateManager,
96
+ resetStateManager,
97
+ type KitchenState,
98
+ type StateListener,
99
+
100
+ // Error Catching
101
+ ErrorCatcher,
102
+ getErrorCatcher,
103
+ initializeErrorCatcher,
104
+ destroyErrorCatcher,
105
+
106
+ // Network Proxy
107
+ NetworkProxy,
108
+ getNetworkProxy,
109
+ initializeNetworkProxy,
110
+ destroyNetworkProxy,
111
+
112
+ // Filters
113
+ removeComments,
114
+ handleStrings,
115
+ redactBuiltInSecrets,
116
+ redactCustomPatterns,
117
+ truncate,
118
+ applyContextFilters,
119
+ sanitizeStackTrace,
120
+ sanitizeErrorMessage,
121
+ type FilterOptions,
122
+
123
+ // Persistence
124
+ PersistenceManager,
125
+ getPersistenceManager,
126
+ initializePersistence,
127
+ destroyPersistence,
128
+
129
+ // Components
130
+ ManduCharacter,
131
+ ManduBadge,
132
+ ErrorOverlay,
133
+ mountKitchen,
134
+ unmountKitchen,
135
+ isKitchenMounted,
136
+ type ManduCharacterProps,
137
+ type ManduBadgeProps,
138
+ type ErrorOverlayProps,
139
+ } from './client';
140
+
141
+ // ============================================================================
142
+ // Initialization
143
+ // ============================================================================
144
+
145
+ export {
146
+ initManduKitchen,
147
+ destroyManduKitchen,
148
+ autoInit,
149
+ type KitchenInstance,
150
+ } from './init';
151
+
152
+ // ============================================================================
153
+ // Protocol
154
+ // ============================================================================
155
+
156
+ export type { KitchenEvents } from './protocol';
157
+
158
+ export {
159
+ // Type guards
160
+ isErrorEvent,
161
+ isIslandEvent,
162
+ isNetworkEvent,
163
+ isGuardEvent,
164
+ isHmrEvent,
165
+
166
+ // Event factories
167
+ createErrorEvent,
168
+ createIslandRegisterEvent,
169
+ createIslandHydrateStartEvent,
170
+ createIslandHydrateEndEvent,
171
+ createNetworkRequestEvent,
172
+ createNetworkResponseEvent,
173
+ createGuardViolationEvent,
174
+ createHmrUpdateEvent,
175
+ createHmrErrorEvent,
176
+
177
+ // Constants
178
+ DEVTOOLS_VERSION,
179
+ DEFAULT_CONFIG,
180
+ ALLOWED_HEADERS,
181
+ BLOCKED_HEADERS,
182
+ } from './protocol';
183
+
184
+ // ============================================================================
185
+ // Hook
186
+ // ============================================================================
187
+
188
+ export {
189
+ createDevtoolsHook,
190
+ getOrCreateHook,
191
+ getHook,
192
+ initializeHook,
193
+ type ManduDevtoolsHook,
194
+ type EventSink,
195
+ } from './hook';
196
+
197
+ // ============================================================================
198
+ // Convenience Functions
199
+ // ============================================================================
200
+
201
+ import { getOrCreateHook } from './hook';
202
+ import type { NormalizedError, GuardViolation } from './types';
203
+ import {
204
+ createErrorEvent,
205
+ createHmrUpdateEvent,
206
+ createHmrErrorEvent,
207
+ createGuardViolationEvent,
208
+ } from './protocol';
209
+
210
+ /**
211
+ * 에러 리포트 (간편 API)
212
+ *
213
+ * @example
214
+ * ```typescript
215
+ * import { reportError } from '@mandu/core/devtools';
216
+ *
217
+ * try {
218
+ * // ...
219
+ * } catch (e) {
220
+ * reportError(e);
221
+ * }
222
+ * ```
223
+ */
224
+ export function reportError(
225
+ error: Error | string,
226
+ options?: Partial<Omit<NormalizedError, 'id' | 'timestamp' | 'message'>>
227
+ ): void {
228
+ const hook = getOrCreateHook();
229
+ const message = typeof error === 'string' ? error : error.message;
230
+ const stack = typeof error === 'string' ? undefined : error.stack;
231
+
232
+ hook.emit(
233
+ createErrorEvent({
234
+ type: options?.type ?? 'runtime',
235
+ severity: options?.severity ?? 'error',
236
+ message,
237
+ stack,
238
+ url: typeof window !== 'undefined' ? window.location.href : '',
239
+ ...options,
240
+ })
241
+ );
242
+ }
243
+
244
+ /**
245
+ * HMR 업데이트 알림 (간편 API)
246
+ */
247
+ export function notifyHmrUpdate(routeId: string): void {
248
+ const hook = getOrCreateHook();
249
+ hook.emit(createHmrUpdateEvent(routeId));
250
+ }
251
+
252
+ /**
253
+ * HMR 에러 알림 (간편 API)
254
+ */
255
+ export function notifyHmrError(message: string, stack?: string): void {
256
+ const hook = getOrCreateHook();
257
+ hook.emit(createHmrErrorEvent(message, stack));
258
+ }
259
+
260
+ /**
261
+ * Guard 위반 리포트 (간편 API)
262
+ */
263
+ export function reportGuardViolation(
264
+ violation: Omit<GuardViolation, 'id' | 'timestamp'>
265
+ ): void {
266
+ const hook = getOrCreateHook();
267
+ hook.emit(createGuardViolationEvent(violation));
268
+ }
269
+
270
+ // ============================================================================
271
+ // DevTools API (for external use)
272
+ // ============================================================================
273
+
274
+ /**
275
+ * DevTools 공개 API
276
+ * window.ManduDevTools로 접근 가능
277
+ */
278
+ export const ManduDevTools = {
279
+ /**
280
+ * 로그 출력
281
+ */
282
+ log(level: 'info' | 'warn' | 'error', message: string, data?: unknown): void {
283
+ const hook = getOrCreateHook();
284
+ hook.emit({
285
+ type: 'log',
286
+ timestamp: Date.now(),
287
+ data: { level, message, data },
288
+ });
289
+ },
290
+
291
+ /**
292
+ * 에러 리포트
293
+ */
294
+ reportError,
295
+
296
+ /**
297
+ * 타이머 시작
298
+ */
299
+ time(label: string): void {
300
+ if (typeof performance !== 'undefined') {
301
+ performance.mark(`mandu-time-${label}`);
302
+ }
303
+ },
304
+
305
+ /**
306
+ * 타이머 종료 및 시간 반환
307
+ */
308
+ timeEnd(label: string): number {
309
+ if (typeof performance !== 'undefined') {
310
+ const markName = `mandu-time-${label}`;
311
+ try {
312
+ performance.measure(`mandu-measure-${label}`, markName);
313
+ const entries = performance.getEntriesByName(`mandu-measure-${label}`);
314
+ const duration = entries[entries.length - 1]?.duration ?? 0;
315
+ performance.clearMarks(markName);
316
+ performance.clearMeasures(`mandu-measure-${label}`);
317
+ return duration;
318
+ } catch {
319
+ return 0;
320
+ }
321
+ }
322
+ return 0;
323
+ },
324
+
325
+ /**
326
+ * DevTools 토글
327
+ */
328
+ toggle(): void {
329
+ const hook = getOrCreateHook();
330
+ hook.emit({
331
+ type: 'devtools:toggle',
332
+ timestamp: Date.now(),
333
+ data: {},
334
+ });
335
+ },
336
+
337
+ /**
338
+ * DevTools 열기
339
+ */
340
+ open(): void {
341
+ const hook = getOrCreateHook();
342
+ hook.emit({
343
+ type: 'devtools:open',
344
+ timestamp: Date.now(),
345
+ data: {},
346
+ });
347
+ },
348
+
349
+ /**
350
+ * DevTools 닫기
351
+ */
352
+ close(): void {
353
+ const hook = getOrCreateHook();
354
+ hook.emit({
355
+ type: 'devtools:close',
356
+ timestamp: Date.now(),
357
+ data: {},
358
+ });
359
+ },
360
+
361
+ /**
362
+ * 에러 목록 클리어
363
+ */
364
+ clearErrors(): void {
365
+ const hook = getOrCreateHook();
366
+ hook.emit({
367
+ type: 'error:clear',
368
+ timestamp: Date.now(),
369
+ data: {},
370
+ });
371
+ },
372
+ };
373
+
374
+ // 브라우저 환경에서 전역 객체에 등록
375
+ if (typeof window !== 'undefined') {
376
+ (window as Window & { ManduDevTools?: typeof ManduDevTools }).ManduDevTools =
377
+ ManduDevTools;
378
+ }
379
+
380
+ // ============================================================================
381
+ // v1.1: Server Module (Source Context Provider)
382
+ // ============================================================================
383
+
384
+ export {
385
+ SourceContextProvider,
386
+ SourcemapParser,
387
+ createViteMiddleware,
388
+ manduSourceContextPlugin,
389
+ type SourceContextRequest,
390
+ type SourceContextResponse,
391
+ type SourceContextProviderOptions,
392
+ type SourcemapPosition,
393
+ type SourcemapParseResult,
394
+ } from './server';
395
+
396
+ // ============================================================================
397
+ // v1.1: Worker Module (Redaction Worker)
398
+ // ============================================================================
399
+
400
+ export {
401
+ // Redaction Worker
402
+ redactText,
403
+ truncateText,
404
+ BUILT_IN_SECRET_PATTERNS,
405
+ PII_PATTERNS,
406
+ type WorkerRequest,
407
+ type WorkerResponse,
408
+
409
+ // Worker Manager
410
+ WorkerManager,
411
+ getWorkerManager,
412
+ initializeWorkerManager,
413
+ destroyWorkerManager,
414
+ type WorkerStatus,
415
+ type WorkerManagerOptions,
416
+ } from './worker';
417
+
418
+ // ============================================================================
419
+ // v1.1: AI Module (Context Builder, MCP Connector)
420
+ // ============================================================================
421
+
422
+ export {
423
+ // Context Builder
424
+ AIContextBuilder,
425
+ getContextBuilder,
426
+ resetContextBuilder,
427
+ type ContextBuilderOptions,
428
+ type UserAction,
429
+
430
+ // MCP Connector
431
+ MCPConnector,
432
+ getMCPConnector,
433
+ destroyMCPConnector,
434
+ type MCPConnectorOptions,
435
+ type MCPMessage,
436
+ type AnalysisRequest,
437
+ type AnalysisResponse,
438
+ type MCPConnectionStatus,
439
+ } from './ai';
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Mandu Kitchen DevTools - Initialization
3
+ * @version 1.1.0
4
+ *
5
+ * 통합 초기화 함수
6
+ */
7
+
8
+ import type { DevToolsConfig } from './types';
9
+ import { initializeHook } from './hook';
10
+ import {
11
+ mountKitchen,
12
+ unmountKitchen,
13
+ initializeErrorCatcher,
14
+ destroyErrorCatcher,
15
+ initializeNetworkProxy,
16
+ destroyNetworkProxy,
17
+ initializePersistence,
18
+ destroyPersistence,
19
+ getStateManager,
20
+ getPersistenceManager,
21
+ } from './client';
22
+ import { initializeWorkerManager, destroyWorkerManager } from './worker';
23
+ import { getContextBuilder, resetContextBuilder, getMCPConnector, destroyMCPConnector } from './ai';
24
+
25
+ // ============================================================================
26
+ // Types
27
+ // ============================================================================
28
+
29
+ export interface KitchenInstance {
30
+ /** DevTools 언마운트 및 정리 */
31
+ destroy: () => void;
32
+ /** 상태 관리자 접근 */
33
+ getState: () => any;
34
+ /** 에러 리포트 */
35
+ reportError: (error: Error | string) => void;
36
+ /** DevTools 열기 */
37
+ open: () => void;
38
+ /** DevTools 닫기 */
39
+ close: () => void;
40
+ /** DevTools 토글 */
41
+ toggle: () => void;
42
+ }
43
+
44
+ // ============================================================================
45
+ // Initialization
46
+ // ============================================================================
47
+
48
+ let isInitialized = false;
49
+
50
+ /**
51
+ * Mandu Kitchen DevTools 초기화
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { initManduKitchen } from '@mandu/core/devtools';
56
+ *
57
+ * // 앱 시작 시 초기화
58
+ * const kitchen = initManduKitchen({
59
+ * position: 'bottom-right',
60
+ * features: {
61
+ * errorOverlay: true,
62
+ * networkMonitor: true,
63
+ * },
64
+ * });
65
+ *
66
+ * // 나중에 정리
67
+ * kitchen.destroy();
68
+ * ```
69
+ */
70
+ export function initManduKitchen(config: DevToolsConfig = {}): KitchenInstance {
71
+ // Production 환경에서는 noop 반환
72
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') {
73
+ return createNoopInstance();
74
+ }
75
+
76
+ // 브라우저 환경이 아니면 noop 반환
77
+ if (typeof window === 'undefined') {
78
+ return createNoopInstance();
79
+ }
80
+
81
+ // 이미 초기화되었으면 기존 인스턴스 반환
82
+ if (isInitialized) {
83
+ console.warn('[Mandu Kitchen] Already initialized. Call destroy() first to reinitialize.');
84
+ return createInstance();
85
+ }
86
+
87
+ // 비활성화 설정이면 noop 반환
88
+ if (config.enabled === false) {
89
+ return createNoopInstance();
90
+ }
91
+
92
+ // 초기화 시작
93
+ try {
94
+ // 1. Hook 초기화
95
+ initializeHook();
96
+
97
+ // 2. Error Catcher 초기화
98
+ if (config.features?.errorOverlay !== false) {
99
+ initializeErrorCatcher();
100
+ }
101
+
102
+ // 3. Network Proxy 초기화
103
+ if (config.features?.networkMonitor !== false) {
104
+ initializeNetworkProxy({
105
+ bodyPolicy: {
106
+ collectBody: config.network?.collectBody ?? false,
107
+ optInPolicy: {
108
+ maxBytes: config.network?.bodyMaxBytes ?? 10_000,
109
+ applyPIIFilter: true,
110
+ applySecretFilter: true,
111
+ allowedContentTypes: ['application/json', 'text/plain', 'text/event-stream'],
112
+ },
113
+ },
114
+ });
115
+ }
116
+
117
+ // 4. Persistence 초기화
118
+ if (config.persistence?.enabled !== false) {
119
+ const persistence = initializePersistence(config.persistence);
120
+
121
+ // 저장된 이벤트 복원
122
+ const savedEvents = persistence.loadEvents();
123
+ if (savedEvents.length > 0) {
124
+ const stateManager = getStateManager(config);
125
+ for (const event of savedEvents) {
126
+ stateManager.handleEvent(event);
127
+ }
128
+ }
129
+ }
130
+
131
+ // 5. v1.1: Worker Manager 초기화 (백그라운드, 실패해도 계속)
132
+ initializeWorkerManager().catch((err) => {
133
+ console.warn('[Mandu Kitchen] Worker initialization failed, using main thread fallback:', err);
134
+ });
135
+
136
+ // 6. v1.1: AI Context Builder 초기화
137
+ const contextBuilder = getContextBuilder({ config });
138
+
139
+ // 7. UI 마운트
140
+ mountKitchen(config);
141
+
142
+ isInitialized = true;
143
+
144
+ console.log('[Mandu Kitchen] DevTools v1.1 initialized 🥟');
145
+
146
+ return createInstance();
147
+ } catch (error) {
148
+ console.error('[Mandu Kitchen] Initialization failed:', error);
149
+ return createNoopInstance();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * DevTools 정리
155
+ */
156
+ export function destroyManduKitchen(): void {
157
+ if (!isInitialized) return;
158
+
159
+ try {
160
+ // UI 언마운트
161
+ unmountKitchen();
162
+
163
+ // v1.0 모듈 정리
164
+ destroyErrorCatcher();
165
+ destroyNetworkProxy();
166
+ destroyPersistence();
167
+
168
+ // v1.1 모듈 정리
169
+ destroyWorkerManager();
170
+ resetContextBuilder();
171
+ destroyMCPConnector();
172
+
173
+ isInitialized = false;
174
+
175
+ console.log('[Mandu Kitchen] DevTools destroyed');
176
+ } catch (error) {
177
+ console.error('[Mandu Kitchen] Cleanup failed:', error);
178
+ }
179
+ }
180
+
181
+ // ============================================================================
182
+ // Instance Factories
183
+ // ============================================================================
184
+
185
+ function createInstance(): KitchenInstance {
186
+ return {
187
+ destroy: destroyManduKitchen,
188
+
189
+ getState: () => getStateManager().getState(),
190
+
191
+ reportError: (error: Error | string) => {
192
+ const stateManager = getStateManager();
193
+ const message = typeof error === 'string' ? error : error.message;
194
+ const stack = typeof error === 'string' ? undefined : error.stack;
195
+
196
+ stateManager.addError({
197
+ id: `manual-${Date.now()}`,
198
+ type: 'runtime',
199
+ severity: 'error',
200
+ message,
201
+ stack,
202
+ timestamp: Date.now(),
203
+ url: window.location.href,
204
+ });
205
+ },
206
+
207
+ open: () => getStateManager().open(),
208
+ close: () => getStateManager().close(),
209
+ toggle: () => getStateManager().toggle(),
210
+ };
211
+ }
212
+
213
+ function createNoopInstance(): KitchenInstance {
214
+ return {
215
+ destroy: () => {},
216
+ getState: () => ({} as any),
217
+ reportError: () => {},
218
+ open: () => {},
219
+ close: () => {},
220
+ toggle: () => {},
221
+ };
222
+ }
223
+
224
+ // ============================================================================
225
+ // Auto-initialization (optional)
226
+ // ============================================================================
227
+
228
+ /**
229
+ * 자동 초기화 (script 태그로 로드 시)
230
+ *
231
+ * HTML에서 사용:
232
+ * <script src="mandu-kitchen.js" data-auto-init data-position="bottom-left"></script>
233
+ */
234
+ export function autoInit(): void {
235
+ if (typeof document === 'undefined') return;
236
+
237
+ // DOMContentLoaded 이후에 실행
238
+ if (document.readyState === 'loading') {
239
+ document.addEventListener('DOMContentLoaded', () => {
240
+ checkAndInit();
241
+ });
242
+ } else {
243
+ checkAndInit();
244
+ }
245
+ }
246
+
247
+ function checkAndInit(): void {
248
+ // data-auto-init 속성이 있는 script 태그 찾기
249
+ const script = document.querySelector('script[data-mandu-kitchen-auto-init]');
250
+ if (!script) return;
251
+
252
+ const config: DevToolsConfig = {};
253
+
254
+ // data 속성에서 설정 읽기
255
+ const position = script.getAttribute('data-position');
256
+ if (position) {
257
+ config.position = position as DevToolsConfig['position'];
258
+ }
259
+
260
+ const theme = script.getAttribute('data-theme');
261
+ if (theme) {
262
+ config.theme = theme as DevToolsConfig['theme'];
263
+ }
264
+
265
+ initManduKitchen(config);
266
+ }