@mandujs/core 0.7.1 → 0.7.3

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.1",
3
+ "version": "0.7.3",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -55,8 +55,12 @@ function generateRuntimeSource(): string {
55
55
  * Mandu Hydration Runtime (Generated)
56
56
  */
57
57
 
58
- const islandRegistry = new Map();
59
- const hydratedRoots = new Map();
58
+ // 글로벌 레지스트리 사용 (Island 번들과 공유)
59
+ window.__MANDU_ISLANDS__ = window.__MANDU_ISLANDS__ || new Map();
60
+ window.__MANDU_ROOTS__ = window.__MANDU_ROOTS__ || new Map();
61
+
62
+ const islandRegistry = window.__MANDU_ISLANDS__;
63
+ const hydratedRoots = window.__MANDU_ROOTS__;
60
64
 
61
65
  // 서버 데이터
62
66
  const serverData = window.__MANDU_DATA__ || {};
@@ -65,7 +69,7 @@ const serverData = window.__MANDU_DATA__ || {};
65
69
  * Island 등록
66
70
  */
67
71
  export function registerIsland(id, loader) {
68
- islandRegistry.set(id, loader);
72
+ window.__MANDU_ISLANDS__.set(id, loader);
69
73
  }
70
74
 
71
75
  /**
@@ -605,6 +609,7 @@ async function buildRouterRuntime(
605
609
 
606
610
  /**
607
611
  * Island 엔트리 래퍼 생성
612
+ * 주의: 글로벌 레지스트리 직접 사용 (번들러 인라인 문제 방지)
608
613
  */
609
614
  function generateIslandEntry(routeId: string, clientModulePath: string): string {
610
615
  // Windows 경로의 백슬래시를 슬래시로 변환 (JS escape 문제 방지)
@@ -615,9 +620,10 @@ function generateIslandEntry(routeId: string, clientModulePath: string): string
615
620
  */
616
621
 
617
622
  import island from "${normalizedPath}";
618
- import { registerIsland } from "./_runtime.js";
619
623
 
620
- registerIsland("${routeId}", () => island);
624
+ // 글로벌 레지스트리에 직접 등록 (런타임과 공유)
625
+ window.__MANDU_ISLANDS__ = window.__MANDU_ISLANDS__ || new Map();
626
+ window.__MANDU_ISLANDS__.set("${routeId}", () => island);
621
627
 
622
628
  export default island;
623
629
  `;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Mandu Auth Guards - 인증/인가 헬퍼 🔐
3
3
  *
4
- * Guard에서 사용할 수 있는 타입-안전 인증 헬퍼
5
- * 인증 실패 시 적절한 에러를 throw하여 Guard 체인 중단
4
+ * beforeHandle에서 사용할 수 있는 타입-안전 인증 헬퍼
5
+ * 인증 실패 시 적절한 에러를 throw하여 체인 중단
6
6
  */
7
7
 
8
8
  import type { ManduContext } from "./context";
@@ -57,12 +57,12 @@ export interface UserWithRoles extends BaseUser {
57
57
  }
58
58
 
59
59
  // ============================================
60
- // 🔐 Auth Guard Helpers
60
+ // 🔐 Auth Helpers
61
61
  // ============================================
62
62
 
63
63
  /**
64
64
  * 인증된 사용자 필수
65
- * Guard에서 user가 없으면 AuthenticationError throw
65
+ * beforeHandle에서 user가 없으면 AuthenticationError throw
66
66
  *
67
67
  * @param ctx ManduContext
68
68
  * @param key store에서 user를 찾을 키 (기본: 'user')
@@ -70,21 +70,21 @@ export interface UserWithRoles extends BaseUser {
70
70
  * @throws AuthenticationError
71
71
  *
72
72
  * @example
73
- * ```typescript
73
+ * typescript
74
74
  * import { requireUser } from '@mandujs/core'
75
75
  *
76
76
  * export default Mandu.filling()
77
- * .guard(async (ctx) => {
77
+ * .beforeHandle(async (ctx) => {
78
78
  * // JWT 토큰 검증 후 user 저장
79
79
  * const user = await verifyToken(ctx.headers.get('Authorization'));
80
80
  * ctx.set('user', user);
81
- * return ctx.next();
81
+ * // void 반환 시 계속 진행
82
82
  * })
83
83
  * .get((ctx) => {
84
84
  * const user = requireUser(ctx); // User 타입 확정, 없으면 401
85
- * return ctx.ok({ message: `Hello, ${user.id}!` });
85
+ * return ctx.ok({ message: "Hello, " + user.id + "!" });
86
86
  * })
87
- * ```
87
+ *
88
88
  */
89
89
  export function requireUser<T extends BaseUser = BaseUser>(
90
90
  ctx: ManduContext,
@@ -114,12 +114,12 @@ export function requireUser<T extends BaseUser = BaseUser>(
114
114
  * @throws AuthorizationError (역할 불일치)
115
115
  *
116
116
  * @example
117
- * ```typescript
118
- * .guard((ctx) => {
117
+ * typescript
118
+ * .beforeHandle((ctx) => {
119
119
  * requireRole(ctx, 'admin', 'moderator'); // admin 또는 moderator만 허용
120
- * return ctx.next();
120
+ * // void 반환 시 계속 진행
121
121
  * })
122
- * ```
122
+ *
123
123
  */
124
124
  export function requireRole<T extends UserWithRole = UserWithRole>(
125
125
  ctx: ManduContext,
@@ -133,7 +133,7 @@ export function requireRole<T extends UserWithRole = UserWithRole>(
133
133
 
134
134
  if (!roles.includes(user.role)) {
135
135
  throw new AuthorizationError(
136
- `Required role: ${roles.join(" or ")}`,
136
+ "Required role: " + roles.join(" or "),
137
137
  roles
138
138
  );
139
139
  }
@@ -152,12 +152,12 @@ export function requireRole<T extends UserWithRole = UserWithRole>(
152
152
  * @throws AuthorizationError (역할 불일치)
153
153
  *
154
154
  * @example
155
- * ```typescript
156
- * .guard((ctx) => {
155
+ * typescript
156
+ * .beforeHandle((ctx) => {
157
157
  * requireAnyRole(ctx, 'editor', 'admin'); // editor 또는 admin 역할 필요
158
- * return ctx.next();
158
+ * // void 반환 시 계속 진행
159
159
  * })
160
- * ```
160
+ *
161
161
  */
162
162
  export function requireAnyRole<T extends UserWithRoles = UserWithRoles>(
163
163
  ctx: ManduContext,
@@ -173,7 +173,7 @@ export function requireAnyRole<T extends UserWithRoles = UserWithRoles>(
173
173
 
174
174
  if (!hasRole) {
175
175
  throw new AuthorizationError(
176
- `Required one of roles: ${roles.join(", ")}`,
176
+ "Required one of roles: " + roles.join(", "),
177
177
  roles
178
178
  );
179
179
  }
@@ -191,12 +191,12 @@ export function requireAnyRole<T extends UserWithRoles = UserWithRoles>(
191
191
  * @throws AuthorizationError (역할 불일치)
192
192
  *
193
193
  * @example
194
- * ```typescript
195
- * .guard((ctx) => {
194
+ * typescript
195
+ * .beforeHandle((ctx) => {
196
196
  * requireAllRoles(ctx, 'verified', 'premium'); // verified AND premium 필요
197
- * return ctx.next();
197
+ * // void 반환 시 계속 진행
198
198
  * })
199
- * ```
199
+ *
200
200
  */
201
201
  export function requireAllRoles<T extends UserWithRoles = UserWithRoles>(
202
202
  ctx: ManduContext,
@@ -212,7 +212,7 @@ export function requireAllRoles<T extends UserWithRoles = UserWithRoles>(
212
212
 
213
213
  if (missingRoles.length > 0) {
214
214
  throw new AuthorizationError(
215
- `Missing required roles: ${missingRoles.join(", ")}`,
215
+ "Missing required roles: " + missingRoles.join(", "),
216
216
  roles
217
217
  );
218
218
  }
@@ -221,28 +221,28 @@ export function requireAllRoles<T extends UserWithRoles = UserWithRoles>(
221
221
  }
222
222
 
223
223
  // ============================================
224
- // 🔐 Auth Guard Factory
224
+ // 🔐 Auth Handler Factory
225
225
  // ============================================
226
226
 
227
227
  /**
228
- * 인증 Guard 생성 팩토리
229
- * 반복되는 인증 로직을 Guard로 변환
228
+ * 인증 beforeHandle 생성 팩토리
229
+ * 반복되는 인증 로직을 beforeHandle로 변환
230
230
  *
231
231
  * @example
232
- * ```typescript
233
- * const authGuard = createAuthGuard(async (ctx) => {
232
+ * typescript
233
+ * const authHandler = createAuthGuard(async (ctx) => {
234
234
  * const token = ctx.headers.get('Authorization')?.replace('Bearer ', '');
235
235
  * if (!token) return null;
236
236
  * return await verifyJwt(token);
237
237
  * });
238
238
  *
239
239
  * export default Mandu.filling()
240
- * .guard(authGuard)
240
+ * .beforeHandle(authHandler)
241
241
  * .get((ctx) => {
242
242
  * const user = requireUser(ctx);
243
243
  * return ctx.ok({ user });
244
244
  * })
245
- * ```
245
+ *
246
246
  */
247
247
  export function createAuthGuard<T extends BaseUser>(
248
248
  authenticator: (ctx: ManduContext) => T | null | Promise<T | null>,
@@ -253,13 +253,13 @@ export function createAuthGuard<T extends BaseUser>(
253
253
  ) {
254
254
  const { key = "user", onUnauthenticated } = options;
255
255
 
256
- return async (ctx: ManduContext): Promise<symbol | Response> => {
256
+ return async (ctx: ManduContext): Promise<Response | void> => {
257
257
  try {
258
258
  const user = await authenticator(ctx);
259
259
 
260
260
  if (user) {
261
261
  ctx.set(key, user);
262
- return ctx.next();
262
+ return; // void 반환 시 계속 진행
263
263
  }
264
264
 
265
265
  if (onUnauthenticated) {
@@ -277,24 +277,24 @@ export function createAuthGuard<T extends BaseUser>(
277
277
  }
278
278
 
279
279
  /**
280
- * 역할 기반 Guard 생성 팩토리
280
+ * 역할 기반 beforeHandle 생성 팩토리
281
281
  *
282
282
  * @example
283
- * ```typescript
283
+ * typescript
284
284
  * const adminOnly = createRoleGuard('admin');
285
285
  * const editorOrAdmin = createRoleGuard('editor', 'admin');
286
286
  *
287
287
  * export default Mandu.filling()
288
- * .guard(authGuard)
289
- * .guard(adminOnly) // admin만 접근 가능
288
+ * .beforeHandle(authHandler)
289
+ * .beforeHandle(adminOnly) // admin만 접근 가능
290
290
  * .delete((ctx) => ctx.noContent())
291
- * ```
291
+ *
292
292
  */
293
293
  export function createRoleGuard(...allowedRoles: string[]) {
294
- return (ctx: ManduContext): symbol | Response => {
294
+ return (ctx: ManduContext): Response | void => {
295
295
  try {
296
296
  requireRole(ctx, ...allowedRoles);
297
- return ctx.next();
297
+ return; // void 반환 시 계속 진행
298
298
  } catch (error) {
299
299
  if (error instanceof AuthenticationError) {
300
300
  return ctx.unauthorized(error.message);