@navios/core 0.1.0 → 0.1.1

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,214 @@
1
+ import type { LogLevel } from './log-levels.mjs'
2
+ import type { LoggerService } from './logger-service.interface.mjs'
3
+
4
+ import { Injectable } from '../service-locator/index.mjs'
5
+ import { ConsoleLogger } from './console-logger.service.mjs'
6
+ import { isLogLevelEnabled, isObject } from './utils/index.mjs'
7
+
8
+ const DEFAULT_LOGGER = new ConsoleLogger()
9
+
10
+ const dateTimeFormatter = new Intl.DateTimeFormat(undefined, {
11
+ year: 'numeric',
12
+ hour: 'numeric',
13
+ minute: 'numeric',
14
+ second: 'numeric',
15
+ day: '2-digit',
16
+ month: '2-digit',
17
+ })
18
+
19
+ // @ts-expect-error We don't need to support this in the current version
20
+ @Injectable()
21
+ export class LoggerInstance implements LoggerService {
22
+ protected static staticInstanceRef?: LoggerService = DEFAULT_LOGGER
23
+ protected static logLevels?: LogLevel[]
24
+
25
+ protected localInstanceRef?: LoggerService
26
+
27
+ constructor()
28
+ constructor(context: string)
29
+ constructor(context: string, options?: { timestamp?: boolean })
30
+ constructor(
31
+ protected context?: string,
32
+ protected options: { timestamp?: boolean } = {},
33
+ ) {}
34
+
35
+ get localInstance(): LoggerService {
36
+ if (LoggerInstance.staticInstanceRef === DEFAULT_LOGGER) {
37
+ return this.registerLocalInstanceRef()
38
+ } else if (LoggerInstance.staticInstanceRef instanceof LoggerInstance) {
39
+ const prototype = Object.getPrototypeOf(LoggerInstance.staticInstanceRef)
40
+ if (prototype.constructor === LoggerInstance) {
41
+ return this.registerLocalInstanceRef()
42
+ }
43
+ }
44
+ return LoggerInstance.staticInstanceRef!
45
+ }
46
+
47
+ /**
48
+ * Write an 'error' level log.
49
+ */
50
+ error(message: any, stack?: string, context?: string): void
51
+ error(message: any, ...optionalParams: [...any, string?, string?]): void
52
+ error(message: any, ...optionalParams: any[]) {
53
+ optionalParams = this.context
54
+ ? (optionalParams.length ? optionalParams : [undefined]).concat(
55
+ this.context,
56
+ )
57
+ : optionalParams
58
+
59
+ this.localInstance?.error(message, ...optionalParams)
60
+ }
61
+
62
+ /**
63
+ * Write a 'log' level log.
64
+ */
65
+ log(message: any, context?: string): void
66
+ log(message: any, ...optionalParams: [...any, string?]): void
67
+ log(message: any, ...optionalParams: any[]) {
68
+ optionalParams = this.context
69
+ ? optionalParams.concat(this.context)
70
+ : optionalParams
71
+ this.localInstance?.log(message, ...optionalParams)
72
+ }
73
+
74
+ /**
75
+ * Write a 'warn' level log.
76
+ */
77
+ warn(message: any, context?: string): void
78
+ warn(message: any, ...optionalParams: [...any, string?]): void
79
+ warn(message: any, ...optionalParams: any[]) {
80
+ optionalParams = this.context
81
+ ? optionalParams.concat(this.context)
82
+ : optionalParams
83
+ this.localInstance?.warn(message, ...optionalParams)
84
+ }
85
+
86
+ /**
87
+ * Write a 'debug' level log.
88
+ */
89
+ debug(message: any, context?: string): void
90
+ debug(message: any, ...optionalParams: [...any, string?]): void
91
+ debug(message: any, ...optionalParams: any[]) {
92
+ optionalParams = this.context
93
+ ? optionalParams.concat(this.context)
94
+ : optionalParams
95
+ this.localInstance?.debug?.(message, ...optionalParams)
96
+ }
97
+
98
+ /**
99
+ * Write a 'verbose' level log.
100
+ */
101
+ verbose(message: any, context?: string): void
102
+ verbose(message: any, ...optionalParams: [...any, string?]): void
103
+ verbose(message: any, ...optionalParams: any[]) {
104
+ optionalParams = this.context
105
+ ? optionalParams.concat(this.context)
106
+ : optionalParams
107
+ this.localInstance?.verbose?.(message, ...optionalParams)
108
+ }
109
+
110
+ /**
111
+ * Write a 'fatal' level log.
112
+ */
113
+ fatal(message: any, context?: string): void
114
+ fatal(message: any, ...optionalParams: [...any, string?]): void
115
+ fatal(message: any, ...optionalParams: any[]) {
116
+ optionalParams = this.context
117
+ ? optionalParams.concat(this.context)
118
+ : optionalParams
119
+ this.localInstance?.fatal?.(message, ...optionalParams)
120
+ }
121
+
122
+ /**
123
+ * Write an 'error' level log.
124
+ */
125
+ static error(message: any, stackOrContext?: string): void
126
+ static error(message: any, context?: string): void
127
+ static error(message: any, stack?: string, context?: string): void
128
+ static error(
129
+ message: any,
130
+ ...optionalParams: [...any, string?, string?]
131
+ ): void
132
+ static error(message: any, ...optionalParams: any[]) {
133
+ this.staticInstanceRef?.error(message, ...optionalParams)
134
+ }
135
+
136
+ /**
137
+ * Write a 'log' level log.
138
+ */
139
+ static log(message: any, context?: string): void
140
+ static log(message: any, ...optionalParams: [...any, string?]): void
141
+ static log(message: any, ...optionalParams: any[]) {
142
+ this.staticInstanceRef?.log(message, ...optionalParams)
143
+ }
144
+
145
+ /**
146
+ * Write a 'warn' level log.
147
+ */
148
+ static warn(message: any, context?: string): void
149
+ static warn(message: any, ...optionalParams: [...any, string?]): void
150
+ static warn(message: any, ...optionalParams: any[]) {
151
+ this.staticInstanceRef?.warn(message, ...optionalParams)
152
+ }
153
+
154
+ /**
155
+ * Write a 'debug' level log, if the configured level allows for it.
156
+ * Prints to `stdout` with newline.
157
+ */
158
+ static debug(message: any, context?: string): void
159
+ static debug(message: any, ...optionalParams: [...any, string?]): void
160
+ static debug(message: any, ...optionalParams: any[]) {
161
+ this.staticInstanceRef?.debug?.(message, ...optionalParams)
162
+ }
163
+
164
+ /**
165
+ * Write a 'verbose' level log.
166
+ */
167
+ static verbose(message: any, context?: string): void
168
+ static verbose(message: any, ...optionalParams: [...any, string?]): void
169
+ static verbose(message: any, ...optionalParams: any[]) {
170
+ this.staticInstanceRef?.verbose?.(message, ...optionalParams)
171
+ }
172
+
173
+ /**
174
+ * Write a 'fatal' level log.
175
+ */
176
+ static fatal(message: any, context?: string): void
177
+ static fatal(message: any, ...optionalParams: [...any, string?]): void
178
+ static fatal(message: any, ...optionalParams: any[]) {
179
+ this.staticInstanceRef?.fatal?.(message, ...optionalParams)
180
+ }
181
+
182
+ static getTimestamp() {
183
+ return dateTimeFormatter.format(Date.now())
184
+ }
185
+
186
+ static overrideLogger(logger: LoggerService | LogLevel[] | boolean) {
187
+ console.log(logger)
188
+ if (Array.isArray(logger)) {
189
+ LoggerInstance.logLevels = logger
190
+ return this.staticInstanceRef?.setLogLevels?.(logger)
191
+ }
192
+ if (isObject(logger)) {
193
+ this.staticInstanceRef = logger as LoggerService
194
+ } else {
195
+ this.staticInstanceRef = undefined
196
+ }
197
+ }
198
+
199
+ static isLevelEnabled(level: LogLevel): boolean {
200
+ const logLevels = LoggerInstance.logLevels
201
+ return isLogLevelEnabled(level, logLevels)
202
+ }
203
+
204
+ private registerLocalInstanceRef() {
205
+ if (this.localInstanceRef) {
206
+ return this.localInstanceRef
207
+ }
208
+ this.localInstanceRef = new ConsoleLogger(this.context!, {
209
+ timestamp: this.options?.timestamp,
210
+ logLevels: LoggerInstance.logLevels,
211
+ })
212
+ return this.localInstanceRef
213
+ }
214
+ }
@@ -0,0 +1,63 @@
1
+ import type { LoggerService } from './logger-service.interface.mjs'
2
+
3
+ import { LoggerInstance } from './logger.service.mjs'
4
+
5
+ export class PinoWrapper {
6
+ constructor(protected readonly logger: LoggerService) {}
7
+
8
+ fatal(message: any, ...optionalParams: any[]) {
9
+ if (this.logger.fatal === undefined) {
10
+ return this.error(message, ...optionalParams)
11
+ }
12
+ this.logger.fatal(message, ...optionalParams)
13
+ }
14
+
15
+ error(message: any, ...optionalParams: any[]) {
16
+ this.logger.error(message, ...optionalParams)
17
+ }
18
+
19
+ warn(message: any, ...optionalParams: any[]) {
20
+ this.logger.warn(message, ...optionalParams)
21
+ }
22
+
23
+ info(message: any, ...optionalParams: any[]) {
24
+ this.logger.log(message, ...optionalParams)
25
+ }
26
+
27
+ debug(message: any, ...optionalParams: any[]) {
28
+ this.logger.debug?.(message, ...optionalParams)
29
+ }
30
+
31
+ trace(message: any, ...optionalParams: any[]) {
32
+ this.logger.verbose?.(message, ...optionalParams)
33
+ }
34
+
35
+ silent(message: any, ...optionalParams: any[]) {
36
+ // noop
37
+ }
38
+
39
+ child(options: any) {
40
+ const keys = Object.keys(options)
41
+ // @ts-expect-error We don't need to support this in the current version
42
+ let newContext = this.logger['context'] ?? ''
43
+ if (keys.length > 1) {
44
+ // @ts-expect-error We don't need to support this in the current version
45
+ newContext = `${this.logger['context'] ?? ''}:${JSON.stringify(options)}`
46
+ }
47
+ return new PinoWrapper(
48
+ // @ts-expect-error We don't need to support this in the current version
49
+ new LoggerInstance(newContext, this.logger['options']),
50
+ )
51
+ }
52
+
53
+ get level(): any {
54
+ if ('level' in this.logger && this.logger.level) {
55
+ return this.logger.level
56
+ }
57
+ const levels = LoggerInstance['logLevels']
58
+ if (levels) {
59
+ return levels[0]
60
+ }
61
+ return 'info'
62
+ }
63
+ }
@@ -0,0 +1,17 @@
1
+ type ColorTextFn = (text: string) => string
2
+
3
+ const isColorAllowed = () => !process.env.NO_COLOR
4
+ const colorIfAllowed = (colorFn: ColorTextFn) => (text: string) =>
5
+ isColorAllowed() ? colorFn(text) : text
6
+
7
+ export const clc = {
8
+ bold: colorIfAllowed((text: string) => `\x1B[1m${text}\x1B[0m`),
9
+ green: colorIfAllowed((text: string) => `\x1B[32m${text}\x1B[39m`),
10
+ yellow: colorIfAllowed((text: string) => `\x1B[33m${text}\x1B[39m`),
11
+ red: colorIfAllowed((text: string) => `\x1B[31m${text}\x1B[39m`),
12
+ magentaBright: colorIfAllowed((text: string) => `\x1B[95m${text}\x1B[39m`),
13
+ cyanBright: colorIfAllowed((text: string) => `\x1B[96m${text}\x1B[39m`),
14
+ }
15
+ export const yellow = colorIfAllowed(
16
+ (text: string) => `\x1B[38;5;3m${text}\x1B[39m`,
17
+ )
@@ -0,0 +1,29 @@
1
+ import type { LogLevel } from '../log-levels.mjs'
2
+
3
+ import { LOG_LEVELS } from '../log-levels.mjs'
4
+ import { isLogLevel } from './is-log-level.util.mjs'
5
+
6
+ /**
7
+ * @publicApi
8
+ */
9
+ export function filterLogLevels(parseableString = ''): LogLevel[] {
10
+ const sanitizedString = parseableString.replaceAll(' ', '').toLowerCase()
11
+
12
+ if (sanitizedString[0] === '>') {
13
+ const orEqual = sanitizedString[1] === '='
14
+
15
+ const logLevelIndex = (LOG_LEVELS as string[]).indexOf(
16
+ sanitizedString.substring(orEqual ? 2 : 1),
17
+ )
18
+
19
+ if (logLevelIndex === -1) {
20
+ throw new Error(`parse error (unknown log level): ${sanitizedString}`)
21
+ }
22
+
23
+ return LOG_LEVELS.slice(orEqual ? logLevelIndex : logLevelIndex + 1)
24
+ } else if (sanitizedString.includes(',')) {
25
+ return sanitizedString.split(',').filter(isLogLevel)
26
+ }
27
+
28
+ return isLogLevel(sanitizedString) ? [sanitizedString] : LOG_LEVELS
29
+ }
@@ -0,0 +1,5 @@
1
+ export * from './cli-colors.util.mjs'
2
+ export * from './filter-log-levelts.util.mjs'
3
+ export * from './is-log-level.util.mjs'
4
+ export * from './is-log-level-enabled.mjs'
5
+ export * from './shared.utils.mjs'
@@ -0,0 +1,33 @@
1
+ import type { LogLevel } from '../log-levels.mjs'
2
+
3
+ const LOG_LEVEL_VALUES: Record<LogLevel, number> = {
4
+ verbose: 0,
5
+ debug: 1,
6
+ log: 2,
7
+ warn: 3,
8
+ error: 4,
9
+ fatal: 5,
10
+ }
11
+
12
+ /**
13
+ * Checks if target level is enabled.
14
+ * @param targetLevel target level
15
+ * @param logLevels array of enabled log levels
16
+ */
17
+ export function isLogLevelEnabled(
18
+ targetLevel: LogLevel,
19
+ logLevels: LogLevel[] | undefined,
20
+ ): boolean {
21
+ if (!logLevels || (Array.isArray(logLevels) && logLevels?.length === 0)) {
22
+ return false
23
+ }
24
+ if (logLevels.includes(targetLevel)) {
25
+ return true
26
+ }
27
+ const highestLogLevelValue = logLevels
28
+ .map((level) => LOG_LEVEL_VALUES[level])
29
+ .sort((a, b) => b - a)?.[0]
30
+
31
+ const targetLevelValue = LOG_LEVEL_VALUES[targetLevel]
32
+ return targetLevelValue >= highestLogLevelValue
33
+ }
@@ -0,0 +1,10 @@
1
+ import type { LogLevel } from '../log-levels.mjs'
2
+
3
+ import { LOG_LEVELS } from '../log-levels.mjs'
4
+
5
+ /**
6
+ * @publicApi
7
+ */
8
+ export function isLogLevel(maybeLogLevel: any): maybeLogLevel is LogLevel {
9
+ return LOG_LEVELS.includes(maybeLogLevel)
10
+ }
@@ -0,0 +1,51 @@
1
+ export const isUndefined = (obj: any): obj is undefined =>
2
+ typeof obj === 'undefined'
3
+
4
+ export const isObject = (fn: any): fn is object =>
5
+ !isNil(fn) && typeof fn === 'object'
6
+
7
+ export const isPlainObject = (fn: any): fn is object => {
8
+ if (!isObject(fn)) {
9
+ return false
10
+ }
11
+ const proto = Object.getPrototypeOf(fn)
12
+ if (proto === null) {
13
+ return true
14
+ }
15
+ const ctor =
16
+ Object.prototype.hasOwnProperty.call(proto, 'constructor') &&
17
+ proto.constructor
18
+ return (
19
+ typeof ctor === 'function' &&
20
+ ctor instanceof ctor &&
21
+ Function.prototype.toString.call(ctor) ===
22
+ Function.prototype.toString.call(Object)
23
+ )
24
+ }
25
+
26
+ export const addLeadingSlash = (path?: string): string =>
27
+ path && typeof path === 'string'
28
+ ? path.charAt(0) !== '/' && path.substring(0, 2) !== '{/'
29
+ ? '/' + path
30
+ : path
31
+ : ''
32
+
33
+ export const normalizePath = (path?: string): string =>
34
+ path
35
+ ? path.startsWith('/')
36
+ ? ('/' + path.replace(/\/+$/, '')).replace(/\/+/g, '/')
37
+ : '/' + path.replace(/\/+$/, '')
38
+ : '/'
39
+
40
+ export const stripEndSlash = (path: string) =>
41
+ path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path
42
+
43
+ export const isFunction = (val: any): val is Function =>
44
+ typeof val === 'function'
45
+ export const isString = (val: any): val is string => typeof val === 'string'
46
+ export const isNumber = (val: any): val is number => typeof val === 'number'
47
+ export const isConstructor = (val: any): boolean => val === 'constructor'
48
+ export const isNil = (val: any): val is null | undefined =>
49
+ isUndefined(val) || val === null
50
+ export const isEmpty = (array: any): boolean => !(array && array.length > 0)
51
+ export const isSymbol = (val: any): val is symbol => typeof val === 'symbol'
@@ -13,10 +13,13 @@ import {
13
13
  } from 'fastify-type-provider-zod'
