@shadow-library/fastify 0.0.6 → 0.0.7

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.
Files changed (46) hide show
  1. package/README.md +451 -14
  2. package/cjs/classes/default-error-handler.js +5 -4
  3. package/cjs/decorators/http-input.decorator.d.ts +2 -1
  4. package/cjs/decorators/http-input.decorator.js +4 -5
  5. package/cjs/decorators/index.d.ts +1 -0
  6. package/cjs/decorators/index.js +1 -0
  7. package/cjs/decorators/middleware.decorator.js +2 -2
  8. package/cjs/decorators/sensitive.decorator.d.ts +4 -0
  9. package/cjs/decorators/sensitive.decorator.js +15 -0
  10. package/cjs/interfaces/middleware.interface.d.ts +9 -6
  11. package/cjs/interfaces/route-handler.interface.d.ts +6 -2
  12. package/cjs/interfaces/server-metadata.interface.d.ts +5 -4
  13. package/cjs/module/error-response.dto.d.ts +1 -1
  14. package/cjs/module/error-response.dto.js +1 -1
  15. package/cjs/module/fastify-module.interface.d.ts +6 -1
  16. package/cjs/module/fastify-router.d.ts +25 -2
  17. package/cjs/module/fastify-router.js +89 -20
  18. package/cjs/module/fastify.module.js +23 -6
  19. package/cjs/module/fastify.utils.js +20 -7
  20. package/cjs/server.error.d.ts +2 -0
  21. package/cjs/server.error.js +9 -7
  22. package/cjs/services/context.service.d.ts +13 -5
  23. package/cjs/services/context.service.js +45 -6
  24. package/esm/classes/default-error-handler.js +6 -5
  25. package/esm/decorators/http-input.decorator.d.ts +2 -1
  26. package/esm/decorators/http-input.decorator.js +2 -3
  27. package/esm/decorators/index.d.ts +1 -0
  28. package/esm/decorators/index.js +1 -0
  29. package/esm/decorators/middleware.decorator.js +1 -1
  30. package/esm/decorators/sensitive.decorator.d.ts +4 -0
  31. package/esm/decorators/sensitive.decorator.js +12 -0
  32. package/esm/interfaces/middleware.interface.d.ts +9 -6
  33. package/esm/interfaces/route-handler.interface.d.ts +6 -2
  34. package/esm/interfaces/server-metadata.interface.d.ts +5 -4
  35. package/esm/module/error-response.dto.d.ts +1 -1
  36. package/esm/module/error-response.dto.js +1 -1
  37. package/esm/module/fastify-module.interface.d.ts +6 -1
  38. package/esm/module/fastify-router.d.ts +25 -2
  39. package/esm/module/fastify-router.js +87 -18
  40. package/esm/module/fastify.module.js +24 -7
  41. package/esm/module/fastify.utils.js +19 -6
  42. package/esm/server.error.d.ts +2 -0
  43. package/esm/server.error.js +9 -7
  44. package/esm/services/context.service.d.ts +13 -5
  45. package/esm/services/context.service.js +44 -5
  46. package/package.json +6 -4
package/README.md CHANGED
@@ -1,24 +1,461 @@
1
- # Shadow Fastify Server
1
+ # @shadow-library/fastify
2
2
 
3
- This TypeScript package is a powerful Node.js server implementation built as a wrapper around the Fastify package. It offers features such as body, query,
4
- and URL parameter validation using AJV for speed, response formatting with fast-json-stringify, and a unified approach to authentication and authorization.
5
- The package uses decorators to define controller classes and HTTP methods, and includes a render decorator for templating engine support.
3
+ A powerful TypeScript-first Fastify wrapper featuring decorator-based routing, automatic validation, response serialization, and comprehensive middleware support.
6
4
 
7
5
  ## Features
8
6
 
