@mandujs/core 0.7.5 → 0.7.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -198,12 +198,15 @@ export async function hydrateIslands() {
198
198
 
199
199
  /**
200
200
  * 자동 초기화
201
+ * queueMicrotask로 지연: Island 등록 코드가 먼저 실행되도록 함
201
202
  */
202
- if (document.readyState === 'loading') {
203
- document.addEventListener('DOMContentLoaded', hydrateIslands);
204
- } else {
205
- hydrateIslands();
206
- }
203
+ queueMicrotask(() => {
204
+ if (document.readyState === 'loading') {
205
+ document.addEventListener('DOMContentLoaded', hydrateIslands);
206
+ } else {
207
+ hydrateIslands();
208
+ }
209
+ });
207
210
 
208
211
  // 글로벌 레지스트리 접근용 getter
209
212
  export const getIslandRegistry = () => window.__MANDU_ISLANDS__;
@@ -338,6 +338,7 @@ export function initializeRuntime(): void {
338
338
  }
339
339
 
340
340
  // 자동 초기화 여부 (번들 시 설정)
341
+ // queueMicrotask로 지연: 번들 내 Island 등록 코드가 먼저 실행되도록 함
341
342
  if (typeof window !== "undefined" && (window as any).__MANDU_AUTO_INIT__ !== false) {
342
- initializeRuntime();
343
+ queueMicrotask(() => initializeRuntime());
343
344
  }
@@ -141,6 +141,9 @@ export class ManduFilling<TLoaderData = unknown> {
141
141
  return this;
142
142
  }
143
143
 
144
+ /**
145
+ * 요청 시작 훅
146
+ */
144
147
  onRequest(fn: OnRequestHandler): this {
145
148
  this.config.lifecycle.onRequest.push({ fn, scope: "local" });
146
149
  return this;
@@ -159,6 +162,10 @@ export class ManduFilling<TLoaderData = unknown> {
159
162
  return this;
160
163
  }
161
164
 
165
+ /**
166
+ * 바디 파싱 훅
167
+ * body를 읽을 때는 req.clone() 사용 권장
168
+ */
162
169
  onParse(fn: OnParseHandler): this {
163
170
  this.config.lifecycle.onParse.push({ fn, scope: "local" });
164
171
  return this;
@@ -184,21 +191,33 @@ export class ManduFilling<TLoaderData = unknown> {
184
191
  return this.guard(fn);
185
192
  }
186
193
 
194
+ /**
195
+ * 핸들러 후 훅
196
+ */
187
197
  afterHandle(fn: AfterHandleHandler): this {
188
198
  this.config.lifecycle.afterHandle.push({ fn, scope: "local" });
189
199
  return this;
190
200
  }
191
201
 
202
+ /**
203
+ * 최종 응답 매핑 훅
204
+ */
192
205
  mapResponse(fn: MapResponseHandler): this {
193
206
  this.config.lifecycle.mapResponse.push({ fn, scope: "local" });
194
207
  return this;
195
208
  }
196
209
 
210
+ /**
211
+ * 에러 핸들링 훅
212
+ */
197
213
  onError(fn: OnErrorHandler): this {
198
214
  this.config.lifecycle.onError.push({ fn, scope: "local" });
199
215
  return this;
200
216
  }
201
217
 
218
+ /**
219
+ * 응답 후 훅 (비동기)
220
+ */
202
221
  afterResponse(fn: AfterResponseHandler): this {
203
222
  this.config.lifecycle.afterResponse.push({ fn, scope: "local" });
204
223
  return this;
@@ -72,19 +72,19 @@ function generateHydrationScripts(
72
72
  scripts.push(importMap);
73
73
  }
74
74
 
75
- // Runtime 로드
76
- if (manifest.shared.runtime) {
77
- scripts.push(`<script type="module" src="${manifest.shared.runtime}"></script>`);
78
- }
79
-
80
- // Island 번들 로드
75
+ // Island 번들 먼저 로드 (등록)
76
+ // 주의: ES Module 병렬 로드로 인해 Island가 먼저 등록되어야 함
81
77
  const bundle = manifest.bundles[routeId];
82
78
  if (bundle) {
83
- // Preload (선택적)
84
79
  scripts.push(`<link rel="modulepreload" href="${bundle.js}">`);
85
80
  scripts.push(`<script type="module" src="${bundle.js}"></script>`);
86
81
  }
87
82
 
83
+ // Runtime 나중에 로드 (hydrateIslands 실행)
84
+ if (manifest.shared.runtime) {
85
+ scripts.push(`<script type="module" src="${manifest.shared.runtime}"></script>`);
86
+ }
87
+
88
88
  return scripts.join("\n");
89
89
  }
90
90
 
@@ -30,6 +30,19 @@ export interface TraceCollector {
30
30
  records: TraceEntry[];
31
31
  }
32
32
 
33
+ export interface TraceReportEntry {
34
+ event: TraceEvent;
35
+ name?: string;
36
+ start: number;
37
+ end: number;
38
+ duration: number;
39
+ }
40
+
41
+ export interface TraceReport {
42
+ entries: TraceReportEntry[];
43
+ errors: TraceEntry[];
44
+ }
45
+
33
46
  export const TRACE_KEY = "__mandu_trace";
34
47
 
35
48
  const now = (): number => {
@@ -51,6 +64,52 @@ export function getTrace(ctx: ManduContext): TraceCollector | undefined {
51
64
  return ctx.get<TraceCollector>(TRACE_KEY);
52
65
  }
53
66
 
67
+ /**
68
+ * Build a normalized trace report with durations
69
+ */
70
+ export function buildTraceReport(collector: TraceCollector): TraceReport {
71
+ const entries: TraceReportEntry[] = [];
72
+ const errors: TraceEntry[] = [];
73
+ const stacks = new Map<string, TraceEntry[]>();
74
+
75
+ for (const record of collector.records) {
76
+ if (record.phase === "error") {
77
+ errors.push(record);
78
+ continue;
79
+ }
80
+
81
+ const key = `${record.event}:${record.name ?? ""}`;
82
+ if (record.phase === "begin") {
83
+ const stack = stacks.get(key) ?? [];
84
+ stack.push(record);
85
+ stacks.set(key, stack);
86
+ continue;
87
+ }
88
+
89
+ if (record.phase === "end") {
90
+ const stack = stacks.get(key);
91
+ const begin = stack?.pop();
92
+ if (!begin) continue;
93
+ entries.push({
94
+ event: record.event,
95
+ name: record.name,
96
+ start: begin.time,
97
+ end: record.time,
98
+ duration: record.time - begin.time,
99
+ });
100
+ }
101
+ }
102
+
103
+ return { entries, errors };
104
+ }
105
+
106
+ /**
107
+ * Convert trace report to JSON string
108
+ */
109
+ export function formatTraceReport(report: TraceReport): string {
110
+ return JSON.stringify(report, null, 2);
111
+ }
112
+
54
113
  export interface Tracer {
55
114
  enabled: boolean;
56
115
  begin: (event: TraceEvent, name?: string) => () => void;
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/filling
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/runtime
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/runtime
@@ -1 +0,0 @@
1
- /c/Users/LamySolution/workspace/mandu/packages/core/src/runtime