@mandujs/core 0.3.2 → 0.3.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/README.ko.md +200 -200
- package/README.md +200 -200
- package/package.json +4 -2
- package/src/change/history.ts +145 -0
- package/src/change/index.ts +40 -0
- package/src/change/integrity.ts +81 -0
- package/src/change/snapshot.ts +233 -0
- package/src/change/transaction.ts +293 -0
- package/src/change/types.ts +102 -0
- package/src/error/classifier.ts +314 -0
- package/src/error/formatter.ts +237 -0
- package/src/error/index.ts +25 -0
- package/src/error/stack-analyzer.ts +295 -0
- package/src/error/types.ts +140 -0
- package/src/filling/context.ts +228 -219
- package/src/filling/filling.ts +256 -234
- package/src/filling/index.ts +7 -7
- package/src/generator/generate.ts +85 -3
- package/src/generator/index.ts +2 -2
- package/src/guard/auto-correct.ts +257 -203
- package/src/index.ts +2 -0
- package/src/report/index.ts +1 -1
- package/src/runtime/index.ts +3 -3
- package/src/runtime/router.ts +65 -65
- package/src/runtime/server.ts +189 -139
- package/src/runtime/ssr.ts +38 -38
- package/src/spec/index.ts +3 -3
- package/src/spec/load.ts +76 -76
- package/src/spec/lock.ts +56 -56
package/src/runtime/server.ts
CHANGED
|
@@ -1,139 +1,189 @@
|
|
|
1
|
-
import type { Server } from "bun";
|
|
2
|
-
import type { RoutesManifest } from "../spec/schema";
|
|
3
|
-
import { Router } from "./router";
|
|
4
|
-
import { renderSSR } from "./ssr";
|
|
5
|
-
import React from "react";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
1
|
+
import type { Server } from "bun";
|
|
2
|
+
import type { RoutesManifest } from "../spec/schema";
|
|
3
|
+
import { Router } from "./router";
|
|
4
|
+
import { renderSSR } from "./ssr";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import {
|
|
7
|
+
formatErrorResponse,
|
|
8
|
+
createNotFoundResponse,
|
|
9
|
+
createHandlerNotFoundResponse,
|
|
10
|
+
createPageLoadErrorResponse,
|
|
11
|
+
createSSRErrorResponse,
|
|
12
|
+
} from "../error";
|
|
13
|
+
|
|
14
|
+
export interface ServerOptions {
|
|
15
|
+
port?: number;
|
|
16
|
+
hostname?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ManduServer {
|
|
20
|
+
server: Server;
|
|
21
|
+
router: Router;
|
|
22
|
+
stop: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ApiHandler = (req: Request, params: Record<string, string>) => Response | Promise<Response>;
|
|
26
|
+
export type PageLoader = () => Promise<{ default: React.ComponentType<{ params: Record<string, string> }> }>;
|
|
27
|
+
|
|
28
|
+
export interface AppContext {
|
|
29
|
+
routeId: string;
|
|
30
|
+
url: string;
|
|
31
|
+
params: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type RouteComponent = (props: { params: Record<string, string> }) => React.ReactElement;
|
|
35
|
+
type CreateAppFn = (context: AppContext) => React.ReactElement;
|
|
36
|
+
|
|
37
|
+
// Registry
|
|
38
|
+
const apiHandlers: Map<string, ApiHandler> = new Map();
|
|
39
|
+
const pageLoaders: Map<string, PageLoader> = new Map();
|
|
40
|
+
const routeComponents: Map<string, RouteComponent> = new Map();
|
|
41
|
+
let createAppFn: CreateAppFn | null = null;
|
|
42
|
+
|
|
43
|
+
export function registerApiHandler(routeId: string, handler: ApiHandler): void {
|
|
44
|
+
apiHandlers.set(routeId, handler);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function registerPageLoader(routeId: string, loader: PageLoader): void {
|
|
48
|
+
pageLoaders.set(routeId, loader);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function registerRouteComponent(routeId: string, component: RouteComponent): void {
|
|
52
|
+
routeComponents.set(routeId, component);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function setCreateApp(fn: CreateAppFn): void {
|
|
56
|
+
createAppFn = fn;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Default createApp implementation
|
|
60
|
+
function defaultCreateApp(context: AppContext): React.ReactElement {
|
|
61
|
+
const Component = routeComponents.get(context.routeId);
|
|
62
|
+
|
|
63
|
+
if (!Component) {
|
|
64
|
+
return React.createElement("div", null,
|
|
65
|
+
React.createElement("h1", null, "404 - Route Not Found"),
|
|
66
|
+
React.createElement("p", null, `Route ID: ${context.routeId}`)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return React.createElement(Component, { params: context.params });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function handleRequest(req: Request, router: Router): Promise<Response> {
|
|
74
|
+
const url = new URL(req.url);
|
|
75
|
+
const pathname = url.pathname;
|
|
76
|
+
|
|
77
|
+
const match = router.match(pathname);
|
|
78
|
+
|
|
79
|
+
if (!match) {
|
|
80
|
+
const error = createNotFoundResponse(pathname);
|
|
81
|
+
const response = formatErrorResponse(error, {
|
|
82
|
+
isDev: process.env.NODE_ENV !== "production",
|
|
83
|
+
});
|
|
84
|
+
return Response.json(response, { status: 404 });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { route, params } = match;
|
|
88
|
+
|
|
89
|
+
if (route.kind === "api") {
|
|
90
|
+
const handler = apiHandlers.get(route.id);
|
|
91
|
+
if (!handler) {
|
|
92
|
+
const error = createHandlerNotFoundResponse(route.id, route.pattern);
|
|
93
|
+
const response = formatErrorResponse(error, {
|
|
94
|
+
isDev: process.env.NODE_ENV !== "production",
|
|
95
|
+
});
|
|
96
|
+
return Response.json(response, { status: 500 });
|
|
97
|
+
}
|
|
98
|
+
return handler(req, params);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (route.kind === "page") {
|
|
102
|
+
const loader = pageLoaders.get(route.id);
|
|
103
|
+
if (loader) {
|
|
104
|
+
try {
|
|
105
|
+
const module = await loader();
|
|
106
|
+
registerRouteComponent(route.id, module.default);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
const pageError = createPageLoadErrorResponse(
|
|
109
|
+
route.id,
|
|
110
|
+
route.pattern,
|
|
111
|
+
err instanceof Error ? err : new Error(String(err))
|
|
112
|
+
);
|
|
113
|
+
console.error(`[Mandu] ${pageError.errorType}:`, pageError.message);
|
|
114
|
+
const response = formatErrorResponse(pageError, {
|
|
115
|
+
isDev: process.env.NODE_ENV !== "production",
|
|
116
|
+
});
|
|
117
|
+
return Response.json(response, { status: 500 });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const appCreator = createAppFn || defaultCreateApp;
|
|
122
|
+
try {
|
|
123
|
+
const app = appCreator({
|
|
124
|
+
routeId: route.id,
|
|
125
|
+
url: req.url,
|
|
126
|
+
params,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return renderSSR(app, { title: `${route.id} - Mandu` });
|
|
130
|
+
} catch (err) {
|
|
131
|
+
const ssrError = createSSRErrorResponse(
|
|
132
|
+
route.id,
|
|
133
|
+
route.pattern,
|
|
134
|
+
err instanceof Error ? err : new Error(String(err))
|
|
135
|
+
);
|
|
136
|
+
console.error(`[Mandu] ${ssrError.errorType}:`, ssrError.message);
|
|
137
|
+
const response = formatErrorResponse(ssrError, {
|
|
138
|
+
isDev: process.env.NODE_ENV !== "production",
|
|
139
|
+
});
|
|
140
|
+
return Response.json(response, { status: 500 });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return Response.json({
|
|
145
|
+
errorType: "FRAMEWORK_BUG",
|
|
146
|
+
code: "MANDU_F003",
|
|
147
|
+
message: `Unknown route kind: ${route.kind}`,
|
|
148
|
+
summary: "알 수 없는 라우트 종류 - 프레임워크 버그",
|
|
149
|
+
fix: {
|
|
150
|
+
file: "spec/routes.manifest.json",
|
|
151
|
+
suggestion: "라우트의 kind는 'api' 또는 'page'여야 합니다",
|
|
152
|
+
},
|
|
153
|
+
route: {
|
|
154
|
+
id: route.id,
|
|
155
|
+
pattern: route.pattern,
|
|
156
|
+
},
|
|
157
|
+
timestamp: new Date().toISOString(),
|
|
158
|
+
}, { status: 500 });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function startServer(manifest: RoutesManifest, options: ServerOptions = {}): ManduServer {
|
|
162
|
+
const { port = 3000, hostname = "localhost" } = options;
|
|
163
|
+
|
|
164
|
+
const router = new Router(manifest.routes);
|
|
165
|
+
|
|
166
|
+
const server = Bun.serve({
|
|
167
|
+
port,
|
|
168
|
+
hostname,
|
|
169
|
+
fetch: (req) => handleRequest(req, router),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
console.log(`🥟 Mandu server running at http://${hostname}:${port}`);
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
server,
|
|
176
|
+
router,
|
|
177
|
+
stop: () => server.stop(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Clear registries (useful for testing)
|
|
182
|
+
export function clearRegistry(): void {
|
|
183
|
+
apiHandlers.clear();
|
|
184
|
+
pageLoaders.clear();
|
|
185
|
+
routeComponents.clear();
|
|
186
|
+
createAppFn = null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { apiHandlers, pageLoaders, routeComponents };
|
package/src/runtime/ssr.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { renderToString } from "react-dom/server";
|
|
2
|
-
import type { ReactElement } from "react";
|
|
3
|
-
|
|
4
|
-
export interface SSROptions {
|
|
5
|
-
title?: string;
|
|
6
|
-
lang?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function renderToHTML(element: ReactElement, options: SSROptions = {}): string {
|
|
10
|
-
const { title = "Mandu App", lang = "ko" } = options;
|
|
11
|
-
const content = renderToString(element);
|
|
12
|
-
|
|
13
|
-
return `<!doctype html>
|
|
14
|
-
<html lang="${lang}">
|
|
15
|
-
<head>
|
|
16
|
-
<meta charset="UTF-8">
|
|
17
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
18
|
-
<title>${title}</title>
|
|
19
|
-
</head>
|
|
20
|
-
<body>
|
|
21
|
-
<div id="root">${content}</div>
|
|
22
|
-
</body>
|
|
23
|
-
</html>`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function createHTMLResponse(html: string, status: number = 200): Response {
|
|
27
|
-
return new Response(html, {
|
|
28
|
-
status,
|
|
29
|
-
headers: {
|
|
30
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function renderSSR(element: ReactElement, options: SSROptions = {}): Response {
|
|
36
|
-
const html = renderToHTML(element, options);
|
|
37
|
-
return createHTMLResponse(html);
|
|
38
|
-
}
|
|
1
|
+
import { renderToString } from "react-dom/server";
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
|
|
4
|
+
export interface SSROptions {
|
|
5
|
+
title?: string;
|
|
6
|
+
lang?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function renderToHTML(element: ReactElement, options: SSROptions = {}): string {
|
|
10
|
+
const { title = "Mandu App", lang = "ko" } = options;
|
|
11
|
+
const content = renderToString(element);
|
|
12
|
+
|
|
13
|
+
return `<!doctype html>
|
|
14
|
+
<html lang="${lang}">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charset="UTF-8">
|
|
17
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
18
|
+
<title>${title}</title>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<div id="root">${content}</div>
|
|
22
|
+
</body>
|
|
23
|
+
</html>`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createHTMLResponse(html: string, status: number = 200): Response {
|
|
27
|
+
return new Response(html, {
|
|
28
|
+
status,
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function renderSSR(element: ReactElement, options: SSROptions = {}): Response {
|
|
36
|
+
const html = renderToHTML(element, options);
|
|
37
|
+
return createHTMLResponse(html);
|
|
38
|
+
}
|
package/src/spec/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from "./schema";
|
|
2
|
-
export * from "./load";
|
|
3
|
-
export * from "./lock";
|
|
1
|
+
export * from "./schema";
|
|
2
|
+
export * from "./load";
|
|
3
|
+
export * from "./lock";
|
package/src/spec/load.ts
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
import { RoutesManifest, type RoutesManifest as RoutesManifestType } from "./schema";
|
|
2
|
-
import { ZodError } from "zod";
|
|
3
|
-
|
|
4
|
-
export interface LoadResult {
|
|
5
|
-
success: boolean;
|
|
6
|
-
data?: RoutesManifestType;
|
|
7
|
-
errors?: string[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function formatZodError(error: ZodError): string[] {
|
|
11
|
-
return error.errors.map((e) => {
|
|
12
|
-
const path = e.path.length > 0 ? `[${e.path.join(".")}] ` : "";
|
|
13
|
-
return `${path}${e.message}`;
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function loadManifest(filePath: string): Promise<LoadResult> {
|
|
18
|
-
try {
|
|
19
|
-
const file = Bun.file(filePath);
|
|
20
|
-
const exists = await file.exists();
|
|
21
|
-
|
|
22
|
-
if (!exists) {
|
|
23
|
-
return {
|
|
24
|
-
success: false,
|
|
25
|
-
errors: [`파일을 찾을 수 없습니다: ${filePath}`],
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const content = await file.text();
|
|
30
|
-
let json: unknown;
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
json = JSON.parse(content);
|
|
34
|
-
} catch {
|
|
35
|
-
return {
|
|
36
|
-
success: false,
|
|
37
|
-
errors: ["JSON 파싱 실패: 올바른 JSON 형식이 아닙니다"],
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const result = RoutesManifest.safeParse(json);
|
|
42
|
-
|
|
43
|
-
if (!result.success) {
|
|
44
|
-
return {
|
|
45
|
-
success: false,
|
|
46
|
-
errors: formatZodError(result.error),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
success: true,
|
|
52
|
-
data: result.data,
|
|
53
|
-
};
|
|
54
|
-
} catch (error) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
errors: [`예상치 못한 오류: ${error instanceof Error ? error.message : String(error)}`],
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function validateManifest(data: unknown): LoadResult {
|
|
63
|
-
const result = RoutesManifest.safeParse(data);
|
|
64
|
-
|
|
65
|
-
if (!result.success) {
|
|
66
|
-
return {
|
|
67
|
-
success: false,
|
|
68
|
-
errors: formatZodError(result.error),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
success: true,
|
|
74
|
-
data: result.data,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
1
|
+
import { RoutesManifest, type RoutesManifest as RoutesManifestType } from "./schema";
|
|
2
|
+
import { ZodError } from "zod";
|
|
3
|
+
|
|
4
|
+
export interface LoadResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
data?: RoutesManifestType;
|
|
7
|
+
errors?: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function formatZodError(error: ZodError): string[] {
|
|
11
|
+
return error.errors.map((e) => {
|
|
12
|
+
const path = e.path.length > 0 ? `[${e.path.join(".")}] ` : "";
|
|
13
|
+
return `${path}${e.message}`;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function loadManifest(filePath: string): Promise<LoadResult> {
|
|
18
|
+
try {
|
|
19
|
+
const file = Bun.file(filePath);
|
|
20
|
+
const exists = await file.exists();
|
|
21
|
+
|
|
22
|
+
if (!exists) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
errors: [`파일을 찾을 수 없습니다: ${filePath}`],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = await file.text();
|
|
30
|
+
let json: unknown;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
json = JSON.parse(content);
|
|
34
|
+
} catch {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
errors: ["JSON 파싱 실패: 올바른 JSON 형식이 아닙니다"],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const result = RoutesManifest.safeParse(json);
|
|
42
|
+
|
|
43
|
+
if (!result.success) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
errors: formatZodError(result.error),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
data: result.data,
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
errors: [`예상치 못한 오류: ${error instanceof Error ? error.message : String(error)}`],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function validateManifest(data: unknown): LoadResult {
|
|
63
|
+
const result = RoutesManifest.safeParse(data);
|
|
64
|
+
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
errors: formatZodError(result.error),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
data: result.data,
|
|
75
|
+
};
|
|
76
|
+
}
|