ts-typed-api 0.2.16 → 0.2.17
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/handler.js +6 -0
- package/dist/hono-cloudflare-workers.js +7 -0
- package/dist/router.d.ts +1 -0
- package/examples/simple/definitions.ts +3 -0
- package/examples/simple/server.ts +13 -4
- package/package.json +1 -1
- package/src/handler.ts +7 -0
- package/src/hono-cloudflare-workers.ts +7 -0
- package/src/router.ts +1 -0
- package/tests/setup.ts +5 -1
- package/tests/simple-api.test.ts +13 -0
package/dist/handler.js
CHANGED
|
@@ -347,6 +347,12 @@ middlewares) {
|
|
|
347
347
|
});
|
|
348
348
|
}
|
|
349
349
|
};
|
|
350
|
+
typedExpressRes.respondContentType = (status, data, contentType) => {
|
|
351
|
+
// Set the content type header
|
|
352
|
+
typedExpressRes.setHeader('Content-Type', contentType);
|
|
353
|
+
// Send the raw data without JSON wrapping or validation
|
|
354
|
+
typedExpressRes.status(status).send(data);
|
|
355
|
+
};
|
|
350
356
|
typedExpressRes.setHeader = (name, value) => {
|
|
351
357
|
// Call the original Express setHeader method to avoid recursion
|
|
352
358
|
Object.getPrototypeOf(expressRes).setHeader.call(expressRes, name, value);
|
|
@@ -330,6 +330,13 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
|
|
|
330
330
|
};
|
|
331
331
|
const fakeRes = {
|
|
332
332
|
respond: c.respond,
|
|
333
|
+
respondContentType: (status, data, contentType) => {
|
|
334
|
+
// For Hono, set the response directly
|
|
335
|
+
c.__response = new Response(data, {
|
|
336
|
+
status: status,
|
|
337
|
+
headers: { 'Content-Type': contentType }
|
|
338
|
+
});
|
|
339
|
+
},
|
|
333
340
|
setHeader: (name, value) => {
|
|
334
341
|
c.header(name, value);
|
|
335
342
|
return fakeRes;
|
package/dist/router.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ type ResponseDataForStatus<TDef extends ApiDefinitionSchema, TDomain extends key
|
|
|
14
14
|
type RespondFunction<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteName extends keyof TDef['endpoints'][TDomain]> = <TStatusLocal extends keyof TDef['endpoints'][TDomain][TRouteName]['responses'] & number>(status: TStatusLocal, data: ResponseDataForStatus<TDef, TDomain, TRouteName, TStatusLocal>) => void;
|
|
15
15
|
export interface TypedResponse<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteName extends keyof TDef['endpoints'][TDomain], L extends Record<string, any> = Record<string, any>> extends express.Response<any, L> {
|
|
16
16
|
respond: RespondFunction<TDef, TDomain, TRouteName>;
|
|
17
|
+
respondContentType: (status: number, data: any, contentType: string) => void;
|
|
17
18
|
setHeader: (name: string, value: string) => this;
|
|
18
19
|
json: <B = any>(body: B) => this;
|
|
19
20
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { PrivateApiDefinition, PublicApiDefinition } from './definitions';
|
|
3
|
-
import { RegisterHandlers, EndpointMiddleware
|
|
3
|
+
import { RegisterHandlers, EndpointMiddleware } from '../../src';
|
|
4
4
|
const app = express();
|
|
5
5
|
const port = 3001;
|
|
6
6
|
app.set('etag', false);
|
|
@@ -39,7 +39,16 @@ RegisterHandlers(app, PublicApiDefinition, {
|
|
|
39
39
|
// TypeScript will give you type errors if this handler is missing
|
|
40
40
|
ping: async (req, res) => {
|
|
41
41
|
// req and res are fully typed based on the API definition
|
|
42
|
-
|
|
42
|
+
if (req.query.format === 'html') {
|
|
43
|
+
res.respondContentType(200, "<h1>pong</h1>", "text/html");
|
|
44
|
+
} else {
|
|
45
|
+
res.respond(200, "pong");
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
customHeaders: async (req, res) => {
|
|
49
|
+
res.setHeader('x-custom-test', 'test-value');
|
|
50
|
+
res.setHeader('x-another-header', 'another-value');
|
|
51
|
+
res.respond(200, { message: "headers set" });
|
|
43
52
|
}
|
|
44
53
|
},
|
|
45
54
|
status: {
|
|
@@ -59,10 +68,10 @@ RegisterHandlers(app, PublicApiDefinition, {
|
|
|
59
68
|
// Add another api definition
|
|
60
69
|
RegisterHandlers(app, PrivateApiDefinition, {
|
|
61
70
|
user: {
|
|
62
|
-
get:
|
|
71
|
+
get: async (req, res) => {
|
|
63
72
|
console.log('Fetching user', req.params.id);
|
|
64
73
|
res.respond(200, "ok");
|
|
65
|
-
}
|
|
74
|
+
}
|
|
66
75
|
}
|
|
67
76
|
}, [loggingMiddlewareTyped, authMiddleware]);
|
|
68
77
|
|
package/package.json
CHANGED
package/src/handler.ts
CHANGED
|
@@ -408,6 +408,13 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
|
|
|
408
408
|
}
|
|
409
409
|
};
|
|
410
410
|
|
|
411
|
+
typedExpressRes.respondContentType = (status: number, data: any, contentType: string) => {
|
|
412
|
+
// Set the content type header
|
|
413
|
+
typedExpressRes.setHeader('Content-Type', contentType);
|
|
414
|
+
// Send the raw data without JSON wrapping or validation
|
|
415
|
+
typedExpressRes.status(status).send(data);
|
|
416
|
+
};
|
|
417
|
+
|
|
411
418
|
typedExpressRes.setHeader = (name: string, value: string) => {
|
|
412
419
|
// Call the original Express setHeader method to avoid recursion
|
|
413
420
|
Object.getPrototypeOf(expressRes).setHeader.call(expressRes, name, value);
|
|
@@ -408,6 +408,13 @@ export function registerHonoRouteHandlers<
|
|
|
408
408
|
|
|
409
409
|
const fakeRes = {
|
|
410
410
|
respond: (c as any).respond,
|
|
411
|
+
respondContentType: (status: number, data: any, contentType: string) => {
|
|
412
|
+
// For Hono, set the response directly
|
|
413
|
+
(c as any).__response = new Response(data, {
|
|
414
|
+
status: status,
|
|
415
|
+
headers: { 'Content-Type': contentType }
|
|
416
|
+
});
|
|
417
|
+
},
|
|
411
418
|
setHeader: (name: string, value: string) => {
|
|
412
419
|
c.header(name, value);
|
|
413
420
|
return fakeRes;
|
package/src/router.ts
CHANGED
|
@@ -59,6 +59,7 @@ export interface TypedResponse<
|
|
|
59
59
|
L extends Record<string, any> = Record<string, any>
|
|
60
60
|
> extends express.Response<any, L> {
|
|
61
61
|
respond: RespondFunction<TDef, TDomain, TRouteName>;
|
|
62
|
+
respondContentType: (status: number, data: any, contentType: string) => void;
|
|
62
63
|
setHeader: (name: string, value: string) => this;
|
|
63
64
|
json: <B = any>(body: B) => this; // Keep original json
|
|
64
65
|
}
|
package/tests/setup.ts
CHANGED
|
@@ -13,7 +13,11 @@ import { EndpointMiddlewareCtx } from '../src/object-handlers';
|
|
|
13
13
|
const simplePublicHandlers = {
|
|
14
14
|
common: {
|
|
15
15
|
ping: async (req: any, res: any) => {
|
|
16
|
-
|
|
16
|
+
if (req.query?.format === 'html') {
|
|
17
|
+
res.respondContentType(200, "<h1>pong</h1>", "text/html");
|
|
18
|
+
} else {
|
|
19
|
+
res.respond(200, "pong");
|
|
20
|
+
}
|
|
17
21
|
},
|
|
18
22
|
customHeaders: async (req: any, res: any) => {
|
|
19
23
|
res.setHeader('X-Custom-Test', 'test-value');
|
package/tests/simple-api.test.ts
CHANGED
|
@@ -27,6 +27,19 @@ describe.each([
|
|
|
27
27
|
expect(result).toBe('pong');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
test('should ping successfully with HTML format', async () => {
|
|
31
|
+
const response = await fetch(`${baseUrl}/api/v1/public/ping?format=html`);
|
|
32
|
+
expect(response.status).toBe(200);
|
|
33
|
+
const contentType = response.headers.get('content-type');
|
|
34
|
+
if (serverName === 'Express') {
|
|
35
|
+
expect(contentType).toBe('text/html; charset=utf-8');
|
|
36
|
+
} else {
|
|
37
|
+
expect(contentType).toBe('text/html');
|
|
38
|
+
}
|
|
39
|
+
const html = await response.text();
|
|
40
|
+
expect(html).toBe('<h1>pong</h1>');
|
|
41
|
+
});
|
|
42
|
+
|
|
30
43
|
test('should handle probe1 with match=true', async () => {
|
|
31
44
|
const result = await client.callApi('status', 'probe1', {
|
|
32
45
|
query: { match: true }
|