@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.
- package/package.json +1 -1
- package/src/bundler/build.ts +91 -73
- package/src/bundler/dev.ts +21 -14
- package/src/client/globals.ts +44 -0
- package/src/client/index.ts +5 -4
- package/src/client/island.ts +8 -13
- package/src/client/router.ts +33 -41
- package/src/client/runtime.ts +23 -51
- package/src/client/window-state.ts +101 -0
- package/src/config/index.ts +1 -0
- package/src/config/mandu.ts +45 -9
- package/src/config/validate.ts +158 -0
- package/src/constants.ts +25 -0
- package/src/contract/client.ts +4 -3
- package/src/contract/define.ts +459 -0
- package/src/devtools/ai/context-builder.ts +375 -0
- package/src/devtools/ai/index.ts +25 -0
- package/src/devtools/ai/mcp-connector.ts +465 -0
- package/src/devtools/client/catchers/error-catcher.ts +327 -0
- package/src/devtools/client/catchers/index.ts +18 -0
- package/src/devtools/client/catchers/network-proxy.ts +363 -0
- package/src/devtools/client/components/index.ts +39 -0
- package/src/devtools/client/components/kitchen-root.tsx +362 -0
- package/src/devtools/client/components/mandu-character.tsx +241 -0
- package/src/devtools/client/components/overlay.tsx +368 -0
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -0
- package/src/devtools/client/components/panel/guard-panel.tsx +244 -0
- package/src/devtools/client/components/panel/index.ts +32 -0
- package/src/devtools/client/components/panel/islands-panel.tsx +304 -0
- package/src/devtools/client/components/panel/network-panel.tsx +292 -0
- package/src/devtools/client/components/panel/panel-container.tsx +259 -0
- package/src/devtools/client/filters/context-filters.ts +282 -0
- package/src/devtools/client/filters/index.ts +16 -0
- package/src/devtools/client/index.ts +63 -0
- package/src/devtools/client/persistence.ts +335 -0
- package/src/devtools/client/state-manager.ts +478 -0
- package/src/devtools/design-tokens.ts +263 -0
- package/src/devtools/hook/create-hook.ts +207 -0
- package/src/devtools/hook/index.ts +13 -0
- package/src/devtools/index.ts +439 -0
- package/src/devtools/init.ts +266 -0
- package/src/devtools/protocol.ts +237 -0
- package/src/devtools/server/index.ts +17 -0
- package/src/devtools/server/source-context.ts +444 -0
- package/src/devtools/types.ts +319 -0
- package/src/devtools/worker/index.ts +25 -0
- package/src/devtools/worker/redaction-worker.ts +222 -0
- package/src/devtools/worker/worker-manager.ts +409 -0
- package/src/error/formatter.ts +28 -24
- package/src/error/index.ts +13 -9
- package/src/error/result.ts +46 -0
- package/src/error/types.ts +6 -4
- package/src/filling/filling.ts +6 -5
- package/src/guard/check.ts +60 -56
- package/src/guard/types.ts +3 -1
- package/src/guard/watcher.ts +10 -1
- package/src/index.ts +81 -0
- package/src/intent/index.ts +310 -0
- package/src/island/index.ts +304 -0
- package/src/router/fs-patterns.ts +7 -0
- package/src/router/fs-routes.ts +20 -8
- package/src/router/fs-scanner.ts +117 -133
- package/src/runtime/server.ts +261 -201
- package/src/runtime/ssr.ts +5 -4
- package/src/runtime/streaming-ssr.ts +5 -4
- package/src/utils/bun.ts +8 -0
- 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';
|