@navios/core 0.2.1 → 0.3.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 (41) hide show
  1. package/README.md +7 -3
  2. package/docs/README.md +6 -0
  3. package/docs/recipes/prisma.md +60 -0
  4. package/examples/simple-test/api/index.mts +64 -0
  5. package/examples/simple-test/config/config.service.mts +14 -0
  6. package/examples/simple-test/config/configuration.mts +7 -0
  7. package/examples/simple-test/index.mts +16 -0
  8. package/examples/simple-test/src/acl/acl-modern.guard.mts +15 -0
  9. package/examples/simple-test/src/acl/acl.guard.mts +14 -0
  10. package/examples/simple-test/src/acl/app.guard.mts +27 -0
  11. package/examples/simple-test/src/acl/one-more.guard.mts +15 -0
  12. package/examples/simple-test/src/acl/public.attribute.mts +21 -0
  13. package/examples/simple-test/src/app.module.mts +9 -0
  14. package/examples/simple-test/src/user/user.controller.mts +72 -0
  15. package/examples/simple-test/src/user/user.module.mts +14 -0
  16. package/examples/simple-test/src/user/user.service.mts +14 -0
  17. package/{dist → lib}/_tsup-dts-rollup.d.mts +76 -55
  18. package/{dist → lib}/_tsup-dts-rollup.d.ts +76 -55
  19. package/{dist → lib}/index.d.mts +13 -4
  20. package/{dist → lib}/index.d.ts +13 -4
  21. package/{dist → lib}/index.js +270 -490
  22. package/lib/index.js.map +1 -0
  23. package/{dist → lib}/index.mjs +140 -353
  24. package/lib/index.mjs.map +1 -0
  25. package/package.json +12 -13
  26. package/project.json +53 -0
  27. package/src/__tests__/config.service.spec.mts +41 -0
  28. package/src/__tests__/controller.spec.mts +1 -1
  29. package/src/adapters/endpoint-adapter.service.mts +1 -1
  30. package/src/adapters/multipart-adapter.service.mts +1 -1
  31. package/src/adapters/stream-adapter.service.mts +1 -1
  32. package/src/config/config-service.interface.mts +1 -1
  33. package/src/config/config.provider.mts +22 -50
  34. package/src/config/config.service.mts +26 -9
  35. package/src/decorators/endpoint.decorator.mts +1 -6
  36. package/src/decorators/multipart.decorator.mts +1 -1
  37. package/src/decorators/stream.decorator.mts +1 -1
  38. package/src/metadata/handler.metadata.mts +1 -1
  39. package/tsconfig.json +16 -0
  40. package/tsup.config.mts +12 -0
  41. package/vitest.config.mts +9 -0
package/README.md CHANGED
@@ -23,7 +23,6 @@ It uses Fastify under the hood, which is a fast and low-overhead web framework f
23
23
  - **Service**: A service is a class that defines the business logic for a specific resource. It is used to separate the business logic from the controller and provide a clear structure for your API.
24
24
  - **Guard**: A guard is a class that is used to validate incoming requests and ensure that they meet certain criteria. Guards can be used to validate request parameters, headers, and body. They can also be used to check authentication and authorization.
25
25
  - **Attribute**: An attribute is a decorator that is used to add metadata to a class or method. Attributes can be used in guards, controllers, modules, and endpoints to provide additional information about the class or method.
26
-
27
26
 
28
27
  ## Getting Started
29
28
 
@@ -33,6 +32,7 @@ Define your API in a shared location accessible to both the client and server. T
33
32
 
34
33
  ```ts
35
34
  import { builder } from '@navios/core'
35
+
36
36
  import { z } from 'zod'
37
37
 
38
38
  const api = builder({
@@ -66,7 +66,7 @@ const login = api.declareEndpoint({
66
66
  ### Create your server
67
67
 
68
68
  ```bash
69
- yarn install --save @navios/core @navios/common zod
69
+ yarn install --save @navios/core @navios/builder zod
70
70
  ```
71
71
 
72
72
  Create AuthService:
@@ -86,7 +86,9 @@ export class LoginService {
86
86
  Create your first Controller:
87
87
 
88
88
  ```ts
89
- import { Controller, Endpoint, type EndpointParams, syncInject } from '@navios/core'
89
+ import type { EndpointParams } from '@navios/core'
90
+
91
+ import { Controller, Endpoint, syncInject } from '@navios/core'
90
92
 
91
93
  import { AuthService } from './auth.service.mjs'
92
94
 
@@ -111,6 +113,7 @@ export class AuthController {
111
113
  }
112
114
  }
