glasswork 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/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/bootstrap.d.ts +55 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +359 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/core/bootstrap.d.ts +55 -0
- package/dist/core/bootstrap.d.ts.map +1 -0
- package/dist/core/bootstrap.js +329 -0
- package/dist/core/bootstrap.js.map +1 -0
- package/dist/core/module.d.ts +21 -0
- package/dist/core/module.d.ts.map +1 -0
- package/dist/core/module.js +85 -0
- package/dist/core/module.js.map +1 -0
- package/dist/core/types.d.ts +273 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/environment.d.ts +21 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +29 -0
- package/dist/environment.js.map +1 -0
- package/dist/error-handler.d.ts +33 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +53 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +65 -0
- package/dist/errors.js.map +1 -0
- package/dist/get-client-ip.d.ts +24 -0
- package/dist/get-client-ip.d.ts.map +1 -0
- package/dist/get-client-ip.js +49 -0
- package/dist/get-client-ip.js.map +1 -0
- package/dist/http/error-handler.d.ts +33 -0
- package/dist/http/error-handler.d.ts.map +1 -0
- package/dist/http/error-handler.js +55 -0
- package/dist/http/error-handler.js.map +1 -0
- package/dist/http/errors.d.ts +36 -0
- package/dist/http/errors.d.ts.map +1 -0
- package/dist/http/errors.js +65 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/route-helpers.d.ts +172 -0
- package/dist/http/route-helpers.d.ts.map +1 -0
- package/dist/http/route-helpers.js +249 -0
- package/dist/http/route-helpers.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +52 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +7 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +182 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/module.d.ts +21 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +85 -0
- package/dist/module.js.map +1 -0
- package/dist/omit.d.ts +20 -0
- package/dist/omit.d.ts.map +1 -0
- package/dist/omit.js +28 -0
- package/dist/omit.js.map +1 -0
- package/dist/openapi/defaults.d.ts +11 -0
- package/dist/openapi/defaults.d.ts.map +1 -0
- package/dist/openapi/defaults.js +127 -0
- package/dist/openapi/defaults.js.map +1 -0
- package/dist/openapi/hooks.d.ts +14 -0
- package/dist/openapi/hooks.d.ts.map +1 -0
- package/dist/openapi/hooks.js +80 -0
- package/dist/openapi/hooks.js.map +1 -0
- package/dist/openapi/openapi.d.ts +24 -0
- package/dist/openapi/openapi.d.ts.map +1 -0
- package/dist/openapi/openapi.js +108 -0
- package/dist/openapi/openapi.js.map +1 -0
- package/dist/openapi-defaults.d.ts +11 -0
- package/dist/openapi-defaults.d.ts.map +1 -0
- package/dist/openapi-defaults.js +127 -0
- package/dist/openapi-defaults.js.map +1 -0
- package/dist/openapi-hooks.d.ts +14 -0
- package/dist/openapi-hooks.d.ts.map +1 -0
- package/dist/openapi-hooks.js +80 -0
- package/dist/openapi-hooks.js.map +1 -0
- package/dist/openapi.d.ts +24 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +106 -0
- package/dist/openapi.js.map +1 -0
- package/dist/pagination.d.ts +48 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +43 -0
- package/dist/pagination.js.map +1 -0
- package/dist/pick.d.ts +20 -0
- package/dist/pick.d.ts.map +1 -0
- package/dist/pick.js +28 -0
- package/dist/pick.js.map +1 -0
- package/dist/rate-limit.d.ts +7 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +180 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/route-helpers.d.ts +172 -0
- package/dist/route-helpers.d.ts.map +1 -0
- package/dist/route-helpers.js +249 -0
- package/dist/route-helpers.js.map +1 -0
- package/dist/serialize-dates.d.ts +27 -0
- package/dist/serialize-dates.d.ts.map +1 -0
- package/dist/serialize-dates.js +39 -0
- package/dist/serialize-dates.js.map +1 -0
- package/dist/testing.d.ts +87 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +94 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +273 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/deep-merge.d.ts +5 -0
- package/dist/utils/deep-merge.d.ts.map +1 -0
- package/dist/utils/deep-merge.js +21 -0
- package/dist/utils/deep-merge.js.map +1 -0
- package/dist/utils/environment.d.ts +21 -0
- package/dist/utils/environment.d.ts.map +1 -0
- package/dist/utils/environment.js +29 -0
- package/dist/utils/environment.js.map +1 -0
- package/dist/utils/get-client-ip.d.ts +24 -0
- package/dist/utils/get-client-ip.d.ts.map +1 -0
- package/dist/utils/get-client-ip.js +49 -0
- package/dist/utils/get-client-ip.js.map +1 -0
- package/dist/utils/logger.d.ts +37 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/omit.d.ts +20 -0
- package/dist/utils/omit.d.ts.map +1 -0
- package/dist/utils/omit.js +28 -0
- package/dist/utils/omit.js.map +1 -0
- package/dist/utils/pagination.d.ts +48 -0
- package/dist/utils/pagination.d.ts.map +1 -0
- package/dist/utils/pagination.js +42 -0
- package/dist/utils/pagination.js.map +1 -0
- package/dist/utils/pick.d.ts +20 -0
- package/dist/utils/pick.d.ts.map +1 -0
- package/dist/utils/pick.js +28 -0
- package/dist/utils/pick.js.map +1 -0
- package/dist/utils/serialize-dates.d.ts +27 -0
- package/dist/utils/serialize-dates.d.ts.map +1 -0
- package/dist/utils/serialize-dates.js +39 -0
- package/dist/utils/serialize-dates.js.map +1 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +31 -0
- package/dist/utils.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { describeRoute as honoDescribeRoute } from 'hono-openapi';
|
|
2
|
+
import { resolver, validator } from 'hono-openapi/valibot';
|
|
3
|
+
import { getClientIp } from './get-client-ip.js';
|
|
4
|
+
/**
|
|
5
|
+
* Global storage for OpenAPI response hooks.
|
|
6
|
+
* Set during bootstrap and used when defining routes.
|
|
7
|
+
*/
|
|
8
|
+
let globalResponseHooks = [];
|
|
9
|
+
/**
|
|
10
|
+
* Set the global OpenAPI response hooks.
|
|
11
|
+
* This is called during bootstrap and makes hooks available to route definitions.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export function setGlobalResponseHooks(hooks) {
|
|
16
|
+
globalResponseHooks = hooks;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the current global OpenAPI response hooks.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export function getGlobalResponseHooks() {
|
|
24
|
+
return globalResponseHooks;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* HTTP status codes with descriptions
|
|
28
|
+
*/
|
|
29
|
+
const STATUS_DESCRIPTIONS = {
|
|
30
|
+
200: 'Success',
|
|
31
|
+
201: 'Created',
|
|
32
|
+
204: 'No Content',
|
|
33
|
+
400: 'Bad Request',
|
|
34
|
+
401: 'Unauthorized',
|
|
35
|
+
403: 'Forbidden',
|
|
36
|
+
404: 'Not Found',
|
|
37
|
+
409: 'Conflict',
|
|
38
|
+
422: 'Unprocessable Entity',
|
|
39
|
+
429: 'Too Many Requests',
|
|
40
|
+
500: 'Internal Server Error',
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Create routes with typed service injection.
|
|
44
|
+
*
|
|
45
|
+
* @template TServices - The shape of services required by these routes
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* interface AuthRoutesServices {
|
|
50
|
+
* authService: AuthService;
|
|
51
|
+
* userService: UserService;
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* export const authRoutes = createRoutes<AuthRoutesServices>((router, services) => {
|
|
55
|
+
* // services.authService is typed as AuthService!
|
|
56
|
+
* // services.userService is typed as UserService!
|
|
57
|
+
* router.post('/login', ...);
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function createRoutes(factory) {
|
|
62
|
+
return factory;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a route handler with validation and OpenAPI metadata.
|
|
66
|
+
*
|
|
67
|
+
* Type inference works automatically:
|
|
68
|
+
* - Body type is inferred from the `body` schema
|
|
69
|
+
* - Return type is inferred from the `responses[200]` schema
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* route({
|
|
74
|
+
* body: StartSessionDto,
|
|
75
|
+
* responses: { 200: StartSessionResponseDto },
|
|
76
|
+
* handler: async ({ body }) => {
|
|
77
|
+
* // body is typed as InferOutput<typeof StartSessionDto>
|
|
78
|
+
* return { email: body.email }; // Must match StartSessionResponseDto
|
|
79
|
+
* }
|
|
80
|
+
* })
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function route(config) {
|
|
84
|
+
const middlewares = [];
|
|
85
|
+
// Add OpenAPI metadata
|
|
86
|
+
middlewares.push(buildOpenAPIMiddleware(config));
|
|
87
|
+
// Add custom middleware (e.g., auth) before validation
|
|
88
|
+
if (config.middleware) {
|
|
89
|
+
middlewares.push(...config.middleware);
|
|
90
|
+
}
|
|
91
|
+
// Add validation middleware with custom error handler (returns 422 instead of 400)
|
|
92
|
+
if (config.body) {
|
|
93
|
+
middlewares.push(validator('json', config.body, (result, c) => {
|
|
94
|
+
if (!result.success) {
|
|
95
|
+
return c.json({ error: 'Validation failed', issues: result.issues }, 422);
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
if (config.query) {
|
|
100
|
+
middlewares.push(validator('query', config.query, (result, c) => {
|
|
101
|
+
if (!result.success) {
|
|
102
|
+
return c.json({ error: 'Validation failed', issues: result.issues }, 422);
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
if (config.params) {
|
|
107
|
+
middlewares.push(validator('param', config.params, (result, c) => {
|
|
108
|
+
if (!result.success) {
|
|
109
|
+
return c.json({ error: 'Validation failed', issues: result.issues }, 422);
|
|
110
|
+
}
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
// Add handler wrapper
|
|
114
|
+
middlewares.push(async (c) => {
|
|
115
|
+
const routeContext = buildRouteContext(c);
|
|
116
|
+
const result = await config.handler(
|
|
117
|
+
// @ts-expect-error - Session typing issue to be resolved separately
|
|
118
|
+
routeContext);
|
|
119
|
+
// If handler returned a Response, return it directly (for custom responses like text)
|
|
120
|
+
if (result instanceof Response) {
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
// Return appropriate response
|
|
124
|
+
if (result === null || result === undefined) {
|
|
125
|
+
c.status(204);
|
|
126
|
+
return c.body(null);
|
|
127
|
+
}
|
|
128
|
+
return c.json(result);
|
|
129
|
+
});
|
|
130
|
+
return middlewares;
|
|
131
|
+
}
|
|
132
|
+
function buildOpenAPIMiddleware(config) {
|
|
133
|
+
const responses = buildOpenAPIResponses(config);
|
|
134
|
+
const openApiConfig = {
|
|
135
|
+
operationId: config.operationId || config.summary,
|
|
136
|
+
tags: config.tags,
|
|
137
|
+
summary: config.summary,
|
|
138
|
+
description: config.description,
|
|
139
|
+
responses: responses,
|
|
140
|
+
security: config.public ? [] : [{ bearerAuth: [] }],
|
|
141
|
+
...(config.paginate && { parameters: buildPaginationParameters() }),
|
|
142
|
+
...config.openapi,
|
|
143
|
+
};
|
|
144
|
+
return honoDescribeRoute(openApiConfig);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Build pagination parameters for OpenAPI spec
|
|
148
|
+
*/
|
|
149
|
+
function buildPaginationParameters() {
|
|
150
|
+
return [
|
|
151
|
+
{
|
|
152
|
+
name: 'page',
|
|
153
|
+
in: 'query',
|
|
154
|
+
required: false,
|
|
155
|
+
schema: {
|
|
156
|
+
type: 'integer',
|
|
157
|
+
format: 'int32',
|
|
158
|
+
minimum: 1,
|
|
159
|
+
maximum: 2147483647, // Max int32
|
|
160
|
+
default: 1,
|
|
161
|
+
},
|
|
162
|
+
description: 'Page number',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'limit',
|
|
166
|
+
in: 'query',
|
|
167
|
+
required: false,
|
|
168
|
+
schema: {
|
|
169
|
+
type: 'integer',
|
|
170
|
+
format: 'int32',
|
|
171
|
+
minimum: 1,
|
|
172
|
+
maximum: 100,
|
|
173
|
+
default: 50,
|
|
174
|
+
},
|
|
175
|
+
description: 'Number of items per page',
|
|
176
|
+
},
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
function buildOpenAPIResponses(config) {
|
|
180
|
+
const responses = {};
|
|
181
|
+
// Add default error responses
|
|
182
|
+
const defaultResponses = config.public
|
|
183
|
+
? { 400: undefined, 429: undefined, 500: undefined }
|
|
184
|
+
: { 400: undefined, 401: undefined, 429: undefined, 500: undefined };
|
|
185
|
+
const allResponses = { ...defaultResponses, ...(config.responses || {}) };
|
|
186
|
+
// Get global hooks
|
|
187
|
+
const hooks = getGlobalResponseHooks();
|
|
188
|
+
for (const [statusCode, schema] of Object.entries(allResponses)) {
|
|
189
|
+
const code = Number(statusCode);
|
|
190
|
+
const description = STATUS_DESCRIPTIONS[code] || 'Response';
|
|
191
|
+
// Determine if this is an error response that should use default error schema
|
|
192
|
+
const isErrorResponse = code >= 400;
|
|
193
|
+
const shouldUseDefaultErrorSchema = isErrorResponse && !schema;
|
|
194
|
+
// Start with base response (no headers)
|
|
195
|
+
let response = {
|
|
196
|
+
description,
|
|
197
|
+
...(schema
|
|
198
|
+
? {
|
|
199
|
+
content: {
|
|
200
|
+
'application/json': {
|
|
201
|
+
// resolver type only supports BaseSchema, but works with BaseSchemaAsync at runtime
|
|
202
|
+
// @ts-expect-error - resolver accepts both BaseSchema and BaseSchemaAsync
|
|
203
|
+
schema: resolver(schema, { errorMode: 'ignore' }),
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
: shouldUseDefaultErrorSchema
|
|
208
|
+
? {
|
|
209
|
+
content: {
|
|
210
|
+
'application/json': {
|
|
211
|
+
schema: {
|
|
212
|
+
$ref: '#/components/schemas/ErrorResponse',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
: {}),
|
|
218
|
+
};
|
|
219
|
+
// Apply hooks to build up headers
|
|
220
|
+
for (const hook of hooks) {
|
|
221
|
+
response = hook(response, {
|
|
222
|
+
statusCode,
|
|
223
|
+
routeConfig: {
|
|
224
|
+
public: config.public,
|
|
225
|
+
paginate: config.paginate,
|
|
226
|
+
tags: config.tags,
|
|
227
|
+
summary: config.summary,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
responses[statusCode] = response;
|
|
232
|
+
}
|
|
233
|
+
return responses;
|
|
234
|
+
}
|
|
235
|
+
function buildRouteContext(c) {
|
|
236
|
+
const req = c.req;
|
|
237
|
+
return {
|
|
238
|
+
body: req.valid('json'),
|
|
239
|
+
query: req.valid('query'),
|
|
240
|
+
params: req.valid('param'),
|
|
241
|
+
services: c.get('services') || {},
|
|
242
|
+
// @ts-expect-error - Session typing issue to be resolved separately
|
|
243
|
+
session: c.get('session'),
|
|
244
|
+
ip: getClientIp(c),
|
|
245
|
+
userAgent: c.req.header('user-agent'),
|
|
246
|
+
context: c,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=route-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-helpers.js","sourceRoot":"","sources":["../src/route-helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type helper to recursively convert Date fields to string in an object type
|
|
3
|
+
*/
|
|
4
|
+
export type SerializedDates<T> = T extends Date ? string : T extends Array<infer U> ? Array<SerializedDates<U>> : T extends object ? {
|
|
5
|
+
[K in keyof T]: SerializedDates<T[K]>;
|
|
6
|
+
} : T;
|
|
7
|
+
/**
|
|
8
|
+
* Recursively converts all Date objects to ISO 8601 strings in an entity or array of entities.
|
|
9
|
+
*
|
|
10
|
+
* This utility handles the mismatch between Prisma's Date types and API response DTOs
|
|
11
|
+
* that expect string timestamps. It preserves null values and handles nested objects and arrays.
|
|
12
|
+
*
|
|
13
|
+
* @template T - The type of data to transform
|
|
14
|
+
* @param data - The data to transform (object, array, or primitive)
|
|
15
|
+
* @returns The same structure with all Date objects converted to ISO strings
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const ranger = await prisma.ranger.findUnique({
|
|
20
|
+
* where: { id },
|
|
21
|
+
* include: { category: true }
|
|
22
|
+
* });
|
|
23
|
+
* return serializeDates(ranger); // All Date fields → ISO strings
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function serializeDates<T>(data: T): SerializedDates<T>;
|
|
27
|
+
//# sourceMappingURL=serialize-dates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize-dates.d.ts","sourceRoot":"","sources":["../src/serialize-dates.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,GAC3C,MAAM,GACN,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACtB,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACzB,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACzC,CAAC,CAAC;AAEV;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAsB7D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively converts all Date objects to ISO 8601 strings in an entity or array of entities.
|
|
3
|
+
*
|
|
4
|
+
* This utility handles the mismatch between Prisma's Date types and API response DTOs
|
|
5
|
+
* that expect string timestamps. It preserves null values and handles nested objects and arrays.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of data to transform
|
|
8
|
+
* @param data - The data to transform (object, array, or primitive)
|
|
9
|
+
* @returns The same structure with all Date objects converted to ISO strings
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const ranger = await prisma.ranger.findUnique({
|
|
14
|
+
* where: { id },
|
|
15
|
+
* include: { category: true }
|
|
16
|
+
* });
|
|
17
|
+
* return serializeDates(ranger); // All Date fields → ISO strings
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function serializeDates(data) {
|
|
21
|
+
if (data === null || data === undefined) {
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
if (data instanceof Date) {
|
|
25
|
+
return data.toISOString();
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(data)) {
|
|
28
|
+
return data.map((item) => serializeDates(item));
|
|
29
|
+
}
|
|
30
|
+
if (typeof data === 'object') {
|
|
31
|
+
const result = {};
|
|
32
|
+
for (const [key, value] of Object.entries(data)) {
|
|
33
|
+
result[key] = serializeDates(value);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=serialize-dates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize-dates.js","sourceRoot":"","sources":["../src/serialize-dates.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,cAAc,CAAI,IAAO;IACvC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACxC,OAAO,IAA0B,CAAC;IACpC,CAAC;IAED,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,WAAW,EAAwB,CAAC;IAClD,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAuB,CAAC;IACxE,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,MAA4B,CAAC;IACtC,CAAC;IAED,OAAO,IAA0B,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { AwilixContainer } from 'awilix';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import type { ModuleConfig } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for creating a test app
|
|
6
|
+
*/
|
|
7
|
+
export interface CreateTestAppOptions {
|
|
8
|
+
/**
|
|
9
|
+
* API base path
|
|
10
|
+
* @default '/api'
|
|
11
|
+
*/
|
|
12
|
+
apiBasePath?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Service mocks to override module providers
|
|
15
|
+
* These will be registered as values in the container, overriding any class/factory providers
|
|
16
|
+
*/
|
|
17
|
+
services?: Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Error handler
|
|
20
|
+
* @default defaultErrorHandler
|
|
21
|
+
* Set to false to disable error handling
|
|
22
|
+
*/
|
|
23
|
+
errorHandler?: false | import('hono').ErrorHandler;
|
|
24
|
+
/**
|
|
25
|
+
* Enable debug logging
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
debug?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Test context returned by createTestApp
|
|
32
|
+
*/
|
|
33
|
+
export interface TestContext {
|
|
34
|
+
/**
|
|
35
|
+
* Hono app instance with mounted routes
|
|
36
|
+
*/
|
|
37
|
+
app: Hono;
|
|
38
|
+
/**
|
|
39
|
+
* Awilix container with all services
|
|
40
|
+
*/
|
|
41
|
+
container: AwilixContainer;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a test app for testing a specific module.
|
|
45
|
+
*
|
|
46
|
+
* This function:
|
|
47
|
+
* 1. Creates an Awilix container
|
|
48
|
+
* 2. Registers all module providers (from the module and its imports)
|
|
49
|
+
* 3. Overrides providers with mock services
|
|
50
|
+
* 4. Creates a Hono app with error handler
|
|
51
|
+
* 5. Mounts the module's routes
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const mockEmailService = {
|
|
56
|
+
* sendEmail: vi.fn(),
|
|
57
|
+
* };
|
|
58
|
+
*
|
|
59
|
+
* const { app, container } = createTestApp(AuthModule, {
|
|
60
|
+
* services: {
|
|
61
|
+
* emailService: mockEmailService,
|
|
62
|
+
* prismaService: testDb,
|
|
63
|
+
* },
|
|
64
|
+
* });
|
|
65
|
+
*
|
|
66
|
+
* // Test routes
|
|
67
|
+
* const res = await app.request('/api/auth/session', { method: 'POST', ... });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function createTestApp(module: ModuleConfig, options?: CreateTestAppOptions): TestContext;
|
|
71
|
+
/**
|
|
72
|
+
* Create a test container with specific services.
|
|
73
|
+
*
|
|
74
|
+
* Useful for testing services in isolation without creating a full app.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const container = createTestContainer({
|
|
79
|
+
* prismaService: testDb,
|
|
80
|
+
* hashService: new HashService(),
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* const authService = new AuthService(container.cradle);
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function createTestContainer(services: Record<string, unknown>): AwilixContainer;
|
|
87
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,MAAM,EAAE,YAAY,CAAC;IAEnD;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,GAAG,EAAE,IAAI,CAAC;IAEV;;OAEG;IACH,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,oBAAyB,GACjC,WAAW,CA+Cb;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CAatF"}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { asValue, createContainer, InjectionMode } from 'awilix';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import { collectModules, registerModuleProviders, validateNoCycles } from './bootstrap.js';
|
|
4
|
+
import { defaultErrorHandler } from './error-handler.js';
|
|
5
|
+
/**
|
|
6
|
+
* Create a test app for testing a specific module.
|
|
7
|
+
*
|
|
8
|
+
* This function:
|
|
9
|
+
* 1. Creates an Awilix container
|
|
10
|
+
* 2. Registers all module providers (from the module and its imports)
|
|
11
|
+
* 3. Overrides providers with mock services
|
|
12
|
+
* 4. Creates a Hono app with error handler
|
|
13
|
+
* 5. Mounts the module's routes
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const mockEmailService = {
|
|
18
|
+
* sendEmail: vi.fn(),
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* const { app, container } = createTestApp(AuthModule, {
|
|
22
|
+
* services: {
|
|
23
|
+
* emailService: mockEmailService,
|
|
24
|
+
* prismaService: testDb,
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Test routes
|
|
29
|
+
* const res = await app.request('/api/auth/session', { method: 'POST', ... });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function createTestApp(module, options = {}) {
|
|
33
|
+
const { apiBasePath = '/api', services = {}, errorHandler = defaultErrorHandler, debug = false, } = options;
|
|
34
|
+
// Create Awilix container
|
|
35
|
+
const container = createContainer({
|
|
36
|
+
injectionMode: InjectionMode.PROXY,
|
|
37
|
+
strict: true,
|
|
38
|
+
});
|
|
39
|
+
// Collect all modules (flatten imports)
|
|
40
|
+
const allModules = collectModules(module);
|
|
41
|
+
validateNoCycles(allModules);
|
|
42
|
+
// Register all providers from the module and its imports
|
|
43
|
+
for (const mod of allModules) {
|
|
44
|
+
registerModuleProviders(mod, container, debug);
|
|
45
|
+
}
|
|
46
|
+
// Override with mock services AFTER module providers
|
|
47
|
+
// This allows both overriding services and providing dependencies
|
|
48
|
+
for (const [key, value] of Object.entries(services)) {
|
|
49
|
+
container.register({
|
|
50
|
+
[key]: asValue(value),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Create Hono app
|
|
54
|
+
const app = new Hono();
|
|
55
|
+
// Apply error handler
|
|
56
|
+
if (errorHandler !== false) {
|
|
57
|
+
app.onError(errorHandler);
|
|
58
|
+
}
|
|
59
|
+
// Mount module routes
|
|
60
|
+
if (module.routes && module.basePath) {
|
|
61
|
+
const router = new Hono();
|
|
62
|
+
module.routes(router, container.cradle);
|
|
63
|
+
app.route(`${apiBasePath}/${module.basePath}`, router);
|
|
64
|
+
}
|
|
65
|
+
return { app, container };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a test container with specific services.
|
|
69
|
+
*
|
|
70
|
+
* Useful for testing services in isolation without creating a full app.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const container = createTestContainer({
|
|
75
|
+
* prismaService: testDb,
|
|
76
|
+
* hashService: new HashService(),
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* const authService = new AuthService(container.cradle);
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export function createTestContainer(services) {
|
|
83
|
+
const container = createContainer({
|
|
84
|
+
injectionMode: InjectionMode.PROXY,
|
|
85
|
+
strict: true,
|
|
86
|
+
});
|
|
87
|
+
for (const [key, value] of Object.entries(services)) {
|
|
88
|
+
container.register({
|
|
89
|
+
[key]: asValue(value),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return container;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAgDzD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAoB,EACpB,UAAgC,EAAE;IAElC,MAAM,EACJ,WAAW,GAAG,MAAM,EACpB,QAAQ,GAAG,EAAE,EACb,YAAY,GAAG,mBAAmB,EAClC,KAAK,GAAG,KAAK,GACd,GAAG,OAAO,CAAC;IAEZ,0BAA0B;IAC1B,MAAM,SAAS,GAAG,eAAe,CAAC;QAChC,aAAa,EAAE,aAAa,CAAC,KAAK;QAClC,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;IAEH,wCAAwC;IACxC,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAE7B,yDAAyD;IACzD,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,uBAAuB,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,qDAAqD;IACrD,kEAAkE;IAClE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,SAAS,CAAC,QAAQ,CAAC;YACjB,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;SACtB,CAAC,CAAC;IACL,CAAC;IAED,kBAAkB;IAClB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,sBAAsB;IACtB,IAAI,YAAY,KAAK,KAAK,EAAE,CAAC;QAC3B,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IAED,sBAAsB;IACtB,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,MAAiC,CAAC,CAAC;QACnE,GAAG,CAAC,KAAK,CAAC,GAAG,WAAW,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAiC;IACnE,MAAM,SAAS,GAAG,eAAe,CAAC;QAChC,aAAa,EAAE,aAAa,CAAC,KAAK;QAClC,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,SAAS,CAAC,QAAQ,CAAC;YACjB,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;SACtB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|