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 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
  }
@@ -30,6 +30,9 @@ export const PublicApiDefinition = CreateApiDefinition({
30
30
  ping: {
31
31
  method: 'GET',
32
32
  path: '/ping',
33
+ query: z.object({
34
+ format: z.enum(["json", "html"]).optional()
35
+ }),
33
36
  responses: CreateResponses({
34
37
  200: z.enum(["pong"]),
35
38
  })
@@ -1,6 +1,6 @@
1
1
  import express from 'express';
2
2
  import { PrivateApiDefinition, PublicApiDefinition } from './definitions';
3
- import { RegisterHandlers, EndpointMiddleware, createTypedHandler } from '../../src';
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
- res.respond(200, "pong");
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: createTypedHandler(async (req, res) => {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-typed-api",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
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",
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
- res.respond(200, "pong");
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');
@@ -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 }