9
- **Fastify Wrapper:** Built on top of the high-performance Fastify framework.
10
- **Validation:** Fast validation for body, query, and URL parameters using AJV.
11
- **Response Serialization:** Consistent serialization of response types with fast-json-stringify to ensure only expected fields are returned.
12
- **Decorator-Based:** Use decorators to define controllers and HTTP methods.
13
- **Authentication and Authorization:** Unified methods to implement authentication and authorization.
14
- **Templating Support:** Render decorator for integrating templating engines.
7
+ - 🚀 **High Performance**: Built on top of Fastify, one of the fastest Node.js web frameworks
8
+ - 🎯 **Decorator-Based**: Clean, intuitive API using TypeScript decorators
9
+ - **Automatic Validation**: Fast validation for body, query, and URL parameters using AJV
10
+ - 📝 **Response Serialization**: Consistent response formatting with fast-json-stringify
11
+ - 🔒 **Authentication & Authorization**: Built-in support for guards and middleware
12
+ - 🛡️ **Error Handling**: Comprehensive error handling with custom error types
13
+ - 🔄 **Middleware Support**: Flexible middleware system with lifecycle hooks
14
+ - 📊 **Type Safety**: Full TypeScript support with schema generation
15
+ - 🎨 **Templating Ready**: Built-in support for templating engines
15
16
 
16
- ## Usage
17
+ ## Installation
17
18
 
