@mandujs/core 0.12.1 → 0.13.0
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/README.ko.md +304 -304
- package/README.md +653 -653
- package/package.json +8 -8
- package/src/brain/architecture/analyzer.ts +28 -26
- package/src/brain/doctor/analyzer.ts +1 -1
- package/src/bundler/build.ts +91 -91
- package/src/bundler/css.ts +302 -302
- package/src/bundler/dev.ts +0 -1
- package/src/change/history.ts +3 -3
- package/src/change/snapshot.ts +10 -9
- package/src/change/transaction.ts +2 -2
- package/src/client/Link.tsx +227 -227
- package/src/client/globals.ts +44 -44
- package/src/client/hooks.ts +267 -267
- package/src/client/index.ts +5 -5
- package/src/client/island.ts +8 -8
- package/src/client/router.ts +435 -435
- package/src/client/runtime.ts +23 -23
- package/src/client/serialize.ts +404 -404
- package/src/client/window-state.ts +101 -101
- package/src/config/mandu.ts +94 -96
- package/src/config/validate.ts +213 -215
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/content-layer.ts +314 -314
- package/src/content/content.test.ts +433 -433
- package/src/content/data-store.ts +245 -245
- package/src/content/digest.ts +133 -133
- package/src/content/index.ts +164 -164
- package/src/content/loader-context.ts +172 -172
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +169 -169
- package/src/content/loaders/glob.ts +252 -252
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/client.ts +16 -16
- package/src/contract/define.ts +459 -459
- package/src/contract/handler.ts +10 -10
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +404 -404
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/registry.ts +568 -568
- package/src/contract/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/contract/validator.ts +32 -32
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +465 -465
- package/src/devtools/client/catchers/error-catcher.ts +327 -327
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/catchers/network-proxy.ts +363 -363
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/kitchen-root.tsx +362 -362
- package/src/devtools/client/components/mandu-character.tsx +241 -241
- package/src/devtools/client/components/overlay.tsx +368 -368
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +244 -244
- package/src/devtools/client/components/panel/index.ts +32 -32
- package/src/devtools/client/components/panel/islands-panel.tsx +304 -304
- package/src/devtools/client/components/panel/network-panel.tsx +292 -292
- package/src/devtools/client/components/panel/panel-container.tsx +259 -259
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/client/persistence.ts +335 -335
- package/src/devtools/client/state-manager.ts +478 -478
- package/src/devtools/design-tokens.ts +263 -263
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +266 -266
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/server/source-context.ts +444 -444
- package/src/devtools/types.ts +319 -319
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +222 -222
- package/src/devtools/worker/worker-manager.ts +409 -409
- package/src/error/classifier.ts +2 -2
- package/src/error/domains.ts +265 -265
- package/src/error/formatter.ts +32 -32
- package/src/error/result.ts +46 -46
- package/src/error/stack-analyzer.ts +5 -0
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/auth.ts +308 -308
- package/src/filling/context.ts +569 -569
- package/src/filling/deps.ts +238 -238
- package/src/generator/contract-glue.ts +2 -1
- package/src/generator/generate.ts +12 -10
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.ts +80 -79
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/ast-analyzer.ts +806 -806
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +128 -128
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/healing.ts +2 -0
- package/src/guard/index.ts +2 -0
- package/src/guard/negotiation.ts +430 -4
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/cqrs.test.ts +175 -0
- package/src/guard/presets/cqrs.ts +107 -0
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/presets/index.ts +291 -288
- package/src/guard/reporter.ts +445 -445
- package/src/guard/rules.ts +12 -12
- package/src/guard/statistics.ts +578 -578
- package/src/guard/suggestions.ts +358 -352
- package/src/guard/types.ts +348 -347
- package/src/guard/validator.ts +834 -834
- package/src/guard/watcher.ts +404 -404
- package/src/index.ts +1 -0
- package/src/intent/index.ts +310 -310
- package/src/island/index.ts +304 -304
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/paths.test.ts +47 -0
- package/src/paths.ts +47 -0
- package/src/plugins/index.ts +38 -38
- package/src/plugins/registry.ts +377 -377
- package/src/plugins/types.ts +363 -363
- package/src/report/build.ts +1 -1
- package/src/report/index.ts +1 -1
- package/src/router/fs-patterns.ts +387 -387
- package/src/router/fs-routes.ts +344 -401
- package/src/router/fs-scanner.ts +497 -497
- package/src/router/fs-types.ts +270 -278
- package/src/router/index.ts +81 -81
- package/src/runtime/boundary.tsx +232 -232
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/logger.ts +677 -677
- package/src/runtime/router.test.ts +476 -476
- package/src/runtime/router.ts +105 -105
- package/src/runtime/security.ts +155 -155
- package/src/runtime/server.ts +24 -24
- package/src/runtime/session-key.ts +328 -328
- package/src/runtime/ssr.ts +367 -367
- package/src/runtime/streaming-ssr.ts +1245 -1245
- package/src/runtime/trace.ts +144 -144
- package/src/seo/index.ts +214 -214
- package/src/seo/integration/ssr.ts +307 -307
- package/src/seo/render/basic.ts +427 -427
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +191 -191
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +126 -126
- package/src/seo/resolve/index.ts +353 -353
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +97 -97
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +575 -575
- package/src/slot/validator.ts +39 -39
- package/src/spec/index.ts +3 -3
- package/src/spec/load.ts +76 -76
- package/src/spec/lock.ts +56 -56
- package/src/utils/bun.ts +8 -8
- package/src/utils/lru-cache.ts +75 -75
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/rules.ts +5 -5
package/src/runtime/compose.ts
CHANGED
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu Middleware Compose 🔗
|
|
3
|
-
* Hono 스타일 미들웨어 조합 패턴
|
|
4
|
-
*
|
|
5
|
-
* @see https://github.com/honojs/hono/blob/main/src/compose.ts
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ManduContext } from "../filling/context";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Next 함수 타입
|
|
12
|
-
*/
|
|
13
|
-
export type Next = () => Promise<void>;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 미들웨어 함수 타입
|
|
17
|
-
* - Response 반환: 체인 중단 (Guard 역할)
|
|
18
|
-
* - void 반환: 다음 미들웨어 실행
|
|
19
|
-
*/
|
|
20
|
-
export type Middleware = (
|
|
21
|
-
ctx: ManduContext,
|
|
22
|
-
next: Next
|
|
23
|
-
) => Response | void | Promise<Response | void>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 에러 핸들러 타입
|
|
27
|
-
*/
|
|
28
|
-
export type ErrorHandler = (
|
|
29
|
-
error: Error,
|
|
30
|
-
ctx: ManduContext
|
|
31
|
-
) => Response | Promise<Response>;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* NotFound 핸들러 타입
|
|
35
|
-
*/
|
|
36
|
-
export type NotFoundHandler = (ctx: ManduContext) => Response | Promise<Response>;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 미들웨어 엔트리 (메타데이터 포함)
|
|
40
|
-
*/
|
|
41
|
-
export interface MiddlewareEntry {
|
|
42
|
-
fn: Middleware;
|
|
43
|
-
name?: string;
|
|
44
|
-
isAsync?: boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Compose 옵션
|
|
49
|
-
*/
|
|
50
|
-
export interface ComposeOptions {
|
|
51
|
-
onError?: ErrorHandler;
|
|
52
|
-
onNotFound?: NotFoundHandler;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* 미들웨어 함수들을 하나의 실행 함수로 조합
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* const middleware = [
|
|
61
|
-
* { fn: async (ctx, next) => { console.log('before'); await next(); console.log('after'); } },
|
|
62
|
-
* { fn: async (ctx, next) => { return ctx.ok({ data: 'hello' }); } },
|
|
63
|
-
* ];
|
|
64
|
-
*
|
|
65
|
-
* const handler = compose(middleware, {
|
|
66
|
-
* onError: (err, ctx) => ctx.json({ error: err.message }, 500),
|
|
67
|
-
* onNotFound: (ctx) => ctx.notFound(),
|
|
68
|
-
* });
|
|
69
|
-
*
|
|
70
|
-
* const response = await handler(context);
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export function compose(
|
|
74
|
-
middleware: MiddlewareEntry[],
|
|
75
|
-
options: ComposeOptions = {}
|
|
76
|
-
): (ctx: ManduContext) => Promise<Response> {
|
|
77
|
-
const { onError, onNotFound } = options;
|
|
78
|
-
|
|
79
|
-
return async (ctx: ManduContext): Promise<Response> => {
|
|
80
|
-
let index = -1;
|
|
81
|
-
let finalResponse: Response | undefined;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 미들웨어 순차 실행
|
|
85
|
-
* @param i 현재 인덱스
|
|
86
|
-
*/
|
|
87
|
-
async function dispatch(i: number): Promise<void> {
|
|
88
|
-
// next() 이중 호출 방지
|
|
89
|
-
if (i <= index) {
|
|
90
|
-
throw new Error("next() called multiple times");
|
|
91
|
-
}
|
|
92
|
-
index = i;
|
|
93
|
-
|
|
94
|
-
const entry = middleware[i];
|
|
95
|
-
|
|
96
|
-
if (!entry) {
|
|
97
|
-
// 모든 미들웨어 통과 후 핸들러 없음
|
|
98
|
-
if (!finalResponse && onNotFound) {
|
|
99
|
-
finalResponse = await onNotFound(ctx);
|
|
100
|
-
}
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
const result = await entry.fn(ctx, () => dispatch(i + 1));
|
|
106
|
-
|
|
107
|
-
// Response 반환 시 체인 중단
|
|
108
|
-
if (result instanceof Response) {
|
|
109
|
-
finalResponse = result;
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
} catch (err) {
|
|
113
|
-
if (err instanceof Error && onError) {
|
|
114
|
-
finalResponse = await onError(err, ctx);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
throw err;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
await dispatch(0);
|
|
122
|
-
|
|
123
|
-
// 응답이 없으면 404
|
|
124
|
-
if (!finalResponse) {
|
|
125
|
-
if (onNotFound) {
|
|
126
|
-
finalResponse = await onNotFound(ctx);
|
|
127
|
-
} else {
|
|
128
|
-
finalResponse = new Response("Not Found", { status: 404 });
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return finalResponse;
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* 미들웨어 배열 생성 헬퍼
|
|
138
|
-
*
|
|
139
|
-
* @example
|
|
140
|
-
* ```typescript
|
|
141
|
-
* const mw = createMiddleware([
|
|
142
|
-
* authGuard,
|
|
143
|
-
* rateLimitGuard,
|
|
144
|
-
* mainHandler,
|
|
145
|
-
* ]);
|
|
146
|
-
* ```
|
|
147
|
-
*/
|
|
148
|
-
export function createMiddleware(
|
|
149
|
-
fns: Middleware[]
|
|
150
|
-
): MiddlewareEntry[] {
|
|
151
|
-
return fns.map((fn, i) => ({
|
|
152
|
-
fn,
|
|
153
|
-
name: fn.name || `middleware_${i}`,
|
|
154
|
-
isAsync: fn.constructor.name === "AsyncFunction",
|
|
155
|
-
}));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 미들웨어 체인 빌더
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* ```typescript
|
|
163
|
-
* const chain = new MiddlewareChain()
|
|
164
|
-
* .use(authGuard)
|
|
165
|
-
* .use(rateLimitGuard)
|
|
166
|
-
* .use(mainHandler)
|
|
167
|
-
* .onError((err, ctx) => ctx.json({ error: err.message }, 500))
|
|
168
|
-
* .build();
|
|
169
|
-
*
|
|
170
|
-
* const response = await chain(ctx);
|
|
171
|
-
* ```
|
|
172
|
-
*/
|
|
173
|
-
export class MiddlewareChain {
|
|
174
|
-
private middleware: MiddlewareEntry[] = [];
|
|
175
|
-
private errorHandler?: ErrorHandler;
|
|
176
|
-
private notFoundHandler?: NotFoundHandler;
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 미들웨어 추가
|
|
180
|
-
*/
|
|
181
|
-
use(fn: Middleware, name?: string): this {
|
|
182
|
-
this.middleware.push({
|
|
183
|
-
fn,
|
|
184
|
-
name: name || fn.name || `middleware_${this.middleware.length}`,
|
|
185
|
-
isAsync: fn.constructor.name === "AsyncFunction",
|
|
186
|
-
});
|
|
187
|
-
return this;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* 에러 핸들러 설정
|
|
192
|
-
*/
|
|
193
|
-
onError(handler: ErrorHandler): this {
|
|
194
|
-
this.errorHandler = handler;
|
|
195
|
-
return this;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* NotFound 핸들러 설정
|
|
200
|
-
*/
|
|
201
|
-
onNotFound(handler: NotFoundHandler): this {
|
|
202
|
-
this.notFoundHandler = handler;
|
|
203
|
-
return this;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 미들웨어 체인 빌드
|
|
208
|
-
*/
|
|
209
|
-
build(): (ctx: ManduContext) => Promise<Response> {
|
|
210
|
-
return compose(this.middleware, {
|
|
211
|
-
onError: this.errorHandler,
|
|
212
|
-
onNotFound: this.notFoundHandler,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* 미들웨어 목록 조회
|
|
218
|
-
*/
|
|
219
|
-
getMiddleware(): MiddlewareEntry[] {
|
|
220
|
-
return [...this.middleware];
|
|
221
|
-
}
|
|
222
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Middleware Compose 🔗
|
|
3
|
+
* Hono 스타일 미들웨어 조합 패턴
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/honojs/hono/blob/main/src/compose.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ManduContext } from "../filling/context";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Next 함수 타입
|
|
12
|
+
*/
|
|
13
|
+
export type Next = () => Promise<void>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 미들웨어 함수 타입
|
|
17
|
+
* - Response 반환: 체인 중단 (Guard 역할)
|
|
18
|
+
* - void 반환: 다음 미들웨어 실행
|
|
19
|
+
*/
|
|
20
|
+
export type Middleware = (
|
|
21
|
+
ctx: ManduContext,
|
|
22
|
+
next: Next
|
|
23
|
+
) => Response | void | Promise<Response | void>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 에러 핸들러 타입
|
|
27
|
+
*/
|
|
28
|
+
export type ErrorHandler = (
|
|
29
|
+
error: Error,
|
|
30
|
+
ctx: ManduContext
|
|
31
|
+
) => Response | Promise<Response>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* NotFound 핸들러 타입
|
|
35
|
+
*/
|
|
36
|
+
export type NotFoundHandler = (ctx: ManduContext) => Response | Promise<Response>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 미들웨어 엔트리 (메타데이터 포함)
|
|
40
|
+
*/
|
|
41
|
+
export interface MiddlewareEntry {
|
|
42
|
+
fn: Middleware;
|
|
43
|
+
name?: string;
|
|
44
|
+
isAsync?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Compose 옵션
|
|
49
|
+
*/
|
|
50
|
+
export interface ComposeOptions {
|
|
51
|
+
onError?: ErrorHandler;
|
|
52
|
+
onNotFound?: NotFoundHandler;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 미들웨어 함수들을 하나의 실행 함수로 조합
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const middleware = [
|
|
61
|
+
* { fn: async (ctx, next) => { console.log('before'); await next(); console.log('after'); } },
|
|
62
|
+
* { fn: async (ctx, next) => { return ctx.ok({ data: 'hello' }); } },
|
|
63
|
+
* ];
|
|
64
|
+
*
|
|
65
|
+
* const handler = compose(middleware, {
|
|
66
|
+
* onError: (err, ctx) => ctx.json({ error: err.message }, 500),
|
|
67
|
+
* onNotFound: (ctx) => ctx.notFound(),
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* const response = await handler(context);
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function compose(
|
|
74
|
+
middleware: MiddlewareEntry[],
|
|
75
|
+
options: ComposeOptions = {}
|
|
76
|
+
): (ctx: ManduContext) => Promise<Response> {
|
|
77
|
+
const { onError, onNotFound } = options;
|
|
78
|
+
|
|
79
|
+
return async (ctx: ManduContext): Promise<Response> => {
|
|
80
|
+
let index = -1;
|
|
81
|
+
let finalResponse: Response | undefined;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 미들웨어 순차 실행
|
|
85
|
+
* @param i 현재 인덱스
|
|
86
|
+
*/
|
|
87
|
+
async function dispatch(i: number): Promise<void> {
|
|
88
|
+
// next() 이중 호출 방지
|
|
89
|
+
if (i <= index) {
|
|
90
|
+
throw new Error("next() called multiple times");
|
|
91
|
+
}
|
|
92
|
+
index = i;
|
|
93
|
+
|
|
94
|
+
const entry = middleware[i];
|
|
95
|
+
|
|
96
|
+
if (!entry) {
|
|
97
|
+
// 모든 미들웨어 통과 후 핸들러 없음
|
|
98
|
+
if (!finalResponse && onNotFound) {
|
|
99
|
+
finalResponse = await onNotFound(ctx);
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const result = await entry.fn(ctx, () => dispatch(i + 1));
|
|
106
|
+
|
|
107
|
+
// Response 반환 시 체인 중단
|
|
108
|
+
if (result instanceof Response) {
|
|
109
|
+
finalResponse = result;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (err instanceof Error && onError) {
|
|
114
|
+
finalResponse = await onError(err, ctx);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await dispatch(0);
|
|
122
|
+
|
|
123
|
+
// 응답이 없으면 404
|
|
124
|
+
if (!finalResponse) {
|
|
125
|
+
if (onNotFound) {
|
|
126
|
+
finalResponse = await onNotFound(ctx);
|
|
127
|
+
} else {
|
|
128
|
+
finalResponse = new Response("Not Found", { status: 404 });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return finalResponse;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 미들웨어 배열 생성 헬퍼
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const mw = createMiddleware([
|
|
142
|
+
* authGuard,
|
|
143
|
+
* rateLimitGuard,
|
|
144
|
+
* mainHandler,
|
|
145
|
+
* ]);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function createMiddleware(
|
|
149
|
+
fns: Middleware[]
|
|
150
|
+
): MiddlewareEntry[] {
|
|
151
|
+
return fns.map((fn, i) => ({
|
|
152
|
+
fn,
|
|
153
|
+
name: fn.name || `middleware_${i}`,
|
|
154
|
+
isAsync: fn.constructor.name === "AsyncFunction",
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 미들웨어 체인 빌더
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* const chain = new MiddlewareChain()
|
|
164
|
+
* .use(authGuard)
|
|
165
|
+
* .use(rateLimitGuard)
|
|
166
|
+
* .use(mainHandler)
|
|
167
|
+
* .onError((err, ctx) => ctx.json({ error: err.message }, 500))
|
|
168
|
+
* .build();
|
|
169
|
+
*
|
|
170
|
+
* const response = await chain(ctx);
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export class MiddlewareChain {
|
|
174
|
+
private middleware: MiddlewareEntry[] = [];
|
|
175
|
+
private errorHandler?: ErrorHandler;
|
|
176
|
+
private notFoundHandler?: NotFoundHandler;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 미들웨어 추가
|
|
180
|
+
*/
|
|
181
|
+
use(fn: Middleware, name?: string): this {
|
|
182
|
+
this.middleware.push({
|
|
183
|
+
fn,
|
|
184
|
+
name: name || fn.name || `middleware_${this.middleware.length}`,
|
|
185
|
+
isAsync: fn.constructor.name === "AsyncFunction",
|
|
186
|
+
});
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 에러 핸들러 설정
|
|
192
|
+
*/
|
|
193
|
+
onError(handler: ErrorHandler): this {
|
|
194
|
+
this.errorHandler = handler;
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* NotFound 핸들러 설정
|
|
200
|
+
*/
|
|
201
|
+
onNotFound(handler: NotFoundHandler): this {
|
|
202
|
+
this.notFoundHandler = handler;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 미들웨어 체인 빌드
|
|
208
|
+
*/
|
|
209
|
+
build(): (ctx: ManduContext) => Promise<Response> {
|
|
210
|
+
return compose(this.middleware, {
|
|
211
|
+
onError: this.errorHandler,
|
|
212
|
+
onNotFound: this.notFoundHandler,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 미들웨어 목록 조회
|
|
218
|
+
*/
|
|
219
|
+
getMiddleware(): MiddlewareEntry[] {
|
|
220
|
+
return [...this.middleware];
|
|
221
|
+
}
|
|
222
|
+
}
|