@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 CHANGED
@@ -1,52 +1,52 @@
1
- {
2
- "name": "@mandujs/core",
3
- "version": "0.9.4",
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
+ }
@@ -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
- let currentRoute = window.__MANDU_ROUTE__ || null;
430
- let currentLoaderData = window.__MANDU_DATA__?.[currentRoute?.id]?.serverData;
431
- let navigationState = { state: 'idle' };
432
- const listeners = new Set();
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
- currentRoute,
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
- listeners.add(listener);
481
- return () => listeners.delete(listener);
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
- navigationState = { state: 'loading', location: to };
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
- currentRoute = { id: data.routeId, pattern: data.pattern, params: data.params };
522
- currentLoaderData = data.loaderData;
523
- navigationState = { state: 'idle' };
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 };
@@ -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
- let routerState: RouterState = {
39
- currentRoute: null,
40
- loaderData: undefined,
41
- navigation: { state: "idle" },
42
- };
38
+ declare global {
39
+ interface Window {
40
+ __MANDU_ROUTER_STATE__?: RouterState;
41
+ __MANDU_ROUTER_LISTENERS__?: Set<RouterListener>;
42
+ }
43
+ }
43
44
 
44
- const listeners = new Set<RouterListener>();
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
- routerState = {
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
- routerState = {
161
- ...routerState,
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
- routerState = {
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
- for (const listener of listeners) {
273
+ const state = getRouterStateInternal();
274
+ for (const listener of listeners.current) {
240
275
  try {
241
- listener(routerState);
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 routerState;
295
+ return getRouterStateInternal();
261
296
  }
262
297
 
263
298
  /**
264
299
  * 현재 라우트 정보 가져오기
265
300
  */
266
301
  export function getCurrentRoute(): RouteInfo | null {
267
- return routerState.currentRoute;
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 routerState.loaderData as T | undefined;
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 routerState.navigation;
316
+ return getRouterStateInternal().navigation;
282
317
  }
283
318
 
284
319
  // ========== Link Click Handler ==========