@riktajs/core 0.5.0 → 0.7.0

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.
@@ -0,0 +1,8 @@
1
+ import 'reflect-metadata';
2
+ type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
3
+ type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
4
+ type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
5
+ type AnyDecorator = ClassDecorator | MethodDecorator | PropertyDecorator;
6
+ export declare function applyDecorators(...decorators: AnyDecorator[]): MethodDecorator & ClassDecorator & PropertyDecorator;
7
+ export declare function SetMetadata<T = unknown>(key: string | symbol, value: T): MethodDecorator & ClassDecorator;
8
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyDecorators = applyDecorators;
4
+ exports.SetMetadata = SetMetadata;
5
+ require("reflect-metadata");
6
+ function applyDecorators(...decorators) {
7
+ return ((target, propertyKey, descriptor) => {
8
+ for (const decorator of decorators.reverse()) {
9
+ if (descriptor) {
10
+ const result = decorator(target, propertyKey, descriptor);
11
+ if (result) {
12
+ descriptor = result;
13
+ }
14
+ }
15
+ else if (propertyKey !== undefined) {
16
+ decorator(target, propertyKey);
17
+ }
18
+ else {
19
+ const result = decorator(target);
20
+ if (result) {
21
+ return result;
22
+ }
23
+ }
24
+ }
25
+ if (descriptor) {
26
+ return descriptor;
27
+ }
28
+ });
29
+ }
30
+ function SetMetadata(key, value) {
31
+ const decoratorFactory = (target, propertyKey, descriptor) => {
32
+ if (descriptor) {
33
+ Reflect.defineMetadata(key, value, target.constructor, propertyKey);
34
+ }
35
+ else {
36
+ Reflect.defineMetadata(key, value, target);
37
+ }
38
+ };
39
+ return decoratorFactory;
40
+ }
@@ -0,0 +1,11 @@
1
+ import 'reflect-metadata';
2
+ import { ExecutionContext } from '../guards/execution-context';
3
+ export declare const CUSTOM_PARAM_METADATA: unique symbol;
4
+ export interface CustomParamMetadata<T = unknown> {
5
+ index: number;
6
+ factory: CustomParamFactory<T, unknown>;
7
+ data?: T;
8
+ }
9
+ export type CustomParamFactory<TData = unknown, TResult = unknown> = (data: TData, ctx: ExecutionContext) => TResult | Promise<TResult>;
10
+ export declare function createParamDecorator<TData = unknown, TResult = unknown>(factory: CustomParamFactory<TData, TResult>): (data?: TData) => ParameterDecorator;
11
+ export declare function getCustomParamMetadata<T = unknown>(target: Function, propertyKey: string | symbol): CustomParamMetadata<T>[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CUSTOM_PARAM_METADATA = void 0;
4
+ exports.createParamDecorator = createParamDecorator;
5
+ exports.getCustomParamMetadata = getCustomParamMetadata;
6
+ require("reflect-metadata");
7
+ exports.CUSTOM_PARAM_METADATA = Symbol.for('rikta:custom:param:metadata');
8
+ function createParamDecorator(factory) {
9
+ return (data) => {
10
+ return (target, propertyKey, parameterIndex) => {
11
+ if (propertyKey === undefined)
12
+ return;
13
+ const existingParams = Reflect.getMetadata(exports.CUSTOM_PARAM_METADATA, target.constructor, propertyKey) ?? [];
14
+ const metadata = {
15
+ index: parameterIndex,
16
+ factory: factory,
17
+ data,
18
+ };
19
+ existingParams.push(metadata);
20
+ Reflect.defineMetadata(exports.CUSTOM_PARAM_METADATA, existingParams, target.constructor, propertyKey);
21
+ };
22
+ };
23
+ }
24
+ function getCustomParamMetadata(target, propertyKey) {
25
+ return Reflect.getMetadata(exports.CUSTOM_PARAM_METADATA, target, propertyKey) ?? [];
26
+ }
@@ -5,3 +5,5 @@ export * from './param.decorator';
5
5
  export * from './autowired.decorator';
6
6
  export * from './provider.decorator';
7
7
  export * from './config-property.decorator';
8
+ export * from './create-param-decorator';
9
+ export * from './apply-decorators';
@@ -21,3 +21,5 @@ __exportStar(require("./param.decorator"), exports);
21
21
  __exportStar(require("./autowired.decorator"), exports);
22
22
  __exportStar(require("./provider.decorator"), exports);
23
23
  __exportStar(require("./config-property.decorator"), exports);
24
+ __exportStar(require("./create-param-decorator"), exports);
25
+ __exportStar(require("./apply-decorators"), exports);
@@ -9,6 +9,7 @@ export * from './application';
9
9
  export * from './decorators';
10
10
  export * from './exceptions';
11
11
  export * from './guards';
12
+ export * from './middleware';
12
13
  export * from './config';
13
14
  export * from './metadata';
14
15
  export { z } from 'zod';
@@ -26,6 +26,7 @@ __exportStar(require("./application"), exports);
26
26
  __exportStar(require("./decorators"), exports);
27
27
  __exportStar(require("./exceptions"), exports);
28
28
  __exportStar(require("./guards"), exports);
29
+ __exportStar(require("./middleware"), exports);
29
30
  __exportStar(require("./config"), exports);
30
31
  __exportStar(require("./metadata"), exports);
31
32
  var zod_1 = require("zod");
@@ -0,0 +1,3 @@
1
+ export { Middleware } from './middleware.decorator';
2
+ export { RiktaMiddleware, NextFunction } from './rikta-middleware.interface';
3
+ export { UseMiddleware, getMiddlewareMetadata, getMiddlewareMetadata as getMiddlewaresMetadata, MiddlewareClass } from './use-middleware.decorator';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getMiddlewaresMetadata = exports.getMiddlewareMetadata = exports.UseMiddleware = exports.Middleware = void 0;
4
+ var middleware_decorator_1 = require("./middleware.decorator");
5
+ Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return middleware_decorator_1.Middleware; } });
6
+ var use_middleware_decorator_1 = require("./use-middleware.decorator");
7
+ Object.defineProperty(exports, "UseMiddleware", { enumerable: true, get: function () { return use_middleware_decorator_1.UseMiddleware; } });
8
+ Object.defineProperty(exports, "getMiddlewareMetadata", { enumerable: true, get: function () { return use_middleware_decorator_1.getMiddlewareMetadata; } });
9
+ Object.defineProperty(exports, "getMiddlewaresMetadata", { enumerable: true, get: function () { return use_middleware_decorator_1.getMiddlewareMetadata; } });
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ export declare function Middleware(): ClassDecorator;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Middleware = Middleware;
4
+ require("reflect-metadata");
5
+ function Middleware() {
6
+ return (target) => {
7
+ Reflect.defineMetadata('middleware', true, target);
8
+ Reflect.defineMetadata('injectable', true, target);
9
+ };
10
+ }
@@ -0,0 +1,5 @@
1
+ import type { FastifyRequest, FastifyReply } from 'fastify';
2
+ export type NextFunction = () => void | Promise<void>;
3
+ export interface RiktaMiddleware {
4
+ use(req: FastifyRequest, res: FastifyReply, next: NextFunction): void | Promise<void>;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ import 'reflect-metadata';
2
+ import type { Constructor } from '../types';
3
+ import type { RiktaMiddleware } from './rikta-middleware.interface';
4
+ export type MiddlewareClass = Constructor<RiktaMiddleware>;
5
+ export declare function UseMiddleware(...middleware: MiddlewareClass[]): ClassDecorator & MethodDecorator;
6
+ export declare function getMiddlewareMetadata(target: Constructor, propertyKey?: string | symbol): MiddlewareClass[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UseMiddleware = UseMiddleware;
4
+ exports.getMiddlewareMetadata = getMiddlewareMetadata;
5
+ require("reflect-metadata");
6
+ const constants_1 = require("../constants");
7
+ function UseMiddleware(...middleware) {
8
+ return (target, propertyKey) => {
9
+ if (propertyKey !== undefined) {
10
+ const existingMiddleware = Reflect.getMetadata(constants_1.MIDDLEWARE_METADATA, target.constructor, propertyKey) ?? [];
11
+ Reflect.defineMetadata(constants_1.MIDDLEWARE_METADATA, [...existingMiddleware, ...middleware], target.constructor, propertyKey);
12
+ }
13
+ else {
14
+ const existingMiddleware = Reflect.getMetadata(constants_1.MIDDLEWARE_METADATA, target) ?? [];
15
+ Reflect.defineMetadata(constants_1.MIDDLEWARE_METADATA, [...existingMiddleware, ...middleware], target);
16
+ }
17
+ };
18
+ }
19
+ function getMiddlewareMetadata(target, propertyKey) {
20
+ const controllerMiddleware = Reflect.getMetadata(constants_1.MIDDLEWARE_METADATA, target) ?? [];
21
+ if (!propertyKey) {
22
+ return controllerMiddleware;
23
+ }
24
+ const methodMiddleware = Reflect.getMetadata(constants_1.MIDDLEWARE_METADATA, target, propertyKey) ?? [];
25
+ return [...controllerMiddleware, ...methodMiddleware];
26
+ }
@@ -7,13 +7,16 @@ export declare class Router {
7
7
  private readonly container;
8
8
  private readonly globalPrefix;
9
9
  private readonly guardCache;
10
+ private readonly middlewareCache;
10
11
  constructor(server: FastifyInstance, container: Container, globalPrefix?: string);
11
12
  registerController(controllerClass: Constructor, silent?: boolean): void;
12
13
  private registerRoute;
13
14
  private compileParamResolvers;
14
15
  private createParamExtractor;
15
- private resolveParamsOptimized;
16
+ private resolveAllParams;
16
17
  private resolveGuardInstances;
17
18
  private executeGuardsOptimized;
19
+ private resolveMiddlewareInstances;
20
+ private executeMiddlewareChain;
18
21
  private buildPath;
19
22
  }
@@ -3,15 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Router = void 0;
4
4
  require("reflect-metadata");
5
5
  const constants_1 = require("../constants");
6
+ const create_param_decorator_1 = require("../decorators/create-param-decorator");
6
7
  const validation_exception_1 = require("../exceptions/validation.exception");
7
8
  const exceptions_1 = require("../exceptions/exceptions");
8
9
  const execution_context_1 = require("../guards/execution-context");
9
10
  const use_guards_decorator_1 = require("../guards/use-guards.decorator");
11
+ const use_middleware_decorator_1 = require("../middleware/use-middleware.decorator");
10
12
  class Router {
11
13
  server;
12
14
  container;
13
15
  globalPrefix;
14
16
  guardCache = new Map();
17
+ middlewareCache = new Map();
15
18
  constructor(server, container, globalPrefix = '') {
16
19
  this.server = server;
17
20
  this.container = container;
@@ -36,49 +39,46 @@ class Router {
36
39
  throw new Error(`Handler ${String(route.handlerName)} not found on ${controllerClass.name}`);
37
40
  }
38
41
  const paramsMeta = Reflect.getMetadata(constants_1.PARAM_METADATA, controllerClass, route.handlerName) ?? [];
42
+ const customParamsMeta = (0, create_param_decorator_1.getCustomParamMetadata)(controllerClass, route.handlerName);
39
43
  const statusCode = Reflect.getMetadata(constants_1.HTTP_CODE_METADATA, controllerClass, route.handlerName);
40
44
  const guards = (0, use_guards_decorator_1.getGuardsMetadata)(controllerClass, route.handlerName);
45
+ const middleware = (0, use_middleware_decorator_1.getMiddlewareMetadata)(controllerClass, route.handlerName);
41
46
  const compiledResolvers = this.compileParamResolvers(paramsMeta);
42
- const hasParams = compiledResolvers.length > 0;
43
- const maxParamIndex = hasParams ? Math.max(...compiledResolvers.map(r => r.index)) : -1;
47
+ const hasBuiltinParams = compiledResolvers.length > 0;
48
+ const hasCustomParams = customParamsMeta.length > 0;
49
+ const hasParams = hasBuiltinParams || hasCustomParams;
50
+ const allParamIndexes = [
51
+ ...compiledResolvers.map(r => r.index),
52
+ ...customParamsMeta.map(r => r.index)
53
+ ];
54
+ const maxParamIndex = hasParams ? Math.max(...allParamIndexes) : -1;
44
55
  const guardInstances = this.resolveGuardInstances(guards);
45
56
  const hasGuards = guardInstances.length > 0;
46
- let routeHandler;
47
- if (!hasGuards && !hasParams && !statusCode) {
48
- routeHandler = async (_request, _reply) => {
49
- return handler.call(controllerInstance);
50
- };
51
- }
52
- else if (!hasGuards && !hasParams) {
53
- routeHandler = async (_request, reply) => {
54
- const result = await handler.call(controllerInstance);
55
- if (statusCode)
56
- reply.status(statusCode);
57
- return result;
58
- };
59
- }
60
- else if (!hasGuards) {
61
- routeHandler = async (request, reply) => {
62
- const args = this.resolveParamsOptimized(compiledResolvers, maxParamIndex, request, reply);
63
- const result = await handler.apply(controllerInstance, args);
64
- if (statusCode)
65
- reply.status(statusCode);
66
- return result;
67
- };
68
- }
69
- else {
70
- const createContext = (req, rep) => new execution_context_1.ExecutionContextImpl(req, rep, controllerClass, route.handlerName);
71
- routeHandler = async (request, reply) => {
72
- await this.executeGuardsOptimized(guardInstances, createContext(request, reply));
73
- const args = hasParams
74
- ? this.resolveParamsOptimized(compiledResolvers, maxParamIndex, request, reply)
75
- : [];
76
- const result = await handler.apply(controllerInstance, args);
77
- if (statusCode)
78
- reply.status(statusCode);
79
- return result;
80
- };
81
- }
57
+ const middlewareInstances = this.resolveMiddlewareInstances(middleware);
58
+ const hasMiddleware = middlewareInstances.length > 0;
59
+ const needsContext = hasGuards || hasCustomParams;
60
+ const createContext = needsContext
61
+ ? (req, rep) => new execution_context_1.ExecutionContextImpl(req, rep, controllerClass, route.handlerName)
62
+ : null;
63
+ const routeHandler = async (request, reply) => {
64
+ const executionContext = createContext ? createContext(request, reply) : null;
65
+ if (hasGuards && executionContext) {
66
+ await this.executeGuardsOptimized(guardInstances, executionContext);
67
+ }
68
+ if (hasMiddleware) {
69
+ await this.executeMiddlewareChain(middlewareInstances, request, reply);
70
+ }
71
+ let args;
72
+ if (hasParams) {
73
+ args = await this.resolveAllParams(compiledResolvers, customParamsMeta, maxParamIndex, request, reply, executionContext);
74
+ }
75
+ const result = args
76
+ ? await handler.apply(controllerInstance, args)
77
+ : await handler.call(controllerInstance);
78
+ if (statusCode)
79
+ reply.status(statusCode);
80
+ return result;
81
+ };
82
82
  const method = route.method.toLowerCase();
83
83
  this.server[method](fullPath, routeHandler);
84
84
  if (!silent)
@@ -127,8 +127,8 @@ class Router {
127
127
  return () => undefined;
128
128
  }
129
129
  }
130
- resolveParamsOptimized(resolvers, maxIndex, request, reply) {
131
- const context = {
130
+ async resolveAllParams(compiledResolvers, customParams, maxIndex, request, reply, executionContext) {
131
+ const routeContext = {
132
132
  request,
133
133
  reply,
134
134
  params: request.params,
@@ -136,9 +136,9 @@ class Router {
136
136
  body: request.body,
137
137
  };
138
138
  const args = new Array(maxIndex + 1);
139
- for (let i = 0; i < resolvers.length; i++) {
140
- const resolver = resolvers[i];
141
- let value = resolver.extract(context);
139
+ for (let i = 0; i < compiledResolvers.length; i++) {
140
+ const resolver = compiledResolvers[i];
141
+ let value = resolver.extract(routeContext);
142
142
  if (resolver.zodSchema) {
143
143
  const result = resolver.zodSchema.safeParse(value);
144
144
  if (!result.success) {
@@ -148,6 +148,13 @@ class Router {
148
148
  }
149
149
  args[resolver.index] = value;
150
150
  }
151
+ if (customParams.length > 0) {
152
+ const ctx = executionContext ?? new execution_context_1.ExecutionContextImpl(request, reply, {}, '');
153
+ for (const customParam of customParams) {
154
+ const value = await customParam.factory(customParam.data, ctx);
155
+ args[customParam.index] = value;
156
+ }
157
+ }
151
158
  return args;
152
159
  }
153
160
  resolveGuardInstances(guards) {
@@ -179,6 +186,37 @@ class Router {
179
186
  }
180
187
  }
181
188
  }
189
+ resolveMiddlewareInstances(middleware) {
190
+ return middleware.map(MiddlewareClass => {
191
+ let instance = this.middlewareCache.get(MiddlewareClass);
192
+ if (instance)
193
+ return instance;
194
+ try {
195
+ instance = this.container.resolve(MiddlewareClass);
196
+ }
197
+ catch (error) {
198
+ throw new Error(`Failed to resolve middleware ${MiddlewareClass.name}. ` +
199
+ `Make sure it is decorated with @Injectable(). ` +
200
+ `Original error: ${error instanceof Error ? error.message : String(error)}`);
201
+ }
202
+ if (typeof instance.use !== 'function') {
203
+ throw new Error(`${MiddlewareClass.name} does not implement RiktaMiddleware interface. ` +
204
+ `The middleware must have a use(req, res, next) method.`);
205
+ }
206
+ this.middlewareCache.set(MiddlewareClass, instance);
207
+ return instance;
208
+ });
209
+ }
210
+ async executeMiddlewareChain(middlewareInstances, request, reply) {
211
+ let index = 0;
212
+ const next = async () => {
213
+ if (index < middlewareInstances.length) {
214
+ const middleware = middlewareInstances[index++];
215
+ await middleware.use(request, reply, next);
216
+ }
217
+ };
218
+ await next();
219
+ }
182
220
  buildPath(controllerPrefix, routePath) {
183
221
  const parts = [this.globalPrefix, controllerPrefix, routePath]
184
222
  .filter(Boolean)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riktajs/core",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "A fast and modern TypeScript backend framework with zero-config autowiring, powered by Fastify",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",