ts-typed-api 0.2.13 → 0.2.14
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.d.ts +2 -1
- package/dist/handler.js +3 -2
- package/dist/hono-cloudflare-workers.js +14 -0
- package/dist/object-handlers.d.ts +11 -4
- package/package.json +1 -1
- package/src/handler.ts +5 -3
- package/src/hono-cloudflare-workers.ts +14 -0
- package/src/object-handlers.ts +13 -4
- package/tests/setup.ts +33 -61
package/dist/handler.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ApiDefinitionSchema } from "./definition";
|
|
2
2
|
import { createRouteHandler } from "./router";
|
|
3
|
+
import { MiddlewareResponse } from "./object-handlers";
|
|
3
4
|
import express from "express";
|
|
4
5
|
export type SpecificRouteHandler<TDef extends ApiDefinitionSchema> = {
|
|
5
6
|
[TDomain_ in keyof TDef['endpoints']]: {
|
|
6
7
|
[TRouteKey_ in keyof TDef['endpoints'][TDomain_]]: ReturnType<typeof createRouteHandler<TDef, TDomain_, TRouteKey_>>;
|
|
7
8
|
}[keyof TDef['endpoints'][TDomain_]];
|
|
8
9
|
}[keyof TDef['endpoints']];
|
|
9
|
-
export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res:
|
|
10
|
+
export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: {
|
|
10
11
|
[TDomain in keyof TDef['endpoints']]: {
|
|
11
12
|
domain: TDomain;
|
|
12
13
|
routeKey: keyof TDef['endpoints'][TDomain];
|
package/dist/handler.js
CHANGED
|
@@ -412,10 +412,11 @@ middlewares) {
|
|
|
412
412
|
const wrappedMiddleware = async (req, res, next) => {
|
|
413
413
|
try {
|
|
414
414
|
// Add respond method to res for middleware compatibility
|
|
415
|
-
|
|
415
|
+
const middlewareRes = res;
|
|
416
|
+
middlewareRes.respond = createRespondFunction(routeDefinition, (status, data) => {
|
|
416
417
|
res.status(status).json(data);
|
|
417
418
|
});
|
|
418
|
-
await middleware(req,
|
|
419
|
+
await middleware(req, middlewareRes, next, { domain: currentDomain, routeKey: currentRouteKey });
|
|
419
420
|
}
|
|
420
421
|
catch (error) {
|
|
421
422
|
next(error);
|
|
@@ -457,6 +457,20 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
|
|
|
457
457
|
error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
|
|
458
458
|
}, 500);
|
|
459
459
|
}
|
|
460
|
+
},
|
|
461
|
+
status: (status) => {
|
|
462
|
+
// For middleware that might use status, but since not used, stub
|
|
463
|
+
c.__response = new Response(null, { status: status });
|
|
464
|
+
},
|
|
465
|
+
json: (data) => {
|
|
466
|
+
// Send json response
|
|
467
|
+
c.__response = c.json(data);
|
|
468
|
+
},
|
|
469
|
+
setHeader: (name, value) => {
|
|
470
|
+
c.header(name, value);
|
|
471
|
+
},
|
|
472
|
+
end: () => {
|
|
473
|
+
// Perhaps do nothing or set response
|
|
460
474
|
}
|
|
461
475
|
};
|
|
462
476
|
// Call Express-style middleware
|
|
@@ -7,15 +7,22 @@ export type EndpointInfo<TDef extends ApiDefinitionSchema = ApiDefinitionSchema>
|
|
|
7
7
|
routeKey: keyof TDef['endpoints'][TDomain];
|
|
8
8
|
};
|
|
9
9
|
}[keyof TDef['endpoints']];
|
|
10
|
-
export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res:
|
|
11
|
-
export type SimpleMiddleware = (req: express.Request, res:
|
|
12
|
-
export type UniversalEndpointMiddleware = (req: express.Request, res:
|
|
10
|
+
export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>;
|
|
11
|
+
export type SimpleMiddleware = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction) => void | Promise<void>;
|
|
12
|
+
export type UniversalEndpointMiddleware = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: {
|
|
13
13
|
domain: string;
|
|
14
14
|
routeKey: string;
|
|
15
15
|
}) => void | Promise<void>;
|
|
16
|
+
export interface MiddlewareResponse {
|
|
17
|
+
respond(status: number, data: any): void;
|
|
18
|
+
status(code: number): this;
|
|
19
|
+
json(data: any): void;
|
|
20
|
+
setHeader(name: string, value: string): void;
|
|
21
|
+
end(): void;
|
|
22
|
+
}
|
|
16
23
|
export type EndpointMiddlewareCtx<Ctx extends Record<string, any> = Record<string, any>, TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = ((req: express.Request & {
|
|
17
24
|
ctx?: Ctx;
|
|
18
|
-
}, res:
|
|
25
|
+
}, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) | ((c: any, next: any) => void | Promise<void>);
|
|
19
26
|
export type AnyMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = EndpointMiddleware<TDef> | UniversalEndpointMiddleware | SimpleMiddleware;
|
|
20
27
|
type HandlerFunction<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain], Ctx extends Record<string, any> = Record<string, any>> = (req: TypedRequest<TDef, TDomain, TRouteKey, ApiParams<TDef, TDomain, TRouteKey>, ApiBody<TDef, TDomain, TRouteKey>, ApiQuery<TDef, TDomain, TRouteKey>, Record<string, any>, Ctx>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void;
|
|
21
28
|
export type ObjectHandlers<TDef extends ApiDefinitionSchema, Ctx extends Record<string, any> = Record<string, any>> = {
|
package/package.json
CHANGED
package/src/handler.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ApiDefinitionSchema, RouteSchema, UnifiedError, FileUploadConfig } from "./definition";
|
|
3
3
|
import { createRouteHandler, TypedRequest, TypedResponse } from "./router";
|
|
4
|
+
import { MiddlewareResponse } from "./object-handlers";
|
|
4
5
|
import express from "express";
|
|
5
6
|
import multer from "multer";
|
|
6
7
|
|
|
@@ -16,7 +17,7 @@ export type SpecificRouteHandler<TDef extends ApiDefinitionSchema> = {
|
|
|
16
17
|
// Type for middleware function that receives endpoint information with type safety
|
|
17
18
|
export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (
|
|
18
19
|
req: express.Request,
|
|
19
|
-
res:
|
|
20
|
+
res: MiddlewareResponse,
|
|
20
21
|
next: express.NextFunction,
|
|
21
22
|
endpointInfo: {
|
|
22
23
|
[TDomain in keyof TDef['endpoints']]: {
|
|
@@ -474,10 +475,11 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
|
|
|
474
475
|
const wrappedMiddleware: express.RequestHandler = async (req, res, next) => {
|
|
475
476
|
try {
|
|
476
477
|
// Add respond method to res for middleware compatibility
|
|
477
|
-
|
|
478
|
+
const middlewareRes = res as any;
|
|
479
|
+
middlewareRes.respond = createRespondFunction(routeDefinition, (status, data) => {
|
|
478
480
|
res.status(status).json(data);
|
|
479
481
|
});
|
|
480
|
-
await middleware(req,
|
|
482
|
+
await middleware(req, middlewareRes as MiddlewareResponse, next, { domain: currentDomain, routeKey: currentRouteKey } as any);
|
|
481
483
|
} catch (error) {
|
|
482
484
|
next(error);
|
|
483
485
|
}
|
|
@@ -546,6 +546,20 @@ export function registerHonoRouteHandlers<
|
|
|
546
546
|
error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
|
|
547
547
|
}, 500);
|
|
548
548
|
}
|
|
549
|
+
},
|
|
550
|
+
status: (status: number) => {
|
|
551
|
+
// For middleware that might use status, but since not used, stub
|
|
552
|
+
(c as any).__response = new Response(null, { status: status });
|
|
553
|
+
},
|
|
554
|
+
json: (data: any) => {
|
|
555
|
+
// Send json response
|
|
556
|
+
(c as any).__response = c.json(data);
|
|
557
|
+
},
|
|
558
|
+
setHeader: (name: string, value: string) => {
|
|
559
|
+
c.header(name, value);
|
|
560
|
+
},
|
|
561
|
+
end: () => {
|
|
562
|
+
// Perhaps do nothing or set response
|
|
549
563
|
}
|
|
550
564
|
};
|
|
551
565
|
|
package/src/object-handlers.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type EndpointInfo<TDef extends ApiDefinitionSchema = ApiDefinitionSchema>
|
|
|
13
13
|
// Type for middleware function that receives endpoint information
|
|
14
14
|
export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (
|
|
15
15
|
req: express.Request,
|
|
16
|
-
res:
|
|
16
|
+
res: MiddlewareResponse,
|
|
17
17
|
next: express.NextFunction,
|
|
18
18
|
endpointInfo: EndpointInfo<TDef>
|
|
19
19
|
) => void | Promise<void>;
|
|
@@ -21,14 +21,14 @@ export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionS
|
|
|
21
21
|
// Type for simple middleware that doesn't need endpoint information
|
|
22
22
|
export type SimpleMiddleware = (
|
|
23
23
|
req: express.Request,
|
|
24
|
-
res:
|
|
24
|
+
res: MiddlewareResponse,
|
|
25
25
|
next: express.NextFunction
|
|
26
26
|
) => void | Promise<void>;
|
|
27
27
|
|
|
28
28
|
// Type for middleware that can work with any API definition
|
|
29
29
|
export type UniversalEndpointMiddleware = (
|
|
30
30
|
req: express.Request,
|
|
31
|
-
res:
|
|
31
|
+
res: MiddlewareResponse,
|
|
32
32
|
next: express.NextFunction,
|
|
33
33
|
endpointInfo: {
|
|
34
34
|
domain: string;
|
|
@@ -36,13 +36,22 @@ export type UniversalEndpointMiddleware = (
|
|
|
36
36
|
}
|
|
37
37
|
) => void | Promise<void>;
|
|
38
38
|
|
|
39
|
+
// Custom response interface for framework-agnostic middleware
|
|
40
|
+
export interface MiddlewareResponse {
|
|
41
|
+
respond(status: number, data: any): void;
|
|
42
|
+
status(code: number): this;
|
|
43
|
+
json(data: any): void;
|
|
44
|
+
setHeader(name: string, value: string): void;
|
|
45
|
+
end(): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
// Unified middleware type that works for both Express and Hono with context typing
|
|
40
49
|
export type EndpointMiddlewareCtx<
|
|
41
50
|
Ctx extends Record<string, any> = Record<string, any>,
|
|
42
51
|
TDef extends ApiDefinitionSchema = ApiDefinitionSchema
|
|
43
52
|
> =
|
|
44
53
|
// Express version: (req, res, next, endpointInfo)
|
|
45
|
-
((req: express.Request & { ctx?: Ctx }, res:
|
|
54
|
+
((req: express.Request & { ctx?: Ctx }, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) |
|
|
46
55
|
// Hono version: (c, next) - context is passed via c.req.ctx -> c.ctx copying
|
|
47
56
|
((c: any, next: any) => void | Promise<void>);
|
|
48
57
|
|
package/tests/setup.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { PublicApiDefinition as SimplePublicApiDefinition, PrivateApiDefinition
|
|
|
7
7
|
import { PublicApiDefinition as AdvancedPublicApiDefinition, PrivateApiDefinition as AdvancedPrivateApiDefinition } from '../examples/advanced/definitions';
|
|
8
8
|
import { RegisterHandlers, RegisterHonoHandlers, CreateApiDefinition, CreateResponses, CreateTypedHonoHandlerWithContext } from '../src';
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
+
import { EndpointMiddlewareCtx } from '../src/object-handlers';
|
|
10
11
|
|
|
11
12
|
// Shared handler definitions for simple API
|
|
12
13
|
const simplePublicHandlers = {
|
|
@@ -444,7 +445,7 @@ async function startHonoServer(): Promise<void> {
|
|
|
444
445
|
});
|
|
445
446
|
}
|
|
446
447
|
|
|
447
|
-
type Ctx = { user?: string; noAuth?: boolean; forbidden?: boolean }
|
|
448
|
+
type Ctx = { user?: string; noAuth?: boolean; forbidden?: boolean, middlewareData?: string }
|
|
448
449
|
|
|
449
450
|
// Shared handlers for middleware tests
|
|
450
451
|
const middlewareTestHandlers = {
|
|
@@ -471,74 +472,45 @@ const middlewareTestHandlers = {
|
|
|
471
472
|
// Generic middleware setup function
|
|
472
473
|
function setupMiddlewareApp(app: any, isHono: boolean) {
|
|
473
474
|
// Define middleware functions
|
|
474
|
-
const loggingMiddleware =
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
const authMiddleware = isHono ?
|
|
495
|
-
async (req: any, res: any, next: any, endpointInfo: any) => {
|
|
496
|
-
// Only apply auth checks to protected routes
|
|
497
|
-
if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
|
|
498
|
-
const authHeader = req.headers?.authorization;
|
|
499
|
-
if (!authHeader) {
|
|
500
|
-
(res as any).respond(401, { error: "No authorization header" });
|
|
501
|
-
} else if (authHeader === 'Bearer valid-token') {
|
|
502
|
-
req.ctx = { ...req.ctx, user: 'testuser' };
|
|
503
|
-
await next();
|
|
504
|
-
} else {
|
|
505
|
-
(res as any).respond(403, { error: "Forbidden" });
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
475
|
+
const loggingMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next, endpointInfo) => {
|
|
476
|
+
console.log(`[Test] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
|
|
477
|
+
await next();
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const contextMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next) => {
|
|
481
|
+
req.ctx = { ...req.ctx, middlewareData: "middleware-added-data" };
|
|
482
|
+
await next();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const authMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next, endpointInfo) => {
|
|
486
|
+
// Only apply auth checks to protected routes
|
|
487
|
+
if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
|
|
488
|
+
const authHeader = req.headers?.authorization;
|
|
489
|
+
if (!authHeader) {
|
|
490
|
+
res.respond(401, { error: "No authorization header" });
|
|
491
|
+
} else if (authHeader === 'Bearer valid-token') {
|
|
492
|
+
req.ctx = { ...req.ctx, user: 'testuser' };
|
|
508
493
|
await next();
|
|
509
|
-
}
|
|
510
|
-
} :
|
|
511
|
-
(req: any, res: any, next: any, endpointInfo: any) => {
|
|
512
|
-
// Only apply auth checks to protected routes
|
|
513
|
-
if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
|
|
514
|
-
const authHeader = req.headers?.authorization;
|
|
515
|
-
if (!authHeader) {
|
|
516
|
-
(res as any).respond(401, { error: "No authorization header" });
|
|
517
|
-
} else if (authHeader === 'Bearer valid-token') {
|
|
518
|
-
req.ctx = { ...req.ctx, user: 'testuser' };
|
|
519
|
-
next();
|
|
520
|
-
} else {
|
|
521
|
-
(res as any).respond(403, { error: "Forbidden" });
|
|
522
|
-
}
|
|
523
494
|
} else {
|
|
524
|
-
|
|
495
|
+
res.respond(403, { error: "Forbidden" });
|
|
525
496
|
}
|
|
526
|
-
}
|
|
497
|
+
} else {
|
|
498
|
+
await next();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const middlewares = [
|
|
503
|
+
loggingMiddleware,
|
|
504
|
+
contextMiddleware,
|
|
505
|
+
authMiddleware
|
|
506
|
+
]
|
|
527
507
|
|
|
528
508
|
// Register handlers with middleware
|
|
529
509
|
if (isHono) {
|
|
530
510
|
const hndl = CreateTypedHonoHandlerWithContext<Ctx>();
|
|
531
|
-
hndl(app, MiddlewareTestApiDefinition, middlewareTestHandlers,
|
|
532
|
-
loggingMiddleware,
|
|
533
|
-
contextMiddleware,
|
|
534
|
-
authMiddleware
|
|
535
|
-
]);
|
|
511
|
+
hndl(app, MiddlewareTestApiDefinition, middlewareTestHandlers, middlewares);
|
|
536
512
|
} else {
|
|
537
|
-
RegisterHandlers(app, MiddlewareTestApiDefinition, middlewareTestHandlers,
|
|
538
|
-
loggingMiddleware,
|
|
539
|
-
contextMiddleware,
|
|
540
|
-
authMiddleware
|
|
541
|
-
]);
|
|
513
|
+
RegisterHandlers(app, MiddlewareTestApiDefinition, middlewareTestHandlers, middlewares);
|
|
542
514
|
}
|
|
543
515
|
}
|
|
544
516
|
|