ts-typed-api 0.1.21 → 0.1.23

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.
@@ -0,0 +1,136 @@
1
+ # Hono Cloudflare Workers Adapter
2
+
3
+ This adapter allows you to use `ts-typed-api` with [Hono](https://hono.dev/) framework in Cloudflare Workers, while maintaining the same API definitions and handler functions as the Express version.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install hono
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Setup
14
+
15
+ ```typescript
16
+ import { Hono } from 'hono';
17
+ import { RegisterHonoHandlers } from 'ts-typed-api';
18
+ import { ApiDefinition } from './definitions';
19
+
20
+ const app = new Hono();
21
+
22
+ // Use the same API definitions and handlers as Express
23
+ RegisterHonoHandlers(app, ApiDefinition, {
24
+ domain: {
25
+ endpoint: async (req, res) => {
26
+ res.respond(200, { message: 'Hello from Cloudflare Workers!' });
27
+ }
28
+ }
29
+ });
30
+
31
+ // Export for Cloudflare Workers
32
+ export default app;
33
+ ```
34
+
35
+ ### File Upload Support
36
+
37
+ The adapter supports file uploads using Hono's `parseBody()` method, which is compatible with Cloudflare Workers:
38
+
39
+ ```typescript
40
+ import { CreateApiDefinition, CreateResponses } from 'ts-typed-api';
41
+
42
+ const ApiDefinition = CreateApiDefinition({
43
+ endpoints: {
44
+ upload: {
45
+ file: {
46
+ method: 'POST',
47
+ path: '/upload',
48
+ fileUpload: {
49
+ single: {
50
+ fieldName: 'file',
51
+ maxSize: 10 * 1024 * 1024, // 10MB
52
+ allowedMimeTypes: ['image/jpeg', 'image/png']
53
+ }
54
+ },
55
+ responses: CreateResponses({
56
+ 200: z.object({ filename: z.string() })
57
+ })
58
+ }
59
+ }
60
+ }
61
+ });
62
+
63
+ // Handler works the same as Express
64
+ RegisterHonoHandlers(app, ApiDefinition, {
65
+ upload: {
66
+ file: async (req, res) => {
67
+ const file = req.file; // File object with buffer, mimetype, etc.
68
+ // Process file...
69
+ res.respond(200, { filename: file.originalname });
70
+ }
71
+ }
72
+ });
73
+ ```
74
+
75
+ ### Middleware Support
76
+
77
+ ```typescript
78
+ const authMiddleware = async (req, res, next, endpointInfo) => {
79
+ const authHeader = req.headers.get('authorization');
80
+ if (!authHeader?.startsWith('Bearer ')) {
81
+ return res.status(401).json({
82
+ error: [{ field: "authorization", type: "general", message: "Unauthorized" }]
83
+ });
84
+ }
85
+ await next();
86
+ };
87
+
88
+ RegisterHonoHandlers(app, ApiDefinition, handlers, [authMiddleware]);
89
+ ```
90
+
91
+ ## Key Differences from Express
92
+
93
+ 1. **File Handling**: Uses `Uint8Array` instead of `Buffer` for file contents
94
+ 2. **Headers**: Use `req.headers.get()` instead of `req.headers[]`
95
+ 3. **Response Methods**: Hono uses `c.json()` instead of Express `res.json()`
96
+ 4. **Middleware**: Adapted to work with Hono's middleware system
97
+
98
+ ## Cloudflare Workers Deployment
99
+
100
+ ```javascript
101
+ // wrangler.toml
102
+ name = "my-api"
103
+ main = "src/index.ts"
104
+ compatibility_date = "2023-01-01"
105
+
106
+ // src/index.ts
107
+ import app from './app';
108
+
109
+ export default {
110
+ fetch: app.fetch
111
+ };
112
+ ```
113
+
114
+ ## API Compatibility
115
+
116
+ The `RegisterHonoHandlers` function has the same signature as `RegisterHandlers`:
117
+
118
+ ```typescript
119
+ RegisterHonoHandlers<TDef extends ApiDefinitionSchema>(
120
+ app: Hono,
121
+ apiDefinition: TDef,
122
+ objectHandlers: ObjectHandlers<TDef>,
123
+ middlewares?: AnyMiddleware<TDef>[]
124
+ ): void
125
+ ```
126
+
127
+ This means you can switch between Express and Hono by simply changing the import and app initialization, while keeping your API definitions and handlers unchanged.
128
+
129
+ ## Supported File Upload Configurations
130
+
131
+ - `single`: Single file upload
132
+ - `array`: Multiple files with same field name
133
+ - `fields`: Multiple files with different field names
134
+ - `any`: Accept any files
135
+
136
+ All configurations work the same as in the Express version but use Workers-compatible APIs.
@@ -0,0 +1,33 @@
1
+ import { Hono, Context } from 'hono';
2
+ import { z } from 'zod';
3
+ import { ApiDefinitionSchema } from './definition';
4
+ import { TypedRequest, TypedResponse } from './router';
5
+ import { SpecificRouteHandler } from './handler';
6
+ import { ObjectHandlers, AnyMiddleware, EndpointMiddleware } from './object-handlers';
7
+ export type HonoFile = File;
8
+ export declare const honoFileSchema: z.ZodObject<{
9
+ fieldname: z.ZodString;
10
+ originalname: z.ZodString;
11
+ encoding: z.ZodString;
12
+ mimetype: z.ZodString;
13
+ size: z.ZodNumber;
14
+ buffer: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
15
+ file: z.ZodOptional<z.ZodCustom<import("buffer").File, import("buffer").File>>;
16
+ destination: z.ZodOptional<z.ZodString>;
17
+ filename: z.ZodOptional<z.ZodString>;
18
+ path: z.ZodOptional<z.ZodString>;
19
+ stream: z.ZodOptional<z.ZodAny>;
20
+ }, z.core.$strip>;
21
+ export type HonoFileType = z.infer<typeof honoFileSchema>;
22
+ export type HonoTypedContext<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain]> = Context & {
23
+ params: TypedRequest<TDef, TDomain, TRouteKey>['params'];
24
+ query: TypedRequest<TDef, TDomain, TRouteKey>['query'];
25
+ body: TypedRequest<TDef, TDomain, TRouteKey>['body'];
26
+ file?: HonoFile;
27
+ files?: HonoFile[] | {
28
+ [fieldname: string]: HonoFile[];
29
+ };
30
+ respond: TypedResponse<TDef, TDomain, TRouteKey>['respond'];
31
+ };
32
+ export declare function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(app: Hono, apiDefinition: TDef, routeHandlers: Array<SpecificRouteHandler<TDef>>, middlewares?: EndpointMiddleware<TDef>[]): void;
33
+ export declare function RegisterHonoHandlers<TDef extends ApiDefinitionSchema>(app: Hono, apiDefinition: TDef, objectHandlers: ObjectHandlers<TDef>, middlewares?: AnyMiddleware<TDef>[]): void;
@@ -0,0 +1,474 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.honoFileSchema = void 0;
4
+ exports.registerHonoRouteHandlers = registerHonoRouteHandlers;
5
+ exports.RegisterHonoHandlers = RegisterHonoHandlers;
6
+ const zod_1 = require("zod");
7
+ // Hono-compatible file schema for Workers environment
8
+ exports.honoFileSchema = zod_1.z.object({
9
+ fieldname: zod_1.z.string(),
10
+ originalname: zod_1.z.string(),
11
+ encoding: zod_1.z.string(),
12
+ mimetype: zod_1.z.string(),
13
+ size: zod_1.z.number(),
14
+ buffer: zod_1.z.instanceof(Uint8Array).optional(), // Workers use Uint8Array instead of Buffer
15
+ file: zod_1.z.instanceof(File).optional(), // Direct File object access
16
+ destination: zod_1.z.string().optional(),
17
+ filename: zod_1.z.string().optional(),
18
+ path: zod_1.z.string().optional(),
19
+ stream: zod_1.z.any().optional(),
20
+ });
21
+ // Helper function to preprocess query parameters for type coercion
22
+ function preprocessQueryParams(query, querySchema) {
23
+ if (!querySchema || !query)
24
+ return query;
25
+ const processedQuery = { ...query };
26
+ if (querySchema instanceof zod_1.z.ZodObject) {
27
+ const shape = querySchema.shape;
28
+ for (const [key, value] of Object.entries(processedQuery)) {
29
+ if (typeof value === 'string' && shape[key]) {
30
+ const fieldSchema = shape[key];
31
+ let innerSchema = fieldSchema;
32
+ if (fieldSchema instanceof zod_1.z.ZodOptional) {
33
+ innerSchema = fieldSchema._def.innerType;
34
+ }
35
+ if (fieldSchema instanceof zod_1.z.ZodDefault) {
36
+ innerSchema = fieldSchema._def.innerType;
37
+ }
38
+ while (innerSchema instanceof zod_1.z.ZodOptional || innerSchema instanceof zod_1.z.ZodDefault) {
39
+ if (innerSchema instanceof zod_1.z.ZodOptional) {
40
+ innerSchema = innerSchema._def.innerType;
41
+ }
42
+ else if (innerSchema instanceof zod_1.z.ZodDefault) {
43
+ innerSchema = innerSchema._def.innerType;
44
+ }
45
+ }
46
+ if (innerSchema instanceof zod_1.z.ZodNumber) {
47
+ const numValue = Number(value);
48
+ if (!isNaN(numValue)) {
49
+ processedQuery[key] = numValue;
50
+ }
51
+ }
52
+ else if (innerSchema instanceof zod_1.z.ZodBoolean) {
53
+ if (value === 'true') {
54
+ processedQuery[key] = true;
55
+ }
56
+ else if (value === 'false') {
57
+ processedQuery[key] = false;
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ return processedQuery;
64
+ }
65
+ // Helper function to create file upload middleware for Hono/Workers
66
+ function createHonoFileUploadMiddleware(config) {
67
+ return async (c, next) => {
68
+ try {
69
+ if (config.single) {
70
+ const formData = await c.req.parseBody({ all: false });
71
+ const file = formData[config.single.fieldName];
72
+ if (file instanceof File) {
73
+ // Validate file
74
+ if (config.single.maxSize && file.size > config.single.maxSize) {
75
+ return c.json({
76
+ data: null,
77
+ error: [{ field: 'file', message: `File size exceeds ${config.single.maxSize} bytes`, type: 'body' }]
78
+ }, 422);
79
+ }
80
+ if (config.single.allowedMimeTypes && !config.single.allowedMimeTypes.includes(file.type)) {
81
+ return c.json({
82
+ data: null,
83
+ error: [{ field: 'file', message: `File type ${file.type} not allowed`, type: 'body' }]
84
+ }, 422);
85
+ }
86
+ // Attach file to context
87
+ c.file = {
88
+ fieldname: config.single.fieldName,
89
+ originalname: file.name,
90
+ encoding: '7bit',
91
+ mimetype: file.type,
92
+ size: file.size,
93
+ buffer: new Uint8Array(await file.arrayBuffer()),
94
+ file: file
95
+ };
96
+ }
97
+ }
98
+ else if (config.array) {
99
+ const formData = await c.req.parseBody({ all: true });
100
+ const files = formData[config.array.fieldName];
101
+ if (Array.isArray(files)) {
102
+ // Validate files
103
+ if (config.array.maxCount && files.length > config.array.maxCount) {
104
+ return c.json({
105
+ data: null,
106
+ error: [{ field: 'file', message: `Maximum ${config.array.maxCount} files allowed`, type: 'body' }]
107
+ }, 422);
108
+ }
109
+ const processedFiles = [];
110
+ for (const file of files) {
111
+ if (file instanceof File) {
112
+ if (config.array.maxSize && file.size > config.array.maxSize) {
113
+ return c.json({
114
+ data: null,
115
+ error: [{ field: 'file', message: `File size exceeds ${config.array.maxSize} bytes`, type: 'body' }]
116
+ }, 422);
117
+ }
118
+ if (config.array.allowedMimeTypes && !config.array.allowedMimeTypes.includes(file.type)) {
119
+ return c.json({
120
+ data: null,
121
+ error: [{ field: 'file', message: `File type ${file.type} not allowed`, type: 'body' }]
122
+ }, 422);
123
+ }
124
+ processedFiles.push({
125
+ fieldname: config.array.fieldName,
126
+ originalname: file.name,
127
+ encoding: '7bit',
128
+ mimetype: file.type,
129
+ size: file.size,
130
+ buffer: new Uint8Array(await file.arrayBuffer()),
131
+ file: file
132
+ });
133
+ }
134
+ }
135
+ c.files = processedFiles;
136
+ }
137
+ }
138
+ else if (config.fields) {
139
+ const formData = await c.req.parseBody({ all: true });
140
+ const filesMap = {};
141
+ for (const fieldConfig of config.fields) {
142
+ const files = formData[fieldConfig.fieldName];
143
+ if (Array.isArray(files)) {
144
+ if (fieldConfig.maxCount && files.length > fieldConfig.maxCount) {
145
+ return c.json({
146
+ data: null,
147
+ error: [{ field: fieldConfig.fieldName, message: `Maximum ${fieldConfig.maxCount} files allowed`, type: 'body' }]
148
+ }, 422);
149
+ }
150
+ const processedFiles = [];
151
+ for (const file of files) {
152
+ if (file instanceof File) {
153
+ if (fieldConfig.maxSize && file.size > fieldConfig.maxSize) {
154
+ return c.json({
155
+ data: null,
156
+ error: [{ field: fieldConfig.fieldName, message: `File size exceeds ${fieldConfig.maxSize} bytes`, type: 'body' }]
157
+ }, 422);
158
+ }
159
+ if (fieldConfig.allowedMimeTypes && !fieldConfig.allowedMimeTypes.includes(file.type)) {
160
+ return c.json({
161
+ data: null,
162
+ error: [{ field: fieldConfig.fieldName, message: `File type ${file.type} not allowed`, type: 'body' }]
163
+ }, 422);
164
+ }
165
+ processedFiles.push({
166
+ fieldname: fieldConfig.fieldName,
167
+ originalname: file.name,
168
+ encoding: '7bit',
169
+ mimetype: file.type,
170
+ size: file.size,
171
+ buffer: new Uint8Array(await file.arrayBuffer()),
172
+ file: file
173
+ });
174
+ }
175
+ }
176
+ filesMap[fieldConfig.fieldName] = processedFiles;
177
+ }
178
+ }
179
+ c.files = filesMap;
180
+ }
181
+ else if (config.any) {
182
+ const formData = await c.req.parseBody({ all: true });
183
+ // Process all files
184
+ const allFiles = [];
185
+ for (const [key, value] of Object.entries(formData)) {
186
+ if (value instanceof File) {
187
+ if (config.any.maxSize && value.size > config.any.maxSize) {
188
+ return c.json({
189
+ data: null,
190
+ error: [{ field: key, message: `File size exceeds ${config.any.maxSize} bytes`, type: 'body' }]
191
+ }, 422);
192
+ }
193
+ if (config.any.allowedMimeTypes && !config.any.allowedMimeTypes.includes(value.type)) {
194
+ return c.json({
195
+ data: null,
196
+ error: [{ field: key, message: `File type ${value.type} not allowed`, type: 'body' }]
197
+ }, 422);
198
+ }
199
+ allFiles.push({
200
+ fieldname: key,
201
+ originalname: value.name,
202
+ encoding: '7bit',
203
+ mimetype: value.type,
204
+ size: value.size,
205
+ buffer: new Uint8Array(await value.arrayBuffer()),
206
+ file: value
207
+ });
208
+ }
209
+ }
210
+ c.files = allFiles;
211
+ }
212
+ await next();
213
+ }
214
+ catch (error) {
215
+ console.error('File upload middleware error:', error);
216
+ return c.json({
217
+ data: null,
218
+ error: [{ field: 'file', message: 'File upload processing failed', type: 'body' }]
219
+ }, 422);
220
+ }
221
+ };
222
+ }
223
+ // Register route handlers with Hono, now generic over TDef
224
+ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middlewares) {
225
+ routeHandlers.forEach((specificHandlerIterationItem) => {
226
+ const { domain, routeKey, handler } = specificHandlerIterationItem;
227
+ const currentDomain = domain;
228
+ const currentRouteKey = routeKey;
229
+ const routeDefinition = apiDefinition.endpoints[currentDomain][currentRouteKey];
230
+ if (!routeDefinition) {
231
+ console.error(`Route definition not found for domain "${String(currentDomain)}" and routeKey "${String(currentRouteKey)}"`);
232
+ return;
233
+ }
234
+ const { path, method } = routeDefinition;
235
+ // Apply prefix from API definition if it exists
236
+ const fullPath = apiDefinition.prefix
237
+ ? `${apiDefinition.prefix.startsWith('/') ? apiDefinition.prefix : `/${apiDefinition.prefix}`}${path}`.replace(/\/+/g, '/')
238
+ : path;
239
+ const honoMiddleware = async (c) => {
240
+ try {
241
+ // Parse and validate request
242
+ const parsedParams = ('params' in routeDefinition && routeDefinition.params)
243
+ ? routeDefinition.params.parse(c.req.param())
244
+ : c.req.param();
245
+ const preprocessedQuery = ('query' in routeDefinition && routeDefinition.query)
246
+ ? preprocessQueryParams(c.req.query(), routeDefinition.query)
247
+ : c.req.query();
248
+ const parsedQuery = ('query' in routeDefinition && routeDefinition.query)
249
+ ? routeDefinition.query.parse(preprocessedQuery)
250
+ : preprocessedQuery;
251
+ let parsedBody = undefined;
252
+ if (method === 'POST' || method === 'PUT' || method === 'DELETE' || method === 'PATCH') {
253
+ if ('body' in routeDefinition && routeDefinition.body) {
254
+ // For JSON requests
255
+ if (c.req.header('content-type')?.includes('application/json')) {
256
+ parsedBody = routeDefinition.body.parse(await c.req.json());
257
+ }
258
+ else {
259
+ // For form data or other body types
260
+ parsedBody = routeDefinition.body.parse(await c.req.parseBody());
261
+ }
262
+ }
263
+ else {
264
+ parsedBody = await c.req.parseBody();
265
+ }
266
+ }
267
+ // Attach parsed data to context
268
+ c.params = parsedParams;
269
+ c.query = parsedQuery;
270
+ c.body = parsedBody;
271
+ // Add respond method to context
272
+ c.respond = (status, data) => {
273
+ const responseSchema = routeDefinition.responses[status];
274
+ if (!responseSchema) {
275
+ console.error(`No response schema defined for status ${status} in route ${String(currentDomain)}/${String(currentRouteKey)}`);
276
+ c.__response = c.json({
277
+ data: null,
278
+ error: [{ field: "general", type: "general", message: "Internal server error: Undefined response schema for status." }]
279
+ }, 500);
280
+ return;
281
+ }
282
+ let responseBody;
283
+ if (status === 422) {
284
+ responseBody = {
285
+ data: null,
286
+ error: data
287
+ };
288
+ }
289
+ else {
290
+ // Always use unified response format since CreateResponses wraps all schemas
291
+ responseBody = {
292
+ data: data,
293
+ error: null
294
+ };
295
+ }
296
+ const validationResult = responseSchema.safeParse(responseBody);
297
+ if (validationResult.success) {
298
+ // Handle 204 responses specially - they must not have a body
299
+ if (status === 204) {
300
+ c.__response = new Response(null, { status: status });
301
+ }
302
+ else {
303
+ c.__response = c.json(validationResult.data, status);
304
+ }
305
+ }
306
+ else {
307
+ console.error(`FATAL: Constructed response body failed Zod validation for status ${status} in route ${String(currentDomain)}/${String(currentRouteKey)}.`, validationResult.error.issues, 'Expected schema shape:', responseSchema._def?.shape, 'Provided data:', data, 'Constructed response body:', responseBody);
308
+ c.__response = c.json({
309
+ data: null,
310
+ error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
311
+ }, 500);
312
+ }
313
+ };
314
+ // Create Express-like req/res objects for handler compatibility
315
+ const fakeReq = {
316
+ params: parsedParams,
317
+ query: parsedQuery,
318
+ body: parsedBody,
319
+ file: c.file,
320
+ files: c.files,
321
+ headers: c.req.header(),
322
+ ip: c.req.header('CF-Connecting-IP') || '127.0.0.1',
323
+ method: c.req.method,
324
+ path: c.req.path,
325
+ originalUrl: c.req.url
326
+ };
327
+ const fakeRes = {
328
+ respond: c.respond
329
+ };
330
+ const specificHandlerFn = handler;
331
+ await specificHandlerFn(fakeReq, fakeRes);
332
+ // Return the response created by the handler
333
+ if (c.__response) {
334
+ return c.__response;
335
+ }
336
+ else {
337
+ console.error('No response was set by the handler');
338
+ return c.json({
339
+ data: null,
340
+ error: [{ field: "general", type: "general", message: "Internal server error: No response set by handler." }]
341
+ }, 500);
342
+ }
343
+ }
344
+ catch (error) {
345
+ if (error instanceof zod_1.z.ZodError) {
346
+ const mappedErrors = error.issues.map(err => {
347
+ let errorType = 'general';
348
+ const pathZero = String(err.path[0]);
349
+ if (pathZero === 'params')
350
+ errorType = 'param';
351
+ else if (pathZero === 'query')
352
+ errorType = 'query';
353
+ else if (pathZero === 'body')
354
+ errorType = 'body';
355
+ return {
356
+ field: err.path.join('.') || 'request',
357
+ message: err.message,
358
+ type: errorType,
359
+ };
360
+ });
361
+ const errorResponseBody = { data: null, error: mappedErrors };
362
+ const schema422 = routeDefinition.responses[422];
363
+ if (schema422) {
364
+ const validationResult = schema422.safeParse(errorResponseBody);
365
+ if (validationResult.success) {
366
+ return c.json(validationResult.data, 422);
367
+ }
368
+ else {
369
+ console.error("FATAL: Constructed 422 error response failed its own schema validation.", validationResult.error.issues);
370
+ return c.json({ error: [{ field: "general", type: "general", message: "Internal server error constructing validation error response." }] }, 500);
371
+ }
372
+ }
373
+ else {
374
+ console.error("Error: 422 schema not found for route, sending raw Zod errors.");
375
+ return c.json({ error: mappedErrors }, 422);
376
+ }
377
+ }
378
+ else if (error instanceof Error) {
379
+ console.error(`Error in ${method} ${path}:`, error.message);
380
+ return c.json({ error: [{ field: "general", type: "general", message: 'Internal server error' }] }, 500);
381
+ }
382
+ else {
383
+ console.error(`Unknown error in ${method} ${path}:`, error);
384
+ return c.json({ error: [{ field: "general", type: "general", message: 'An unknown error occurred' }] }, 500);
385
+ }
386
+ }
387
+ };
388
+ // Create middleware wrappers
389
+ const middlewareWrappers = [];
390
+ // Add file upload middleware if configured
391
+ if (routeDefinition.fileUpload) {
392
+ try {
393
+ const fileUploadMiddleware = createHonoFileUploadMiddleware(routeDefinition.fileUpload);
394
+ middlewareWrappers.push(fileUploadMiddleware);
395
+ }
396
+ catch (error) {
397
+ console.error(`Error creating file upload middleware for ${currentDomain}.${currentRouteKey}:`, error);
398
+ return;
399
+ }
400
+ }
401
+ if (middlewares && middlewares.length > 0) {
402
+ middlewares.forEach(middleware => {
403
+ const wrappedMiddleware = async (c, next) => {
404
+ try {
405
+ await middleware(c.req, c.res, next, { domain: currentDomain, routeKey: currentRouteKey });
406
+ }
407
+ catch (error) {
408
+ console.error('Middleware error:', error);
409
+ return next();
410
+ }
411
+ };
412
+ middlewareWrappers.push(wrappedMiddleware);
413
+ });
414
+ }
415
+ // Register route with middlewares
416
+ const allHandlers = [...middlewareWrappers, honoMiddleware];
417
+ // Register with Hono
418
+ switch (method.toUpperCase()) {
419
+ case 'GET':
420
+ app.get(fullPath, ...allHandlers);
421
+ break;
422
+ case 'POST':
423
+ app.post(fullPath, ...allHandlers);
424
+ break;
425
+ case 'PATCH':
426
+ app.patch(fullPath, ...allHandlers);
427
+ break;
428
+ case 'PUT':
429
+ app.put(fullPath, ...allHandlers);
430
+ break;
431
+ case 'DELETE':
432
+ app.delete(fullPath, ...allHandlers);
433
+ break;
434
+ default:
435
+ console.warn(`Unsupported HTTP method: ${method} for path ${fullPath}`);
436
+ }
437
+ });
438
+ }
439
+ // Transform object-based handlers to array format
440
+ function transformObjectHandlersToArray(objectHandlers) {
441
+ const handlerArray = [];
442
+ for (const domain in objectHandlers) {
443
+ if (Object.prototype.hasOwnProperty.call(objectHandlers, domain)) {
444
+ const domainHandlers = objectHandlers[domain];
445
+ for (const routeKey in domainHandlers) {
446
+ if (Object.prototype.hasOwnProperty.call(domainHandlers, routeKey)) {
447
+ const handler = domainHandlers[routeKey];
448
+ handlerArray.push({
449
+ domain,
450
+ routeKey,
451
+ handler
452
+ });
453
+ }
454
+ }
455
+ }
456
+ }
457
+ return handlerArray;
458
+ }
459
+ // Main utility function that registers object-based handlers with Hono
460
+ function RegisterHonoHandlers(app, apiDefinition, objectHandlers, middlewares) {
461
+ const handlerArray = transformObjectHandlersToArray(objectHandlers);
462
+ // Convert AnyMiddleware to EndpointMiddleware by checking function arity
463
+ const endpointMiddlewares = middlewares?.map(middleware => {
464
+ if (middleware.length === 4) {
465
+ return middleware;
466
+ }
467
+ else {
468
+ return ((req, res, next) => {
469
+ return middleware(req, res, next);
470
+ });
471
+ }
472
+ }) || [];
473
+ registerHonoRouteHandlers(app, apiDefinition, handlerArray, endpointMiddlewares);
474
+ }
package/dist/index.d.ts CHANGED
@@ -5,3 +5,4 @@ export { CreateApiDefinition, CreateResponses, ApiDefinitionSchema } from './def
5
5
  export { RegisterHandlers, EndpointMiddleware, UniversalEndpointMiddleware, SimpleMiddleware, EndpointInfo } from './object-handlers';
6
6
  export { File as UploadedFile } from './router';
7
7
  export { z as ZodSchema } from 'zod';
8
+ export { RegisterHonoHandlers, registerHonoRouteHandlers, HonoFile, HonoFileType, honoFileSchema, HonoTypedContext } from './hono-cloudflare-workers';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ZodSchema = exports.RegisterHandlers = exports.CreateResponses = exports.CreateApiDefinition = exports.generateOpenApiSpec2 = exports.generateOpenApiSpec = exports.FetchHttpClientAdapter = exports.ApiClient = void 0;
3
+ exports.honoFileSchema = exports.registerHonoRouteHandlers = exports.RegisterHonoHandlers = exports.ZodSchema = exports.RegisterHandlers = exports.CreateResponses = exports.CreateApiDefinition = exports.generateOpenApiSpec2 = exports.generateOpenApiSpec = exports.FetchHttpClientAdapter = exports.ApiClient = void 0;
4
4
  var client_1 = require("./client");
5
5
  Object.defineProperty(exports, "ApiClient", { enumerable: true, get: function () { return client_1.ApiClient; } });
6
6
  Object.defineProperty(exports, "FetchHttpClientAdapter", { enumerable: true, get: function () { return client_1.FetchHttpClientAdapter; } });
@@ -15,3 +15,8 @@ var object_handlers_1 = require("./object-handlers");
15
15
  Object.defineProperty(exports, "RegisterHandlers", { enumerable: true, get: function () { return object_handlers_1.RegisterHandlers; } });
16
16
  var zod_1 = require("zod");
17
17
  Object.defineProperty(exports, "ZodSchema", { enumerable: true, get: function () { return zod_1.z; } });
18
+ // Hono adapter for Cloudflare Workers
19
+ var hono_cloudflare_workers_1 = require("./hono-cloudflare-workers");
20
+ Object.defineProperty(exports, "RegisterHonoHandlers", { enumerable: true, get: function () { return hono_cloudflare_workers_1.RegisterHonoHandlers; } });
21
+ Object.defineProperty(exports, "registerHonoRouteHandlers", { enumerable: true, get: function () { return hono_cloudflare_workers_1.registerHonoRouteHandlers; } });
22
+ Object.defineProperty(exports, "honoFileSchema", { enumerable: true, get: function () { return hono_cloudflare_workers_1.honoFileSchema; } });