@mandujs/core 0.9.4 → 0.9.6
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 +52 -52
- package/src/bundler/build.ts +43 -22
- package/src/client/router.ts +57 -22
package/package.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@mandujs/core",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./src/index.ts",
|
|
7
|
-
"types": "./src/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./src/index.ts",
|
|
10
|
-
"./client": "./src/client/index.ts",
|
|
11
|
-
"./*": "./src/*"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"src/**/*"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"test": "bun test",
|
|
18
|
-
"test:hydration": "bun test tests/hydration",
|
|
19
|
-
"test:watch": "bun test --watch"
|
|
20
|
-
},
|
|
21
|
-
"devDependencies": {
|
|
22
|
-
"@happy-dom/global-registrator": "^15.0.0"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"mandu",
|
|
26
|
-
"framework",
|
|
27
|
-
"agent",
|
|
28
|
-
"ai",
|
|
29
|
-
"code-generation"
|
|
30
|
-
],
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "git+https://github.com/konamgil/mandu.git",
|
|
34
|
-
"directory": "packages/core"
|
|
35
|
-
},
|
|
36
|
-
"author": "konamgil",
|
|
37
|
-
"license": "MIT",
|
|
38
|
-
"publishConfig": {
|
|
39
|
-
"access": "public"
|
|
40
|
-
},
|
|
41
|
-
"engines": {
|
|
42
|
-
"bun": ">=1.0.0"
|
|
43
|
-
},
|
|
44
|
-
"peerDependencies": {
|
|
45
|
-
"react": ">=18.0.0",
|
|
46
|
-
"react-dom": ">=18.0.0",
|
|
47
|
-
"zod": ">=3.0.0"
|
|
48
|
-
},
|
|
49
|
-
"dependencies": {
|
|
50
|
-
"ollama": "^0.6.3"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@mandujs/core",
|
|
3
|
+
"version": "0.9.6",
|
|
4
|
+
"description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.ts",
|
|
10
|
+
"./client": "./src/client/index.ts",
|
|
11
|
+
"./*": "./src/*"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src/**/*"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"test:hydration": "bun test tests/hydration",
|
|
19
|
+
"test:watch": "bun test --watch"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@happy-dom/global-registrator": "^15.0.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mandu",
|
|
26
|
+
"framework",
|
|
27
|
+
"agent",
|
|
28
|
+
"ai",
|
|
29
|
+
"code-generation"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/konamgil/mandu.git",
|
|
34
|
+
"directory": "packages/core"
|
|
35
|
+
},
|
|
36
|
+
"author": "konamgil",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"bun": ">=1.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": ">=18.0.0",
|
|
46
|
+
"react-dom": ">=18.0.0",
|
|
47
|
+
"zod": ">=3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"ollama": "^0.6.3"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/bundler/build.ts
CHANGED
|
@@ -423,13 +423,37 @@ function generateRouterRuntimeSource(): string {
|
|
|
423
423
|
/**
|
|
424
424
|
* Mandu Client Router Runtime (Generated)
|
|
425
425
|
* Client-side Routing을 위한 런타임
|
|
426
|
+
* 전역 상태를 사용하여 모든 모듈에서 동일 인스턴스 공유
|
|
426
427
|
*/
|
|
427
428
|
|
|
428
|
-
//
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
429
|
+
// 전역 상태 초기화 (Island와 공유)
|
|
430
|
+
function getGlobalState() {
|
|
431
|
+
if (!window.__MANDU_ROUTER_STATE__) {
|
|
432
|
+
const route = window.__MANDU_ROUTE__;
|
|
433
|
+
window.__MANDU_ROUTER_STATE__ = {
|
|
434
|
+
currentRoute: route ? {
|
|
435
|
+
id: route.id,
|
|
436
|
+
pattern: route.pattern,
|
|
437
|
+
params: route.params || {}
|
|
438
|
+
} : null,
|
|
439
|
+
loaderData: window.__MANDU_DATA__?.[route?.id]?.serverData,
|
|
440
|
+
navigation: { state: 'idle' }
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
return window.__MANDU_ROUTER_STATE__;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function setGlobalState(state) {
|
|
447
|
+
window.__MANDU_ROUTER_STATE__ = state;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// 전역 listeners
|
|
451
|
+
function getListeners() {
|
|
452
|
+
if (!window.__MANDU_ROUTER_LISTENERS__) {
|
|
453
|
+
window.__MANDU_ROUTER_LISTENERS__ = new Set();
|
|
454
|
+
}
|
|
455
|
+
return window.__MANDU_ROUTER_LISTENERS__;
|
|
456
|
+
}
|
|
433
457
|
|
|
434
458
|
// 패턴 매칭 캐시
|
|
435
459
|
const patternCache = new Map();
|
|
@@ -468,25 +492,17 @@ function extractParams(pattern, pathname) {
|
|
|
468
492
|
}
|
|
469
493
|
|
|
470
494
|
function notifyListeners() {
|
|
471
|
-
const state =
|
|
472
|
-
|
|
473
|
-
loaderData: currentLoaderData,
|
|
474
|
-
navigation: navigationState
|
|
475
|
-
};
|
|
476
|
-
listeners.forEach(fn => { try { fn(state); } catch(e) {} });
|
|
495
|
+
const state = getGlobalState();
|
|
496
|
+
getListeners().forEach(fn => { try { fn(state); } catch(e) {} });
|
|
477
497
|
}
|
|
478
498
|
|
|
479
499
|
export function subscribe(listener) {
|
|
480
|
-
|
|
481
|
-
return () =>
|
|
500
|
+
getListeners().add(listener);
|
|
501
|
+
return () => getListeners().delete(listener);
|
|
482
502
|
}
|
|
483
503
|
|
|
484
504
|
export function getRouterState() {
|
|
485
|
-
return
|
|
486
|
-
currentRoute,
|
|
487
|
-
loaderData: currentLoaderData,
|
|
488
|
-
navigation: navigationState
|
|
489
|
-
};
|
|
505
|
+
return getGlobalState();
|
|
490
506
|
}
|
|
491
507
|
|
|
492
508
|
export async function navigate(to, options = {}) {
|
|
@@ -499,7 +515,9 @@ export async function navigate(to, options = {}) {
|
|
|
499
515
|
return;
|
|
500
516
|
}
|
|
501
517
|
|
|
502
|
-
|
|
518
|
+
// 로딩 상태로 전환
|
|
519
|
+
const state = getGlobalState();
|
|
520
|
+
setGlobalState({ ...state, navigation: { state: 'loading', location: to } });
|
|
503
521
|
notifyListeners();
|
|
504
522
|
|
|
505
523
|
const dataUrl = url.pathname + (url.search ? url.search + '&' : '?') + '_data=1';
|
|
@@ -518,9 +536,12 @@ export async function navigate(to, options = {}) {
|
|
|
518
536
|
history.pushState({ routeId: data.routeId }, '', to);
|
|
519
537
|
}
|
|
520
538
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
539
|
+
// 전역 상태 업데이트
|
|
540
|
+
setGlobalState({
|
|
541
|
+
currentRoute: { id: data.routeId, pattern: data.pattern, params: data.params },
|
|
542
|
+
loaderData: data.loaderData,
|
|
543
|
+
navigation: { state: 'idle' }
|
|
544
|
+
});
|
|
524
545
|
|
|
525
546
|
window.__MANDU_DATA__ = window.__MANDU_DATA__ || {};
|
|
526
547
|
window.__MANDU_DATA__[data.routeId] = { serverData: data.loaderData };
|
package/src/client/router.ts
CHANGED
|
@@ -33,15 +33,49 @@ export interface NavigateOptions {
|
|
|
33
33
|
|
|
34
34
|
type RouterListener = (state: RouterState) => void;
|
|
35
35
|
|
|
36
|
-
// ========== Router State ==========
|
|
36
|
+
// ========== Global Router State (모든 모듈에서 동일 인스턴스 공유) ==========
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
38
|
+
declare global {
|
|
39
|
+
interface Window {
|
|
40
|
+
__MANDU_ROUTER_STATE__?: RouterState;
|
|
41
|
+
__MANDU_ROUTER_LISTENERS__?: Set<RouterListener>;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
function getGlobalRouterState(): RouterState {
|
|
46
|
+
if (typeof window === "undefined") {
|
|
47
|
+
return { currentRoute: null, loaderData: undefined, navigation: { state: "idle" } };
|
|
48
|
+
}
|
|
49
|
+
if (!window.__MANDU_ROUTER_STATE__) {
|
|
50
|
+
window.__MANDU_ROUTER_STATE__ = {
|
|
51
|
+
currentRoute: null,
|
|
52
|
+
loaderData: undefined,
|
|
53
|
+
navigation: { state: "idle" },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return window.__MANDU_ROUTER_STATE__;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function setGlobalRouterState(state: RouterState): void {
|
|
60
|
+
if (typeof window !== "undefined") {
|
|
61
|
+
window.__MANDU_ROUTER_STATE__ = state;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getGlobalListeners(): Set<RouterListener> {
|
|
66
|
+
if (typeof window === "undefined") {
|
|
67
|
+
return new Set();
|
|
68
|
+
}
|
|
69
|
+
if (!window.__MANDU_ROUTER_LISTENERS__) {
|
|
70
|
+
window.__MANDU_ROUTER_LISTENERS__ = new Set();
|
|
71
|
+
}
|
|
72
|
+
return window.__MANDU_ROUTER_LISTENERS__;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Getter for routerState (전역 상태 참조)
|
|
76
|
+
const getRouterStateInternal = () => getGlobalRouterState();
|
|
77
|
+
const setRouterStateInternal = (state: RouterState) => setGlobalRouterState(state);
|
|
78
|
+
const listeners = { get current() { return getGlobalListeners(); } };
|
|
45
79
|
|
|
46
80
|
/**
|
|
47
81
|
* 초기화: 서버에서 전달된 라우트 정보로 상태 설정
|
|
@@ -56,7 +90,7 @@ function initializeFromServer(): void {
|
|
|
56
90
|
// URL에서 실제 params 추출
|
|
57
91
|
const params = extractParamsFromPath(route.pattern, window.location.pathname);
|
|
58
92
|
|
|
59
|
-
|
|
93
|
+
setRouterStateInternal({
|
|
60
94
|
currentRoute: {
|
|
61
95
|
id: route.id,
|
|
62
96
|
pattern: route.pattern,
|
|
@@ -64,7 +98,7 @@ function initializeFromServer(): void {
|
|
|
64
98
|
},
|
|
65
99
|
loaderData: data?.[route.id]?.serverData,
|
|
66
100
|
navigation: { state: "idle" },
|
|
67
|
-
};
|
|
101
|
+
});
|
|
68
102
|
}
|
|
69
103
|
}
|
|
70
104
|
|
|
@@ -157,10 +191,10 @@ export async function navigate(
|
|
|
157
191
|
}
|
|
158
192
|
|
|
159
193
|
// 로딩 상태 시작
|
|
160
|
-
|
|
161
|
-
...
|
|
194
|
+
setRouterStateInternal({
|
|
195
|
+
...getRouterStateInternal(),
|
|
162
196
|
navigation: { state: "loading", location: to },
|
|
163
|
-
};
|
|
197
|
+
});
|
|
164
198
|
notifyListeners();
|
|
165
199
|
|
|
166
200
|
// 데이터 fetch
|
|
@@ -184,7 +218,7 @@ export async function navigate(
|
|
|
184
218
|
}
|
|
185
219
|
|
|
186
220
|
// 상태 업데이트
|
|
187
|
-
|
|
221
|
+
setRouterStateInternal({
|
|
188
222
|
currentRoute: {
|
|
189
223
|
id: data.routeId,
|
|
190
224
|
pattern: data.pattern,
|
|
@@ -192,7 +226,7 @@ export async function navigate(
|
|
|
192
226
|
},
|
|
193
227
|
loaderData: data.loaderData,
|
|
194
228
|
navigation: { state: "idle" },
|
|
195
|
-
};
|
|
229
|
+
});
|
|
196
230
|
|
|
197
231
|
// __MANDU_DATA__ 업데이트
|
|
198
232
|
if (typeof window !== "undefined") {
|
|
@@ -236,9 +270,10 @@ function handlePopState(event: PopStateEvent): void {
|
|
|
236
270
|
* 리스너에게 상태 변경 알림
|
|
237
271
|
*/
|
|
238
272
|
function notifyListeners(): void {
|
|
239
|
-
|
|
273
|
+
const state = getRouterStateInternal();
|
|
274
|
+
for (const listener of listeners.current) {
|
|
240
275
|
try {
|
|
241
|
-
listener(
|
|
276
|
+
listener(state);
|
|
242
277
|
} catch (error) {
|
|
243
278
|
console.error("[Mandu Router] Listener error:", error);
|
|
244
279
|
}
|
|
@@ -249,36 +284,36 @@ function notifyListeners(): void {
|
|
|
249
284
|
* 상태 변경 구독
|
|
250
285
|
*/
|
|
251
286
|
export function subscribe(listener: RouterListener): () => void {
|
|
252
|
-
listeners.add(listener);
|
|
253
|
-
return () => listeners.delete(listener);
|
|
287
|
+
listeners.current.add(listener);
|
|
288
|
+
return () => listeners.current.delete(listener);
|
|
254
289
|
}
|
|
255
290
|
|
|
256
291
|
/**
|
|
257
292
|
* 현재 라우터 상태 가져오기
|
|
258
293
|
*/
|
|
259
294
|
export function getRouterState(): RouterState {
|
|
260
|
-
return
|
|
295
|
+
return getRouterStateInternal();
|
|
261
296
|
}
|
|
262
297
|
|
|
263
298
|
/**
|
|
264
299
|
* 현재 라우트 정보 가져오기
|
|
265
300
|
*/
|
|
266
301
|
export function getCurrentRoute(): RouteInfo | null {
|
|
267
|
-
return
|
|
302
|
+
return getRouterStateInternal().currentRoute;
|
|
268
303
|
}
|
|
269
304
|
|
|
270
305
|
/**
|
|
271
306
|
* 현재 loader 데이터 가져오기
|
|
272
307
|
*/
|
|
273
308
|
export function getLoaderData<T = unknown>(): T | undefined {
|
|
274
|
-
return
|
|
309
|
+
return getRouterStateInternal().loaderData as T | undefined;
|
|
275
310
|
}
|
|
276
311
|
|
|
277
312
|
/**
|
|
278
313
|
* 네비게이션 상태 가져오기
|
|
279
314
|
*/
|
|
280
315
|
export function getNavigationState(): NavigationState {
|
|
281
|
-
return
|
|
316
|
+
return getRouterStateInternal().navigation;
|
|
282
317
|
}
|
|
283
318
|
|
|
284
319
|
// ========== Link Click Handler ==========
|