@navios/core 0.1.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.
Files changed (72) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +145 -0
  3. package/dist/index.d.mts +425 -0
  4. package/dist/index.d.ts +425 -0
  5. package/dist/index.js +1744 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +1657 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +45 -0
  10. package/src/__tests__/controller.spec.mts +59 -0
  11. package/src/attribute.factory.mts +155 -0
  12. package/src/decorators/controller.decorator.mts +36 -0
  13. package/src/decorators/endpoint.decorator.mts +81 -0
  14. package/src/decorators/index.mts +4 -0
  15. package/src/decorators/module.decorator.mts +47 -0
  16. package/src/decorators/use-guards.decorator.mts +43 -0
  17. package/src/exceptions/bad-request.exception.mts +7 -0
  18. package/src/exceptions/conflict.exception.mts +7 -0
  19. package/src/exceptions/forbidden.exception.mts +7 -0
  20. package/src/exceptions/http.exception.mts +7 -0
  21. package/src/exceptions/index.mts +7 -0
  22. package/src/exceptions/internal-server-error.exception.mts +7 -0
  23. package/src/exceptions/not-found.exception.mts +10 -0
  24. package/src/exceptions/unauthorized.exception.mts +7 -0
  25. package/src/index.mts +10 -0
  26. package/src/interfaces/can-activate.mts +5 -0
  27. package/src/interfaces/index.mts +2 -0
  28. package/src/interfaces/navios-module.mts +3 -0
  29. package/src/metadata/controller.metadata.mts +71 -0
  30. package/src/metadata/endpoint.metadata.mts +69 -0
  31. package/src/metadata/index.mts +4 -0
  32. package/src/metadata/injectable.metadata.mts +11 -0
  33. package/src/metadata/module.metadata.mts +62 -0
  34. package/src/navios.application.mts +113 -0
  35. package/src/navios.factory.mts +17 -0
  36. package/src/service-locator/__tests__/injectable.spec.mts +170 -0
  37. package/src/service-locator/decorators/get-injectable-token.mts +23 -0
  38. package/src/service-locator/decorators/index.mts +2 -0
  39. package/src/service-locator/decorators/injectable.decorator.mts +103 -0
  40. package/src/service-locator/enums/index.mts +1 -0
  41. package/src/service-locator/enums/injectable-scope.enum.mts +10 -0
  42. package/src/service-locator/errors/errors.enum.mts +7 -0
  43. package/src/service-locator/errors/factory-not-found.mts +8 -0
  44. package/src/service-locator/errors/index.mts +6 -0
  45. package/src/service-locator/errors/instance-destroying.mts +8 -0
  46. package/src/service-locator/errors/instance-expired.mts +8 -0
  47. package/src/service-locator/errors/instance-not-found.mts +8 -0
  48. package/src/service-locator/errors/unknown-error.mts +15 -0
  49. package/src/service-locator/event-emitter.mts +107 -0
  50. package/src/service-locator/index.mts +14 -0
  51. package/src/service-locator/inject.mts +37 -0
  52. package/src/service-locator/injection-token.mts +36 -0
  53. package/src/service-locator/injector.mts +18 -0
  54. package/src/service-locator/interfaces/factory.interface.mts +3 -0
  55. package/src/service-locator/override.mts +22 -0
  56. package/src/service-locator/proxy-service-locator.mts +80 -0
  57. package/src/service-locator/service-locator-abstract-factory-context.mts +23 -0
  58. package/src/service-locator/service-locator-event-bus.mts +96 -0
  59. package/src/service-locator/service-locator-instance-holder.mts +63 -0
  60. package/src/service-locator/service-locator-manager.mts +89 -0
  61. package/src/service-locator/service-locator.mts +445 -0
  62. package/src/service-locator/sync-injector.mts +55 -0
  63. package/src/services/controller-adapter.service.mts +124 -0
  64. package/src/services/execution-context.mts +53 -0
  65. package/src/services/guard-runner.service.mts +93 -0
  66. package/src/services/index.mts +4 -0
  67. package/src/services/module-loader.service.mts +68 -0
  68. package/src/tokens/application.token.mts +9 -0
  69. package/src/tokens/execution-context.token.mts +8 -0
  70. package/src/tokens/index.mts +4 -0
  71. package/src/tokens/reply.token.mts +7 -0
  72. package/src/tokens/request.token.mts +9 -0
