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.
- package/README_HONO_ADAPTER.md +136 -0
- package/dist/hono-cloudflare-workers.d.ts +33 -0
- package/dist/hono-cloudflare-workers.js +474 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -1
- package/dist/openapi-self.js +39 -2
- package/examples/hono-cloudflare-worker-example.ts +66 -0
- package/examples/test-hono-server.ts +125 -0
- package/package.json +3 -2
- package/src/hono-cloudflare-workers.ts +552 -0
- package/src/index.ts +3 -0
- package/src/openapi-self.ts +42 -2
- package/tests/advanced-api.test.ts +11 -4
- package/tests/openapi-spec.test.ts +130 -0
- package/tests/setup.ts +324 -176
- package/tests/simple-api.test.ts +33 -3
package/dist/openapi-self.js
CHANGED
|
@@ -166,10 +166,47 @@ class SchemaRegistry {
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
if (zodSchema instanceof zod_1.ZodArray) {
|
|
169
|
-
|
|
169
|
+
// Try multiple ways to get the array item type
|
|
170
|
+
let itemType;
|
|
171
|
+
// Method 1: Try _def.element (this is the correct property for ZodArray)
|
|
172
|
+
try {
|
|
173
|
+
const def = getZodDef(zodSchema);
|
|
174
|
+
if (def && def.element && typeof def.element === 'object' && def.element.constructor) {
|
|
175
|
+
itemType = def.element;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// Continue to next method
|
|
180
|
+
}
|
|
181
|
+
// Method 2: Try getZodType if method 1 failed
|
|
182
|
+
if (!itemType) {
|
|
183
|
+
const typeResult = getZodType(zodSchema);
|
|
184
|
+
if (typeResult && typeof typeResult === 'object' && typeResult.constructor) {
|
|
185
|
+
itemType = typeResult;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Method 3: Direct access to _def.element as fallback
|
|
189
|
+
if (!itemType) {
|
|
190
|
+
try {
|
|
191
|
+
const directElement = zodSchema._def?.element;
|
|
192
|
+
if (directElement && typeof directElement === 'object' && directElement.constructor) {
|
|
193
|
+
itemType = directElement;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
// Continue to fallback
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (itemType) {
|
|
201
|
+
return {
|
|
202
|
+
type: 'array',
|
|
203
|
+
items: this.zodToOpenAPI(itemType, shouldRegister)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// Ultimate fallback
|
|
170
207
|
return {
|
|
171
208
|
type: 'array',
|
|
172
|
-
items:
|
|
209
|
+
items: { type: 'string' }
|
|
173
210
|
};
|
|
174
211
|
}
|
|
175
212
|
if (zodSchema instanceof zod_1.ZodObject) {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { RegisterHonoHandlers } from '../src';
|
|
3
|
+
import { PublicApiDefinition, PrivateApiDefinition } from './simple/definitions';
|
|
4
|
+
|
|
5
|
+
// Create Hono app for Cloudflare Workers
|
|
6
|
+
const app = new Hono();
|
|
7
|
+
|
|
8
|
+
// Example middlewares that receive endpoint information
|
|
9
|
+
const loggingMiddleware = async (req: any, res: any, next: any, endpointInfo: any) => {
|
|
10
|
+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path} - Endpoint: ${endpointInfo.domain}.${endpointInfo.routeKey}`);
|
|
11
|
+
await next();
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const authMiddleware = async (req: any, res: any, next: any) => {
|
|
15
|
+
// Example auth logic for Workers
|
|
16
|
+
const authHeader = req.headers.get('authorization');
|
|
17
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
18
|
+
console.log(`Auth failed - No valid auth header`);
|
|
19
|
+
return res.status(401).json({ error: [{ field: "authorization", type: "general", message: "Unauthorized" }] });
|
|
20
|
+
}
|
|
21
|
+
console.log(`Auth passed`);
|
|
22
|
+
await next();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Register public handlers (same as Express version)
|
|
26
|
+
RegisterHonoHandlers(app, PublicApiDefinition, {
|
|
27
|
+
common: {
|
|
28
|
+
ping: async (req, res) => {
|
|
29
|
+
res.respond(200, "pong");
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
status: {
|
|
33
|
+
probe1: async (req, res) => {
|
|
34
|
+
if (req.query.match) {
|
|
35
|
+
return res.respond(201, { status: true });
|
|
36
|
+
}
|
|
37
|
+
res.respond(200, "pong");
|
|
38
|
+
},
|
|
39
|
+
probe2: async (req, res) => {
|
|
40
|
+
res.respond(200, "pong");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, [loggingMiddleware]);
|
|
44
|
+
|
|
45
|
+
// Register private handlers with auth
|
|
46
|
+
RegisterHonoHandlers(app, PrivateApiDefinition, {
|
|
47
|
+
user: {
|
|
48
|
+
get: async (req, res) => {
|
|
49
|
+
console.log('Fetching user', req.params.id);
|
|
50
|
+
res.respond(200, "ok");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}, [loggingMiddleware, authMiddleware]);
|
|
54
|
+
|
|
55
|
+
// Export for Cloudflare Workers
|
|
56
|
+
export default app;
|
|
57
|
+
|
|
58
|
+
// For local development/testing (commented out for Workers compatibility)
|
|
59
|
+
// if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
60
|
+
// console.log('Hono Cloudflare Worker example running locally');
|
|
61
|
+
// console.log('Available routes:');
|
|
62
|
+
// console.log('- GET /api/v1/public/ping');
|
|
63
|
+
// console.log('- GET /api/v1/public/status/probe1');
|
|
64
|
+
// console.log('- GET /api/v1/public/status/probe2');
|
|
65
|
+
// console.log('- GET /api/v1/private/user/:id (requires Bearer token)');
|
|
66
|
+
// }
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { PublicApiDefinition as SimplePublicApiDefinition, PrivateApiDefinition as SimplePrivateApiDefinition } from './examples/simple/definitions';
|
|
4
|
+
import { RegisterHonoHandlers } from './src';
|
|
5
|
+
|
|
6
|
+
const HONO_PORT = 3004;
|
|
7
|
+
|
|
8
|
+
async function startHonoServer() {
|
|
9
|
+
console.log('Starting Hono server...');
|
|
10
|
+
|
|
11
|
+
const app = new Hono();
|
|
12
|
+
|
|
13
|
+
console.log('Registering handlers...');
|
|
14
|
+
|
|
15
|
+
// Register public handlers using Hono
|
|
16
|
+
RegisterHonoHandlers(app, SimplePublicApiDefinition, {
|
|
17
|
+
common: {
|
|
18
|
+
ping: async (req, res) => {
|
|
19
|
+
console.log('Handling ping request');
|
|
20
|
+
res.respond(200, "pong");
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
status: {
|
|
24
|
+
probe1: async (req, res) => {
|
|
25
|
+
console.log('Handling probe1 request, query:', req.query);
|
|
26
|
+
if (req.query.match) {
|
|
27
|
+
return res.respond(201, { status: true });
|
|
28
|
+
}
|
|
29
|
+
res.respond(200, "pong");
|
|
30
|
+
},
|
|
31
|
+
probe2: async (req, res) => {
|
|
32
|
+
console.log('Handling probe2 request');
|
|
33
|
+
res.respond(200, "pong");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Register private handlers using Hono
|
|
39
|
+
RegisterHonoHandlers(app, SimplePrivateApiDefinition, {
|
|
40
|
+
user: {
|
|
41
|
+
get: async (req, res) => {
|
|
42
|
+
console.log('Handling user get request, params:', req.params);
|
|
43
|
+
res.respond(200, "ok");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log('Creating HTTP server wrapper...');
|
|
49
|
+
|
|
50
|
+
// Create HTTP server from Hono app
|
|
51
|
+
const server = app.fetch;
|
|
52
|
+
|
|
53
|
+
// Create a simple HTTP server wrapper for Hono
|
|
54
|
+
const honoServer = http.createServer(async (req: any, res: any) => {
|
|
55
|
+
console.log(`Incoming request: ${req.method} ${req.url}`);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Read the request body for non-GET/HEAD methods
|
|
59
|
+
let body: ReadableStream | undefined;
|
|
60
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
61
|
+
console.log('Reading request body...');
|
|
62
|
+
const chunks: Buffer[] = [];
|
|
63
|
+
for await (const chunk of req) {
|
|
64
|
+
chunks.push(chunk);
|
|
65
|
+
}
|
|
66
|
+
const buffer = Buffer.concat(chunks);
|
|
67
|
+
console.log('Request body length:', buffer.length);
|
|
68
|
+
body = new ReadableStream({
|
|
69
|
+
start(controller) {
|
|
70
|
+
controller.enqueue(buffer);
|
|
71
|
+
controller.close();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('Creating Web API Request...');
|
|
77
|
+
const request = new Request(`http://localhost:${HONO_PORT}${req.url}`, {
|
|
78
|
+
method: req.method,
|
|
79
|
+
headers: req.headers,
|
|
80
|
+
body: body
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
console.log('Calling Hono app.fetch...');
|
|
84
|
+
const response = await server(request);
|
|
85
|
+
|
|
86
|
+
console.log('Response status:', response.status);
|
|
87
|
+
console.log('Response headers:', Object.fromEntries(response.headers.entries()));
|
|
88
|
+
|
|
89
|
+
res.statusCode = response.status;
|
|
90
|
+
for (const [key, value] of response.headers) {
|
|
91
|
+
res.setHeader(key, value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const responseBody = await response.text();
|
|
95
|
+
console.log('Response body:', responseBody);
|
|
96
|
+
res.end(responseBody);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('Hono server error:', error);
|
|
99
|
+
res.statusCode = 500;
|
|
100
|
+
res.end('Internal Server Error');
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log(`Starting server on port ${HONO_PORT}...`);
|
|
105
|
+
honoServer.listen(HONO_PORT, () => {
|
|
106
|
+
console.log(`Hono server listening on port ${HONO_PORT}`);
|
|
107
|
+
console.log('Available endpoints:');
|
|
108
|
+
console.log(' GET /api/v1/public/ping');
|
|
109
|
+
console.log(' GET /api/v1/public/status/probe1');
|
|
110
|
+
console.log(' GET /api/v1/public/status/probe1?match=true');
|
|
111
|
+
console.log(' GET /api/v1/public/status/probe2');
|
|
112
|
+
console.log(' GET /api/v1/private/user/:id');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Graceful shutdown
|
|
116
|
+
process.on('SIGINT', () => {
|
|
117
|
+
console.log('Shutting down server...');
|
|
118
|
+
honoServer.close(() => {
|
|
119
|
+
console.log('Server closed');
|
|
120
|
+
process.exit(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
startHonoServer().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-typed-api",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "A lightweight, type-safe RPC library for TypeScript with Zod validation",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"@types/multer": "^1.4.13",
|
|
49
49
|
"@types/node": "^24.0.3",
|
|
50
50
|
"express": "^5.1.0",
|
|
51
|
+
"hono": "^4.10.0",
|
|
51
52
|
"multer": "^2.0.1",
|
|
52
53
|
"zod": "^4.0.5"
|
|
53
54
|
},
|
|
@@ -77,4 +78,4 @@
|
|
|
77
78
|
"url": "https://github.com/PeterOsinski/ts-typed-api/issues"
|
|
78
79
|
},
|
|
79
80
|
"homepage": "https://github.com/PeterOsinski/ts-typed-api#readme"
|
|
80
|
-
}
|
|
81
|
+
}
|