113
115
  ```
116
+
114
117
  Create your AppModule:
115
118
 
116
119
  ```ts
@@ -128,6 +131,7 @@ Create your server:
128
131
 
129
132
  ```ts
130
133
  import { NaviosFactory } from '@navios/core'
134
+
131
135
  import { AppModule } from './src/app.module.mjs'
132
136
 
133
137
  export async function boot() {
package/docs/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # Introduction
2
+
3
+ Navios us a framework for building Type-Safe Node.js servers. It uses TypeScript and Navios builder to generate a server with a specific structure. The framework is designed to be easy to use and understand, while also being powerful and flexible.
4
+
5
+ Under the hood, Navios makes use of [Fastify](https://www.fastify.io/) for the server implementation, and [Zod](https://zod.dev/) for schema validation. This allows Navios to provide a type-safe experience while still being performant and easy to use.
6
+
@@ -0,0 +1,60 @@
1
+ # Prisma ORM
2
+
3
+ Prisma ORM is a modern database toolkit that simplifies database access and management. It provides a type-safe query builder, migrations, and an intuitive API for working with databases.
4
+
5
+ ## Installation
6
+
7
+ Follow the steps from the [Prisma documentation](https://www.prisma.io/docs/getting-started/quickstart-typescript) to install Prisma in your project.
8
+
9
+ ## Integration
10
+
11
+ The simplest way to integrate Prisma with your project is to create a factory:
12
+
13
+ ```typescript
14
+ import { Injectable, InjectableType, InjectionToken } from '@navios/core'
15
+
16
+ import { PrismaClient } from '@prisma/client'
17
+
18
+ export const PrismaService = InjectionToken.create(PrismaClient)
19
+
20
+ @Injectable({
21
+ token: PrismaService,
22
+ type: InjectableType.Factory,
23
+ })
24
+ export class PrismaServiceFactory {
25
+ async create() {
26
+ const client = new PrismaClient()
27
+ await client.$connect()
28
+ return client
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ You can use the Prisma Service in your application by injecting it into your classes. Here's an example of how to use it in a service:
36
+
37
+ ```typescript
38
+ import { Injectable, syncInject } from '@navios/core'
39
+
40
+ import { PrismaService } from './prisma.service.mjs'
41
+
42
+ @Injectable()
43
+ export class UserService {
44
+ private readonly prisma = syncInject(PrismaService)
45
+
46
+ async getUser(id: string) {
47
+ return this.prisma.user.findUnique({
48
+ where: { id },
49
+ })
50
+ }
51
+
52
+ async createUser(data: { name: string; email: string }) {
53
+ return this.prisma.user.create({
54
+ data,
55
+ })
56
+ }
57
+ }
58
+ ```
59
+
60
+ That's it! You can now use Prisma in your application to interact with your database. You can find more information about Prisma and its features in the [Prisma documentation](https://www.prisma.io/docs/).
@@ -0,0 +1,64 @@
1
+ import { builder } from '@navios/builder'
2
+
3
+ import { z } from 'zod'
4
+
5
+ export const authApi = builder({
6
+ useDiscriminatorResponse: true,
7
+ })
8
+
9
+ export const userEndpoint = authApi.declareEndpoint({
10
+ method: 'GET',
11
+ url: '/user',
12
+ responseSchema: z.object({
13
+ id: z.string(),
14
+ name: z.string(),
15
+ email: z.string(),
16
+ }),
17
+ })
18
+
19
+ export const patchUserEndpoint = authApi.declareEndpoint({
20
+ method: 'PATCH',
21
+ url: '/user',
22
+ requestSchema: z.object({
23
+ name: z.string(),
24
+ email: z.string(),
25
+ }),
26
+ responseSchema: z.object({
27
+ id: z.string(),
28
+ name: z.string(),
29
+ email: z.string(),
30
+ }),
31
+ })
32
+
33
+ export const discriminatorEndpoint = authApi.declareEndpoint({
34
+ method: 'GET',
35
+ url: '/discriminator',
36
+ responseSchema: z.discriminatedUnion('success', [
37
+ z.object({
38
+ success: z.literal(true),
39
+ data: z.object({
40
+ id: z.string(),
41
+ name: z.string(),
42
+
43
+ email: z.string(),
44
+ }),
45
+ }),
46
+ z.object({
47
+ success: z.literal(false),
48
+ error: z.object({
49
+ code: z.string(),
50
+ message: z.string(),
51
+ }),
52
+ }),
53
+ ]),
54
+ })
55
+
56
+ export const multipartEndpoint = authApi.declareMultipart({
57
+ method: 'POST',
58
+ url: '/multipart',
59
+ requestSchema: z.object({
60
+ files: z.array(z.instanceof(File)),
61
+ something: z.string(),
62
+ }),
63
+ responseSchema: z.object({}),
64
+ })
@@ -0,0 +1,14 @@
1
+ import type { Path, PathValue } from '../../../src/index.mjs'
2
+
3
+ import { provideConfig } from '../../../src/index.mjs'
4
+ import { configureConfig } from './configuration.mjs'
5
+
6
+ export type ConfigType = ReturnType<typeof configureConfig>
7
+ export type ConfigTypePaths = Path<ConfigType>
8
+ export type ConfigMap = {
9
+ [K in ConfigTypePaths]: PathValue<ConfigType, K>
10
+ }
11
+
12
+ export const ConfigService = provideConfig<ConfigMap>({
13
+ load: configureConfig,
14
+ })
@@ -0,0 +1,7 @@
1
+ import { envInt } from '../../../src/index.mjs'
2
+
3
+ export function configureConfig() {
4
+ return {
5
+ port: envInt('PORT', 4250),
6
+ }
7
+ }
@@ -0,0 +1,16 @@
1
+ import { inject, NaviosFactory } from '../../src/index.mjs'
2
+ import { ConfigService } from './config/config.service.mjs'
3
+ import { AppModule } from './src/app.module.mjs'
4
+
5
+ export async function boot() {
6
+ const app = await NaviosFactory.create(AppModule)
7
+ app.enableCors({
8
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
9
+ })
10
+ app.enableMultipart({})
11
+ await app.init()
12
+ const configService = await inject(ConfigService)
13
+ const port = configService.getOrThrow('port')
14
+ await app.listen({ port: port, host: '0.0.0.0' })
15
+ }
16
+ await boot()
@@ -0,0 +1,15 @@
1
+ import type { CanActivate, ExecutionContext } from '../../../../src/index.mjs'
2
+
3
+ import { Injectable, Logger, syncInject } from '../../../../src/index.mjs'
4
+
5
+ @Injectable()
6
+ export class AclModernGuard implements CanActivate {
7
+ logger = syncInject(Logger, {
8
+ context: AclModernGuard.name,
9
+ })
10
+
11
+ canActivate(executionContext: ExecutionContext): Promise<boolean> | boolean {
12
+ this.logger.log('ACL Modern Guard activated')
13
+ return true
14
+ }
15
+ }
@@ -0,0 +1,14 @@
1
+ import type { CanActivate, ExecutionContext } from '../../../../src/index.mjs'
2
+
3
+ import { Injectable, Logger, syncInject } from '../../../../src/index.mjs'
4
+
5
+ @Injectable()
6
+ export class AclGuard implements CanActivate {
7
+ logger = syncInject(Logger, {
8
+ context: AclGuard.name,
9
+ })
10
+ canActivate(executionContext: ExecutionContext): Promise<boolean> | boolean {
11
+ this.logger.log('ACL Guard activated')
12
+ return true
13
+ }
14
+ }
@@ -0,0 +1,27 @@
1
+ import type { CanActivate, ExecutionContext } from '../../../../src/index.mjs'
2
+
3
+ import {
4
+ AttributeFactory,
5
+ Injectable,
6
+ Logger,
7
+ syncInject,
8
+ } from '../../../../src/index.mjs'
9
+ import { Public } from './public.attribute.mjs'
10
+
11
+ @Injectable()
12
+ export class AppGuard implements CanActivate {
13
+ logger = syncInject(Logger, {
14
+ context: AppGuard.name,
15
+ })
16
+
17
+ canActivate(executionContext: ExecutionContext): Promise<boolean> | boolean {
18
+ const isPublic = AttributeFactory.getLast(Public, [
19
+ executionContext.getModule(),
20
+ executionContext.getController(),
21
+ executionContext.getHandler(),
22
+ ])
23
+ this.logger.log('App Guard activated')
24
+ this.logger.log('isPublic', isPublic)
25
+ return true
26
+ }
27
+ }
@@ -0,0 +1,15 @@
1
+ import type { CanActivate, ExecutionContext } from '../../../../src/index.mjs'
2
+
3
+ import { Injectable, Logger, syncInject } from '../../../../src/index.mjs'
4
+
5
+ @Injectable()
6
+ export class OneMoreGuard implements CanActivate {
7
+ logger = syncInject(Logger, {
8
+ context: OneMoreGuard.name,
9
+ })
10
+
11
+ canActivate(executionContext: ExecutionContext): Promise<boolean> | boolean {
12
+ this.logger.log('One More Guard activated')
13
+ return true
14
+ }
15
+ }
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod'
2
+
3
+ import { AttributeFactory } from '../../../../src/index.mjs'
4
+
5
+ export const PublicSymbol = Symbol.for('Public')
6
+
7
+ export const Public = AttributeFactory.createAttribute(PublicSymbol)
8
+ export const RolesSymbol = Symbol.for('Roles')
9
+
10
+ export const RolesSchema = z.object({
11
+ roles: z.array(
12
+ z.union([
13
+ z.literal('VIEWER'),
14
+ z.literal('USER'),
15
+ z.literal('ADMIN'),
16
+ z.literal('OWNER'),
17
+ ]),
18
+ ),
19
+ })
20
+
21
+ export const Roles = AttributeFactory.createAttribute(RolesSymbol, RolesSchema)
@@ -0,0 +1,9 @@
1
+ import { Module } from '../../../src/index.mjs'
2
+ import { AppGuard } from './acl/app.guard.mjs'
3
+ import { UserModule } from './user/user.module.mjs'
4
+
5
+ @Module({
6
+ imports: [UserModule],
7
+ guards: [AppGuard],
8
+ })
9
+ export class AppModule {}
@@ -0,0 +1,72 @@
1
+ import type {
2
+ EndpointParams,
3
+ EndpointResult,
4
+ MultipartParams,
5
+ } from '../../../../src/index.mjs'
6
+
7
+ import {
8
+ Controller,
9
+ Endpoint,
10
+ Logger,
11
+ Multipart,
12
+ syncInject,
13
+ UseGuards,
14
+ } from '../../../../src/index.mjs'
15
+ import {
16
+ discriminatorEndpoint,
17
+ multipartEndpoint,
18
+ patchUserEndpoint,
19
+ userEndpoint,
20
+ } from '../../api/index.mjs'
21
+ import { AclGuard } from '../acl/acl.guard.mjs'
22
+ import { OneMoreGuard } from '../acl/one-more.guard.mjs'
23
+ import { Public } from '../acl/public.attribute.mjs'
24
+ import { UserService } from './user.service.mjs'
25
+
26
+ @UseGuards(AclGuard)
27
+ @Controller()
28
+ export class UserController {
29
+ userService = syncInject(UserService)
30
+ logger = syncInject(Logger, {
31
+ context: UserController.name,
32
+ })
33
+
34
+ @Public()
35
+ @UseGuards(OneMoreGuard)
36
+ @Endpoint(userEndpoint)
37
+ async me(params: EndpointParams<typeof userEndpoint>) {
38
+ this.logger.log(params)
39
+ return this.userService.getUser()
40
+ }
41
+
42
+ @Endpoint(patchUserEndpoint)
43
+ async patchMe(params: EndpointParams<typeof patchUserEndpoint>) {
44
+ this.logger.log(params)
45
+ return {
46
+ ...this.userService.getUser(),
47
+ ...params.data,
48
+ }
49
+ }
50
+
51
+ @Endpoint(discriminatorEndpoint)
52
+ async discriminator(
53
+ params: EndpointParams<typeof discriminatorEndpoint>,
54
+ ): EndpointResult<typeof discriminatorEndpoint> {
55
+ this.logger.log(params)
56
+ return {
57
+ success: true,
58
+ data: {
59
+ id: '123',
60
+ name: 'John Doe',
61
+ email: 'test@example.com',
62
+ },
63
+ }
64
+ }
65
+
66
+ @Multipart(multipartEndpoint)
67
+ async multipart(params: MultipartParams<typeof multipartEndpoint>) {
68
+ this.logger.log(params)
69
+ // params.data.
70
+ return {}
71
+ }
72
+ }
@@ -0,0 +1,14 @@
1
+ import { Logger, Module, syncInject } from '../../../../src/index.mjs'
2
+ import { AclModernGuard } from '../acl/acl-modern.guard.mjs'
3
+ import { UserController } from './user.controller.mjs'
4
+
5
+ @Module({
6
+ controllers: [UserController],
7
+ guards: [AclModernGuard],
8
+ })
9
+ export class UserModule {
10
+ logger = syncInject(Logger)
11
+ onModuleInit() {
12
+ this.logger.debug('Inside UserModule.onModuleInit')
13
+ }
14
+ }
@@ -0,0 +1,14 @@
1
+ import { randomUUID } from 'node:crypto'
2
+
3
+ import { Injectable } from '../../../../src/index.mjs'
4
+
5
+ @Injectable()
6
+ export class UserService {
7
+ getUser() {
8
+ return {
9
+ id: randomUUID() as string,
10
+ name: 'John Doe',
11
+ email: 'test@example.com',
12
+ }
13
+ }
14
+ }