@@ -0,0 +1,4 @@
1
+ export * from './controller.decorator.mjs'
2
+ export * from './endpoint.decorator.mjs'
3
+ export * from './module.decorator.mjs'
4
+ export * from './use-guards.decorator.mjs'
@@ -0,0 +1,47 @@
1
+ import type { ClassType } from '../service-locator/index.mjs'
2
+
3
+ import { getModuleMetadata } from '../metadata/index.mjs'
4
+ import {
5
+ Injectable,
6
+ InjectableScope,
7
+ InjectableType,
8
+ InjectionToken,
9
+ } from '../service-locator/index.mjs'
10
+
11
+ export interface ModuleOptions {
12
+ controllers?: ClassType[] | Set<ClassType>
13
+ imports?: ClassType[] | Set<ClassType>
14
+ guards?: ClassType[] | Set<ClassType>
15
+ }
16
+
17
+ export function Module(metadata: ModuleOptions) {
18
+ return (target: ClassType, context: ClassDecoratorContext) => {
19
+ if (context.kind !== 'class') {
20
+ throw new Error('[Navios] @Module decorator can only be used on classes.')
21
+ }
22
+ // Register the module in the service locator
23
+ const token = InjectionToken.create(target)
24
+ const moduleMetadata = getModuleMetadata(target, context)
25
+ if (metadata.controllers) {
26
+ for (const controller of metadata.controllers) {
27
+ moduleMetadata.controllers.add(controller)
28
+ }
29
+ }
30
+ if (metadata.imports) {
31
+ for (const importedModule of metadata.imports) {
32
+ moduleMetadata.imports.add(importedModule)
33
+ }
34
+ }
35
+ if (metadata.guards) {
36
+ for (const guard of Array.from(metadata.guards).reverse()) {
37
+ moduleMetadata.guards.add(guard)
38
+ }
39
+ }
40
+
41
+ return Injectable({
42
+ token,
43
+ type: InjectableType.Class,
44
+ scope: InjectableScope.Singleton,
45
+ })(target, context)
46
+ }
47
+ }
@@ -0,0 +1,43 @@
1
+ import type { CanActivate } from '../interfaces/index.mjs'
2
+ import type {
3
+ ClassType,
4
+ ClassTypeWithInstance,
5
+ InjectionToken,
6
+ } from '../service-locator/index.mjs'
7
+
8
+ import {
9
+ getControllerMetadata,
10
+ getEndpointMetadata,
11
+ } from '../metadata/index.mjs'
12
+
13
+ export function UseGuards(
14
+ ...guards: (
15
+ | ClassTypeWithInstance<CanActivate>
16
+ | InjectionToken<CanActivate, undefined>
17
+ )[]
18
+ ) {
19
+ return function <T extends Function>(
20
+ target: T,
21
+ context: ClassMethodDecoratorContext | ClassDecoratorContext,
22
+ ): T {
23
+ if (context.kind === 'class') {
24
+ const controllerMetadata = getControllerMetadata(
25
+ target as unknown as ClassType,
26
+ context,
27
+ )
28
+ for (const guard of guards.reverse()) {
29
+ controllerMetadata.guards.add(guard)
30
+ }
31
+ } else if (context.kind === 'method') {
32
+ const endpointMetadata = getEndpointMetadata(target, context)
33
+ for (const guard of guards.reverse()) {
34
+ endpointMetadata.guards.add(guard)
35
+ }
36
+ } else {
37
+ throw new Error(
38
+ '[Navios] @UseGuards decorator can only be used on classes or methods.',
39
+ )
40
+ }
41
+ return target
42
+ }
43
+ }
@@ -0,0 +1,7 @@
1
+ import { HttpException } from './http.exception.mjs'
2
+
3
+ export class BadRequestException extends HttpException {
4
+ constructor(message: string | object) {
5
+ super(400, message)
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ import { HttpException } from './http.exception.mjs'
2
+
3
+ export class ConflictException extends HttpException {
4
+ constructor(message: string | object, error?: Error) {
5
+ super(409, message, error)
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ import { HttpException } from './http.exception.mjs'
2
+
3
+ export class ForbiddenException extends HttpException {
4
+ constructor(message: string) {
5
+ super(403, message)
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ export class HttpException {
2
+ constructor(
3
+ public readonly statusCode: number,
4
+ public readonly response: string | object,
5
+ public readonly error?: Error,
6
+ ) {}
7
+ }
@@ -0,0 +1,7 @@
1
+ export * from './http.exception.mjs'
2
+ export * from './bad-request.exception.mjs'
3
+ export * from './forbidden.exception.mjs'
4
+ export * from './internal-server-error.exception.mjs'
5
+ export * from './not-found.exception.mjs'
6
+ export * from './unauthorized.exception.mjs'
7
+ export * from './conflict.exception.mjs'
@@ -0,0 +1,7 @@
1
+ import { HttpException } from './http.exception.mjs'
2
+
3
+ export class InternalServerErrorException extends HttpException {
4
+ constructor(message: string | object, error?: Error) {
5
+ super(500, message, error)
6
+ }
7
+ }
@@ -0,0 +1,10 @@
1
+ import { HttpException } from './http.exception.mjs'
2
+
3
+ export class NotFoundException extends HttpException {
4
+ constructor(
5
+ public readonly response: string | object,
6
+ public readonly error?: Error,
7
+ ) {
8
+ super(404, response, error)
9
+ }
10
+ }
@@ -0,0 +1,7 @@
1
+ import { HttpException } from './http.exception.mjs'
2
+
3
+ export class UnauthorizedException extends HttpException {
4
+ constructor(message: string | object, error?: Error) {
5
+ super(401, message, error)
6
+ }
7
+ }
package/src/index.mts ADDED
@@ -0,0 +1,10 @@
1
+ export * from './decorators/index.mjs'
2
+ export * from './exceptions/index.mjs'
3
+ export * from './interfaces/index.mjs'
4
+ export * from './metadata/index.mjs'
5
+ export * from './service-locator/index.mjs'
6
+ export * from './services/index.mjs'
7
+ export * from './tokens/index.mjs'
8
+ export * from './attribute.factory.mjs'
9
+ export * from './navios.application.mjs'
10
+ export * from './navios.factory.mjs'
@@ -0,0 +1,5 @@
1
+ import type { ExecutionContext } from '../services/index.mjs'
2
+
3
+ export interface CanActivate {
4
+ canActivate(executionContext: ExecutionContext): Promise<boolean> | boolean
5
+ }
@@ -0,0 +1,2 @@
1
+ export * from './can-activate.mjs'
2
+ export * from './navios-module.mjs'
@@ -0,0 +1,3 @@
1
+ export interface NaviosModule {
2
+ onModuleInit?: () => Promise<void> | void
3
+ }
@@ -0,0 +1,71 @@
1
+ import type { CanActivate } from '../interfaces/index.mjs'
2
+ import type {
3
+ ClassType,
4
+ ClassTypeWithInstance,
5
+ InjectionToken,
6
+ } from '../service-locator/index.mjs'
7
+ import type { EndpointMetadata } from './endpoint.metadata.mjs'
8
+
9
+ import { getAllEndpointMetadata } from './endpoint.metadata.mjs'
10
+
11
+ export const ControllerMetadataKey = Symbol('ControllerMetadataKey')
12
+
13
+ export interface ControllerMetadata {
14
+ endpoints: Set<EndpointMetadata>
15
+ guards: Set<
16
+ ClassTypeWithInstance<CanActivate> | InjectionToken<CanActivate, undefined>
17
+ >
18
+ customAttributes: Map<string | symbol, any>
19
+ }
20
+
21
+ export function getControllerMetadata(
22
+ target: ClassType,
23
+ context: ClassDecoratorContext,
24
+ ): ControllerMetadata {
25
+ if (context.metadata) {
26
+ const metadata = context.metadata[ControllerMetadataKey] as
27
+ | ControllerMetadata
28
+ | undefined
29
+ if (metadata) {
30
+ return metadata
31
+ } else {
32
+ const endpointsMetadata = getAllEndpointMetadata(context)
33
+ const newMetadata: ControllerMetadata = {
34
+ endpoints: endpointsMetadata,
35
+ guards: new Set<
36
+ | ClassTypeWithInstance<CanActivate>
37
+ | InjectionToken<CanActivate, undefined>
38
+ >(),
39
+ customAttributes: new Map<string | symbol, any>(),
40
+ }
41
+ context.metadata[ControllerMetadataKey] = newMetadata
42
+ // @ts-expect-error We add a custom metadata key to the target
43
+ target[ControllerMetadataKey] = newMetadata
44
+ return newMetadata
45
+ }
46
+ }
47
+ throw new Error('[Navios] Wrong environment.')
48
+ }
49
+
50
+ export function extractControllerMetadata(
51
+ target: ClassType,
52
+ ): ControllerMetadata {
53
+ // @ts-expect-error We add a custom metadata key to the target
54
+ const metadata = target[ControllerMetadataKey] as
55
+ | ControllerMetadata
56
+ | undefined
57
+ if (!metadata) {
58
+ throw new Error(
59
+ '[Navios] Controller metadata not found. Make sure to use @Controller decorator.',
60
+ )
61
+ }
62
+ return metadata
63
+ }
64
+
65
+ export function hasControllerMetadata(target: ClassType): boolean {
66
+ // @ts-expect-error We add a custom metadata key to the target
67
+ const metadata = target[ControllerMetadataKey] as
68
+ | ControllerMetadata
69
+ | undefined
70
+ return !!metadata
71
+ }
@@ -0,0 +1,69 @@
1
+ import type { BaseEndpointConfig, HttpMethod } from '@navios/common'
2
+
3
+ import type { CanActivate } from '../interfaces/index.mjs'
4
+ import type {
5
+ ClassTypeWithInstance,
6
+ InjectionToken,
7
+ } from '../service-locator/index.mjs'
8
+
9
+ export const EndpointMetadataKey = Symbol('EndpointMetadataKey')
10
+
11
+ export interface EndpointMetadata {
12
+ classMethod: string
13
+ url: string
14
+ httpMethod: HttpMethod
15
+ config: BaseEndpointConfig | null
16
+ guards: Set<
17
+ ClassTypeWithInstance<CanActivate> | InjectionToken<CanActivate, undefined>
18
+ >
19
+ customAttributes: Map<string | symbol, any>
20
+ }
21
+
22
+ export function getAllEndpointMetadata(
23
+ context: ClassMethodDecoratorContext | ClassDecoratorContext,
24
+ ): Set<EndpointMetadata> {
25
+ if (context.metadata) {
26
+ const metadata = context.metadata[EndpointMetadataKey] as
27
+ | Set<EndpointMetadata>
28
+ | undefined
29
+ if (metadata) {
30
+ return metadata
31
+ } else {
32
+ context.metadata[EndpointMetadataKey] = new Set<EndpointMetadata>()
33
+ return context.metadata[EndpointMetadataKey] as Set<EndpointMetadata>
34
+ }
35
+ }
36
+ throw new Error('[Navios] Wrong environment.')
37
+ }
38
+
39
+ export function getEndpointMetadata(
40
+ target: Function,
41
+ context: ClassMethodDecoratorContext,
42
+ ): EndpointMetadata {
43
+ if (context.metadata) {
44
+ const metadata = getAllEndpointMetadata(context)
45
+ if (metadata) {
46
+ const endpointMetadata = Array.from(metadata).find(
47
+ (item) => item.classMethod === target.name,
48
+ )
49
+ if (endpointMetadata) {
50
+ return endpointMetadata
51
+ } else {
52
+ const newMetadata: EndpointMetadata = {
53
+ classMethod: target.name,
54
+ url: '',
55
+ httpMethod: 'GET',
56
+ config: null,
57
+ guards: new Set<
58
+ | ClassTypeWithInstance<CanActivate>
59
+ | InjectionToken<CanActivate, undefined>
60
+ >(),
61
+ customAttributes: new Map<string | symbol, any>(),
62
+ }
63
+ metadata.add(newMetadata)
64
+ return newMetadata
65
+ }
66
+ }
67
+ }
68
+ throw new Error('[Navios] Wrong environment.')
69
+ }
@@ -0,0 +1,4 @@
1
+ export * from './controller.metadata.mjs'
2
+ export * from './endpoint.metadata.mjs'
3
+ export * from './injectable.metadata.mjs'
4
+ export * from './module.metadata.mjs'
@@ -0,0 +1,11 @@
1
+ import type {
2
+ InjectableScope,
3
+ InjectableType,
4
+ InjectionToken,
5
+ } from '../index.mjs'
6
+
7
+ export interface InjectableMetadata<Instance = any, Schema = any> {
8
+ type: InjectableType
9
+ scope: InjectableScope
10
+ token: InjectionToken<Instance, Schema>
11
+ }
@@ -0,0 +1,62 @@
1
+ import type { CanActivate } from '../index.mjs'
2
+ import type {
3
+ ClassType,
4
+ ClassTypeWithInstance,
5
+ InjectionToken,
6
+ } from '../service-locator/index.mjs'
7
+
8
+ export const ModuleMetadataKey = Symbol('ControllerMetadataKey')
9
+
10
+ export interface ModuleMetadata {
11
+ controllers: Set<ClassType>
12
+ imports: Set<ClassType>
13
+ guards: Set<
14
+ ClassTypeWithInstance<CanActivate> | InjectionToken<CanActivate, undefined>
15
+ >
16
+ customAttributes: Map<string | symbol, any>
17
+ }
18
+
19
+ export function getModuleMetadata(
20
+ target: ClassType,
21
+ context: ClassDecoratorContext,
22
+ ): ModuleMetadata {
23
+ if (context.metadata) {
24
+ const metadata = context.metadata[ModuleMetadataKey] as
25
+ | ModuleMetadata
26
+ | undefined
27
+ if (metadata) {
28
+ return metadata
29
+ } else {
30
+ const newMetadata: ModuleMetadata = {
31
+ controllers: new Set<ClassType>(),
32
+ imports: new Set<ClassType>(),
33
+ guards: new Set<
34
+ | ClassTypeWithInstance<CanActivate>
35
+ | InjectionToken<CanActivate, undefined>
36
+ >(),
37
+ customAttributes: new Map<string | symbol, any>(),
38
+ }
39
+ context.metadata[ModuleMetadataKey] = newMetadata
40
+ // @ts-expect-error We add a custom metadata key to the target
41
+ target[ModuleMetadataKey] = newMetadata
42
+ return newMetadata
43
+ }
44
+ }
45
+ throw new Error('[Navios] Wrong environment.')
46
+ }
47
+
48
+ export function extractModuleMetadata(target: ClassType): ModuleMetadata {
49
+ // @ts-expect-error We add a custom metadata key to the target
50
+ const metadata = target[ModuleMetadataKey] as ModuleMetadata | undefined
51
+ if (!metadata) {
52
+ throw new Error(
53
+ '[Navios] Module metadata not found. Make sure to use @Module decorator.',
54
+ )
55
+ }
56
+ return metadata
57
+ }
58
+
59
+ export function hasModuleMetadata(target: ClassType): boolean {
60
+ // @ts-expect-error We add a custom metadata key to the target
61
+ return !!target[ModuleMetadataKey]
62
+ }
@@ -0,0 +1,113 @@
1
+ import type { FastifyCorsOptions } from '@fastify/cors'
2
+ import type {
3
+ FastifyInstance,
4
+ FastifyListenOptions,
5
+ FastifyServerOptions,
6
+ } from 'fastify'
7
+
8
+ import cors from '@fastify/cors'
9
+ import { fastify } from 'fastify'
10
+ import {
11
+ serializerCompiler,
12
+ validatorCompiler,
13
+ } from 'fastify-type-provider-zod'
14
+
15
+ import type { NaviosModule } from './interfaces/index.mjs'
16
+ import type { ClassTypeWithInstance } from './service-locator/index.mjs'
17
+
18
+ import {
19
+ getServiceLocator,
20
+ Injectable,
21
+ syncInject,
22
+ } from './service-locator/index.mjs'
23
+ import {
24
+ ControllerAdapterService,
25
+ ModuleLoaderService,
26
+ } from './services/index.mjs'
27
+ import { Application } from './tokens/index.mjs'
28
+
29
+ export interface NaviosApplicationOptions extends FastifyServerOptions {}
30
+
31
+ @Injectable()
32
+ export class NaviosApplication {
33
+ private moduleLoader = syncInject(ModuleLoaderService)
34
+ private controllerAdapter = syncInject(ControllerAdapterService)
35
+ private server: FastifyInstance | null = null
36
+ private corsOptions: FastifyCorsOptions | null = null
37
+ private globalPrefix: string | null = null
38
+
39
+ private appModule: ClassTypeWithInstance<NaviosModule> | null = null
40
+ private options: NaviosApplicationOptions = {}
41
+
42
+ setup(
43
+ appModule: ClassTypeWithInstance<NaviosModule>,
44
+ options: NaviosApplicationOptions = {},
45
+ ) {
46
+ this.appModule = appModule
47
+ this.options = options
48
+ }
49
+
50
+ async init() {
51
+ if (!this.appModule) {
52
+ throw new Error('App module is not set. Call setAppModule() first.')
53
+ }
54
+ await this.moduleLoader.loadModules(this.appModule)
55
+ this.server = fastify(this.options)
56
+ getServiceLocator().registerInstance(Application, this.server)
57
+ // Add schema validator and serializer
58
+ this.server.setValidatorCompiler(validatorCompiler)
59
+ this.server.setSerializerCompiler(serializerCompiler)
60
+
61
+ if (this.corsOptions) {
62
+ await this.server.register(cors, this.corsOptions)
63
+ }
64
+ const modules = this.moduleLoader.getAllModules()
65
+ const globalPrefix = this.globalPrefix ?? ''
66
+ for (const [moduleName, moduleMetadata] of modules) {
67
+ if (
68
+ !moduleMetadata.controllers ||
69
+ moduleMetadata.controllers.size === 0
70
+ ) {
71
+ continue
72
+ }
73
+ this.server.register(
74
+ (instance, opts, done) => {
75
+ for (const controller of moduleMetadata.controllers) {
76
+ this.controllerAdapter.setupController(
77
+ controller,
78
+ instance,
79
+ moduleMetadata,
80
+ )
81
+ }
82
+ done()
83
+ },
84
+ {
85
+ prefix: globalPrefix,
86
+ },
87
+ )
88
+ }
89
+ }
90
+
91
+ enableCors(options: FastifyCorsOptions) {
92
+ this.corsOptions = options
93
+ this.server?.register
94
+ }
95
+
96
+ setGlobalPrefix(prefix: string) {
97
+ this.globalPrefix = prefix
98
+ }
99
+
100
+ getServer(): FastifyInstance {
101
+ if (!this.server) {
102
+ throw new Error('Server is not initialized. Call init() first.')
103
+ }
104
+ return this.server
105
+ }
106
+
107
+ async listen(options: FastifyListenOptions) {
108
+ if (!this.server) {
109
+ throw new Error('Server is not initialized. Call init() first.')
110
+ }
111
+ await this.server.listen(options)
112
+ }
113
+ }
@@ -0,0 +1,17 @@
1
+ import type { NaviosModule } from './interfaces/index.mjs'
2
+ import type { NaviosApplicationOptions } from './navios.application.mjs'
3
+ import type { ClassTypeWithInstance } from './service-locator/index.mjs'
4
+
5
+ import { NaviosApplication } from './navios.application.mjs'
6
+ import { inject } from './service-locator/index.mjs'
7
+
8
+ export class NaviosFactory {
9
+ static async create(
10
+ appModule: ClassTypeWithInstance<NaviosModule>,
11
+ options: NaviosApplicationOptions = {},
12
+ ) {
13
+ const app = await inject(NaviosApplication)
14
+ app.setup(appModule, options)
15
+ return app
16
+ }
17
+ }