18
- ```ts
19
- import {} from '@shadow-library/server';
19
+ ```bash
20
+ # npm
21
+ npm install @shadow-library/fastify
22
+
23
+ # yarn
24
+ yarn add @shadow-library/fastify
25
+
26
+ # pnpm
27
+ pnpm add @shadow-library/fastify
28
+
29
+ # bun
30
+ bun add @shadow-library/fastify
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ### 1. Create a Controller
36
+
37
+ ```typescript
38
+ import { HttpController, Get, Post, Body, RespondFor } from '@shadow-library/fastify';
39
+ import { Schema, Field } from '@shadow-library/class-schema';
40
+
41
+ @Schema()
42
+ class CreateUserDto {
43
+ @Field(() => String, { minLength: 2, maxLength: 50 })
44
+ name: string;
45
+
46
+ @Field(() => String, { format: 'email' })
47
+ email: string;
48
+ }
49
+
50
+ @Schema()
51
+ class UserResponse {
52
+ @Field(() => Number)
53
+ id: number;
54
+
55
+ @Field(() => String)
56
+ name: string;
57
+
58
+ @Field(() => String)
59
+ email: string;
60
+ }
61
+
62
+ @HttpController('/api/users')
63
+ export class UserController {
64
+ @Get()
65
+ @RespondFor(200, [UserResponse])
66
+ async getUsers(): Promise<UserResponse[]> {
67
+ return [{ id: 1, name: 'John Doe', email: 'john@example.com' }];
68
+ }
69
+
70
+ @Post()
71
+ @RespondFor(201, UserResponse)
72
+ async createUser(@Body() userData: CreateUserDto): Promise<UserResponse> {
73
+ // Your business logic here
74
+ return { id: 2, ...userData };
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### 2. Create a Module
80
+
81
+ ```typescript
82
+ import { FastifyModule } from '@shadow-library/fastify';
83
+ import { UserController } from './user.controller';
84
+
85
+ export const AppModule = FastifyModule.forRoot({
86
+ controllers: [UserController],
87
+ port: 3000,
88
+ host: '0.0.0.0',
89
+ });
90
+ ```
91
+
92
+ ### 3. Bootstrap Your Application
93
+
94
+ ```typescript
95
+ import { ShadowFactory } from '@shadow-library/app';
96
+ import { Logger } from '@shadow-library/common';
97
+ import { AppModule } from './app.module';
98
+
99
+ Logger.addDefaultTransports();
100
+
101
+ async function bootstrap() {
102
+ const app = await ShadowFactory.create(AppModule);
103
+ await app.start();
104
+ }
105
+
106
+ bootstrap();
107
+ ```
108
+
109
+ ## API Reference
110
+
111
+ ### Decorators
112
+
113
+ #### Route Decorators
114
+
115
+ ```typescript
116
+ @Get(path?: string) // GET requests
117
+ @Post(path?: string) // POST requests
118
+ @Put(path?: string) // PUT requests
119
+ @Patch(path?: string) // PATCH requests
120
+ @Delete(path?: string) // DELETE requests
121
+ @Options(path?: string) // OPTIONS requests
122
+ @Head(path?: string) // HEAD requests
123
+ ```
124
+
125
+ #### Parameter Decorators
126
+
127
+ ```typescript
128
+ @Body(schema?: JSONSchema) // Request body
129
+ @Params(schema?: JSONSchema) // URL parameters
130
+ @Query(schema?: JSONSchema) // Query parameters
131
+ @Request() / @Req() // Raw Fastify request
132
+ @Response() / @Res() // Raw Fastify response
133
+ ```
134
+
135
+ #### Response Decorators
136
+
137
+ ```typescript
138
+ @RespondFor(statusCode: number, schema: Class | JSONSchema)
139
+ @HttpStatus(statusCode: number)
140
+ ```
141
+
142
+ #### Controller Decorator
143
+
144
+ ```typescript
145
+ @HttpController(prefix?: string)
20
146
  ```
21
147
 
148
+ ### Example: Advanced Usage with Authentication
149
+
150
+ ```typescript
151
+ import { HttpController, Get, Post, Middleware, Body, Params } from '@shadow-library/fastify';
152
+
153
+ // Custom Authentication Guard
154
+ @Middleware({ type: 'preHandler', weight: 100 })
155
+ class AuthGuard {
156
+ use(request: HttpRequest, reply: HttpResponse, done: HttpCallback) {
157
+ const token = request.headers.authorization?.replace('Bearer ', '');
158
+ if (!token) {
159
+ return reply.status(401).send({ error: 'Unauthorized' });
160
+ }
161
+ // Validate token logic here
162
+ done();
163
+ }
164
+ }
165
+
166
+ @HttpController('/api/protected')
167
+ export class ProtectedController {
168
+ @Get('/profile')
169
+ @RespondFor(200, UserResponse)
170
+ async getProfile(@Request() req): Promise<UserResponse> {
171
+ // Access authenticated user from request
172
+ return req.user;
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### Error Handling
178
+
179
+ ```typescript
180
+ import { ServerError, ServerErrorCode } from '@shadow-library/fastify';
181
+
182
+ @HttpController('/api')
183
+ export class ExampleController {
184
+ @Get('/error')
185
+ throwError() {
186
+ // Throws a predefined server error
187
+ throw new ServerError(ServerErrorCode.S008);
188
+ }
189
+
190
+ @Get('/custom-error')
191
+ throwCustomError() {
192
+ // Throws a custom error
193
+ throw new Error('Something went wrong');
194
+ }
195
+ }
196
+ ```
197
+
198
+ ### Child Routes and Route Resolution
199
+
200
+ ```typescript
201
+ @HttpController('/api')
202
+ export class RoutesController {
203
+ constructor(@Inject(Router) private readonly fastifyRouter: FastifyRouter) {}
204
+
205
+ @Get('/unified')
206
+ async unifiedRoute(@Query() query: Record<string, any>) {
207
+ const results = [];
208
+ for (const route of query.routes?.split(',') ?? []) {
209
+ const result = await this.fastifyRouter.resolveChildRoute(route);
210
+ results.push(result);
211
+ }
212
+ return { results };
213
+ }
214
+ }
215
+ ```
216
+
217
+ ## Configuration
218
+
219
+ ### Module Configuration
220
+
221
+ ```typescript
222
+ const AppModule = FastifyModule.forRoot({
223
+ // Basic server configuration
224
+ host: 'localhost',
225
+ port: 8080,
226
+
227
+ // Controllers to register
228
+ controllers: [UserController, AuthController],
229
+
230
+ // Additional providers
231
+ providers: [UserService, AuthService],
232
+
233
+ // Error handling
234
+ errorHandler: new CustomErrorHandler(),
235
+
236
+ // Security
237
+ maskSensitiveData: true,
238
+
239
+ // Request ID generation
240
+ requestIdLogLabel: 'rid',
241
+ genReqId: () => uuid(),
242
+
243
+ // Router options
244
+ routerOptions: {
245
+ ignoreTrailingSlash: true,
246
+ ignoreDuplicateSlashes: true,
247
+ },
248
+
249
+ // Response schemas for error handling
250
+ responseSchema: {
251
+ '4xx': ErrorResponseSchema,
252
+ '5xx': ErrorResponseSchema,
253
+ },
254
+
255
+ // Extend Fastify instance before registering controllers
256
+ fastifyFactory: async fastify => {
257
+ // Register plugins, add hooks, or configure Fastify
258
+ await fastify.register(require('@fastify/cors'), {
259
+ origin: true,
260
+ });
261
+
262
+ fastify.addHook('onRequest', async (request, reply) => {
263
+ console.log(`Incoming request: ${request.method} ${request.url}`);
264
+ });
265
+
266
+ return fastify;
267
+ },
268
+ });
269
+ ```
270
+
271
+ ### Async Configuration
272
+
273
+ ```typescript
274
+ const AppModule = FastifyModule.forRootAsync({
275
+ useFactory: async (configService: ConfigService) => ({
276
+ host: configService.get('HOST'),
277
+ port: configService.get('PORT'),
278
+ controllers: [UserController],
279
+ }),
280
+ inject: [ConfigService],
281
+ });
282
+ ```
283
+
284
+ ### Extending Fastify Instance
285
+
286
+ Use the `fastifyFactory` option to customize the Fastify instance before controllers are registered. This is perfect for adding plugins, global hooks, or custom configurations:
287
+
288
+ ```typescript
289
+ import cors from '@fastify/cors';
290
+ import helmet from '@fastify/helmet';
291
+ import rateLimit from '@fastify/rate-limit';
292
+
293
+ const AppModule = FastifyModule.forRoot({
294
+ controllers: [UserController],
295
+ fastifyFactory: async fastify => {
296
+ // Register security plugins
297
+ await fastify.register(helmet, {
298
+ contentSecurityPolicy: {
299
+ directives: {
300
+ defaultSrc: ["'self'"],
301
+ styleSrc: ["'self'", "'unsafe-inline'"],
302
+ },
303
+ },
304
+ });
305
+
306
+ // Add CORS support
307
+ await fastify.register(cors, {
308
+ origin: (origin, callback) => {
309
+ const allowedOrigins = ['http://localhost:3000', 'https://myapp.com'];
310
+ if (!origin || allowedOrigins.includes(origin)) {
311
+ callback(null, true);
312
+ } else {
313
+ callback(new Error('Not allowed by CORS'), false);
314
+ }
315
+ },
316
+ credentials: true,
317
+ });
318
+
319
+ // Add rate limiting
320
+ await fastify.register(rateLimit, {
321
+ max: 100,
322
+ timeWindow: '1 minute',
323
+ });
324
+
325
+ // Add global hooks
326
+ fastify.addHook('onRequest', async (request, reply) => {
327
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`);
328
+ });
329
+
330
+ fastify.addHook('onResponse', async (request, reply) => {
331
+ const responseTime = reply.elapsedTime;
332
+ console.log(`Response sent in ${responseTime}ms`);
333
+ });
334
+
335
+ // Add custom context or decorators
336
+ fastify.decorate('config', {
337
+ apiVersion: 'v1',
338
+ environment: process.env.NODE_ENV,
339
+ });
340
+
341
+ // Register custom content type parsers
342
+ fastify.addContentTypeParser('text/plain', { parseAs: 'string' }, (req, body, done) => {
343
+ done(null, body);
344
+ });
345
+
346
+ return fastify;
347
+ },
348
+ });
349
+ ```
350
+
351
+ #### Common Use Cases for fastifyFactory:
352
+
353
+ - **Security**: Add helmet, CORS, rate limiting
354
+ - **Logging**: Custom request/response logging hooks
355
+ - **Authentication**: Register authentication plugins
356
+ - **File Upload**: Configure multipart/file upload handling
357
+ - **Custom Parsers**: Add support for custom content types
358
+ - **Swagger/OpenAPI**: Register documentation plugins
359
+ - **Database**: Add database connection decorators
360
+ - **Caching**: Configure caching plugins
361
+
362
+ ## Middleware
363
+
364
+ Create custom middleware by implementing the `Middleware` decorator:
365
+
366
+ ```typescript
367
+ @Middleware({ type: 'preHandler', weight: 50 })
368
+ export class LoggingMiddleware {
369
+ use(request: HttpRequest, reply: HttpResponse, done: HttpCallback) {
370
+ console.log(`${request.method} ${request.url}`);
371
+ done();
372
+ }
373
+ }
374
+
375
+ // Or generate middleware dynamically
376
+ @Middleware({ type: 'preValidation', weight: 75 })
377
+ export class ValidationMiddleware {
378
+ generate(route: RouteMetadata) {
379
+ return (request: HttpRequest, reply: HttpResponse, done: HttpCallback) => {
380
+ // Custom validation logic based on route metadata
381
+ done();
382
+ };
383
+ }
384
+ }
385
+ ```
386
+
387
+ ## Validation
388
+
389
+ The package uses `@shadow-library/class-schema` for automatic validation:
390
+
391
+ ```typescript
392
+ @Schema()
393
+ class CreateProductDto {
394
+ @Field(() => String, {
395
+ minLength: 1,
396
+ maxLength: 100,
397
+ description: 'Product name',
398
+ })
399
+ name: string;
400
+
401
+ @Field(() => Number, {
402
+ minimum: 0,
403
+ description: 'Product price in cents',
404
+ })
405
+ price: number;
406
+
407
+ @Field(() => [String], {
408
+ maxItems: 10,
409
+ description: 'Product tags',
410
+ })
411
+ tags?: string[];
412
+ }
413
+ ```
414
+
415
+ ## Response Serialization
416
+
417
+ Define response schemas for automatic serialization and documentation:
418
+
419
+ ```typescript
420
+ @Schema()
421
+ class ProductResponse {
422
+ @Field(() => Number)
423
+ id: number;
424
+
425
+ @Field(() => String)
426
+ name: string;
427
+
428
+ @Field(() => Number)
429
+ price: number;
430
+
431
+ @Field(() => Date)
432
+ createdAt: Date;
433
+ }
434
+
435
+ @HttpController('/products')
436
+ export class ProductController {
437
+ @Get('/:id')
438
+ @RespondFor(200, ProductResponse)
439
+ @RespondFor(404, ErrorResponse)
440
+ async getProduct(@Params() params: { id: number }): Promise<ProductResponse> {
441
+ // Only the fields defined in ProductResponse will be serialized
442
+ return this.productService.findById(params.id);
443
+ }
444
+ }
445
+ ```
446
+
447
+ ## Examples
448
+
449
+ Check out the [examples](./examples) directory for complete working examples:
450
+
451
+ - **hello-world**: Basic HTTP controller with GET/POST routes
452
+ - **user-auth**: Advanced example with authentication guards
453
+ - **child-routes**: Route resolution and unified endpoints
454
+
455
+ ## Contributing
456
+
457
+ Contributions are welcome! Please read the [Contributing Guide](./CONTRIBUTING.md) for details.
458
+
22
459
  ## License
23
460
 
24
- This package is licensed under the MIT License. See the `LICENSE` file for more information.
461
+ This package is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more information.
@@ -6,28 +6,29 @@ const constants_1 = require("../constants.js");
6
6
  const server_error_1 = require("../server.error.js");
7
7
  const unexpectedError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S001);
8
8
  const validationError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S003);
9
+ const invalidRequestError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S006);
9
10
  class DefaultErrorHandler {
10
11
  logger = common_1.Logger.getLogger(constants_1.NAMESPACE, 'DefaultErrorHandler');
11
12
  parseFastifyError(err) {
12
13
  if (err.statusCode === 500)
13
14
  return { statusCode: 500, error: unexpectedError.toObject() };
14
- return { statusCode: err.statusCode, error: { code: err.code, type: common_1.ErrorType.CLIENT_ERROR, message: err.message } };
15
+ return { statusCode: err.statusCode, error: { ...invalidRequestError.toObject(), message: err.message } };
15
16
  }
16
17
  handle(err, _req, res) {
17
18
  this.logger.warn('Handling error', err);
18
19
  if (err.cause)
19
- this.logger.warn('Caused by:', err.cause);
20
+ this.logger.warn('Caused by', err.cause);
20
21
  if (err instanceof server_error_1.ServerError)
21
22
  return res.status(err.getStatusCode()).send(err.toObject());
22
23
  else if (err instanceof common_1.ValidationError)
23
- return res.status(400).send({ ...err.toObject(), code: validationError.getCode() });
24
+ return res.status(validationError.getStatusCode()).send({ ...err.toObject(), ...validationError.toObject() });
24
25
  else if (err instanceof common_1.AppError)
25
26
  return res.status(500).send(err.toObject());
26
27
  else if (err.name === 'FastifyError') {
27
28
  const { statusCode, error } = this.parseFastifyError(err);
28
29
  return res.status(statusCode).send(error);
29
30
  }
30
- this.logger.error('Unhandler error has occurred:', err);
31
+ this.logger.error('Unhandled error has occurred', err);
31
32
  return res.status(500).send(unexpectedError.toObject());
32
33
  }
33
34
  }
@@ -1,4 +1,5 @@
1
1
  import { JSONSchema } from '@shadow-library/class-schema';
2
+ import { Class } from 'type-fest';
2
3
  export declare enum RouteInputType {
3
4
  BODY = "body",
4
5
  PARAMS = "params",
@@ -6,7 +7,7 @@ export declare enum RouteInputType {
6
7
  REQUEST = "request",
7
8
  RESPONSE = "response"
8
9
  }
9
- export type RouteInputSchemas = Partial<Record<'body' | 'params' | 'query', JSONSchema>>;
10
+ export type RouteInputSchemas = Partial<Record<'body' | 'params' | 'query', JSONSchema | Class<unknown>>>;
10
11
  export declare function HttpInput(type: RouteInputType, schema?: JSONSchema): ParameterDecorator;
11
12
  export declare const Body: (schema?: JSONSchema) => ParameterDecorator;
12
13
  export declare const Params: (schema?: JSONSchema) => ParameterDecorator;
@@ -5,9 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Res = exports.Response = exports.Req = exports.Request = exports.Query = exports.Params = exports.Body = exports.RouteInputType = void 0;
7
7
  exports.HttpInput = HttpInput;
8
- const assert_1 = __importDefault(require("assert"));
8
+ const node_assert_1 = __importDefault(require("node:assert"));
9
9
  const app_1 = require("@shadow-library/app");
10
- const class_schema_1 = require("@shadow-library/class-schema");
11
10
  const constants_1 = require("../constants.js");
12
11
  var RouteInputType;
13
12
  (function (RouteInputType) {
@@ -19,16 +18,16 @@ var RouteInputType;
19
18
  })(RouteInputType || (exports.RouteInputType = RouteInputType = {}));
20
19
  function HttpInput(type, schema) {
21
20
  return (target, propertyKey, index) => {
22
- (0, assert_1.default)(propertyKey, 'Cannot apply decorator to a constructor parameter');
21
+ (0, node_assert_1.default)(propertyKey, 'Cannot apply decorator to a constructor parameter');
23
22
  const inputs = Reflect.getMetadata(constants_1.HTTP_CONTROLLER_INPUTS, target, propertyKey) ?? [];
24
23
  Reflect.defineMetadata(constants_1.HTTP_CONTROLLER_INPUTS, inputs, target, propertyKey);
25
24
  inputs[index] = type;
26
25
  if (!schema) {
27
26
  const paramTypes = Reflect.getMetadata(constants_1.PARAMTYPES_METADATA, target, propertyKey);
28
- schema = class_schema_1.ClassSchema.generate(paramTypes[index]);
27
+ schema = paramTypes[index];
29
28
  }
30
29
  const descriptor = Reflect.getOwnPropertyDescriptor(target, propertyKey);
31
- (0, assert_1.default)(descriptor, 'Cannot apply decorator to a non-method');
30
+ (0, node_assert_1.default)(descriptor, 'Cannot apply decorator to a non-method');
32
31
  (0, app_1.Route)({ schemas: { [type]: schema } })(target, propertyKey, descriptor);
33
32
  };
34
33
  }
@@ -3,3 +3,4 @@ export * from './http-input.decorator.js';
3
3
  export * from './http-output.decorator.js';
4
4
  export * from './http-route.decorator.js';
5
5
  export * from './middleware.decorator.js';
6
+ export * from './sensitive.decorator.js';
@@ -19,3 +19,4 @@ __exportStar(require("./http-input.decorator.js"), exports);
19
19
  __exportStar(require("./http-output.decorator.js"), exports);
20
20
  __exportStar(require("./http-route.decorator.js"), exports);
21
21
  __exportStar(require("./middleware.decorator.js"), exports);
22
+ __exportStar(require("./sensitive.decorator.js"), exports);
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Middleware = Middleware;
7
- const assert_1 = __importDefault(require("assert"));
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
8
  const app_1 = require("@shadow-library/app");
9
9
  const constants_1 = require("../constants.js");
10
10
  const propertyKeys = ['generate', 'use'];
@@ -15,7 +15,7 @@ function Middleware(options = {}) {
15
15
  options.weight = 0;
16
16
  return target => {
17
17
  const key = propertyKeys.find(key => key in target.prototype);
18
- (0, assert_1.default)(key, `Cannot apply @Middleware to a class without a 'generate()' or 'use()' method`);
18
+ (0, node_assert_1.default)(key, `Cannot apply @Middleware to a class without a 'generate()' or 'use()' method`);
19
19
  (0, app_1.Controller)({ ...options, [constants_1.HTTP_CONTROLLER_TYPE]: 'middleware', generates: key === 'generate' })(target);
20
20
  };
21
21
  }
@@ -0,0 +1,4 @@
1
+ import { MaskOptions } from '@shadow-library/common';
2
+ export type SensitiveDataType = 'secret' | 'email' | 'number' | 'words';
3
+ export declare function Sensitive(type?: SensitiveDataType): PropertyDecorator;
4
+ export declare function Sensitive(maskOptions: MaskOptions): PropertyDecorator;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Sensitive = Sensitive;
4
+ const class_schema_1 = require("@shadow-library/class-schema");
5
+ function Sensitive(typeOrMaskOptions = 'secret') {
6
+ return (target, propertyKey) => {
7
+ const options = { sensitive: true };
8
+ if (typeof typeOrMaskOptions === 'string')
9
+ options.type = typeOrMaskOptions;
10
+ else
11
+ options.maskOptions = typeOrMaskOptions;
12
+ const decorator = (0, class_schema_1.FieldMetadata)({ 'x-fastify': options });
13
+ decorator(target, propertyKey);
14
+ };
15
+ }
@@ -1,9 +1,12 @@
1
- import { RouteMetdata } from '@shadow-library/app';
2
- import { HttpRequest, HttpResponse } from './route-handler.interface.js';
3
- export type MiddlewareHandler = (request: HttpRequest, response: HttpResponse) => Promise<any>;
1
+ import { RouteMetadata } from '@shadow-library/app';
2
+ import { HttpCallback, HttpRequest, HttpResponse, RouteHandler } from './route-handler.interface.js';
4
3
  export interface MiddlewareGenerator {
5
- generate(metadata: RouteMetdata): MiddlewareHandler | undefined | Promise<MiddlewareHandler | undefined>;
4
+ cacheKey?: (metadata: RouteMetadata) => string;
5
+ generate(metadata: RouteMetadata): RouteHandler | undefined | Promise<RouteHandler | undefined>;
6
6
  }
7
- export interface HttpMiddleware {
8
- use(request: HttpRequest, response: HttpResponse): Promise<any>;
7
+ export interface AsyncHttpMiddleware {
8
+ use(request: HttpRequest, response: HttpResponse): Promise<unknown>;
9
+ }
10
+ export interface CallbackHttpMiddleware {
11
+ use(request: HttpRequest, response: HttpResponse, done: HttpCallback): void;
9
12
  }
@@ -1,4 +1,8 @@
1
- import { FastifyReply, FastifyRequest } from 'fastify';
1
+ import { SyncValue } from '@shadow-library/common';
2
+ import { DoneFuncWithErrOrRes, FastifyReply, FastifyRequest } from 'fastify';
2
3
  export type HttpRequest = FastifyRequest;
3
4
  export type HttpResponse = FastifyReply;
4
- export type RouteHandler = (req: HttpRequest, res: HttpResponse) => unknown | Promise<unknown>;
5
+ export type HttpCallback = DoneFuncWithErrOrRes;
6
+ export type CallbackRouteHandler = (request: HttpRequest, response: HttpResponse, done: HttpCallback) => SyncValue<unknown>;
7
+ export type AsyncRouteHandler = (request: HttpRequest, response: HttpResponse) => Promise<unknown>;
8
+ export type RouteHandler<T extends (...args: any[]) => any = any> = ReturnType<T> extends Promise<unknown> ? AsyncRouteHandler : CallbackRouteHandler;
@@ -1,24 +1,25 @@
1
- import { RouteMetdata } from '@shadow-library/app';
1
+ import { RouteMetadata } from '@shadow-library/app';
2
2
  import { JSONSchema } from '@shadow-library/class-schema';
3
3
  import { RouteShorthandOptions } from 'fastify';
4
4
  import { HTTP_CONTROLLER_TYPE } from '../constants.js';
5
5
  import { HttpMethod, RouteInputSchemas } from '../decorators/index.js';
6
6
  declare module '@shadow-library/app' {
7
- interface RouteMetdata extends Omit<RouteShorthandOptions, 'config'> {
7
+ interface RouteMetadata extends Omit<RouteShorthandOptions, 'config'> {
8
8
  method?: HttpMethod;
9
9
  path?: string;
10
10
  schemas?: RouteInputSchemas & {
11
11
  response?: Record<number | string, JSONSchema>;
12
12
  };
13
13
  rawBody?: boolean;
14
+ silentValidation?: boolean;
14
15
  status?: number;
15
16
  headers?: Record<string, string | (() => string)>;
16
17
  redirect?: string;
17
18
  render?: string | true;
18
19
  }
19
- interface ControllerMetdata {
20
+ interface ControllerMetadata {
20
21
  [HTTP_CONTROLLER_TYPE]?: 'router' | 'middleware';
21
22
  path?: string;
22
23
  }
23
24
  }
24
- export type ServerMetadata = RouteMetdata;
25
+ export type ServerMetadata = RouteMetadata;
@@ -6,5 +6,5 @@ export declare class ErrorResponseDto {
6
6
  code: string;
7
7
  type: string;
8
8
  message: string;
9
- fields: ErrorFieldDto[];
9
+ fields?: ErrorFieldDto[];
10
10
  }