@cosmneo/onion-lasagna 0.1.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/dist/backend/core/global.cjs +283 -0
- package/dist/backend/core/global.cjs.map +1 -0
- package/dist/backend/core/global.d.cts +294 -0
- package/dist/backend/core/global.d.ts +294 -0
- package/dist/backend/core/global.js +39 -0
- package/dist/backend/core/global.js.map +1 -0
- package/dist/backend/core/onion-layers.cjs +2302 -0
- package/dist/backend/core/onion-layers.cjs.map +1 -0
- package/dist/backend/core/onion-layers.d.cts +1675 -0
- package/dist/backend/core/onion-layers.d.ts +1675 -0
- package/dist/backend/core/onion-layers.js +1158 -0
- package/dist/backend/core/onion-layers.js.map +1 -0
- package/dist/backend/core/presentation.cjs +573 -0
- package/dist/backend/core/presentation.cjs.map +1 -0
- package/dist/backend/core/presentation.d.cts +5 -0
- package/dist/backend/core/presentation.d.ts +5 -0
- package/dist/backend/core/presentation.js +28 -0
- package/dist/backend/core/presentation.js.map +1 -0
- package/dist/backend/core/validators/arktype.cjs +947 -0
- package/dist/backend/core/validators/arktype.cjs.map +1 -0
- package/dist/backend/core/validators/arktype.d.cts +188 -0
- package/dist/backend/core/validators/arktype.d.ts +188 -0
- package/dist/backend/core/validators/arktype.js +287 -0
- package/dist/backend/core/validators/arktype.js.map +1 -0
- package/dist/backend/core/validators/typebox.cjs +939 -0
- package/dist/backend/core/validators/typebox.cjs.map +1 -0
- package/dist/backend/core/validators/typebox.d.cts +189 -0
- package/dist/backend/core/validators/typebox.d.ts +189 -0
- package/dist/backend/core/validators/typebox.js +281 -0
- package/dist/backend/core/validators/typebox.js.map +1 -0
- package/dist/backend/core/validators/valibot.cjs +942 -0
- package/dist/backend/core/validators/valibot.cjs.map +1 -0
- package/dist/backend/core/validators/valibot.d.cts +160 -0
- package/dist/backend/core/validators/valibot.d.ts +160 -0
- package/dist/backend/core/validators/valibot.js +294 -0
- package/dist/backend/core/validators/valibot.js.map +1 -0
- package/dist/backend/core/validators/zod.cjs +934 -0
- package/dist/backend/core/validators/zod.cjs.map +1 -0
- package/dist/backend/core/validators/zod.d.cts +188 -0
- package/dist/backend/core/validators/zod.d.ts +188 -0
- package/dist/backend/core/validators/zod.js +278 -0
- package/dist/backend/core/validators/zod.js.map +1 -0
- package/dist/backend/frameworks/elysia.cjs +715 -0
- package/dist/backend/frameworks/elysia.cjs.map +1 -0
- package/dist/backend/frameworks/elysia.d.cts +208 -0
- package/dist/backend/frameworks/elysia.d.ts +208 -0
- package/dist/backend/frameworks/elysia.js +251 -0
- package/dist/backend/frameworks/elysia.js.map +1 -0
- package/dist/backend/frameworks/fastify.cjs +677 -0
- package/dist/backend/frameworks/fastify.cjs.map +1 -0
- package/dist/backend/frameworks/fastify.d.cts +201 -0
- package/dist/backend/frameworks/fastify.d.ts +201 -0
- package/dist/backend/frameworks/fastify.js +213 -0
- package/dist/backend/frameworks/fastify.js.map +1 -0
- package/dist/backend/frameworks/hono.cjs +715 -0
- package/dist/backend/frameworks/hono.cjs.map +1 -0
- package/dist/backend/frameworks/hono.d.cts +163 -0
- package/dist/backend/frameworks/hono.d.ts +163 -0
- package/dist/backend/frameworks/hono.js +249 -0
- package/dist/backend/frameworks/hono.js.map +1 -0
- package/dist/backend/frameworks/nestjs.cjs +260 -0
- package/dist/backend/frameworks/nestjs.cjs.map +1 -0
- package/dist/backend/frameworks/nestjs.d.cts +168 -0
- package/dist/backend/frameworks/nestjs.d.ts +168 -0
- package/dist/backend/frameworks/nestjs.js +193 -0
- package/dist/backend/frameworks/nestjs.js.map +1 -0
- package/dist/base-dto.class-D7W9iqoU.d.cts +146 -0
- package/dist/base-dto.class-D7W9iqoU.d.ts +146 -0
- package/dist/base-uuid-v7.vo-BPGEIWLM.d.ts +799 -0
- package/dist/base-uuid-v7.vo-BjqKX44G.d.cts +799 -0
- package/dist/chunk-74IKUOSE.js +116 -0
- package/dist/chunk-74IKUOSE.js.map +1 -0
- package/dist/chunk-BKZOLGQW.js +29 -0
- package/dist/chunk-BKZOLGQW.js.map +1 -0
- package/dist/chunk-CGZBV6BD.js +54 -0
- package/dist/chunk-CGZBV6BD.js.map +1 -0
- package/dist/chunk-DDAHJZVK.js +258 -0
- package/dist/chunk-DDAHJZVK.js.map +1 -0
- package/dist/chunk-MQD5GXMT.js +171 -0
- package/dist/chunk-MQD5GXMT.js.map +1 -0
- package/dist/chunk-OKFXZHBC.js +43 -0
- package/dist/chunk-OKFXZHBC.js.map +1 -0
- package/dist/chunk-RLLWYFPI.js +168 -0
- package/dist/chunk-RLLWYFPI.js.map +1 -0
- package/dist/chunk-VCHFXT5W.js +425 -0
- package/dist/chunk-VCHFXT5W.js.map +1 -0
- package/dist/chunk-ZWLYNGO3.js +40 -0
- package/dist/chunk-ZWLYNGO3.js.map +1 -0
- package/dist/http-response-BAhi8lF4.d.cts +124 -0
- package/dist/http-response-BAhi8lF4.d.ts +124 -0
- package/dist/index-DingXh7B.d.cts +1187 -0
- package/dist/index-tOH7XBa3.d.ts +1187 -0
- package/dist/routing.type-DB4pt-d9.d.ts +184 -0
- package/dist/routing.type-DF2BIL7x.d.cts +184 -0
- package/dist/validation-error.type-kD4_qNZ9.d.cts +199 -0
- package/dist/validation-error.type-kD4_qNZ9.d.ts +199 -0
- package/package.json +191 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConflictError,
|
|
3
|
+
InfraError,
|
|
4
|
+
NotFoundError,
|
|
5
|
+
UnprocessableError,
|
|
6
|
+
UseCaseError
|
|
7
|
+
} from "../../chunk-RLLWYFPI.js";
|
|
8
|
+
import {
|
|
9
|
+
AccessDeniedError,
|
|
10
|
+
ControllerError,
|
|
11
|
+
InvalidRequestError
|
|
12
|
+
} from "../../chunk-74IKUOSE.js";
|
|
13
|
+
import {
|
|
14
|
+
DomainError
|
|
15
|
+
} from "../../chunk-ZWLYNGO3.js";
|
|
16
|
+
import {
|
|
17
|
+
CodedError,
|
|
18
|
+
ObjectValidationError
|
|
19
|
+
} from "../../chunk-MQD5GXMT.js";
|
|
20
|
+
import "../../chunk-CGZBV6BD.js";
|
|
21
|
+
|
|
22
|
+
// src/backend/frameworks/hono/routing.ts
|
|
23
|
+
function getRouteRegistrar(app, method) {
|
|
24
|
+
const httpMethod = method;
|
|
25
|
+
switch (httpMethod) {
|
|
26
|
+
case "get":
|
|
27
|
+
return app.get.bind(app);
|
|
28
|
+
case "post":
|
|
29
|
+
return app.post.bind(app);
|
|
30
|
+
case "put":
|
|
31
|
+
return app.put.bind(app);
|
|
32
|
+
case "patch":
|
|
33
|
+
return app.patch.bind(app);
|
|
34
|
+
case "delete":
|
|
35
|
+
return app.delete.bind(app);
|
|
36
|
+
case "options":
|
|
37
|
+
return app.options.bind(app);
|
|
38
|
+
case "head":
|
|
39
|
+
return ((path, ...handlers) => (
|
|
40
|
+
// @ts-expect-error Hono's on() typing is complex, but runtime works
|
|
41
|
+
app.on("HEAD", path, ...handlers)
|
|
42
|
+
));
|
|
43
|
+
default:
|
|
44
|
+
throw new Error(`Unsupported HTTP method: ${method}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function toHonoPath(path) {
|
|
48
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
49
|
+
}
|
|
50
|
+
async function extractRequest(c) {
|
|
51
|
+
let body;
|
|
52
|
+
const method = c.req.method.toUpperCase();
|
|
53
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
54
|
+
try {
|
|
55
|
+
body = await c.req.json();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const headers = {};
|
|
60
|
+
c.req.raw.headers.forEach((value, key) => {
|
|
61
|
+
headers[key.toLowerCase()] = value;
|
|
62
|
+
});
|
|
63
|
+
const queryParams = {};
|
|
64
|
+
new URL(c.req.url).searchParams.forEach((value, key) => {
|
|
65
|
+
const existing = queryParams[key];
|
|
66
|
+
if (existing) {
|
|
67
|
+
queryParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
68
|
+
} else {
|
|
69
|
+
queryParams[key] = value;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
body,
|
|
74
|
+
headers,
|
|
75
|
+
queryParams,
|
|
76
|
+
pathParams: c.req.param()
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function sendResponse(c, response) {
|
|
80
|
+
if (response.headers) {
|
|
81
|
+
for (const [key, value] of Object.entries(response.headers)) {
|
|
82
|
+
if (value != null) c.header(key, String(value));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (response.body === void 0 || response.body === null) {
|
|
86
|
+
return c.body(null, response.statusCode);
|
|
87
|
+
}
|
|
88
|
+
if (typeof response.body === "string") {
|
|
89
|
+
return c.text(response.body, response.statusCode);
|
|
90
|
+
}
|
|
91
|
+
return c.json(response.body, response.statusCode);
|
|
92
|
+
}
|
|
93
|
+
function registerHonoRoutes(app, routes, options) {
|
|
94
|
+
const routeArray = Array.isArray(routes) ? routes : [routes];
|
|
95
|
+
const prefix = options?.prefix ?? "";
|
|
96
|
+
const middlewares = options?.middlewares ?? [];
|
|
97
|
+
for (const { metadata, controller, requestDtoFactory } of routeArray) {
|
|
98
|
+
const path = prefix + toHonoPath(metadata.path);
|
|
99
|
+
const method = metadata.method.toLowerCase();
|
|
100
|
+
const handler = async (c) => {
|
|
101
|
+
const rawRequest = await extractRequest(c);
|
|
102
|
+
const requestDto = requestDtoFactory(rawRequest);
|
|
103
|
+
const responseDto = await controller.execute(requestDto);
|
|
104
|
+
return sendResponse(c, responseDto.data);
|
|
105
|
+
};
|
|
106
|
+
const registrar = getRouteRegistrar(app, method);
|
|
107
|
+
registrar(path, ...middlewares, handler);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/backend/frameworks/hono/map-error-to-http-exception.ts
|
|
112
|
+
import { HTTPException } from "hono/http-exception";
|
|
113
|
+
function createErrorResponse(status, body) {
|
|
114
|
+
return new Response(JSON.stringify(body), {
|
|
115
|
+
status,
|
|
116
|
+
headers: { "Content-Type": "application/json" }
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function mapErrorToHttpException(error) {
|
|
120
|
+
if (error instanceof HTTPException) {
|
|
121
|
+
return error;
|
|
122
|
+
}
|
|
123
|
+
if (error instanceof ObjectValidationError) {
|
|
124
|
+
return new HTTPException(400, {
|
|
125
|
+
res: createErrorResponse(400, {
|
|
126
|
+
message: error.message,
|
|
127
|
+
errorCode: error.code,
|
|
128
|
+
errorItems: error.validationErrors.map((e) => ({
|
|
129
|
+
item: e.field,
|
|
130
|
+
message: e.message
|
|
131
|
+
}))
|
|
132
|
+
}),
|
|
133
|
+
cause: error
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (error instanceof InvalidRequestError) {
|
|
137
|
+
return new HTTPException(400, {
|
|
138
|
+
res: createErrorResponse(400, {
|
|
139
|
+
message: error.message,
|
|
140
|
+
errorCode: error.code,
|
|
141
|
+
errorItems: error.validationErrors.map((e) => ({
|
|
142
|
+
item: e.field,
|
|
143
|
+
message: e.message
|
|
144
|
+
}))
|
|
145
|
+
}),
|
|
146
|
+
cause: error
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (error instanceof AccessDeniedError) {
|
|
150
|
+
return new HTTPException(403, {
|
|
151
|
+
res: createErrorResponse(403, {
|
|
152
|
+
message: error.message,
|
|
153
|
+
errorCode: error.code
|
|
154
|
+
}),
|
|
155
|
+
cause: error
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (error instanceof NotFoundError) {
|
|
159
|
+
return new HTTPException(404, {
|
|
160
|
+
res: createErrorResponse(404, {
|
|
161
|
+
message: error.message,
|
|
162
|
+
errorCode: error.code
|
|
163
|
+
}),
|
|
164
|
+
cause: error
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (error instanceof ConflictError) {
|
|
168
|
+
return new HTTPException(409, {
|
|
169
|
+
res: createErrorResponse(409, {
|
|
170
|
+
message: error.message,
|
|
171
|
+
errorCode: error.code
|
|
172
|
+
}),
|
|
173
|
+
cause: error
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (error instanceof UnprocessableError) {
|
|
177
|
+
return new HTTPException(422, {
|
|
178
|
+
res: createErrorResponse(422, {
|
|
179
|
+
message: error.message,
|
|
180
|
+
errorCode: error.code
|
|
181
|
+
}),
|
|
182
|
+
cause: error
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (error instanceof UseCaseError) {
|
|
186
|
+
return new HTTPException(400, {
|
|
187
|
+
res: createErrorResponse(400, {
|
|
188
|
+
message: error.message,
|
|
189
|
+
errorCode: error.code
|
|
190
|
+
}),
|
|
191
|
+
cause: error
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (error instanceof DomainError) {
|
|
195
|
+
return new HTTPException(500, {
|
|
196
|
+
res: createErrorResponse(500, {
|
|
197
|
+
message: "An unexpected error occurred",
|
|
198
|
+
errorCode: "INTERNAL_ERROR"
|
|
199
|
+
}),
|
|
200
|
+
cause: error
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (error instanceof InfraError) {
|
|
204
|
+
return new HTTPException(500, {
|
|
205
|
+
res: createErrorResponse(500, {
|
|
206
|
+
message: "An unexpected error occurred",
|
|
207
|
+
errorCode: "INTERNAL_ERROR"
|
|
208
|
+
}),
|
|
209
|
+
cause: error
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (error instanceof ControllerError) {
|
|
213
|
+
return new HTTPException(500, {
|
|
214
|
+
res: createErrorResponse(500, {
|
|
215
|
+
message: "An unexpected error occurred",
|
|
216
|
+
errorCode: "INTERNAL_ERROR"
|
|
217
|
+
}),
|
|
218
|
+
cause: error
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (error instanceof CodedError) {
|
|
222
|
+
return new HTTPException(500, {
|
|
223
|
+
res: createErrorResponse(500, {
|
|
224
|
+
message: "An unexpected error occurred",
|
|
225
|
+
errorCode: "INTERNAL_ERROR"
|
|
226
|
+
}),
|
|
227
|
+
cause: error
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return new HTTPException(500, {
|
|
231
|
+
res: createErrorResponse(500, {
|
|
232
|
+
message: "An unexpected error occurred",
|
|
233
|
+
errorCode: "INTERNAL_ERROR"
|
|
234
|
+
}),
|
|
235
|
+
cause: error
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/backend/frameworks/hono/error-handler.ts
|
|
240
|
+
function onionErrorHandler(error, _c) {
|
|
241
|
+
const httpException = mapErrorToHttpException(error);
|
|
242
|
+
return httpException.getResponse();
|
|
243
|
+
}
|
|
244
|
+
export {
|
|
245
|
+
mapErrorToHttpException,
|
|
246
|
+
onionErrorHandler,
|
|
247
|
+
registerHonoRoutes
|
|
248
|
+
};
|
|
249
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/backend/frameworks/hono/routing.ts","../../../src/backend/frameworks/hono/map-error-to-http-exception.ts","../../../src/backend/frameworks/hono/error-handler.ts"],"sourcesContent":["import type { Context, Handler, Hono, MiddlewareHandler } from 'hono';\nimport type { ContentfulStatusCode, StatusCode } from 'hono/utils/http-status';\nimport type { Controller } from '../../core/onion-layers/presentation/interfaces/types/controller.type';\nimport type { HttpRequest } from '../../core/onion-layers/presentation/interfaces/types/http/http-request';\nimport type { HttpResponse } from '../../core/onion-layers/presentation/interfaces/types/http/http-response';\nimport type { RouteInput } from '../../core/onion-layers/presentation/routing';\n\n/**\n * Supported HTTP methods in Hono.\n */\ntype HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'head';\n\n/**\n * Type-safe route registration function signature.\n */\ntype RouteRegistrar = (path: string, ...handlers: (MiddlewareHandler | Handler)[]) => Hono;\n\n/**\n * Gets the type-safe route registrar for a given HTTP method.\n */\nfunction getRouteRegistrar(app: Hono, method: string): RouteRegistrar {\n const httpMethod = method as HttpMethod;\n switch (httpMethod) {\n case 'get':\n return app.get.bind(app);\n case 'post':\n return app.post.bind(app);\n case 'put':\n return app.put.bind(app);\n case 'patch':\n return app.patch.bind(app);\n case 'delete':\n return app.delete.bind(app);\n case 'options':\n return app.options.bind(app);\n case 'head':\n // Hono's on() method accepts method string and path\n return ((path: string, ...handlers: (MiddlewareHandler | Handler)[]) =>\n // @ts-expect-error Hono's on() typing is complex, but runtime works\n app.on('HEAD', path, ...handlers)) as RouteRegistrar;\n default:\n throw new Error(`Unsupported HTTP method: ${method}`);\n }\n}\n\n/**\n * Controller that works with HttpRequest/HttpResponse.\n */\nexport type HttpController = Controller<HttpRequest, HttpResponse>;\n\n/**\n * Hono middleware type.\n */\nexport type HonoMiddleware = MiddlewareHandler;\n\n/**\n * Converts `{param}` to Hono's `:param` format.\n */\nfunction toHonoPath(path: string): string {\n return path.replace(/\\{([^}]+)\\}/g, ':$1');\n}\n\n/**\n * Extracts HttpRequest from Hono context.\n */\nasync function extractRequest(c: Context): Promise<HttpRequest> {\n let body: unknown;\n\n const method = c.req.method.toUpperCase();\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n try {\n body = await c.req.json();\n } catch {\n // No JSON body\n }\n }\n\n const headers: Record<string, string> = {};\n c.req.raw.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n const queryParams: Record<string, string | string[]> = {};\n new URL(c.req.url).searchParams.forEach((value, key) => {\n const existing = queryParams[key];\n if (existing) {\n queryParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n queryParams[key] = value;\n }\n });\n\n return {\n body,\n headers,\n queryParams,\n pathParams: c.req.param() as Record<string, string>,\n };\n}\n\n/**\n * Sends HttpResponse via Hono context.\n */\nfunction sendResponse(c: Context, response: HttpResponse) {\n if (response.headers) {\n for (const [key, value] of Object.entries(response.headers)) {\n if (value != null) c.header(key, String(value));\n }\n }\n\n if (response.body === undefined || response.body === null) {\n return c.body(null, response.statusCode as StatusCode);\n }\n\n if (typeof response.body === 'string') {\n return c.text(response.body, response.statusCode as ContentfulStatusCode);\n }\n\n return c.json(response.body as object, response.statusCode as ContentfulStatusCode);\n}\n\n/**\n * Route input type that accepts either a single route or an array of routes.\n */\nexport type RouteInputOrArray = RouteInput | RouteInput[];\n\n/**\n * Options for registering routes.\n */\nexport interface RegisterRoutesOptions {\n /**\n * Prefix to apply to all routes in this registration.\n *\n * @example\n * ```typescript\n * registerHonoRoutes(app, userRoutes, {\n * prefix: '/api/v1',\n * });\n * // Routes will be: /api/v1/users, /api/v1/users/:id, etc.\n * ```\n */\n prefix?: string;\n\n /**\n * Middlewares to apply to all routes in this registration.\n * These run before the controller handler.\n *\n * @example\n * ```typescript\n * import { jwt } from 'hono/jwt';\n * import { logger } from 'hono/logger';\n *\n * registerHonoRoutes(app, userRoutes, {\n * middlewares: [logger(), jwt({ secret: 'my-secret' })],\n * });\n * ```\n */\n middlewares?: HonoMiddleware[];\n}\n\n/**\n * Registers routes onto a Hono app.\n *\n * Accepts either a single route or an array of routes. Can be called multiple\n * times to register routes from different domains/modules.\n *\n * @param app - The Hono app instance (passed by reference)\n * @param routes - A single route or an array of routes to register\n * @param options - Optional configuration including middlewares\n *\n * @example Single route\n * ```typescript\n * const app = new Hono();\n *\n * registerHonoRoutes(app, {\n * metadata: { path: '/health', method: 'GET' },\n * controller: healthController,\n * });\n * ```\n *\n * @example Multiple routes\n * ```typescript\n * const app = new Hono();\n *\n * registerHonoRoutes(app, [\n * { metadata: { path: '/users', method: 'POST' }, controller: createUserController },\n * { metadata: { path: '/users/{id}', method: 'GET' }, controller: getUserController },\n * { metadata: { path: '/users/{id}', method: 'DELETE' }, controller: deleteUserController },\n * ]);\n * ```\n *\n * @example With prefix\n * ```typescript\n * registerHonoRoutes(app, userRoutes, {\n * prefix: '/api/v1',\n * });\n * // Routes will be: /api/v1/users, /api/v1/users/:id, etc.\n * ```\n *\n * @example With middlewares\n * ```typescript\n * import { jwt } from 'hono/jwt';\n *\n * registerHonoRoutes(app, protectedRoutes, {\n * middlewares: [jwt({ secret: 'my-secret' })],\n * });\n * ```\n *\n * @example Registering from multiple domains\n * ```typescript\n * const app = new Hono();\n *\n * // Public routes - no auth\n * registerHonoRoutes(app, publicRoutes);\n *\n * // Protected routes - with auth middleware\n * registerHonoRoutes(app, userRoutes, { middlewares: [authMiddleware] });\n * registerHonoRoutes(app, orderRoutes, { middlewares: [authMiddleware] });\n *\n * export default app;\n * ```\n */\nexport function registerHonoRoutes(\n app: Hono,\n routes: RouteInputOrArray,\n options?: RegisterRoutesOptions,\n): void {\n const routeArray = Array.isArray(routes) ? routes : [routes];\n const prefix = options?.prefix ?? '';\n const middlewares = options?.middlewares ?? [];\n\n for (const { metadata, controller, requestDtoFactory } of routeArray) {\n const path = prefix + toHonoPath(metadata.path);\n const method = metadata.method.toLowerCase();\n\n const handler = async (c: Context) => {\n const rawRequest = await extractRequest(c);\n const requestDto = requestDtoFactory(rawRequest);\n const responseDto = await controller.execute(requestDto);\n return sendResponse(c, responseDto.data);\n };\n\n // Type-safe route registration\n const registrar = getRouteRegistrar(app, method);\n registrar(path, ...middlewares, handler);\n }\n}\n","import { HTTPException } from 'hono/http-exception';\nimport { CodedError } from '../../core/global/exceptions/coded-error.error';\nimport { ObjectValidationError } from '../../core/global/exceptions/object-validation.error';\nimport { DomainError } from '../../core/onion-layers/domain/exceptions/domain.error';\nimport { UseCaseError } from '../../core/onion-layers/app/exceptions/use-case.error';\nimport { NotFoundError } from '../../core/onion-layers/app/exceptions/not-found.error';\nimport { ConflictError } from '../../core/onion-layers/app/exceptions/conflict.error';\nimport { UnprocessableError } from '../../core/onion-layers/app/exceptions/unprocessable.error';\nimport { InfraError } from '../../core/onion-layers/infra/exceptions/infra.error';\nimport { ControllerError } from '../../core/onion-layers/presentation/exceptions/controller.error';\nimport { AccessDeniedError } from '../../core/onion-layers/presentation/exceptions/access-denied.error';\nimport { InvalidRequestError } from '../../core/onion-layers/presentation/exceptions/invalid-request.error';\n\n/**\n * Error item for field-level validation errors.\n */\ninterface ErrorItem {\n item: string;\n message: string;\n}\n\n/**\n * Standard error response body.\n */\ninterface ErrorResponseBody {\n message: string;\n errorCode: string;\n errorItems?: ErrorItem[];\n}\n\n/**\n * Creates a JSON Response for HTTPException.\n */\nfunction createErrorResponse(status: number, body: ErrorResponseBody): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n/**\n * Maps our error hierarchy to Hono's HTTPException.\n *\n * Mapping strategy:\n * - `ObjectValidationError` → 400 Bad Request\n * - `InvalidRequestError` → 400 Bad Request\n * - `AccessDeniedError` → 403 Forbidden\n * - `NotFoundError` → 404 Not Found\n * - `ConflictError` → 409 Conflict\n * - `UnprocessableError` → 422 Unprocessable Entity\n * - `UseCaseError` (other) → 400 Bad Request\n * - `DomainError` → 500 Internal Server Error (masked)\n * - `InfraError` → 500 Internal Server Error (masked)\n * - `ControllerError` → 500 Internal Server Error (masked)\n * - Unknown → 500 Internal Server Error (masked)\n *\n * **Security Note:** Domain and infrastructure errors are masked to avoid\n * leaking internal implementation details.\n *\n * @param error - The error to map\n * @returns Hono HTTPException with appropriate status and response body\n */\nexport function mapErrorToHttpException(error: unknown): HTTPException {\n // Already an HTTPException - return as-is\n if (error instanceof HTTPException) {\n return error;\n }\n\n // Validation errors → 400 Bad Request\n if (error instanceof ObjectValidationError) {\n return new HTTPException(400, {\n res: createErrorResponse(400, {\n message: error.message,\n errorCode: error.code,\n errorItems: error.validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n }),\n cause: error,\n });\n }\n\n if (error instanceof InvalidRequestError) {\n return new HTTPException(400, {\n res: createErrorResponse(400, {\n message: error.message,\n errorCode: error.code,\n errorItems: error.validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n }),\n cause: error,\n });\n }\n\n // Access control → 403 Forbidden\n if (error instanceof AccessDeniedError) {\n return new HTTPException(403, {\n res: createErrorResponse(403, {\n message: error.message,\n errorCode: error.code,\n }),\n cause: error,\n });\n }\n\n // Use case errors → specific HTTP status codes\n if (error instanceof NotFoundError) {\n return new HTTPException(404, {\n res: createErrorResponse(404, {\n message: error.message,\n errorCode: error.code,\n }),\n cause: error,\n });\n }\n\n if (error instanceof ConflictError) {\n return new HTTPException(409, {\n res: createErrorResponse(409, {\n message: error.message,\n errorCode: error.code,\n }),\n cause: error,\n });\n }\n\n if (error instanceof UnprocessableError) {\n return new HTTPException(422, {\n res: createErrorResponse(422, {\n message: error.message,\n errorCode: error.code,\n }),\n cause: error,\n });\n }\n\n // Other use case errors → 400 Bad Request\n if (error instanceof UseCaseError) {\n return new HTTPException(400, {\n res: createErrorResponse(400, {\n message: error.message,\n errorCode: error.code,\n }),\n cause: error,\n });\n }\n\n // Domain errors → 500 Internal Server Error (masked)\n if (error instanceof DomainError) {\n return new HTTPException(500, {\n res: createErrorResponse(500, {\n message: 'An unexpected error occurred',\n errorCode: 'INTERNAL_ERROR',\n }),\n cause: error,\n });\n }\n\n // Infrastructure errors → 500 Internal Server Error (masked)\n if (error instanceof InfraError) {\n return new HTTPException(500, {\n res: createErrorResponse(500, {\n message: 'An unexpected error occurred',\n errorCode: 'INTERNAL_ERROR',\n }),\n cause: error,\n });\n }\n\n // Controller errors → 500 Internal Server Error (masked)\n if (error instanceof ControllerError) {\n return new HTTPException(500, {\n res: createErrorResponse(500, {\n message: 'An unexpected error occurred',\n errorCode: 'INTERNAL_ERROR',\n }),\n cause: error,\n });\n }\n\n // CodedError (catch-all for known errors) → 500 (masked)\n if (error instanceof CodedError) {\n return new HTTPException(500, {\n res: createErrorResponse(500, {\n message: 'An unexpected error occurred',\n errorCode: 'INTERNAL_ERROR',\n }),\n cause: error,\n });\n }\n\n // Unknown errors → 500 Internal Server Error (masked)\n return new HTTPException(500, {\n res: createErrorResponse(500, {\n message: 'An unexpected error occurred',\n errorCode: 'INTERNAL_ERROR',\n }),\n cause: error,\n });\n}\n","import type { Context } from 'hono';\nimport type { HTTPException } from 'hono/http-exception';\nimport { mapErrorToHttpException } from './map-error-to-http-exception';\n\n/**\n * Hono error handler that maps domain errors to HTTP responses.\n *\n * Apply this to your Hono app using `app.onError()` to automatically\n * convert domain/use-case errors to appropriate HTTP responses.\n *\n * @example\n * ```typescript\n * import { Hono } from 'hono';\n * import { createHonoRouter, onionErrorHandler } from '@onion-lasagna/hono';\n *\n * const app = new Hono();\n *\n * // Apply error handler\n * app.onError(onionErrorHandler);\n *\n * // Register routes\n * createHonoRouter(app, routes);\n *\n * export default app;\n * ```\n */\nexport function onionErrorHandler(error: Error | HTTPException, _c: Context): Response {\n const httpException = mapErrorToHttpException(error);\n return httpException.getResponse();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
8
|
+
var __typeError = (msg) => {
|
|
9
|
+
throw TypeError(msg);
|
|
10
|
+
};
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
26
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
27
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
28
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
29
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
30
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
31
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
32
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
36
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
37
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
38
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
39
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
40
|
+
return __privateGet(this, extra);
|
|
41
|
+
}, set [name](x) {
|
|
42
|
+
return __privateSet(this, extra, x);
|
|
43
|
+
} }, name));
|
|
44
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
45
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
46
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
47
|
+
if (k) {
|
|
48
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
49
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
50
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
51
|
+
}
|
|
52
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
53
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
54
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
55
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
56
|
+
}
|
|
57
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
58
|
+
};
|
|
59
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
60
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
61
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
62
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
63
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
64
|
+
|
|
65
|
+
// src/backend/frameworks/nestjs/index.ts
|
|
66
|
+
var nestjs_exports = {};
|
|
67
|
+
__export(nestjs_exports, {
|
|
68
|
+
BaseNestController: () => BaseNestController,
|
|
69
|
+
OnionLasagnaExceptionFilter: () => OnionLasagnaExceptionFilter,
|
|
70
|
+
OnionLasagnaRequest: () => OnionLasagnaRequest,
|
|
71
|
+
OnionLasagnaResponseInterceptor: () => OnionLasagnaResponseInterceptor
|
|
72
|
+
});
|
|
73
|
+
module.exports = __toCommonJS(nestjs_exports);
|
|
74
|
+
|
|
75
|
+
// src/backend/frameworks/nestjs/classes/base-nest-controller.class.ts
|
|
76
|
+
var import_common3 = require("@nestjs/common");
|
|
77
|
+
|
|
78
|
+
// src/backend/frameworks/nestjs/filters/onion-lasagna-exception.filter.ts
|
|
79
|
+
var import_common = require("@nestjs/common");
|
|
80
|
+
function isErrorType(error, typeName) {
|
|
81
|
+
if (!error || typeof error !== "object") return false;
|
|
82
|
+
const constructor = error.constructor;
|
|
83
|
+
const name = constructor?.name;
|
|
84
|
+
return name === typeName || name === `_${typeName}`;
|
|
85
|
+
}
|
|
86
|
+
function hasValidationErrors(error) {
|
|
87
|
+
if (!error || typeof error !== "object") return false;
|
|
88
|
+
return "validationErrors" in error && Array.isArray(error.validationErrors);
|
|
89
|
+
}
|
|
90
|
+
var INTERNAL_ERROR_TYPES = [
|
|
91
|
+
"DomainError",
|
|
92
|
+
"InfraError",
|
|
93
|
+
"ControllerError",
|
|
94
|
+
"NetworkError",
|
|
95
|
+
"PersistenceError",
|
|
96
|
+
"ExternalServiceError",
|
|
97
|
+
"InvariantViolationError"
|
|
98
|
+
];
|
|
99
|
+
var _OnionLasagnaExceptionFilter_decorators, _init;
|
|
100
|
+
_OnionLasagnaExceptionFilter_decorators = [(0, import_common.Catch)()];
|
|
101
|
+
var OnionLasagnaExceptionFilter = class {
|
|
102
|
+
catch(exception, host) {
|
|
103
|
+
const ctx = host.switchToHttp();
|
|
104
|
+
const response = ctx.getResponse();
|
|
105
|
+
const { status, body } = this.mapError(exception);
|
|
106
|
+
response.status(status).json(body);
|
|
107
|
+
}
|
|
108
|
+
mapError(error) {
|
|
109
|
+
if (isErrorType(error, "ObjectValidationError") && hasValidationErrors(error)) {
|
|
110
|
+
return {
|
|
111
|
+
status: import_common.HttpStatus.BAD_REQUEST,
|
|
112
|
+
body: {
|
|
113
|
+
message: error.message,
|
|
114
|
+
errorCode: error.code,
|
|
115
|
+
errorItems: error.validationErrors.map((e) => ({
|
|
116
|
+
item: e.field,
|
|
117
|
+
message: e.message
|
|
118
|
+
}))
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (isErrorType(error, "InvalidRequestError") && hasValidationErrors(error)) {
|
|
123
|
+
return {
|
|
124
|
+
status: import_common.HttpStatus.BAD_REQUEST,
|
|
125
|
+
body: {
|
|
126
|
+
message: error.message,
|
|
127
|
+
errorCode: error.code,
|
|
128
|
+
errorItems: error.validationErrors.map((e) => ({
|
|
129
|
+
item: e.field,
|
|
130
|
+
message: e.message
|
|
131
|
+
}))
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (isErrorType(error, "AccessDeniedError")) {
|
|
136
|
+
return {
|
|
137
|
+
status: import_common.HttpStatus.FORBIDDEN,
|
|
138
|
+
body: { message: error.message, errorCode: error.code }
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (isErrorType(error, "NotFoundError")) {
|
|
142
|
+
return {
|
|
143
|
+
status: import_common.HttpStatus.NOT_FOUND,
|
|
144
|
+
body: { message: error.message, errorCode: error.code }
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (isErrorType(error, "ConflictError")) {
|
|
148
|
+
return {
|
|
149
|
+
status: import_common.HttpStatus.CONFLICT,
|
|
150
|
+
body: { message: error.message, errorCode: error.code }
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (isErrorType(error, "UnprocessableError")) {
|
|
154
|
+
return {
|
|
155
|
+
status: import_common.HttpStatus.UNPROCESSABLE_ENTITY,
|
|
156
|
+
body: { message: error.message, errorCode: error.code }
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (isErrorType(error, "UseCaseError")) {
|
|
160
|
+
return {
|
|
161
|
+
status: import_common.HttpStatus.BAD_REQUEST,
|
|
162
|
+
body: { message: error.message, errorCode: error.code }
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
for (const errorType of INTERNAL_ERROR_TYPES) {
|
|
166
|
+
if (isErrorType(error, errorType)) {
|
|
167
|
+
return {
|
|
168
|
+
status: import_common.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
169
|
+
body: { message: "An unexpected error occurred", errorCode: "INTERNAL_ERROR" }
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (isErrorType(error, "CodedError")) {
|
|
174
|
+
return {
|
|
175
|
+
status: import_common.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
176
|
+
body: { message: "An unexpected error occurred", errorCode: "INTERNAL_ERROR" }
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
status: import_common.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
181
|
+
body: { message: "An unexpected error occurred", errorCode: "INTERNAL_ERROR" }
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
_init = __decoratorStart(null);
|
|
186
|
+
OnionLasagnaExceptionFilter = __decorateElement(_init, 0, "OnionLasagnaExceptionFilter", _OnionLasagnaExceptionFilter_decorators, OnionLasagnaExceptionFilter);
|
|
187
|
+
__runInitializers(_init, 1, OnionLasagnaExceptionFilter);
|
|
188
|
+
|
|
189
|
+
// src/backend/frameworks/nestjs/interceptors/onion-lasagna-response.interceptor.ts
|
|
190
|
+
var import_common2 = require("@nestjs/common");
|
|
191
|
+
var import_operators = require("rxjs/operators");
|
|
192
|
+
function isHttpResponse(value) {
|
|
193
|
+
if (!value || typeof value !== "object") return false;
|
|
194
|
+
const obj = value;
|
|
195
|
+
return typeof obj.statusCode === "number" && "body" in obj;
|
|
196
|
+
}
|
|
197
|
+
var _OnionLasagnaResponseInterceptor_decorators, _init2;
|
|
198
|
+
_OnionLasagnaResponseInterceptor_decorators = [(0, import_common2.Injectable)()];
|
|
199
|
+
var OnionLasagnaResponseInterceptor = class {
|
|
200
|
+
intercept(context, next) {
|
|
201
|
+
return next.handle().pipe(
|
|
202
|
+
(0, import_operators.map)((data) => {
|
|
203
|
+
if (!isHttpResponse(data)) {
|
|
204
|
+
return data;
|
|
205
|
+
}
|
|
206
|
+
const res = context.switchToHttp().getResponse();
|
|
207
|
+
res.status(data.statusCode);
|
|
208
|
+
if (data.headers) {
|
|
209
|
+
for (const [key, value] of Object.entries(data.headers)) {
|
|
210
|
+
if (value != null) {
|
|
211
|
+
res.setHeader(key, String(value));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return data.body;
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
_init2 = __decoratorStart(null);
|
|
221
|
+
OnionLasagnaResponseInterceptor = __decorateElement(_init2, 0, "OnionLasagnaResponseInterceptor", _OnionLasagnaResponseInterceptor_decorators, OnionLasagnaResponseInterceptor);
|
|
222
|
+
__runInitializers(_init2, 1, OnionLasagnaResponseInterceptor);
|
|
223
|
+
|
|
224
|
+
// src/backend/frameworks/nestjs/classes/base-nest-controller.class.ts
|
|
225
|
+
var _BaseNestController_decorators, _init3;
|
|
226
|
+
_BaseNestController_decorators = [(0, import_common3.UseFilters)(OnionLasagnaExceptionFilter), (0, import_common3.UseInterceptors)(OnionLasagnaResponseInterceptor)];
|
|
227
|
+
var BaseNestController = class {
|
|
228
|
+
};
|
|
229
|
+
_init3 = __decoratorStart(null);
|
|
230
|
+
BaseNestController = __decorateElement(_init3, 0, "BaseNestController", _BaseNestController_decorators, BaseNestController);
|
|
231
|
+
__runInitializers(_init3, 1, BaseNestController);
|
|
232
|
+
|
|
233
|
+
// src/backend/frameworks/nestjs/decorators/onion-lasagna-request.decorator.ts
|
|
234
|
+
var import_common4 = require("@nestjs/common");
|
|
235
|
+
function normalizeHeaders(headers) {
|
|
236
|
+
const normalized = {};
|
|
237
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
238
|
+
normalized[key.toLowerCase()] = String(value);
|
|
239
|
+
}
|
|
240
|
+
return normalized;
|
|
241
|
+
}
|
|
242
|
+
var OnionLasagnaRequest = (0, import_common4.createParamDecorator)(
|
|
243
|
+
(_data, ctx) => {
|
|
244
|
+
const request = ctx.switchToHttp().getRequest();
|
|
245
|
+
return {
|
|
246
|
+
body: request.body,
|
|
247
|
+
headers: normalizeHeaders(request.headers),
|
|
248
|
+
queryParams: request.query,
|
|
249
|
+
pathParams: request.params
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
254
|
+
0 && (module.exports = {
|
|
255
|
+
BaseNestController,
|
|
256
|
+
OnionLasagnaExceptionFilter,
|
|
257
|
+
OnionLasagnaRequest,
|
|
258
|
+
OnionLasagnaResponseInterceptor
|
|
259
|
+
});
|
|
260
|
+
//# sourceMappingURL=nestjs.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/backend/frameworks/nestjs/index.ts","../../../src/backend/frameworks/nestjs/classes/base-nest-controller.class.ts","../../../src/backend/frameworks/nestjs/filters/onion-lasagna-exception.filter.ts","../../../src/backend/frameworks/nestjs/interceptors/onion-lasagna-response.interceptor.ts","../../../src/backend/frameworks/nestjs/decorators/onion-lasagna-request.decorator.ts"],"sourcesContent":["export { BaseNestController } from './classes/base-nest-controller.class';\nexport { OnionLasagnaRequest } from './decorators/onion-lasagna-request.decorator';\nexport { OnionLasagnaExceptionFilter } from './filters/onion-lasagna-exception.filter';\nexport { OnionLasagnaResponseInterceptor } from './interceptors/onion-lasagna-response.interceptor';\n\n// Re-export core types for convenience\nexport type { Controller as HttpController } from '../../core/onion-layers/presentation/interfaces/types/controller.type';\nexport type { HttpRequest } from '../../core/onion-layers/presentation/interfaces/types/http/http-request';\nexport type { HttpResponse } from '../../core/onion-layers/presentation/interfaces/types/http/http-response';\n","import { UseFilters, UseInterceptors } from '@nestjs/common';\nimport { OnionLasagnaExceptionFilter } from '../filters/onion-lasagna-exception.filter';\nimport { OnionLasagnaResponseInterceptor } from '../interceptors/onion-lasagna-response.interceptor';\n\n/**\n * Base controller for NestJS that auto-wires onion-lasagna integration.\n *\n * Extending this class automatically applies:\n * - {@link OnionLasagnaExceptionFilter} - Maps domain errors to HTTP responses\n * - {@link OnionLasagnaResponseInterceptor} - Transforms HttpResponse to NestJS response\n *\n * This reduces boilerplate when creating controllers that work with\n * onion-lasagna's architecture.\n *\n * @example Basic usage\n * ```typescript\n * import { Controller, Get, Post } from '@nestjs/common';\n * import { BaseNestController, OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n * import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/core/presentation';\n *\n * @Controller('users')\n * export class UsersController extends BaseNestController {\n * constructor(\n * private readonly getUserController: GetUserController,\n * private readonly createUserController: CreateUserController,\n * ) {\n * super();\n * }\n *\n * @Get(':id')\n * getUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return this.getUserController.execute(request);\n * }\n *\n * @Post()\n * createUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return this.createUserController.execute(request);\n * }\n * }\n * ```\n *\n * @example With NestJS guards\n * ```typescript\n * @Controller('admin')\n * @UseGuards(JwtAuthGuard, RolesGuard)\n * export class AdminController extends BaseNestController {\n * @Get('dashboard')\n * @Roles('admin')\n * getDashboard(@OnionLasagnaRequest() request: HttpRequest) {\n * return this.dashboardController.execute(request);\n * }\n * }\n * ```\n */\n@UseFilters(OnionLasagnaExceptionFilter)\n@UseInterceptors(OnionLasagnaResponseInterceptor)\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class -- Abstract base with decorator inheritance only\nexport abstract class BaseNestController {}\n","import { Catch, HttpStatus, type ExceptionFilter, type ArgumentsHost } from '@nestjs/common';\nimport type { Response } from 'express';\n\n/**\n * Error item for field-level validation errors.\n */\ninterface ErrorItem {\n item: string;\n message: string;\n}\n\n/**\n * Standard error response body.\n */\ninterface ErrorResponseBody {\n message: string;\n errorCode: string;\n errorItems?: ErrorItem[];\n}\n\n/**\n * Validation error structure.\n */\ninterface ValidationErrorItem {\n field: string;\n message: string;\n}\n\n/**\n * Interface for errors with validation items.\n */\ninterface ErrorWithValidation {\n message: string;\n code: string;\n validationErrors: ValidationErrorItem[];\n}\n\n/**\n * Interface for coded errors.\n */\ninterface CodedErrorLike {\n message: string;\n code: string;\n}\n\n/**\n * Checks if error matches a specific error type by checking its constructor name.\n * This approach avoids issues with multiple class instances in bundled code.\n * Handles both original names and tsup's mangled names (prefixed with _).\n */\nfunction isErrorType(error: unknown, typeName: string): error is CodedErrorLike {\n if (!error || typeof error !== 'object') return false;\n const constructor = (error as object).constructor;\n const name = constructor?.name;\n // Check both the original name and the mangled name (tsup prefixes with _)\n return name === typeName || name === `_${typeName}`;\n}\n\n/**\n * Checks if error has validation errors array.\n */\nfunction hasValidationErrors(error: unknown): error is ErrorWithValidation {\n if (!error || typeof error !== 'object') return false;\n return (\n 'validationErrors' in error && Array.isArray((error as ErrorWithValidation).validationErrors)\n );\n}\n\n/**\n * Known internal error type names that should be masked.\n */\nconst INTERNAL_ERROR_TYPES = [\n 'DomainError',\n 'InfraError',\n 'ControllerError',\n 'NetworkError',\n 'PersistenceError',\n 'ExternalServiceError',\n 'InvariantViolationError',\n];\n\n/**\n * NestJS exception filter that maps onion-lasagna errors to HTTP responses.\n *\n * Mapping strategy:\n * - `ObjectValidationError` → 400 Bad Request\n * - `InvalidRequestError` → 400 Bad Request\n * - `AccessDeniedError` → 403 Forbidden\n * - `NotFoundError` → 404 Not Found\n * - `ConflictError` → 409 Conflict\n * - `UnprocessableError` → 422 Unprocessable Entity\n * - `UseCaseError` (other) → 400 Bad Request\n * - `DomainError` → 500 Internal Server Error (masked)\n * - `InfraError` → 500 Internal Server Error (masked)\n * - `ControllerError` → 500 Internal Server Error (masked)\n * - Unknown → 500 Internal Server Error (masked)\n *\n * **Security Note:** Domain and infrastructure errors are masked to avoid\n * leaking internal implementation details.\n *\n * @example\n * ```typescript\n * import { Controller, UseFilters } from '@nestjs/common';\n * import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * @Controller('users')\n * @UseFilters(OnionLasagnaExceptionFilter)\n * export class UsersController {\n * // ...\n * }\n * ```\n *\n * @example Global registration in main.ts\n * ```typescript\n * import { NestFactory } from '@nestjs/core';\n * import { OnionLasagnaExceptionFilter } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * const app = await NestFactory.create(AppModule);\n * app.useGlobalFilters(new OnionLasagnaExceptionFilter());\n * ```\n */\n@Catch()\nexport class OnionLasagnaExceptionFilter implements ExceptionFilter {\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<Response>();\n\n const { status, body } = this.mapError(exception);\n response.status(status).json(body);\n }\n\n private mapError(error: unknown): { status: number; body: ErrorResponseBody } {\n // Validation errors → 400 Bad Request\n if (isErrorType(error, 'ObjectValidationError') && hasValidationErrors(error)) {\n return {\n status: HttpStatus.BAD_REQUEST,\n body: {\n message: error.message,\n errorCode: error.code,\n errorItems: error.validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n },\n };\n }\n\n if (isErrorType(error, 'InvalidRequestError') && hasValidationErrors(error)) {\n return {\n status: HttpStatus.BAD_REQUEST,\n body: {\n message: error.message,\n errorCode: error.code,\n errorItems: error.validationErrors.map((e) => ({\n item: e.field,\n message: e.message,\n })),\n },\n };\n }\n\n // Access control → 403 Forbidden\n if (isErrorType(error, 'AccessDeniedError')) {\n return {\n status: HttpStatus.FORBIDDEN,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n // Use case errors → specific HTTP status codes\n if (isErrorType(error, 'NotFoundError')) {\n return {\n status: HttpStatus.NOT_FOUND,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n if (isErrorType(error, 'ConflictError')) {\n return {\n status: HttpStatus.CONFLICT,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n if (isErrorType(error, 'UnprocessableError')) {\n return {\n status: HttpStatus.UNPROCESSABLE_ENTITY,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n // Other use case errors → 400 Bad Request\n if (isErrorType(error, 'UseCaseError')) {\n return {\n status: HttpStatus.BAD_REQUEST,\n body: { message: error.message, errorCode: error.code },\n };\n }\n\n // Internal errors → 500 (masked)\n for (const errorType of INTERNAL_ERROR_TYPES) {\n if (isErrorType(error, errorType)) {\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n body: { message: 'An unexpected error occurred', errorCode: 'INTERNAL_ERROR' },\n };\n }\n }\n\n // CodedError (catch-all for known errors) → 500 (masked)\n if (isErrorType(error, 'CodedError')) {\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n body: { message: 'An unexpected error occurred', errorCode: 'INTERNAL_ERROR' },\n };\n }\n\n // Unknown errors → 500 Internal Server Error (masked)\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n body: { message: 'An unexpected error occurred', errorCode: 'INTERNAL_ERROR' },\n };\n }\n}\n","import {\n Injectable,\n type NestInterceptor,\n type ExecutionContext,\n type CallHandler,\n} from '@nestjs/common';\nimport type { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport type { Response } from 'express';\n\n/**\n * Interface matching onion-lasagna's HttpResponse type.\n */\ninterface HttpResponseLike {\n statusCode: number;\n body: unknown;\n headers?: Record<string, string | number | boolean | undefined>;\n}\n\n/**\n * Type guard to check if a value is an HttpResponse-like object.\n */\nfunction isHttpResponse(value: unknown): value is HttpResponseLike {\n if (!value || typeof value !== 'object') return false;\n const obj = value as HttpResponseLike;\n return typeof obj.statusCode === 'number' && 'body' in obj;\n}\n\n/**\n * NestJS interceptor that transforms onion-lasagna `HttpResponse` objects\n * into proper NestJS responses.\n *\n * When a controller returns an `HttpResponse` object (with `statusCode`, `body`, and optional `headers`),\n * this interceptor:\n * 1. Sets the HTTP status code from `response.statusCode`\n * 2. Sets any custom headers from `response.headers`\n * 3. Returns only `response.body` to NestJS for serialization\n *\n * If the response is not an `HttpResponse` object, it passes through unchanged.\n *\n * @example\n * ```typescript\n * import { Controller, Get, UseInterceptors, UseFilters } from '@nestjs/common';\n * import { OnionLasagnaRequest, OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n * import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * @Controller('users')\n * @UseFilters(OnionLasagnaExceptionFilter)\n * @UseInterceptors(OnionLasagnaResponseInterceptor)\n * export class UsersController {\n * @Get(':id')\n * getUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return getUserController.execute(request);\n * }\n * }\n * ```\n *\n * @example Global registration in main.ts\n * ```typescript\n * import { NestFactory } from '@nestjs/core';\n * import { OnionLasagnaExceptionFilter, OnionLasagnaResponseInterceptor } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * const app = await NestFactory.create(AppModule);\n * app.useGlobalFilters(new OnionLasagnaExceptionFilter());\n * app.useGlobalInterceptors(new OnionLasagnaResponseInterceptor());\n * await app.listen(3000);\n * ```\n */\n@Injectable()\nexport class OnionLasagnaResponseInterceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n map((data: unknown) => {\n // If not an HttpResponse, pass through unchanged\n if (!isHttpResponse(data)) {\n return data;\n }\n\n const res = context.switchToHttp().getResponse<Response>();\n\n // Set status code\n res.status(data.statusCode);\n\n // Set custom headers\n if (data.headers) {\n for (const [key, value] of Object.entries(data.headers)) {\n if (value != null) {\n res.setHeader(key, String(value));\n }\n }\n }\n\n // Return only the body for NestJS serialization\n return data.body;\n }),\n );\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport type { HttpRequest } from '../../../core/onion-layers/presentation/interfaces/types/http/http-request';\n\n/**\n * Normalizes HTTP headers to lowercase keys with string values.\n */\nfunction normalizeHeaders(headers: Record<string, unknown>): Record<string, string> {\n const normalized: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n normalized[key.toLowerCase()] = String(value);\n }\n return normalized;\n}\n\n/**\n * Parameter decorator that extracts the request from NestJS context\n * and transforms it into onion-lasagna's `HttpRequest` format.\n *\n * @example\n * ```typescript\n * import { Controller, Get } from '@nestjs/common';\n * import { OnionLasagnaRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n * import type { HttpRequest } from '@cosmneo/onion-lasagna/backend/frameworks/nestjs';\n *\n * @Controller('users')\n * export class UsersController {\n * @Get(':id')\n * getUser(@OnionLasagnaRequest() request: HttpRequest) {\n * return getUserController.execute(request);\n * }\n * }\n * ```\n */\nexport const OnionLasagnaRequest = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): HttpRequest => {\n const request = ctx.switchToHttp().getRequest();\n\n return {\n body: request.body,\n headers: normalizeHeaders(request.headers),\n queryParams: request.query,\n pathParams: request.params,\n };\n },\n);\n"],"mappings":"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","names":["import_common","import_common","_init","_init","import_common"]}
|