14
14
 
15
15
  import type { NaviosModule } from './interfaces/index.mjs'
16
+ import type { LoggerService, LogLevel } from './logger/index.mjs'
16
17
  import type { ClassTypeWithInstance } from './service-locator/index.mjs'
17
18
 
19
+ import { Logger, PinoWrapper } from './logger/index.mjs'
18
20
  import {
19
21
  getServiceLocator,
22
+ inject,
20
23
  Injectable,
21
24
  syncInject,
22
25
  } from './service-locator/index.mjs'
@@ -26,12 +29,24 @@ import {
26
29
  } from './services/index.mjs'
27
30
  import { Application } from './tokens/index.mjs'
28
31
 
29
- export interface NaviosApplicationOptions extends FastifyServerOptions {}
32
+ export interface NaviosApplicationContextOptions {
33
+ /**
34
+ * Specifies the logger to use. Pass `false` to turn off logging.
35
+ */
36
+ logger?: LoggerService | LogLevel[] | false
37
+ }
38
+
39
+ export interface NaviosApplicationOptions
40
+ extends Omit<FastifyServerOptions, 'logger'>,
41
+ NaviosApplicationContextOptions {}
30
42
 
31
43
  @Injectable()
32
44
  export class NaviosApplication {
33
45
  private moduleLoader = syncInject(ModuleLoaderService)
34
46
  private controllerAdapter = syncInject(ControllerAdapterService)
47
+ private logger = syncInject(Logger, {
48
+ context: NaviosApplication.name,
49
+ })
35
50
  private server: FastifyInstance | null = null
36
51
  private corsOptions: FastifyCorsOptions | null = null
37
52
  private globalPrefix: string | null = null
@@ -52,7 +67,7 @@ export class NaviosApplication {
52
67
  throw new Error('App module is not set. Call setAppModule() first.')
53
68
  }
54
69
  await this.moduleLoader.loadModules(this.appModule)
55
- this.server = fastify(this.options)
70
+ this.server = await this.getFastifyInstance(this.options)
56
71
  getServiceLocator().registerInstance(Application, this.server)
57
72
  // Add schema validator and serializer
58
73
  this.server.setValidatorCompiler(validatorCompiler)
@@ -61,8 +76,43 @@ export class NaviosApplication {
61
76
  if (this.corsOptions) {
62
77
  await this.server.register(cors, this.corsOptions)
63
78
  }
79
+
80
+ await this.initModules()
81
+
82
+ this.logger.debug('Navios application initialized')
83
+ }
84
+
85
+ private async getFastifyInstance(rawOptions: NaviosApplicationOptions) {
86
+ const { logger, ...options } = rawOptions
87
+ if (logger) {
88
+ const fastifyOptions = options as FastifyServerOptions
89
+ if (typeof logger === 'boolean') {
90
+ if (!logger) {
91
+ fastifyOptions.logger = false
92
+ }
93
+ } else {
94
+ fastifyOptions.loggerInstance = new PinoWrapper(
95
+ await inject(Logger, {
96
+ context: 'FastifyAdapter',
97
+ }),
98
+ )
99
+ }
100
+ return fastify(fastifyOptions)
101
+ } else {
102
+ return fastify({
103
+ ...options,
104
+ loggerInstance: new PinoWrapper(
105
+ await inject(Logger, {
106
+ context: 'FastifyAdapter',
107
+ }),
108
+ ),
109
+ } as FastifyServerOptions)
110
+ }
111
+ }
112
+
113
+ private async initModules() {
64
114
  const modules = this.moduleLoader.getAllModules()
65
- const globalPrefix = this.globalPrefix ?? ''
115
+ const promises: PromiseLike<any>[] = []
66
116
  for (const [moduleName, moduleMetadata] of modules) {
67
117
  if (
68
118
  !moduleMetadata.controllers ||
@@ -70,27 +120,30 @@ export class NaviosApplication {
70
120
  ) {
71
121
  continue
72
122
  }
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
- },
123
+ promises.push(
124
+ this.server!.register(
125
+ (instance, opts, done) => {
126
+ for (const controller of moduleMetadata.controllers) {
127
+ this.controllerAdapter.setupController(
128
+ controller,
129
+ instance,
130
+ moduleMetadata,
131
+ )
132
+ }
133
+ done()
134
+ },
135
+ {
136
+ prefix: this.globalPrefix ?? '',
137
+ },
138
+ ),
87
139
  )
88
140
  }
141
+
142
+ await Promise.all(promises)
89
143
  }
90
144
 
91
145
  enableCors(options: FastifyCorsOptions) {
92
146
  this.corsOptions = options
93
- this.server?.register
94
147
  }
95
148
 
96
149
  setGlobalPrefix(prefix: string) {
@@ -1,7 +1,11 @@
1
1
  import type { NaviosModule } from './interfaces/index.mjs'
2
- import type { NaviosApplicationOptions } from './navios.application.mjs'
2
+ import type {
3
+ NaviosApplicationContextOptions,
4
+ NaviosApplicationOptions,
5
+ } from './navios.application.mjs'
3
6
  import type { ClassTypeWithInstance } from './service-locator/index.mjs'
4
7
 
8
+ import { isNil, LoggerInstance } from './logger/index.mjs'
5
9
  import { NaviosApplication } from './navios.application.mjs'
6
10
  import { inject } from './service-locator/index.mjs'
7
11
 
@@ -11,7 +15,20 @@ export class NaviosFactory {
11
15
  options: NaviosApplicationOptions = {},
12
16
  ) {
13
17
  const app = await inject(NaviosApplication)
18
+ this.registerLoggerConfiguration(options)
14
19
  app.setup(appModule, options)
15
20
  return app
16
21
  }
22
+
23
+ private static registerLoggerConfiguration(
24
+ options: NaviosApplicationContextOptions,
25
+ ) {
26
+ if (!options) {
27
+ return
28
+ }
29
+ const { logger } = options
30
+ if ((logger as boolean) !== true && !isNil(logger)) {
31
+ LoggerInstance.overrideLogger(logger)
32
+ }
33
+ }
17
34
  }
@@ -1,4 +1,4 @@
1
- import type { AnyZodObject, z } from 'zod'
1
+ import type { AnyZodObject, z, ZodOptional } from 'zod'
2
2
 
3
3
  import type { ClassType } from './injection-token.mjs'
4
4
 
@@ -11,6 +11,11 @@ export function inject<T, S extends AnyZodObject>(
11
11
  token: InjectionToken<T, S>,
12
12
  args: z.input<S>,
13
13
  ): Promise<T>
14
+ export function inject<T, S extends ZodOptional<AnyZodObject>>(
15
+ token: InjectionToken<T, S>,
16
+ args?: z.input<S>,
17
+ ): Promise<T>
18
+
14
19
  export function inject<T>(token: InjectionToken<T, undefined>): Promise<T>
15
20
  export function inject<
16
21
  T,
@@ -2,11 +2,16 @@ import type { AnyZodObject } from 'zod'
2
2
 
3
3
  import { randomUUID } from 'crypto'
4
4
 
5
+ import { ZodOptional } from 'zod'
6
+
5
7
  export type ClassType = new (...args: any[]) => any
6
8
 
7
9
  export type ClassTypeWithInstance<T> = new (...args: any[]) => T
8
10
 
9
- export class InjectionToken<T, S extends AnyZodObject | unknown = unknown> {
11
+ export class InjectionToken<
12
+ T,
13
+ S extends AnyZodObject | ZodOptional<AnyZodObject> | unknown = unknown,
14
+ > {
10
15
  public id = randomUUID()
11
16
  constructor(
12
17
  public readonly name: string | symbol | ClassType,
@@ -16,12 +21,12 @@ export class InjectionToken<T, S extends AnyZodObject | unknown = unknown> {
16
21
  static create<T extends ClassType>(
17
22
  name: T,
18
23
  ): InjectionToken<InstanceType<T>, undefined>
19
- static create<T extends ClassType, Schema extends AnyZodObject>(
20
- name: T,
21
- schema: Schema,
22
- ): InjectionToken<InstanceType<T>, Schema>
24
+ static create<
25
+ T extends ClassType,
26
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject>,
27
+ >(name: T, schema: Schema): InjectionToken<InstanceType<T>, Schema>
23
28
  static create<T>(name: string): InjectionToken<T, undefined>
24
- static create<T, Schema extends AnyZodObject>(
29
+ static create<T, Schema extends AnyZodObject | ZodOptional<AnyZodObject>>(
25
30
  name: string,
26
31
  schema: Schema,
27
32
  ): InjectionToken<T, Schema>