@mandujs/core 0.9.41 → 0.9.42

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 (67) hide show
  1. package/package.json +1 -1
  2. package/src/bundler/build.ts +91 -73
  3. package/src/bundler/dev.ts +21 -14
  4. package/src/client/globals.ts +44 -0
  5. package/src/client/index.ts +5 -4
  6. package/src/client/island.ts +8 -13
  7. package/src/client/router.ts +33 -41
  8. package/src/client/runtime.ts +23 -51
  9. package/src/client/window-state.ts +101 -0
  10. package/src/config/index.ts +1 -0
  11. package/src/config/mandu.ts +45 -9
  12. package/src/config/validate.ts +158 -0
  13. package/src/constants.ts +25 -0
  14. package/src/contract/client.ts +4 -3
  15. package/src/contract/define.ts +459 -0
  16. package/src/devtools/ai/context-builder.ts +375 -0
  17. package/src/devtools/ai/index.ts +25 -0
  18. package/src/devtools/ai/mcp-connector.ts +465 -0
  19. package/src/devtools/client/catchers/error-catcher.ts +327 -0
  20. package/src/devtools/client/catchers/index.ts +18 -0
  21. package/src/devtools/client/catchers/network-proxy.ts +363 -0
  22. package/src/devtools/client/components/index.ts +39 -0
  23. package/src/devtools/client/components/kitchen-root.tsx +362 -0
  24. package/src/devtools/client/components/mandu-character.tsx +241 -0
  25. package/src/devtools/client/components/overlay.tsx +368 -0
  26. package/src/devtools/client/components/panel/errors-panel.tsx +259 -0
  27. package/src/devtools/client/components/panel/guard-panel.tsx +244 -0
  28. package/src/devtools/client/components/panel/index.ts +32 -0
  29. package/src/devtools/client/components/panel/islands-panel.tsx +304 -0
  30. package/src/devtools/client/components/panel/network-panel.tsx +292 -0
  31. package/src/devtools/client/components/panel/panel-container.tsx +259 -0
  32. package/src/devtools/client/filters/context-filters.ts +282 -0
  33. package/src/devtools/client/filters/index.ts +16 -0
  34. package/src/devtools/client/index.ts +63 -0
  35. package/src/devtools/client/persistence.ts +335 -0
  36. package/src/devtools/client/state-manager.ts +478 -0
  37. package/src/devtools/design-tokens.ts +263 -0
  38. package/src/devtools/hook/create-hook.ts +207 -0
  39. package/src/devtools/hook/index.ts +13 -0
  40. package/src/devtools/index.ts +439 -0
  41. package/src/devtools/init.ts +266 -0
  42. package/src/devtools/protocol.ts +237 -0
  43. package/src/devtools/server/index.ts +17 -0
  44. package/src/devtools/server/source-context.ts +444 -0
  45. package/src/devtools/types.ts +319 -0
  46. package/src/devtools/worker/index.ts +25 -0
  47. package/src/devtools/worker/redaction-worker.ts +222 -0
  48. package/src/devtools/worker/worker-manager.ts +409 -0
  49. package/src/error/formatter.ts +28 -24
  50. package/src/error/index.ts +13 -9
  51. package/src/error/result.ts +46 -0
  52. package/src/error/types.ts +6 -4
  53. package/src/filling/filling.ts +6 -5
  54. package/src/guard/check.ts +60 -56
  55. package/src/guard/types.ts +3 -1
  56. package/src/guard/watcher.ts +10 -1
  57. package/src/index.ts +81 -0
  58. package/src/intent/index.ts +310 -0
  59. package/src/island/index.ts +304 -0
  60. package/src/router/fs-patterns.ts +7 -0
  61. package/src/router/fs-routes.ts +20 -8
  62. package/src/router/fs-scanner.ts +117 -133
  63. package/src/runtime/server.ts +261 -201
  64. package/src/runtime/ssr.ts +5 -4
  65. package/src/runtime/streaming-ssr.ts +5 -4
  66. package/src/utils/bun.ts +8 -0
  67. package/src/utils/lru-cache.ts +75 -0
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Mandu Kitchen DevTools - AI Context Builder
3
+ * @version 1.1.0
4
+ *
5
+ * AI 분석을 위한 컨텍스트 페이로드 생성
6
+ * - 에러 정보 정규화
7
+ * - 코드 컨텍스트 수집
8
+ * - 인과관계 체인 구성
9
+ */
10
+
11
+ import type {
12
+ NormalizedError,
13
+ IslandSnapshot,
14
+ AIContextPayload,
15
+ CodeContextInfo,
16
+ DevToolsConfig,
17
+ } from '../types';
18
+ import { DEVTOOLS_VERSION } from '../protocol';
19
+
20
+ // ============================================================================
21
+ // Types
22
+ // ============================================================================
23
+
24
+ export interface ContextBuilderOptions {
25
+ /** DevTools 설정 */
26
+ config?: DevToolsConfig;
27
+ /** 최근 에러 최대 개수 */
28
+ maxRecentErrors?: number;
29
+ /** 사용자 액션 최대 개수 */
30
+ maxUserActions?: number;
31
+ /** Framework 버전 */
32
+ frameworkVersion?: string;
33
+ /** Source Context API URL */
34
+ sourceContextUrl?: string;
35
+ }
36
+
37
+ export interface UserAction {
38
+ type: 'navigation' | 'interaction' | 'reload';
39
+ targetHint?: string;
40
+ timestamp: number;
41
+ }
42
+
43
+ // ============================================================================
44
+ // Constants
45
+ // ============================================================================
46
+
47
+ const DEFAULT_OPTIONS: Required<Omit<ContextBuilderOptions, 'config'>> = {
48
+ maxRecentErrors: 5,
49
+ maxUserActions: 10,
50
+ frameworkVersion: '1.0.0',
51
+ sourceContextUrl: '/api/__mandu_source__',
52
+ };
53
+
54
+ // ============================================================================
55
+ // AI Context Builder
56
+ // ============================================================================
57
+
58
+ export class AIContextBuilder {
59
+ private options: Required<Omit<ContextBuilderOptions, 'config'>> & {
60
+ config?: DevToolsConfig;
61
+ };
62
+ private recentErrors: NormalizedError[] = [];
63
+ private userActions: UserAction[] = [];
64
+ private errorCausalityMap = new Map<string, string[]>();
65
+
66
+ constructor(options: ContextBuilderOptions = {}) {
67
+ this.options = { ...DEFAULT_OPTIONS, ...options };
68
+ }
69
+
70
+ // --------------------------------------------------------------------------
71
+ // Error Tracking
72
+ // --------------------------------------------------------------------------
73
+
74
+ /**
75
+ * 에러 추가 (인과관계 분석용)
76
+ */
77
+ addError(error: NormalizedError): void {
78
+ this.recentErrors.push(error);
79
+
80
+ // 최대 개수 제한
81
+ if (this.recentErrors.length > this.options.maxRecentErrors * 2) {
82
+ this.recentErrors = this.recentErrors.slice(-this.options.maxRecentErrors);
83
+ }
84
+
85
+ // 인과관계 분석
86
+ this.analyzeCausality(error);
87
+ }
88
+
89
+ /**
90
+ * 에러 인과관계 분석
91
+ */
92
+ private analyzeCausality(newError: NormalizedError): void {
93
+ // 최근 에러들과 비교하여 인과관계 파악
94
+ const recentWindow = 5000; // 5초 이내
95
+ const potentialCauses: string[] = [];
96
+
97
+ for (const error of this.recentErrors) {
98
+ if (error.id === newError.id) continue;
99
+
100
+ // 시간적 연관성
101
+ const timeDiff = newError.timestamp - error.timestamp;
102
+ if (timeDiff <= 0 || timeDiff > recentWindow) continue;
103
+
104
+ // 스택 트레이스 연관성
105
+ if (
106
+ newError.stack &&
107
+ error.stack &&
108
+ this.hasStackOverlap(newError.stack, error.stack)
109
+ ) {
110
+ potentialCauses.push(error.id);
111
+ continue;
112
+ }
113
+
114
+ // 같은 컴포넌트/Island에서 발생
115
+ if (newError.islandId && newError.islandId === error.islandId) {
116
+ potentialCauses.push(error.id);
117
+ continue;
118
+ }
119
+
120
+ // 같은 파일에서 발생
121
+ if (newError.source && newError.source === error.source) {
122
+ potentialCauses.push(error.id);
123
+ }
124
+ }
125
+
126
+ if (potentialCauses.length > 0) {
127
+ this.errorCausalityMap.set(newError.id, potentialCauses);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * 스택 트레이스 겹침 확인
133
+ */
134
+ private hasStackOverlap(stack1: string, stack2: string): boolean {
135
+ const frames1 = this.extractStackFrames(stack1);
136
+ const frames2 = this.extractStackFrames(stack2);
137
+
138
+ for (const frame of frames1) {
139
+ if (frames2.includes(frame)) {
140
+ return true;
141
+ }
142
+ }
143
+
144
+ return false;
145
+ }
146
+
147
+ /**
148
+ * 스택 프레임 추출
149
+ */
150
+ private extractStackFrames(stack: string): string[] {
151
+ const frameRegex = /at\s+(?:.*\s+\()?([^)]+:\d+:\d+)/g;
152
+ const frames: string[] = [];
153
+ let match;
154
+
155
+ while ((match = frameRegex.exec(stack)) !== null) {
156
+ frames.push(match[1]);
157
+ }
158
+
159
+ return frames;
160
+ }
161
+
162
+ // --------------------------------------------------------------------------
163
+ // User Action Tracking
164
+ // --------------------------------------------------------------------------
165
+
166
+ /**
167
+ * 사용자 액션 추가
168
+ */
169
+ addUserAction(action: UserAction): void {
170
+ this.userActions.push(action);
171
+
172
+ if (this.userActions.length > this.options.maxUserActions) {
173
+ this.userActions = this.userActions.slice(-this.options.maxUserActions);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * 내비게이션 추적
179
+ */
180
+ trackNavigation(url: string): void {
181
+ this.addUserAction({
182
+ type: 'navigation',
183
+ targetHint: url,
184
+ timestamp: Date.now(),
185
+ });
186
+ }
187
+
188
+ /**
189
+ * 인터랙션 추적 (클릭, 입력 등)
190
+ */
191
+ trackInteraction(targetHint?: string): void {
192
+ this.addUserAction({
193
+ type: 'interaction',
194
+ targetHint,
195
+ timestamp: Date.now(),
196
+ });
197
+ }
198
+
199
+ /**
200
+ * 페이지 리로드 추적
201
+ */
202
+ trackReload(): void {
203
+ this.addUserAction({
204
+ type: 'reload',
205
+ timestamp: Date.now(),
206
+ });
207
+ }
208
+
209
+ // --------------------------------------------------------------------------
210
+ // Context Building
211
+ // --------------------------------------------------------------------------
212
+
213
+ /**
214
+ * AI Context Payload 생성
215
+ */
216
+ async buildContext(
217
+ error: NormalizedError,
218
+ island?: IslandSnapshot
219
+ ): Promise<AIContextPayload> {
220
+ // 기본 페이로드
221
+ const payload: AIContextPayload = {
222
+ error,
223
+ island,
224
+ framework: {
225
+ name: 'mandu',
226
+ version: this.options.frameworkVersion,
227
+ },
228
+ devtools: {
229
+ version: DEVTOOLS_VERSION,
230
+ },
231
+ };
232
+
233
+ // 최근 에러 (인과관계 포함)
234
+ if (this.recentErrors.length > 0) {
235
+ payload.recentErrors = this.recentErrors
236
+ .slice(-this.options.maxRecentErrors)
237
+ .filter((e) => e.id !== error.id)
238
+ .map((e) => ({
239
+ id: e.id,
240
+ message: e.message,
241
+ timestamp: e.timestamp,
242
+ isCausedBy: this.errorCausalityMap.get(e.id)?.[0],
243
+ }));
244
+ }
245
+
246
+ // 사용자 액션
247
+ if (
248
+ this.options.config?.dataSafety?.collectUserActions !== false &&
249
+ this.userActions.length > 0
250
+ ) {
251
+ payload.userActions = this.userActions.slice(-this.options.maxUserActions);
252
+ }
253
+
254
+ // 코드 컨텍스트 (Source Context Provider 사용)
255
+ if (this.options.config?.dataSafety?.collectCodeContext !== false) {
256
+ const codeContext = await this.fetchCodeContext(error);
257
+ if (codeContext) {
258
+ payload.codeContext = codeContext;
259
+ }
260
+ }
261
+
262
+ return payload;
263
+ }
264
+
265
+ /**
266
+ * Source Context 가져오기
267
+ */
268
+ private async fetchCodeContext(
269
+ error: NormalizedError
270
+ ): Promise<CodeContextInfo | undefined> {
271
+ if (!error.source || !error.line) {
272
+ return undefined;
273
+ }
274
+
275
+ const codeContext: CodeContextInfo = {
276
+ filePath: error.source,
277
+ line: error.line,
278
+ column: error.column,
279
+ };
280
+
281
+ // Dev Server에서 소스 코드 가져오기 시도
282
+ try {
283
+ const url = new URL(this.options.sourceContextUrl, window.location.origin);
284
+ url.searchParams.set('file', error.source);
285
+ url.searchParams.set('line', String(error.line));
286
+ url.searchParams.set('context', '5');
287
+
288
+ const response = await fetch(url.toString());
289
+ if (response.ok) {
290
+ const data = await response.json();
291
+ if (data.success && data.data) {
292
+ codeContext.snippet = {
293
+ content: data.data.content,
294
+ lineRange: data.data.lineRange,
295
+ source: 'dev-server',
296
+ };
297
+ }
298
+ }
299
+ } catch {
300
+ // 실패해도 기본 컨텍스트 정보는 반환
301
+ }
302
+
303
+ return codeContext;
304
+ }
305
+
306
+ // --------------------------------------------------------------------------
307
+ // Utilities
308
+ // --------------------------------------------------------------------------
309
+
310
+ /**
311
+ * 에러 목록 초기화
312
+ */
313
+ clearErrors(): void {
314
+ this.recentErrors = [];
315
+ this.errorCausalityMap.clear();
316
+ }
317
+
318
+ /**
319
+ * 사용자 액션 초기화
320
+ */
321
+ clearUserActions(): void {
322
+ this.userActions = [];
323
+ }
324
+
325
+ /**
326
+ * 전체 초기화
327
+ */
328
+ clear(): void {
329
+ this.clearErrors();
330
+ this.clearUserActions();
331
+ }
332
+
333
+ /**
334
+ * 인과관계 체인 가져오기
335
+ */
336
+ getCausalityChain(errorId: string): string[] {
337
+ const chain: string[] = [];
338
+ let currentId: string | undefined = errorId;
339
+
340
+ while (currentId) {
341
+ const causes = this.errorCausalityMap.get(currentId);
342
+ if (!causes || causes.length === 0) break;
343
+
344
+ const cause = causes[0];
345
+ if (chain.includes(cause)) break; // 순환 방지
346
+
347
+ chain.push(cause);
348
+ currentId = cause;
349
+ }
350
+
351
+ return chain;
352
+ }
353
+ }
354
+
355
+ // ============================================================================
356
+ // Singleton Instance
357
+ // ============================================================================
358
+
359
+ let globalContextBuilder: AIContextBuilder | null = null;
360
+
361
+ export function getContextBuilder(
362
+ options?: ContextBuilderOptions
363
+ ): AIContextBuilder {
364
+ if (!globalContextBuilder) {
365
+ globalContextBuilder = new AIContextBuilder(options);
366
+ }
367
+ return globalContextBuilder;
368
+ }
369
+
370
+ export function resetContextBuilder(): void {
371
+ if (globalContextBuilder) {
372
+ globalContextBuilder.clear();
373
+ }
374
+ globalContextBuilder = null;
375
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Mandu Kitchen DevTools - AI Module
3
+ * @version 1.1.0
4
+ */
5
+
6
+ // Context Builder
7
+ export {
8
+ AIContextBuilder,
9
+ getContextBuilder,
10
+ resetContextBuilder,
11
+ type ContextBuilderOptions,
12
+ type UserAction,
13
+ } from './context-builder';
14
+
15
+ // MCP Connector
16
+ export {
17
+ MCPConnector,
18
+ getMCPConnector,
19
+ destroyMCPConnector,
20
+ type MCPConnectorOptions,
21
+ type MCPMessage,
22
+ type AnalysisRequest,
23
+ type AnalysisResponse,
24
+ type MCPConnectionStatus,
25
+ } from './mcp-connector';