@navios/core 0.1.1 → 0.1.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navios/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "author": {
5
5
  "name": "Oleksandr Hanzha",
6
6
  "email": "alex@granted.name"
@@ -0,0 +1,16 @@
1
+ import type { Path, PathValue } from './types.mjs'
2
+
3
+ export interface ConfigService<Config = Record<string, unknown>> {
4
+ getConfig: () => Config
5
+ get: <Key extends Path<Config>>(key: Key) => PathValue<Config, Key> | null
6
+
7
+ getOrDefault: <Key extends Path<Config>>(
8
+ key: Key,
9
+ defaultValue: PathValue<Config, Key>,
10
+ ) => PathValue<Config, Key>
11
+
12
+ getOrThrow: <Key extends Path<Config>>(
13
+ key: Key,
14
+ errorMessage?: string,
15
+ ) => PathValue<Config, Key>
16
+ }
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod'
2
+
3
+ import type { ConfigService } from './config-service.interface.mjs'
4
+
5
+ import { Logger } from '../logger/index.mjs'
6
+ import {
7
+ getInjectableToken,
8
+ inject,
9
+ Injectable,
10
+ InjectableType,
11
+ InjectionToken,
12
+ syncInject,
13
+ } from '../service-locator/index.mjs'
14
+ import { ConfigServiceInstance } from './config.service.mjs'
15
+
16
+ export const ConfigProviderInjectionToken = 'ConfigProvider'
17
+
18
+ export const ConfigProviderOptions = z.object({
19
+ load: z.function(),
20
+ })
21
+ export const ConfigProvider = InjectionToken.create<
22
+ ConfigService,
23
+ typeof ConfigProviderOptions
24
+ >(ConfigProviderInjectionToken, ConfigProviderOptions)
25
+
26
+ @Injectable({
27
+ token: ConfigProvider,
28
+ type: InjectableType.Factory,
29
+ })
30
+ export class ConfigProviderFactory {
31
+ logger = inject(Logger, {
32
+ context: 'ConfigService',
33
+ })
34
+
35
+ async create(ctx: any, args: z.infer<typeof ConfigProviderOptions>) {
36
+ const { load } = args
37
+ const logger = await this.logger
38
+ try {
39
+ const config = await load()
40
+
41
+ return new ConfigServiceInstance(config, logger)
42
+ } catch (error) {
43
+ logger.error('Error loading config', error)
44
+ throw error
45
+ }
46
+ }
47
+ }
48
+
49
+ export function makeConfigToken<Config extends Record<string, unknown>>(
50
+ options: z.input<typeof ConfigProviderOptions>,
51
+ ): InjectionToken<ConfigService<Config>> {
52
+ @Injectable({
53
+ type: InjectableType.Factory,
54
+ })
55
+ class ConfigServiceImpl {
56
+ configService = inject(ConfigProvider, options)
57
+
58
+ create() {
59
+ return this.configService
60
+ }
61
+ }
62
+ return getInjectableToken(ConfigServiceImpl)
63
+ }
@@ -0,0 +1,69 @@
1
+ import { NaviosException } from '@navios/common'
2
+
3
+ import type { LoggerService } from '../logger/index.mjs'
4
+ import type { ConfigService } from './config-service.interface.mjs'
5
+ import type { Path, PathValue } from './types.mjs'
6
+
7
+ export class ConfigServiceInstance<Config = Record<string, unknown>>
8
+ implements ConfigService<Config>
9
+ {
10
+ constructor(
11
+ private config: Config = {} as Config,
12
+ private logger: LoggerService,
13
+ ) {}
14
+
15
+ getConfig(): Config {
16
+ return this.config
17
+ }
18
+
19
+ get<Key extends Path<Config>>(key: Key): PathValue<Config, Key> | null {
20
+ try {
21
+ const parts = String(key).split('.')
22
+ let value: any = this.config
23
+
24
+ for (const part of parts) {
25
+ if (
26
+ value === null ||
27
+ value === undefined ||
28
+ typeof value !== 'object'
29
+ ) {
30
+ return null
31
+ }
32
+ value = value[part]
33
+ }
34
+
35
+ return (value as PathValue<Config, Key>) ?? null
36
+ } catch (error) {
37
+ this.logger.debug?.(
38
+ `Failed to get config value for key ${String(key)}`,
39
+ error,
40
+ )
41
+ return null
42
+ }
43
+ }
44
+
45
+ getOrDefault<Key extends Path<Config>>(
46
+ key: Key,
47
+ defaultValue: PathValue<Config, Key>,
48
+ ): PathValue<Config, Key> {
49
+ const value = this.get(key)
50
+ return value !== null ? value : defaultValue
51
+ }
52
+
53
+ getOrThrow<Key extends Path<Config>>(
54
+ key: Key,
55
+ errorMessage?: string,
56
+ ): PathValue<Config, Key> {
57
+ const value = this.get(key)
58
+
59
+ if (value === null) {
60
+ const message =
61
+ errorMessage ||
62
+ `Configuration value for key "${String(key)}" is not defined`
63
+ this.logger.error(message)
64
+ throw new NaviosException(message)
65
+ }
66
+
67
+ return value
68
+ }
69
+ }
@@ -0,0 +1,5 @@
1
+ export * from './utils/index.mjs'
2
+ export * from './config.provider.mjs'
3
+ export * from './config.service.mjs'
4
+ export * from './config-service.interface.mjs'
5
+ export * from './types.mjs'
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Evaluates to `true` if `T` is `any`. `false` otherwise.
3
+ * (c) https://stackoverflow.com/a/68633327/5290447
4
+ */
5
+ type IsAny<T> = unknown extends T
6
+ ? [keyof T] extends [never]
7
+ ? false
8
+ : true
9
+ : false
10
+
11
+ type ExcludedParts =
12
+ | 'services'
13
+ | 'mailer'
14
+ | 'aws'
15
+ | 'computedTimeRates'
16
+ | 'aiModelsRates'
17
+ type ExcludedKeys = 'computedTimeRates' | 'aiModelsRates' | 'aiModel'
18
+
19
+ export type PathImpl<T, Key extends keyof T> = Key extends string
20
+ ? Key extends ExcludedKeys
21
+ ? never
22
+ : IsAny<T[Key]> extends true
23
+ ? never
24
+ : T[Key] extends string
25
+ ? never
26
+ : T[Key] extends any[]
27
+ ? never
28
+ : T[Key] extends Record<string, any>
29
+ ? Key extends ExcludedParts
30
+ ? `${Key}.${Exclude<keyof T[Key], keyof any[]> & string}`
31
+ :
32
+ | `${Key}.${PathImpl<T[Key], Exclude<keyof T[Key], keyof any[]>> & string}`
33
+ | `${Key}.${Exclude<keyof T[Key], keyof any[]> & string}`
34
+ : never
35
+ : never
36
+
37
+ export type PathImpl2<T> = PathImpl<T, keyof T> | keyof T
38
+
39
+ export type Path<T> = keyof T extends string
40
+ ? PathImpl2<T> extends infer P
41
+ ? P extends string | keyof T
42
+ ? P
43
+ : keyof T
44
+ : keyof T
45
+ : never
46
+
47
+ export type PathValue<
48
+ T,
49
+ P extends Path<T>,
50
+ > = P extends `${infer Key}.${infer Rest}`
51
+ ? Key extends keyof T
52
+ ? Rest extends Path<T[Key]>
53
+ ? PathValue<T[Key], Rest>
54
+ : never
55
+ : never
56
+ : P extends keyof T
57
+ ? T[P]
58
+ : never
@@ -0,0 +1,23 @@
1
+ import { env } from 'node:process'
2
+
3
+ export function envInt(
4
+ key: keyof NodeJS.ProcessEnv,
5
+ defaultValue: number,
6
+ ): number {
7
+ const envKey = env[key] || process.env[key]
8
+
9
+ return envKey ? parseInt(envKey as string, 10) : defaultValue
10
+ }
11
+
12
+ export function envString<
13
+ DefaultValue extends string | undefined,
14
+ Ensured = DefaultValue extends string ? true : false,
15
+ >(
16
+ key: keyof NodeJS.ProcessEnv,
17
+ defaultValue?: DefaultValue,
18
+ ): Ensured extends true ? string : string | undefined {
19
+ return (env[key] ||
20
+ process.env[key] ||
21
+ defaultValue ||
22
+ undefined) as Ensured extends true ? string : string | undefined
23
+ }
@@ -0,0 +1 @@
1
+ export * from './helpers.mjs'
package/src/index.mts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './config/index.mjs'
1
2
  export * from './decorators/index.mjs'
2
3
  export * from './exceptions/index.mjs'
3
4
  export * from './interfaces/index.mjs'
@@ -1,4 +1,4 @@
1
- import type { AnyZodObject, input, output } from 'zod'
1
+ import type { AnyZodObject, output, z, ZodOptional } from 'zod'
2
2
 
3
3
  import type { FactoryNotFound, UnknownError } from './index.mjs'
4
4
  import type { InjectionToken } from './injection-token.mjs'
@@ -33,11 +33,17 @@ export class ProxyServiceLocator implements ServiceLocator {
33
33
  ): void {
34
34
  return this.serviceLocator.registerAbstractFactory(token, factory)
35
35
  }
36
- public getInstance<Instance, Schema extends AnyZodObject | undefined>(
36
+ public getInstance<
37
+ Instance,
38
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject> | undefined,
39
+ >(
37
40
  token: InjectionToken<Instance, Schema>,
38
- args: Schema extends AnyZodObject ? input<Schema> : undefined,
41
+ args: Schema extends AnyZodObject
42
+ ? z.input<Schema>
43
+ : Schema extends ZodOptional<AnyZodObject>
44
+ ? z.input<Schema> | undefined
45
+ : undefined,
39
46
  ): Promise<[undefined, Instance] | [UnknownError | FactoryNotFound]> {
40
- // @ts-expect-error
41
47
  return this.ctx.inject(token, args).then(
42
48
  (instance) => {
43
49
  return [undefined, instance]
@@ -47,16 +53,29 @@ export class ProxyServiceLocator implements ServiceLocator {
47
53
  },
48
54
  )
49
55
  }
50
- public getOrThrowInstance<Instance, Schema extends AnyZodObject | undefined>(
56
+ public getOrThrowInstance<
57
+ Instance,
58
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject> | undefined,
59
+ >(
51
60
  token: InjectionToken<Instance, Schema>,
52
- args: Schema extends AnyZodObject ? input<Schema> : undefined,
61
+ args: Schema extends AnyZodObject
62
+ ? z.input<Schema>
63
+ : Schema extends ZodOptional<AnyZodObject>
64
+ ? z.input<Schema> | undefined
65
+ : undefined,
53
66
  ): Promise<Instance> {
54
- // @ts-expect-error We need to pass the args to the ctx.inject method
55
67
  return this.ctx.inject(token, args)
56
68
  }
57
- public getSyncInstance<Instance, Schema extends AnyZodObject | undefined>(
69
+ public getSyncInstance<
70
+ Instance,
71
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject> | undefined,
72
+ >(
58
73
  token: InjectionToken<Instance, Schema>,
59
- args: Schema extends AnyZodObject ? input<Schema> : undefined,
74
+ args: Schema extends AnyZodObject
75
+ ? z.input<Schema>
76
+ : Schema extends ZodOptional<AnyZodObject>
77
+ ? z.input<Schema> | undefined
78
+ : undefined,
60
79
  ): Instance | null {
61
80
  return this.serviceLocator.getSyncInstance(token, args)
62
81
  }
@@ -456,7 +456,15 @@ export class ServiceLocator {
456
456
  makeInstanceName(token: InjectionToken<any, any>, args: any) {
457
457
  let stringifiedArgs = args
458
458
  ? ':' +
459
- JSON.stringify(args)
459
+ JSON.stringify(args, (_, value) => {
460
+ if (typeof value === 'function') {
461
+ return `function:${value.name}(${value.length})`
462
+ }
463
+ if (typeof value === 'symbol') {
464
+ return value.toString()
465
+ }
466
+ return value
467
+ })
460
468
  .replaceAll(/"/g, '')
461
469
  .replaceAll(/:/g, '=')
462
470
  .replaceAll(/,/g, '|')