@navios/core 0.1.14 → 0.1.15

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 (34) hide show
  1. package/dist/_tsup-dts-rollup.d.mts +124 -49
  2. package/dist/_tsup-dts-rollup.d.ts +124 -49
  3. package/dist/index.d.mts +8 -2
  4. package/dist/index.d.ts +8 -2
  5. package/dist/index.js +537 -471
  6. package/dist/index.mjs +531 -470
  7. package/package.json +1 -1
  8. package/src/adapters/endpoint-adapter.service.mts +75 -0
  9. package/src/adapters/handler-adapter.interface.mts +21 -0
  10. package/src/adapters/index.mts +4 -0
  11. package/src/adapters/multipart-adapter.service.mts +130 -0
  12. package/src/adapters/stream-adapter.service.mts +95 -0
  13. package/src/attribute.factory.mts +13 -13
  14. package/src/config/config.provider.mts +8 -2
  15. package/src/decorators/controller.decorator.mts +0 -2
  16. package/src/decorators/endpoint.decorator.mts +7 -3
  17. package/src/decorators/header.decorator.mts +1 -6
  18. package/src/decorators/http-code.decorator.mts +1 -6
  19. package/src/decorators/module.decorator.mts +13 -15
  20. package/src/decorators/multipart.decorator.mts +7 -3
  21. package/src/decorators/stream.decorator.mts +7 -3
  22. package/src/index.mts +1 -0
  23. package/src/logger/logger.service.mts +0 -1
  24. package/src/metadata/controller.metadata.mts +3 -3
  25. package/src/metadata/{endpoint.metadata.mts → handler.metadata.mts} +17 -24
  26. package/src/metadata/index.mts +1 -1
  27. package/src/navios.application.mts +3 -4
  28. package/src/service-locator/__tests__/injection-token.spec.mts +10 -5
  29. package/src/service-locator/decorators/injectable.decorator.mts +53 -4
  30. package/src/service-locator/inject.mts +14 -0
  31. package/src/service-locator/interfaces/factory.interface.mts +9 -1
  32. package/src/service-locator/sync-injector.mts +14 -0
  33. package/src/services/controller-adapter.service.mts +59 -240
  34. package/src/services/execution-context.mts +4 -3
@@ -4,14 +4,14 @@ import type {
4
4
  ClassTypeWithInstance,
5
5
  InjectionToken,
6
6
  } from '../service-locator/index.mjs'
7
- import type { EndpointMetadata } from './endpoint.metadata.mjs'
7
+ import type { HandlerMetadata } from './handler.metadata.mjs'
8
8
 
9
- import { getAllEndpointMetadata } from './endpoint.metadata.mjs'
9
+ import { getAllEndpointMetadata } from './handler.metadata.mjs'
10
10
 
11
11
  export const ControllerMetadataKey = Symbol('ControllerMetadataKey')
12
12
 
