@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 +1 -1
- package/src/bundler/build.ts +11 -5
- package/src/filling/auth.ts +40 -40
- package/src/filling/context.ts +438 -464
- package/src/filling/filling.ts +252 -552
- package/src/filling/index.ts +21 -21
- package/src/filling/tmpclaude-2f8d-cwd +1 -0
- package/src/filling/tmpclaude-2fc1-cwd +1 -0
- package/src/filling/tmpclaude-59ee-cwd +1 -0
- package/src/filling/tmpclaude-7608-cwd +1 -0
- package/src/filling/tmpclaude-a102-cwd +1 -0
- package/src/filling/tmpclaude-bf2c-cwd +1 -0
- package/src/filling/tmpclaude-fb5a-cwd +1 -0
- package/src/runtime/index.ts +3 -2
- package/src/runtime/lifecycle.ts +25 -4
- package/src/runtime/ssr.ts +315 -313
- package/src/runtime/tmpclaude-1f31-cwd +1 -0
- package/src/runtime/tmpclaude-8527-cwd +1 -0
- package/src/runtime/tmpclaude-e62c-cwd +1 -0
- package/src/runtime/trace.ts +85 -0
package/package.json
CHANGED
package/src/bundler/build.ts
CHANGED
|
@@ -55,8 +55,12 @@ function generateRuntimeSource(): string {
|
|
|
55
55
|
* Mandu Hydration Runtime (Generated)
|
|
56
56
|
*/
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
`;
|
package/src/filling/auth.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mandu Auth Guards - 인증/인가 헬퍼 🔐
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 인증 실패 시 적절한 에러를 throw하여
|
|
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
|
|
60
|
+
// 🔐 Auth Helpers
|
|
61
61
|
// ============================================
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* 인증된 사용자 필수
|
|
65
|
-
*
|
|
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
|
-
*
|
|
73
|
+
* typescript
|
|
74
74
|
* import { requireUser } from '@mandujs/core'
|
|
75
75
|
*
|
|
76
76
|
* export default Mandu.filling()
|
|
77
|
-
* .
|
|
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
|
-
*
|
|
81
|
+
* // void 반환 시 계속 진행
|
|
82
82
|
* })
|
|
83
83
|
* .get((ctx) => {
|
|
84
84
|
* const user = requireUser(ctx); // User 타입 확정, 없으면 401
|
|
85
|
-
* return ctx.ok({ message:
|
|
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
|
-
*
|
|
118
|
-
* .
|
|
117
|
+
* typescript
|
|
118
|
+
* .beforeHandle((ctx) => {
|
|
119
119
|
* requireRole(ctx, 'admin', 'moderator'); // admin 또는 moderator만 허용
|
|
120
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
156
|
-
* .
|
|
155
|
+
* typescript
|
|
156
|
+
* .beforeHandle((ctx) => {
|
|
157
157
|
* requireAnyRole(ctx, 'editor', 'admin'); // editor 또는 admin 역할 필요
|
|
158
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
195
|
-
* .
|
|
194
|
+
* typescript
|
|
195
|
+
* .beforeHandle((ctx) => {
|
|
196
196
|
* requireAllRoles(ctx, 'verified', 'premium'); // verified AND premium 필요
|
|
197
|
-
*
|
|
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
|
-
|
|
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
|
|
224
|
+
// 🔐 Auth Handler Factory
|
|
225
225
|
// ============================================
|
|
226
226
|
|
|
227
227
|
/**
|
|
228
|
-
* 인증
|
|
229
|
-
* 반복되는 인증 로직을
|
|
228
|
+
* 인증 beforeHandle 생성 팩토리
|
|
229
|
+
* 반복되는 인증 로직을 beforeHandle로 변환
|
|
230
230
|
*
|
|
231
231
|
* @example
|
|
232
|
-
*
|
|
233
|
-
* const
|
|
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
|
-
* .
|
|
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<
|
|
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
|
|
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
|
-
* 역할 기반
|
|
280
|
+
* 역할 기반 beforeHandle 생성 팩토리
|
|
281
281
|
*
|
|
282
282
|
* @example
|
|
283
|
-
*
|
|
283
|
+
* typescript
|
|
284
284
|
* const adminOnly = createRoleGuard('admin');
|
|
285
285
|
* const editorOrAdmin = createRoleGuard('editor', 'admin');
|
|
286
286
|
*
|
|
287
287
|
* export default Mandu.filling()
|
|
288
|
-
* .
|
|
289
|
-
* .
|
|
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):
|
|
294
|
+
return (ctx: ManduContext): Response | void => {
|
|
295
295
|
try {
|
|
296
296
|
requireRole(ctx, ...allowedRoles);
|
|
297
|
-
return
|
|
297
|
+
return; // void 반환 시 계속 진행
|
|
298
298
|
} catch (error) {
|
|
299
299
|
if (error instanceof AuthenticationError) {
|
|
300
300
|
return ctx.unauthorized(error.message);
|