@nmtjs/core 0.12.6 → 0.12.8

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.
@@ -1,508 +0,0 @@
1
- import {
2
- type Async,
3
- type Callback,
4
- type ClassConstructor,
5
- type ClassConstructorArgs,
6
- type ClassInstance,
7
- tryCaptureStackTrace,
8
- } from '@nmtjs/common'
9
- import {
10
- kClassInjectable,
11
- kFactoryInjectable,
12
- kHookCollection,
13
- kInjectable,
14
- kLazyInjectable,
15
- kOptionalDependency,
16
- kValueInjectable,
17
- } from './constants.ts'
18
- import type { DisposeFn, InjectFn } from './container.ts'
19
- import { type Hook, Scope } from './enums.ts'
20
- import { Hooks, type HookType } from './hooks.ts'
21
- import type { Logger } from './logger.ts'
22
- import type { Registry } from './registry.ts'
23
-
24
- const ScopeStrictness = {
25
- [Scope.Transient]: Number.NaN, // this should make it always fail to compare with other scopes
26
- [Scope.Global]: 1,
27
- [Scope.Connection]: 2,
28
- [Scope.Call]: 3,
29
- }
30
-
31
- export type DependencyOptional<T extends AnyInjectable = AnyInjectable> = {
32
- [kOptionalDependency]: any
33
- injectable: T
34
- }
35
-
36
- export type Depedency = DependencyOptional | AnyInjectable
37
-
38
- export type Dependencies = Record<string, Depedency>
39
-
40
- export type ResolveInjectableType<T extends AnyInjectable> =
41
- T extends Injectable<infer Type, any, any> ? Type : never
42
-
43
- export interface Dependant<Deps extends Dependencies = Dependencies> {
44
- dependencies: Deps
45
- label?: string
46
- stack?: string
47
- }
48
-
49
- export type DependencyInjectable<T extends Depedency> = T extends AnyInjectable
50
- ? T
51
- : T extends DependencyOptional
52
- ? T['injectable']
53
- : never
54
-
55
- export type DependencyContext<Deps extends Dependencies> = {
56
- readonly [K in keyof Deps as Deps[K] extends AnyInjectable
57
- ? K
58
- : never]: Deps[K] extends AnyInjectable
59
- ? ResolveInjectableType<Deps[K]>
60
- : never
61
- } & {
62
- readonly [K in keyof Deps as Deps[K] extends DependencyOptional
63
- ? K
64
- : never]?: Deps[K] extends DependencyOptional
65
- ? ResolveInjectableType<Deps[K]['injectable']>
66
- : never
67
- }
68
-
69
- export type InjectableFactoryType<
70
- InjectableType,
71
- InjectableDeps extends Dependencies,
72
- > = (context: DependencyContext<InjectableDeps>) => Async<InjectableType>
73
-
74
- export type InjectablePickType<Input, Output> = (injectable: Input) => Output
75
-
76
- export type InjectableDisposeType<
77
- InjectableType,
78
- InjectableDeps extends Dependencies,
79
- > = (
80
- instance: InjectableType,
81
- context: DependencyContext<InjectableDeps>,
82
- ) => any
83
-
84
- export interface LazyInjectable<T, S extends Scope = Scope.Global>
85
- extends Dependant<{}> {
86
- scope: S
87
- [kInjectable]: any
88
- [kLazyInjectable]: T
89
- }
90
-
91
- export interface ValueInjectable<T> extends Dependant<{}> {
92
- scope: Scope.Global
93
- value: T
94
- [kInjectable]: any
95
- [kValueInjectable]: any
96
- }
97
-
98
- export interface FactoryInjectable<
99
- T,
100
- D extends Dependencies = {},
101
- S extends Scope = Scope.Global,
102
- P = T,
103
- > extends Dependant<D> {
104
- scope: S
105
- factory: InjectableFactoryType<P, D>
106
- pick: InjectablePickType<P, T>
107
- dispose?: InjectableDisposeType<P, D>
108
- [kInjectable]: any
109
- [kFactoryInjectable]: any
110
- }
111
-
112
- export interface BaseClassInjectable<
113
- T,
114
- D extends Dependencies = {},
115
- S extends Scope = Scope.Global,
116
- > extends Dependant<D> {
117
- new (...args: any[]): T
118
- scope: S
119
- [kInjectable]: any
120
- [kClassInjectable]: any
121
- }
122
-
123
- export interface ClassInjectable<
124
- T,
125
- D extends Dependencies = {},
126
- S extends Scope = Scope.Global,
127
- A extends any[] = [],
128
- > extends Dependant<D> {
129
- new (
130
- $context: DependencyContext<D>,
131
- ...args: A
132
- ): T & {
133
- $context: DependencyContext<D>
134
- }
135
- scope: S
136
- [kInjectable]: any
137
- [kClassInjectable]: any
138
- }
139
-
140
- export type Injectable<
141
- V = any,
142
- D extends Dependencies = {},
143
- S extends Scope = Scope,
144
- > =
145
- | LazyInjectable<V, S>
146
- | ValueInjectable<V>
147
- | FactoryInjectable<V, D, S, any>
148
- | BaseClassInjectable<V, D, S>
149
-
150
- export type AnyInjectable<T = any, S extends Scope = Scope> = Injectable<
151
- T,
152
- any,
153
- S
154
- >
155
-
156
- export const isLazyInjectable = (
157
- injectable: any,
158
- ): injectable is LazyInjectable<any> => injectable[kLazyInjectable]
159
-
160
- export const isFactoryInjectable = (
161
- injectable: any,
162
- ): injectable is FactoryInjectable<any> => injectable[kFactoryInjectable]
163
-
164
- export const isClassInjectable = (
165
- injectable: any,
166
- ): injectable is ClassInjectable<any> => injectable[kClassInjectable]
167
-
168
- export const isValueInjectable = (
169
- injectable: any,
170
- ): injectable is ValueInjectable<any> => injectable[kValueInjectable]
171
-
172
- export const isInjectable = (
173
- injectable: any,
174
- ): injectable is AnyInjectable<any> => injectable[kInjectable]
175
-
176
- export const isOptionalInjectable = (
177
- injectable: any,
178
- ): injectable is DependencyOptional<any> => injectable[kOptionalDependency]
179
-
180
- export function getInjectableScope(injectable: AnyInjectable) {
181
- let scope = injectable.scope
182
- const deps = injectable.dependencies as Dependencies
183
- for (const key in deps) {
184
- const dependency = deps[key]
185
- const injectable = getDepedencencyInjectable(dependency)
186
- const dependencyScope = getInjectableScope(injectable)
187
- if (
188
- dependencyScope !== Scope.Transient &&
189
- scope !== Scope.Transient &&
190
- compareScope(dependencyScope, '>', scope)
191
- ) {
192
- scope = dependencyScope
193
- }
194
- }
195
- return scope
196
- }
197
-
198
- export function getDepedencencyInjectable(
199
- dependency: Depedency,
200
- ): AnyInjectable {
201
- if (kOptionalDependency in dependency) {
202
- return dependency.injectable
203
- }
204
- return dependency
205
- }
206
-
207
- export function createOptionalInjectable<T extends AnyInjectable>(
208
- injectable: T,
209
- ) {
210
- return Object.freeze({
211
- [kOptionalDependency]: true,
212
- injectable,
213
- }) as DependencyOptional<T>
214
- }
215
-
216
- export function createLazyInjectable<T, S extends Scope = Scope.Global>(
217
- scope = Scope.Global as S,
218
- label?: string,
219
- stackTraceDepth = 0,
220
- ): LazyInjectable<T, S> {
221
- return Object.freeze({
222
- scope,
223
- dependencies: {},
224
- label,
225
- stack: tryCaptureStackTrace(stackTraceDepth),
226
- [kInjectable]: true,
227
- [kLazyInjectable]: true as unknown as T,
228
- })
229
- }
230
-
231
- export function createValueInjectable<T>(
232
- value: T,
233
- label?: string,
234
- stackTraceDepth = 0,
235
- ): ValueInjectable<T> {
236
- return Object.freeze({
237
- value,
238
- scope: Scope.Global,
239
- dependencies: {},
240
- label,
241
- stack: tryCaptureStackTrace(stackTraceDepth),
242
- [kInjectable]: true,
243
- [kValueInjectable]: true,
244
- })
245
- }
246
-
247
- export function createFactoryInjectable<
248
- T,
249
- D extends Dependencies = {},
250
- S extends Scope = Scope.Global,
251
- P = T,
252
- >(
253
- paramsOrFactory:
254
- | {
255
- dependencies?: D
256
- scope?: S
257
- pick?: InjectablePickType<P, T>
258
- factory: InjectableFactoryType<P, D>
259
- dispose?: InjectableDisposeType<P, D>
260
- }
261
- | InjectableFactoryType<P, D>,
262
- label?: string,
263
- stackTraceDepth = 0,
264
- ): FactoryInjectable<null extends T ? P : T, D, S, P> {
265
- const isFactory = typeof paramsOrFactory === 'function'
266
- const params = isFactory ? { factory: paramsOrFactory } : paramsOrFactory
267
- const injectable = {
268
- dependencies: (params.dependencies ?? {}) as D,
269
- scope: (params.scope ?? Scope.Global) as S,
270
- factory: params.factory,
271
- dispose: params.dispose,
272
- pick: params.pick ?? ((instance: P) => instance as unknown as T),
273
- label,
274
- stack: tryCaptureStackTrace(stackTraceDepth),
275
- [kInjectable]: true,
276
- [kFactoryInjectable]: true,
277
- }
278
- injectable.scope = resolveInjectableScope(
279
- typeof params.scope === 'undefined',
280
- injectable,
281
- ) as S
282
- return Object.freeze(injectable) as any
283
- }
284
-
285
- export const createClassInjectable = <
286
- D extends Dependencies = {},
287
- S extends Scope = Scope.Global,
288
- >(
289
- dependencies: D = {} as D,
290
- scope?: S,
291
- stackTraceDepth = 0,
292
- ): ClassInjectable<ClassInstance<typeof InjectableClass>, D, S> => {
293
- const InjectableClass = class {
294
- static dependencies = dependencies
295
- static scope = (scope ?? Scope.Global) as S
296
- static stack = tryCaptureStackTrace(stackTraceDepth + 2)
297
- static [kInjectable] = true
298
- static [kClassInjectable] = true
299
-
300
- static get label() {
301
- // biome-ignore lint/complexity/noThisInStatic: ok
302
- return this.name
303
- }
304
-
305
- constructor(public $context: DependencyContext<D>) {}
306
-
307
- async $onCreate() {}
308
- async $onDispose() {}
309
- }
310
-
311
- InjectableClass.scope = resolveInjectableScope(
312
- typeof scope === 'undefined',
313
- InjectableClass,
314
- ) as S
315
-
316
- return InjectableClass
317
- }
318
-
319
- export function createExtendableClassInjectable<
320
- B extends ClassConstructor<any>,
321
- D extends Dependencies = {},
322
- S extends Scope = Scope.Global,
323
- >(
324
- baseClass: B,
325
- dependencies: D = {} as D,
326
- scope?: S,
327
- stackTraceDepth = 0,
328
- ): B extends ClassInjectable<any>
329
- ? ClassInjectable<ClassInstance<B>, D, S>
330
- : ClassInjectable<ClassInstance<B>, D, S, ClassConstructorArgs<B, []>> {
331
- if (isClassInjectable(baseClass)) {
332
- if (scope && compareScope(baseClass.scope, '>', scope)) {
333
- throw new Error(
334
- `Invalid scope ${scope} for an extendable class injectable: base class have stricter scope - ${baseClass.scope}`,
335
- )
336
- } else {
337
- scope = scope ?? (baseClass.scope as S)
338
- }
339
- dependencies = Object.assign({}, baseClass.dependencies, dependencies)
340
- }
341
-
342
- const InjectableClass = class extends baseClass {
343
- static dependencies = dependencies
344
- static scope = (scope ?? Scope.Global) as S
345
- static stack = tryCaptureStackTrace(stackTraceDepth)
346
- static [kInjectable] = true
347
- static [kClassInjectable] = true
348
-
349
- static get label() {
350
- // biome-ignore lint/complexity/noThisInStatic: ok
351
- return this.name
352
- }
353
-
354
- $context!: DependencyContext<D>
355
-
356
- constructor(...args: any[]) {
357
- const [$context, ...baseClassArgs] = args
358
- if (isClassInjectable(baseClass)) {
359
- super($context)
360
- } else {
361
- super(...baseClassArgs)
362
- }
363
- this.$context = $context
364
- }
365
-
366
- protected async $onCreate() {
367
- await super.$onCreate?.()
368
- }
369
-
370
- protected async $onDispose() {
371
- await super.$onDispose?.()
372
- }
373
- }
374
-
375
- InjectableClass.scope = resolveInjectableScope(
376
- typeof scope === 'undefined',
377
- InjectableClass,
378
- ) as S
379
-
380
- // @ts-expect-error
381
- return InjectableClass
382
- }
383
-
384
- export type DependenciesSubstitution<T extends Dependencies> = {
385
- [K in keyof T]?: T[K] extends AnyInjectable<infer Type>
386
- ? AnyInjectable<Type> | DependenciesSubstitution<T[K]['dependencies']>
387
- : never
388
- }
389
-
390
- export function substitute<
391
- T extends
392
- | FactoryInjectable<any, any, Scope>
393
- | BaseClassInjectable<any, any, Scope>,
394
- >(
395
- injectable: T,
396
- substitution: DependenciesSubstitution<T['dependencies']>,
397
- stackTraceDepth = 0,
398
- ): T {
399
- const dependencies = { ...injectable.dependencies }
400
- const depth = stackTraceDepth + 1
401
- for (const key in substitution) {
402
- const value = substitution[key]!
403
- if (key in dependencies) {
404
- const original = dependencies[key]
405
- if (isInjectable(value)) {
406
- dependencies[key] = value
407
- } else if (isClassInjectable(original) || isFactoryInjectable(original)) {
408
- dependencies[key] = substitute(original, value, depth)
409
- }
410
- }
411
- }
412
-
413
- if (isClassInjectable(injectable)) {
414
- // @ts-expect-error
415
- return createExtendableClassInjectable(
416
- injectable,
417
- dependencies,
418
- injectable.scope,
419
- depth,
420
- )
421
- } else if (isFactoryInjectable(injectable)) {
422
- // @ts-expect-error
423
- return createFactoryInjectable(
424
- {
425
- ...injectable,
426
- dependencies,
427
- },
428
- injectable.label,
429
- depth,
430
- )
431
- }
432
-
433
- throw new Error('Invalid injectable type')
434
- }
435
-
436
- export function compareScope(
437
- left: Scope,
438
- operator: '>' | '<' | '>=' | '<=' | '=' | '!=',
439
- right: Scope,
440
- ) {
441
- const leftScope = ScopeStrictness[left]
442
- const rightScope = ScopeStrictness[right]
443
- switch (operator) {
444
- case '=':
445
- return leftScope === rightScope
446
- case '!=':
447
- return leftScope !== rightScope
448
- case '>':
449
- return leftScope > rightScope
450
- case '<':
451
- return leftScope < rightScope
452
- case '>=':
453
- return leftScope >= rightScope
454
- case '<=':
455
- return leftScope <= rightScope
456
- default:
457
- throw new Error('Invalid operator')
458
- }
459
- }
460
-
461
- const logger = createLazyInjectable<Logger>(Scope.Global, 'Logger')
462
- const registry = createLazyInjectable<Registry>(Scope.Global, 'Registry')
463
- const inject = createLazyInjectable<InjectFn>(Scope.Global, 'Inject function')
464
- const dispose = createLazyInjectable<DisposeFn>(
465
- Scope.Global,
466
- 'Dispose function',
467
- )
468
- const hook = createFactoryInjectable({
469
- scope: Scope.Transient,
470
- dependencies: { registry },
471
- factory: ({ registry }) => {
472
- const hooks = new Hooks()
473
- const on = <T extends Hook>(name: T, callback: HookType[T]) => {
474
- hooks.add(name, callback as Callback)
475
- return registry.hooks.add(name, callback)
476
- }
477
- return { hooks, on }
478
- },
479
- pick: ({ on }) => on,
480
- dispose: ({ hooks }, { registry }) => {
481
- for (const [hook, callbacks] of hooks[kHookCollection].entries()) {
482
- for (const callback of callbacks) {
483
- registry.hooks.remove(hook, callback)
484
- }
485
- }
486
- hooks.clear()
487
- },
488
- })
489
-
490
- function resolveInjectableScope(
491
- isDefaultScope: boolean,
492
- injectable: AnyInjectable,
493
- ) {
494
- const actualScope = getInjectableScope(injectable)
495
- if (!isDefaultScope && compareScope(actualScope, '>', injectable.scope))
496
- throw new Error(
497
- `Invalid scope ${injectable.scope} for an injectable: dependencies have stricter scope - ${actualScope}`,
498
- )
499
- return actualScope
500
- }
501
-
502
- export const CoreInjectables = {
503
- logger,
504
- registry,
505
- inject,
506
- dispose,
507
- hook,
508
- }
package/src/logger.ts DELETED
@@ -1,100 +0,0 @@
1
- import { threadId } from 'node:worker_threads'
2
- import {
3
- type DestinationStream,
4
- type Level,
5
- type Logger as PinoLogger,
6
- pino,
7
- type StreamEntry,
8
- stdTimeFunctions,
9
- } from 'pino'
10
- import { build as pretty } from 'pino-pretty'
11
-
12
- export type { StreamEntry } from 'pino'
13
- export type Logger = PinoLogger
14
- export type LoggingOptions = {
15
- destinations?: Array<DestinationStream | StreamEntry<Level>>
16
- pinoOptions?: any
17
- }
18
-
19
- // TODO: use node:util inspect
20
- const bg = (value, color) => `\x1b[${color}m${value}\x1b[0m`
21
- const fg = (value, color) => `\x1b[38;5;${color}m${value}\x1b[0m`
22
-
23
- const levelColors = {
24
- 10: 100,
25
- 20: 102,
26
- 30: 106,
27
- 40: 104,
28
- 50: 101,
29
- 60: 105,
30
- [Number.POSITIVE_INFINITY]: 0,
31
- }
32
- const messageColors = {
33
- 10: 0,
34
- 20: 2,
35
- 30: 6,
36
- 40: 4,
37
- 50: 1,
38
- 60: 5,
39
- [Number.POSITIVE_INFINITY]: 0,
40
- }
41
-
42
- const levelLabels = {
43
- 10: ' TRACE ',
44
- 20: ' DEBUG ',
45
- 30: ' INFO ',
46
- 40: ' WARN ',
47
- 50: ' ERROR ',
48
- 60: ' FATAL ',
49
- [Number.POSITIVE_INFINITY]: 'SILENT',
50
- }
51
-
52
- export const createLogger = (options: LoggingOptions = {}, $group: string) => {
53
- let { destinations, pinoOptions } = options
54
-
55
- if (!destinations || !destinations?.length) {
56
- destinations = [createConsolePrettyDestination('info')]
57
- }
58
-
59
- const lowestLevelValue = destinations!.reduce(
60
- (acc, destination) =>
61
- Math.min(
62
- acc,
63
- 'stream' in destination
64
- ? pino.levels.values[destination.level!]
65
- : Number.POSITIVE_INFINITY,
66
- ),
67
- Number.POSITIVE_INFINITY,
68
- )
69
- const level = pino.levels.labels[lowestLevelValue]
70
- return pino(
71
- {
72
- timestamp: stdTimeFunctions.isoTime,
73
- ...pinoOptions,
74
- level,
75
- },
76
- pino.multistream(destinations!),
77
- ).child({ $group, $threadId: threadId })
78
- }
79
-
80
- export const createConsolePrettyDestination = (
81
- level: Level,
82
- sync = true,
83
- ): StreamEntry => ({
84
- level,
85
- stream: pretty({
86
- colorize: true,
87
- ignore: 'hostname,$group,$threadId',
88
- errorLikeObjectKeys: ['err', 'error', 'cause'],
89
- messageFormat: (log, messageKey) => {
90
- const group = fg(`[${log.$group}]`, 11)
91
- const msg = fg(log[messageKey], messageColors[log.level as number])
92
- const thread = fg(`(Thread-${log.$threadId})`, 89)
93
- return `\x1b[0m${thread} ${group} ${msg}`
94
- },
95
- customPrettifiers: {
96
- level: (level: any) => bg(levelLabels[level], levelColors[level]),
97
- },
98
- sync,
99
- }),
100
- })
package/src/metadata.ts DELETED
@@ -1,27 +0,0 @@
1
- import { kMetadata } from './constants.ts'
2
-
3
- export type Metadata<T = any> = {
4
- key: MetadataKey<T>
5
- value: T
6
- }
7
-
8
- export type MetadataKey<T = any> = {
9
- [kMetadata]: string
10
- as(value: T): Metadata<T>
11
- }
12
-
13
- export const createMetadataKey = <T>(key: string): MetadataKey<T> => {
14
- const metadataKey = {
15
- [kMetadata]: key,
16
- as(value: T) {
17
- return { key: metadataKey, value }
18
- },
19
- }
20
- return metadataKey
21
- }
22
-
23
- export class MetadataStore extends Map<MetadataKey, Metadata> {
24
- get<T>(key: MetadataKey<T>): T | undefined {
25
- return super.get(key) as T | undefined
26
- }
27
- }
package/src/plugin.ts DELETED
@@ -1,27 +0,0 @@
1
- import type { Async } from '@nmtjs/common'
2
- import { kPlugin } from './constants.ts'
3
- import type { PluginContext } from './types.ts'
4
-
5
- export interface BasePlugin<
6
- Type = any,
7
- Options = unknown,
8
- Context extends PluginContext = PluginContext,
9
- > {
10
- name: string
11
- init: (context: Context, options: Options) => Async<Type>
12
- }
13
-
14
- export interface Plugin<
15
- Type = void,
16
- Options = unknown,
17
- Context extends PluginContext = PluginContext,
18
- > extends BasePlugin<Type, Options, Context> {
19
- [kPlugin]: any
20
- }
21
-
22
- export const createPlugin = <Options = unknown, Type = void>(
23
- name: string,
24
- init: Plugin<Type, Options>['init'],
25
- ): Plugin<Type, Options> => ({ name, init, [kPlugin]: true })
26
-
27
- export const isPlugin = (value: any): value is Plugin => kPlugin in value
package/src/registry.ts DELETED
@@ -1,44 +0,0 @@
1
- import { type Hook, Scope } from './enums.ts'
2
- import { Hooks, type HookType } from './hooks.ts'
3
- import {
4
- type AnyInjectable,
5
- type Dependant,
6
- getInjectableScope,
7
- } from './injectables.ts'
8
- import type { Logger } from './logger.ts'
9
-
10
- export class Registry {
11
- readonly hooks = new Hooks()
12
-
13
- constructor(
14
- protected readonly application: {
15
- logger: Logger
16
- },
17
- ) {}
18
-
19
- registerHooks<T extends Hooks>(hooks: T) {
20
- Hooks.merge(hooks, this.hooks)
21
- }
22
-
23
- registerHook<T extends Hook>(name: T, callback: HookType[T]) {
24
- this.hooks.add(name, callback)
25
- }
26
-
27
- *getDependants(): Generator<Dependant> {}
28
-
29
- clear() {
30
- this.hooks.clear()
31
- }
32
- }
33
-
34
- export const scopeErrorMessage = (name, scope = Scope.Global) =>
35
- `${name} must be a ${scope} scope (including all nested dependencies)`
36
-
37
- export function hasInvalidScopeDeps(
38
- injectables: AnyInjectable[],
39
- scope = Scope.Global,
40
- ) {
41
- return injectables.some(
42
- (injectable) => getInjectableScope(injectable) !== scope,
43
- )
44
- }
package/src/types.ts DELETED
@@ -1,13 +0,0 @@
1
- import type { Container } from './container.ts'
2
- import type { Hooks } from './hooks.ts'
3
- import type { Logger } from './logger.ts'
4
- import type { Registry } from './registry.ts'
5
-
6
- export interface PluginContext {
7
- logger: Logger
8
- registry: Registry
9
- hooks: Hooks
10
- container: Container
11
- }
12
-
13
- export type Pattern = RegExp | string | ((value: string) => boolean)