13
13
  export interface ControllerMetadata {
14
- endpoints: Set<EndpointMetadata>
14
+ endpoints: Set<HandlerMetadata>
15
15
  guards: Set<
16
16
  ClassTypeWithInstance<CanActivate> | InjectionToken<CanActivate, undefined>
17
17
  >
@@ -1,10 +1,7 @@
1
- import type {
2
- BaseEndpointConfig,
3
- BaseStreamConfig,
4
- HttpMethod,
5
- } from '@navios/common'
1
+ import type { HttpMethod } from '@navios/common'
6
2
  import type { HttpHeader } from 'fastify/types/utils.js'
7
3
 
4
+ import type { HandlerAdapterInterface } from '../adapters/index.mjs'
8
5
  import type { CanActivate } from '../interfaces/index.mjs'
9
6
  import type {
10
7
  ClassTypeWithInstance,
@@ -13,22 +10,17 @@ import type {
13
10
 
14
11
  export const EndpointMetadataKey = Symbol('EndpointMetadataKey')
15
12
 
16
- export enum EndpointType {
17
- Unknown = 'unknown',
18
- Endpoint = 'endpoint',
19
- Stream = 'stream',
20
- Multipart = 'multipart',
21
- Handler = 'handler',
22
- }
23
-
24
- export interface EndpointMetadata {
13
+ export interface HandlerMetadata<Config = null> {
25
14
  classMethod: string
26
15
  url: string
27
16
  successStatusCode: number
28
- type: EndpointType
17
+ adapterToken:
18
+ | InjectionToken<HandlerAdapterInterface, undefined>
19
+ | ClassTypeWithInstance<HandlerAdapterInterface>
20
+ | null
29
21
  headers: Partial<Record<HttpHeader, number | string | string[] | undefined>>
30
22
  httpMethod: HttpMethod
31
- config: BaseEndpointConfig | BaseStreamConfig | null
23
+ config: Config
32
24
  guards: Set<
33
25
  ClassTypeWithInstance<CanActivate> | InjectionToken<CanActivate, undefined>
34
26
  >
@@ -37,25 +29,25 @@ export interface EndpointMetadata {
37
29
 
38
30
  export function getAllEndpointMetadata(
39
31
  context: ClassMethodDecoratorContext | ClassDecoratorContext,
40
- ): Set<EndpointMetadata> {
32
+ ): Set<HandlerMetadata<any>> {
41
33
  if (context.metadata) {
42
34
  const metadata = context.metadata[EndpointMetadataKey] as
43
- | Set<EndpointMetadata>
35
+ | Set<HandlerMetadata>
44
36
  | undefined
45
37
  if (metadata) {
46
38
  return metadata
47
39
  } else {
48
- context.metadata[EndpointMetadataKey] = new Set<EndpointMetadata>()
49
- return context.metadata[EndpointMetadataKey] as Set<EndpointMetadata>
40
+ context.metadata[EndpointMetadataKey] = new Set<HandlerMetadata<any>>()
41
+ return context.metadata[EndpointMetadataKey] as Set<HandlerMetadata<any>>
50
42
  }
51
43
  }
52
44
  throw new Error('[Navios] Wrong environment.')
53
45
  }
54
46
 
55
- export function getEndpointMetadata(
47
+ export function getEndpointMetadata<Config = any>(
56
48
  target: Function,
57
49
  context: ClassMethodDecoratorContext,
58
- ): EndpointMetadata {
50
+ ): HandlerMetadata<Config> {
59
51
  if (context.metadata) {
60
52
  const metadata = getAllEndpointMetadata(context)
61
53
  if (metadata) {
@@ -65,13 +57,14 @@ export function getEndpointMetadata(
65
57
  if (endpointMetadata) {
66
58
  return endpointMetadata
67
59
  } else {
68
- const newMetadata: EndpointMetadata = {
60
+ const newMetadata: HandlerMetadata<Config> = {
69
61
  classMethod: target.name,
70
62
  url: '',
71
63
  successStatusCode: 200,
64
+ adapterToken: null,
72
65
  headers: {},
73
- type: EndpointType.Unknown,
74
66
  httpMethod: 'GET',
67
+ // @ts-expect-error We are using a generic type here
75
68
  config: null,
76
69
  guards: new Set<
77
70
  | ClassTypeWithInstance<CanActivate>
@@ -1,4 +1,4 @@
1
1
  export * from './controller.metadata.mjs'
2
- export * from './endpoint.metadata.mjs'
2
+ export * from './handler.metadata.mjs'
3
3
  export * from './injectable.metadata.mjs'
4
4
  export * from './module.metadata.mjs'
@@ -149,7 +149,7 @@ export class NaviosApplication {
149
149
  message: 'Not Found',
150
150
  error: 'NotFound',
151
151
  }
152
- this.logger.error(`Route not found: ${req.url}`)
152
+ this.logger.error(`Route not found: [${req.method}] ${req.url}`)
153
153
  return reply.status(404).send(response)
154
154
  })
155
155
  }
@@ -178,15 +178,14 @@ export class NaviosApplication {
178
178
  }
179
179
  promises.push(
180
180
  this.server!.register(
181
- (instance, opts, done) => {
181
+ async (instance, opts) => {
182
182
  for (const controller of moduleMetadata.controllers) {
183
- this.controllerAdapter.setupController(
183
+ await this.controllerAdapter.setupController(
184
184
  controller,
185
185
  instance,
186
186
  moduleMetadata,
187
187
  )
188
188
  }
189
- done()
190
189
  },
191
190
  {
192
191
  prefix: this.globalPrefix ?? '',
@@ -1,6 +1,11 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import { z } from 'zod'
3
3
 
4
+ import type {
5
+ Factory,
6
+ FactoryWithArgs,
7
+ } from '../interfaces/factory.interface.mjs'
8
+
4
9
  import { Injectable, InjectableType } from '../decorators/index.mjs'
5
10
  import { inject } from '../inject.mjs'
6
11
  import { InjectionToken } from '../injection-token.mjs'
@@ -39,13 +44,13 @@ describe('InjectToken', () => {
39
44
  })
40
45
 
41
46
  it('should work with factory', async () => {
42
- const token = InjectionToken.create('Test')
47
+ const token = InjectionToken.create<string>('Test')
43
48
  @Injectable({
44
49
  token,
45
50
  type: InjectableType.Factory,
46
51
  })
47
- class Test {
48
- create() {
52
+ class Test implements Factory<string> {
53
+ async create() {
49
54
  return 'foo'
50
55
  }
51
56
  }
@@ -64,8 +69,8 @@ describe('InjectToken', () => {
64
69
  token,
65
70
  type: InjectableType.Factory,
66
71
  })
67
- class Test {
68
- create(ctx: any, args: { test: string }) {
72
+ class Test implements FactoryWithArgs<string, typeof schema> {
73
+ async create(ctx: any, args: { test: string }) {
69
74
  return args.test
70
75
  }
71
76
  }
@@ -1,6 +1,12 @@
1
+ import type { AnyZodObject } from 'zod'
2
+
1
3
  import { NaviosException } from '@navios/common'
2
4
 
3
- import type { ClassType } from '../injection-token.mjs'
5
+ import type { ClassType, ClassTypeWithInstance } from '../injection-token.mjs'
6
+ import type {
7
+ Factory,
8
+ FactoryWithArgs,
9
+ } from '../interfaces/factory.interface.mjs'
4
10
 
5
11
  import { InjectableScope } from '../enums/index.mjs'
6
12
  import { InjectionToken } from '../injection-token.mjs'
@@ -18,14 +24,55 @@ export interface InjectableOptions {
18
24
  token?: InjectionToken<any, any>
19
25
  }
20
26
 
21
- export const InjectableTokenMeta = Symbol('InjectableTokenMeta')
27
+ export const InjectableTokenMeta = Symbol.for('InjectableTokenMeta')
22
28
 
29
+ export function Injectable(): <T extends ClassType>(
30
+ target: T,
31
+ context: ClassDecoratorContext,
32
+ ) => T & { [InjectableTokenMeta]: InjectionToken<InstanceType<T>, undefined> }
33
+ export function Injectable<T extends ClassType>(options: {
34
+ scope?: InjectableScope
35
+ token: InjectionToken<T, undefined>
36
+ }): (
37
+ target: T,
38
+ context: ClassDecoratorContext,
39
+ ) => T & {
40
+ [InjectableTokenMeta]: InjectionToken<InstanceType<T>, undefined>
41
+ }
42
+ export function Injectable<R>(options: {
43
+ scope?: InjectableScope
44
+ type: InjectableType.Factory
45
+ }): <T extends ClassTypeWithInstance<Factory<R>>>(
46
+ target: T,
47
+ context: ClassDecoratorContext,
48
+ ) => T & { [InjectableTokenMeta]: InjectionToken<R, undefined> }
49
+ export function Injectable<R, S extends AnyZodObject>(options: {
50
+ scope?: InjectableScope
51
+ type: InjectableType.Factory
52
+ token: InjectionToken<R, S>
53
+ }): <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(
54
+ target: T,
55
+ context: ClassDecoratorContext,
56
+ ) => T & { [InjectableTokenMeta]: InjectionToken<R, S> }
57
+ export function Injectable<R>(options: {
58
+ scope?: InjectableScope
59
+ type: InjectableType.Factory
60
+ token: InjectionToken<R, undefined>
61
+ }): <T extends ClassTypeWithInstance<Factory<R>>>(
62
+ target: T,
63
+ context: ClassDecoratorContext,
64
+ ) => T & { [InjectableTokenMeta]: InjectionToken<R, undefined> }
23
65
  export function Injectable({
24
66
  scope = InjectableScope.Singleton,
25
67
  type = InjectableType.Class,
26
68
  token,
27
69
  }: InjectableOptions = {}) {
28
- return (target: ClassType, context: ClassDecoratorContext) => {
70
+ return <T extends ClassType>(
71
+ target: T,
72
+ context: ClassDecoratorContext,
73
+ ): T & {
74
+ [InjectableTokenMeta]: InjectionToken<any, any>
75
+ } => {
29
76
  if (context.kind !== 'class') {
30
77
  throw new Error(
31
78
  '[ServiceLocator] @Injectable decorator can only be used on classes.',
@@ -59,6 +106,8 @@ export function Injectable({
59
106
  // @ts-expect-error
60
107
  target[InjectableTokenMeta] = injectableToken
61
108
 
62
- return target
109
+ return target as T & {
110
+ [InjectableTokenMeta]: InjectionToken<any, any>
111
+ }
63
112
  }
64
113
  }
@@ -7,6 +7,20 @@ import { InjectionToken } from './injection-token.mjs'
7
7
  import { getServiceLocator } from './injector.mjs'
8
8
 
9
9
  export function inject<T extends ClassType>(token: T): Promise<InstanceType<T>>
10
+ // Not supported by TypeScript yet
11
+ // export function inject<
12
+ // R,
13
+ // T extends ClassType & {
14
+ // [InjectableTokenMeta]: InjectionToken<R, undefined>
15
+ // },
16
+ // >(token: T): Promise<R>
17
+ // export function inject<
18
+ // R,
19
+ // S extends AnyZodObject,
20
+ // T extends ClassType & {
21
+ // [InjectableTokenMeta]: InjectionToken<R, S>
22
+ // },
23
+ // >(token: T, args: z.input<S>): Promise<R>
10
24
  export function inject<T, S extends AnyZodObject>(
11
25
  token: InjectionToken<T, S>,
12
26
  args: z.input<S>,
@@ -1,3 +1,11 @@
1
+ import type { AnyZodObject } from 'zod'
2
+
3
+ import { z } from 'zod'
4
+
1
5
  export interface Factory<T> {
2
- create(ctx: any): Promise<T>
6
+ create(ctx?: any): Promise<T> | T
7
+ }
8
+
9
+ export interface FactoryWithArgs<T, A extends AnyZodObject> {
10
+ create(ctx: any, args: z.output<A>): Promise<T> | T
3
11
  }
@@ -9,6 +9,20 @@ import { getServiceLocator } from './injector.mjs'
9
9
  let promiseCollector: null | ((promise: Promise<any>) => void) = null
10
10
 
11
11
  export function syncInject<T extends ClassType>(token: T): InstanceType<T>
12
+ // Not supported by TypeScript yet
13
+ // export function syncInject<
14
+ // R,
15
+ // T extends ClassType & {
16
+ // [InjectableTokenMeta]: InjectionToken<R, undefined>
17
+ // },
18
+ // >(token: T): R
19
+ // export function syncInject<
20
+ // R,
21
+ // S extends AnyZodObject,
22
+ // T extends ClassType & {
23
+ // [InjectableTokenMeta]: InjectionToken<R, S>
24
+ // },
25
+ // >(token: T, args: z.input<S>): R
12
26
  export function syncInject<T, S extends AnyZodObject>(
13
27
  token: InjectionToken<T, S>,
14
28
  args: z.input<S>,
@@ -1,21 +1,17 @@
1
- import type { BaseEndpointConfig } from '@navios/common'
2
1
  import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
3
2
  import type { ZodTypeProvider } from 'fastify-type-provider-zod'
4
- import type { AnyZodObject } from 'zod'
5
3
 
6
- import { NaviosException } from '@navios/common'
7
-
8
- import { ZodArray } from 'zod'
9
-
10
- import type { EndpointMetadata, ModuleMetadata } from '../metadata/index.mjs'
4
+ import type { HandlerAdapterInterface } from '../adapters/index.mjs'
5
+ import type { ModuleMetadata } from '../metadata/index.mjs'
11
6
  import type { ClassType } from '../service-locator/index.mjs'
12
7
 
13
8
  import { Logger } from '../logger/index.mjs'
14
- import { EndpointType, extractControllerMetadata } from '../metadata/index.mjs'
9
+ import { extractControllerMetadata } from '../metadata/index.mjs'
15
10
  import {
16
11
  getServiceLocator,
17
12
  inject,
18
13
  Injectable,
14
+ InjectionToken,
19
15
  syncInject,
20
16
  } from '../service-locator/index.mjs'
21
17
  import { ExecutionContextToken, Reply, Request } from '../tokens/index.mjs'
@@ -29,32 +25,51 @@ export class ControllerAdapterService {
29
25
  context: ControllerAdapterService.name,
30
26
  })
31
27
 
32
- setupController(
28
+ async setupController(
33
29
  controller: ClassType,
34
30
  instance: FastifyInstance,
35
31
  moduleMetadata: ModuleMetadata,
36
- ): void {
32
+ ) {
37
33
  const controllerMetadata = extractControllerMetadata(controller)
38
34
  for (const endpoint of controllerMetadata.endpoints) {
39
- const { classMethod, url, httpMethod } = endpoint
35
+ const { classMethod, url, httpMethod, adapterToken } = endpoint
40
36
 
41
- if (!url) {
37
+ if (!url || !adapterToken) {
42
38
  throw new Error(
43
39
  `[Navios] Malformed Endpoint ${controller.name}:${classMethod}`,
44
40
  )
45
41
  }
42
+ const adapter = await inject(
43
+ adapterToken as InjectionToken<HandlerAdapterInterface>,
44
+ )
46
45
  const executionContext = new ExecutionContext(
47
46
  moduleMetadata,
48
47
  controllerMetadata,
49
48
  endpoint,
50
49
  )
51
- instance.withTypeProvider<ZodTypeProvider>().route({
52
- method: httpMethod,
53
- url: url.replaceAll('$', ':'),
54
- schema: this.provideSchemaForConfig(endpoint),
55
- preHandler: this.providePreHandler(executionContext),
56
- handler: this.provideHandler(controller, executionContext, endpoint),
57
- })
50
+ const hasSchema = adapter.hasSchema?.(endpoint) ?? false
51
+ if (hasSchema) {
52
+ instance.withTypeProvider<ZodTypeProvider>().route({
53
+ method: httpMethod,
54
+ url: url.replaceAll('$', ':'),
55
+ schema: adapter.provideSchema?.(endpoint) ?? {},
56
+ preHandler: this.providePreHandler(executionContext),
57
+ handler: this.wrapHandler(
58
+ executionContext,
59
+ adapter.provideHandler(controller, executionContext, endpoint),
60
+ ),
61
+ })
62
+ } else {
63
+ instance.route({
64
+ method: httpMethod,
65
+ url: url.replaceAll('$', ':'),
66
+ preHandler: this.providePreHandler(executionContext),
67
+ handler: this.wrapHandler(
68
+ executionContext,
69
+ adapter.provideHandler(controller, executionContext, endpoint),
70
+ ),
71
+ })
72
+ }
58
73
 
59
74
  this.logger.debug(
60
75
  `Registered ${httpMethod} ${url} for ${controller.name}:${classMethod}`,
@@ -65,239 +80,43 @@ export class ControllerAdapterService {
65
80
  providePreHandler(executionContext: ExecutionContext) {
66
81
  const guards = this.guardRunner.makeContext(executionContext)
67
82
  return guards.size > 0
68
- ? async (request: FastifyRequest, reply: FastifyReply) => {
69
- getServiceLocator().registerInstance(Request, request)
70
- getServiceLocator().registerInstance(Reply, reply)
71
- getServiceLocator().registerInstance(
72
- ExecutionContextToken,
73
- executionContext,
74
- )
75
- executionContext.provideRequest(request)
76
- executionContext.provideReply(reply)
77
- let canActivate = true
78
- try {
83
+ ? this.wrapHandler(
84
+ executionContext,
85
+ async (request: FastifyRequest, reply: FastifyReply) => {
86
+ let canActivate = true
79
87
  canActivate = await this.guardRunner.runGuards(
80
88
  guards,
81
89
  executionContext,
82
90
  )
83
- } finally {
84
- getServiceLocator().removeInstance(Request)
85
- getServiceLocator().removeInstance(Reply)
86
- getServiceLocator().removeInstance(ExecutionContextToken)
87
- }
88
- if (!canActivate) {
89
- return reply
90
- }
91
- }
92
- : undefined
93
- }
94
-
95
- private provideSchemaForConfig(endpointMetadata: EndpointMetadata) {
96
- if (!endpointMetadata.config) {
97
- this.logger.warn(`No config found for endpoint ${endpointMetadata.url}`)
98
- return {}
99
- }
100
- const { querySchema, requestSchema, responseSchema } =
101
- endpointMetadata.config as BaseEndpointConfig
102
- const schema: Record<string, any> = {}
103
- if (querySchema) {
104
- schema.querystring = querySchema
105
- }
106
- if (requestSchema && endpointMetadata.type !== EndpointType.Multipart) {
107
- schema.body = requestSchema
108
- }
109
- if (responseSchema) {
110
- schema.response = {
111
- 200: responseSchema,
112
- }
113
- }
114
-
115
- return schema
116
- }
117
-
118
- private provideHandler(
119
- controller: ClassType,
120
- executionContext: ExecutionContext,
121
- endpointMetadata: EndpointMetadata,
122
- ): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
123
- switch (endpointMetadata.type) {
124
- case EndpointType.Unknown:
125
- this.logger.error(
126
- `Unknown endpoint type ${endpointMetadata.type} for ${controller.name}:${endpointMetadata.classMethod}`,
127
- )
128
- throw new NaviosException('Unknown endpoint type')
129
- case EndpointType.Endpoint:
130
- return this.provideHandlerForConfig(
131
- controller,
132
- executionContext,
133
- endpointMetadata,
134
- )
135
- case EndpointType.Stream:
136
- return this.provideHandlerForStream(
137
- controller,
138
- executionContext,
139
- endpointMetadata,
140
- )
141
- case EndpointType.Multipart:
142
- return this.provideHandlerForMultipart(
143
- controller,
144
- executionContext,
145
- endpointMetadata,
91
+ if (!canActivate) {
92
+ return reply
93
+ }
94
+ },
146
95
  )
147
- case EndpointType.Handler:
148
- this.logger.error('Not implemented yet')
149
- throw new NaviosException('Not implemented yet')
150
- }
151
- }
152
-
153
- private provideHandlerForConfig(
154
- controller: ClassType,
155
- executionContext: ExecutionContext,
156
- endpointMetadata: EndpointMetadata,
157
- ): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
158
- return async (request, reply) => {
159
- getServiceLocator().registerInstance(Request, request)
160
- getServiceLocator().registerInstance(Reply, reply)
161
- getServiceLocator().registerInstance(
162
- ExecutionContextToken,
163
- executionContext,
164
- )
165
- executionContext.provideRequest(request)
166
- executionContext.provideReply(reply)
167
- const controllerInstance = await inject(controller)
168
- try {
169
- const { query, params, body } = request
170
- const argument: Record<string, any> = {}
171
- if (query && Object.keys(query).length > 0) {
172
- argument.params = query
173
- }
174
- if (params && Object.keys(params).length > 0) {
175
- argument.urlParams = params
176
- }
177
- if (body) {
178
- argument.data = body
179
- }
180
- const result =
181
- await controllerInstance[endpointMetadata.classMethod](argument)
182
- reply
183
- .status(endpointMetadata.successStatusCode)
184
- .headers(endpointMetadata.headers)
185
- .send(result)
186
- } finally {
187
- getServiceLocator().removeInstance(Request)
188
- getServiceLocator().removeInstance(Reply)
189
- getServiceLocator().removeInstance(ExecutionContextToken)
190
- }
191
- }
192
- }
193
-
194
- private provideHandlerForStream(
195
- controller: ClassType,
196
- executionContext: ExecutionContext,
197
- endpointMetadata: EndpointMetadata,
198
- ): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
199
- return async (request, reply) => {
200
- getServiceLocator().registerInstance(Request, request)
201
- getServiceLocator().registerInstance(Reply, reply)
202
- getServiceLocator().registerInstance(
203
- ExecutionContextToken,
204
- executionContext,
205
- )
206
- executionContext.provideRequest(request)
207
- executionContext.provideReply(reply)
208
- const controllerInstance = await inject(controller)
209
- try {
210
- const { query, params, body } = request
211
- const argument: Record<string, any> = {}
212
- if (query && Object.keys(query).length > 0) {
213
- argument.params = query
214
- }
215
- if (params && Object.keys(params).length > 0) {
216
- argument.urlParams = params
217
- }
218
- if (body) {
219
- argument.data = body
220
- }
221
-
222
- await controllerInstance[endpointMetadata.classMethod](argument, reply)
223
- } finally {
224
- getServiceLocator().removeInstance(Request)
225
- getServiceLocator().removeInstance(Reply)
226
- getServiceLocator().removeInstance(ExecutionContextToken)
227
- }
228
- }
96
+ : undefined
229
97
  }
230
98
 
231
- private provideHandlerForMultipart(
232
- controller: ClassType,
99
+ private wrapHandler(
233
100
  executionContext: ExecutionContext,
234
- endpointMetadata: EndpointMetadata,
235
- ): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
236
- const config = endpointMetadata.config as BaseEndpointConfig
237
- const requestSchema = config.requestSchema as unknown as AnyZodObject
238
- const shape = requestSchema._def.shape()
239
- return async (request, reply) => {
240
- getServiceLocator().registerInstance(Request, request)
241
- getServiceLocator().registerInstance(Reply, reply)
242
- getServiceLocator().registerInstance(
243
- ExecutionContextToken,
244
- executionContext,
245
- )
101
+ handler: (request: FastifyRequest, reply: FastifyReply) => Promise<void>,
102
+ ) {
103
+ const locator = getServiceLocator()
104
+ return async (request: FastifyRequest, reply: FastifyReply) => {
105
+ locator.registerInstance(Request, request)
106
+ locator.registerInstance(Reply, reply)
107
+ locator.registerInstance(ExecutionContextToken, executionContext)
246
108
  executionContext.provideRequest(request)
247
109
  executionContext.provideReply(reply)
248
- const controllerInstance = await inject(controller)
249
110
  try {
250
- const parts = request.parts()
251
- const { query, params } = request
252
- const argument: Record<string, any> = {}
253
- if (query && Object.keys(query).length > 0) {
254
- argument.params = query
255
- }
256
- if (params && Object.keys(params).length > 0) {
257
- argument.urlParams = params
258
- }
259
- const req: Record<string, any> = {}
260
- for await (const part of parts) {
261
- if (!shape[part.fieldname]) {
262
- throw new NaviosException(
263
- `Invalid field name ${part.fieldname} for multipart request`,
264
- )
265
- }
266
- const schema = shape[part.fieldname]
267
- if (part.type === 'file') {
268
- const file = new File([await part.toBuffer()], part.filename, {
269
- type: part.mimetype,
270
- })
271
- if (schema instanceof ZodArray) {
272
- if (!req[part.fieldname]) {
273
- req[part.fieldname] = []
274
- }
275
- req[part.fieldname].push(file)
276
- } else {
277
- req[part.fieldname] = file
278
- }
279
- } else {
280
- if (schema instanceof ZodArray) {
281
- if (!req[part.fieldname]) {
282
- req[part.fieldname] = []
283
- }
284
- req[part.fieldname].push(part.value)
285
- } else {
286
- req[part.fieldname] = part.value
287
- }
288
- }
289
- }
290
- argument.data = requestSchema.parse(req)
291
- const result =
292
- await controllerInstance[endpointMetadata.classMethod](argument)
293
- reply
294
- .status(endpointMetadata.successStatusCode)
295
- .headers(endpointMetadata.headers)
296
- .send(result)
111
+ return await handler(request, reply)
297
112
  } finally {
298
- getServiceLocator().removeInstance(Request)
299
- getServiceLocator().removeInstance(Reply)
300
- getServiceLocator().removeInstance(ExecutionContextToken)
113
+ Promise.all([
114
+ locator.removeInstance(Request),
115
+ locator.removeInstance(Reply),
116
+ locator.removeInstance(ExecutionContextToken),
117
+ ]).catch((err) => {
118
+ this.logger.warn(`Error removing instances: ${err}`)
119
+ })
301
120
  }
302
121
  }
303
122
  }