@velajs/vela 0.4.2 → 0.5.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.
- package/dist/cache/cache.module.d.ts +4 -1
- package/dist/cache/cache.module.js +51 -9
- package/dist/config/config.module.d.ts +4 -1
- package/dist/config/config.module.js +40 -3
- package/dist/config/config.types.d.ts +1 -0
- package/dist/constants.d.ts +6 -1
- package/dist/constants.js +5 -0
- package/dist/container/container.d.ts +4 -0
- package/dist/container/container.js +52 -15
- package/dist/container/decorators.d.ts +3 -1
- package/dist/container/decorators.js +27 -6
- package/dist/container/index.d.ts +4 -2
- package/dist/container/index.js +4 -2
- package/dist/container/mixin.d.ts +24 -0
- package/dist/container/mixin.js +26 -0
- package/dist/container/module-ref.d.ts +21 -0
- package/dist/container/module-ref.js +48 -0
- package/dist/container/types.d.ts +10 -3
- package/dist/container/types.js +9 -0
- package/dist/cors/cors.module.js +2 -2
- package/dist/factory.js +36 -13
- package/dist/fetch/fetch.module.d.ts +6 -0
- package/dist/fetch/fetch.module.js +77 -0
- package/dist/fetch/fetch.service.d.ts +24 -0
- package/dist/fetch/fetch.service.js +145 -0
- package/dist/fetch/fetch.types.d.ts +24 -0
- package/dist/fetch/fetch.types.js +1 -0
- package/dist/fetch/index.d.ts +3 -0
- package/dist/fetch/index.js +2 -0
- package/dist/http/decorators.d.ts +64 -0
- package/dist/http/decorators.js +89 -1
- package/dist/http/index.d.ts +3 -1
- package/dist/http/index.js +2 -1
- package/dist/http/middleware-consumer.d.ts +36 -0
- package/dist/http/middleware-consumer.js +71 -0
- package/dist/http/route.manager.d.ts +13 -1
- package/dist/http/route.manager.js +153 -53
- package/dist/index.d.ts +11 -6
- package/dist/index.js +7 -4
- package/dist/metadata.d.ts +1 -1
- package/dist/module/decorators.d.ts +1 -0
- package/dist/module/decorators.js +24 -8
- package/dist/module/index.d.ts +2 -2
- package/dist/module/index.js +1 -1
- package/dist/module/module-loader.d.ts +9 -1
- package/dist/module/module-loader.js +107 -11
- package/dist/module/types.d.ts +13 -3
- package/dist/pipeline/index.d.ts +3 -2
- package/dist/pipeline/index.js +1 -1
- package/dist/pipeline/pipes.d.ts +22 -0
- package/dist/pipeline/pipes.js +50 -0
- package/dist/pipeline/tokens.d.ts +1 -1
- package/dist/pipeline/tokens.js +1 -1
- package/dist/pipeline/types.d.ts +16 -0
- package/dist/registry/metadata.registry.d.ts +7 -6
- package/dist/registry/metadata.registry.js +13 -0
- package/dist/registry/types.d.ts +1 -0
- package/dist/schedule/schedule.executor.d.ts +11 -2
- package/dist/schedule/schedule.executor.js +134 -19
- package/dist/schedule/schedule.module.d.ts +4 -1
- package/dist/schedule/schedule.module.js +39 -7
- package/dist/testing/testing.builder.js +31 -17
- package/dist/throttler/throttler.module.d.ts +2 -1
- package/dist/throttler/throttler.module.js +47 -9
- package/package.json +1 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { DynamicModule } from '../module/types';
|
|
1
|
+
import type { AsyncModuleOptions, DynamicModule } from '../module/types';
|
|
2
2
|
import type { CacheModuleOptions } from './cache.types';
|
|
3
3
|
export declare class CacheModule {
|
|
4
4
|
static forRoot(options?: CacheModuleOptions): DynamicModule;
|
|
5
|
+
static registerAsync(options: AsyncModuleOptions<CacheModuleOptions> & {
|
|
6
|
+
isGlobal?: boolean;
|
|
7
|
+
}): DynamicModule;
|
|
5
8
|
}
|
|
@@ -6,16 +6,20 @@ import { CacheInterceptor } from "./cache.interceptor.js";
|
|
|
6
6
|
import { CacheService } from "./cache.service.js";
|
|
7
7
|
import { MemoryCacheStore } from "./cache.store.js";
|
|
8
8
|
import { CACHE_MANAGER, CACHE_MODULE_OPTIONS } from "./cache.tokens.js";
|
|
9
|
+
function makeCacheModuleClass(name) {
|
|
10
|
+
const moduleClass = class {
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(moduleClass, 'name', {
|
|
13
|
+
value: name
|
|
14
|
+
});
|
|
15
|
+
defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
|
|
16
|
+
return moduleClass;
|
|
17
|
+
}
|
|
9
18
|
export class CacheModule {
|
|
10
19
|
static forRoot(options = {}) {
|
|
11
20
|
const { ttl = 5, max = 100, isGlobal = false } = options;
|
|
12
21
|
const store = new MemoryCacheStore(ttl, max);
|
|
13
|
-
const moduleClass =
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(moduleClass, 'name', {
|
|
16
|
-
value: 'CacheModule'
|
|
17
|
-
});
|
|
18
|
-
defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
|
|
22
|
+
const moduleClass = makeCacheModuleClass('CacheModule');
|
|
19
23
|
MetadataRegistry.setModuleOptions(moduleClass, {
|
|
20
24
|
exports: [
|
|
21
25
|
CACHE_MANAGER,
|
|
@@ -26,11 +30,11 @@ export class CacheModule {
|
|
|
26
30
|
});
|
|
27
31
|
const providers = [
|
|
28
32
|
{
|
|
29
|
-
|
|
33
|
+
provide: CACHE_MANAGER,
|
|
30
34
|
useValue: store
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
|
-
|
|
37
|
+
provide: CACHE_MODULE_OPTIONS,
|
|
34
38
|
useValue: options
|
|
35
39
|
},
|
|
36
40
|
CacheService,
|
|
@@ -38,7 +42,45 @@ export class CacheModule {
|
|
|
38
42
|
];
|
|
39
43
|
if (isGlobal) {
|
|
40
44
|
providers.push({
|
|
41
|
-
|
|
45
|
+
provide: APP_INTERCEPTOR,
|
|
46
|
+
useExisting: CacheInterceptor
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
module: moduleClass,
|
|
51
|
+
providers
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
static registerAsync(options) {
|
|
55
|
+
const moduleClass = makeCacheModuleClass('CacheModule');
|
|
56
|
+
MetadataRegistry.setModuleOptions(moduleClass, {
|
|
57
|
+
imports: options.imports ?? [],
|
|
58
|
+
exports: [
|
|
59
|
+
CACHE_MANAGER,
|
|
60
|
+
CACHE_MODULE_OPTIONS,
|
|
61
|
+
CacheService,
|
|
62
|
+
CacheInterceptor
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
const providers = [
|
|
66
|
+
{
|
|
67
|
+
provide: CACHE_MODULE_OPTIONS,
|
|
68
|
+
useFactory: options.useFactory,
|
|
69
|
+
inject: options.inject ?? []
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
provide: CACHE_MANAGER,
|
|
73
|
+
useFactory: (opts)=>new MemoryCacheStore(opts.ttl ?? 5, opts.max ?? 100),
|
|
74
|
+
inject: [
|
|
75
|
+
CACHE_MODULE_OPTIONS
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
CacheService,
|
|
79
|
+
CacheInterceptor
|
|
80
|
+
];
|
|
81
|
+
if (options.isGlobal) {
|
|
82
|
+
providers.push({
|
|
83
|
+
provide: APP_INTERCEPTOR,
|
|
42
84
|
useExisting: CacheInterceptor
|
|
43
85
|
});
|
|
44
86
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { DynamicModule } from '../module/types';
|
|
1
|
+
import type { AsyncModuleOptions, DynamicModule } from '../module/types';
|
|
2
2
|
import type { ConfigModuleOptions } from './config.types';
|
|
3
3
|
export declare class ConfigModule {
|
|
4
4
|
static forRoot<T extends Record<string, unknown>>(options: ConfigModuleOptions<T>): DynamicModule;
|
|
5
|
+
static forRootAsync<T extends Record<string, unknown>>(options: AsyncModuleOptions<ConfigModuleOptions<T>> & {
|
|
6
|
+
isGlobal?: boolean;
|
|
7
|
+
}): DynamicModule;
|
|
5
8
|
}
|
|
@@ -16,17 +16,54 @@ export class ConfigModule {
|
|
|
16
16
|
exports: [
|
|
17
17
|
ConfigService,
|
|
18
18
|
CONFIG_OPTIONS
|
|
19
|
-
]
|
|
19
|
+
],
|
|
20
|
+
isGlobal: options.isGlobal
|
|
20
21
|
});
|
|
21
22
|
return {
|
|
22
23
|
module: moduleClass,
|
|
23
24
|
providers: [
|
|
24
25
|
{
|
|
25
|
-
|
|
26
|
+
provide: CONFIG_OPTIONS,
|
|
26
27
|
useValue: config
|
|
27
28
|
},
|
|
28
29
|
ConfigService
|
|
29
|
-
]
|
|
30
|
+
],
|
|
31
|
+
...options.isGlobal ? {
|
|
32
|
+
global: true
|
|
33
|
+
} : {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
static forRootAsync(options) {
|
|
37
|
+
const moduleClass = class ConfigAsyncDynamicModule {
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(moduleClass, 'name', {
|
|
40
|
+
value: 'ConfigModule'
|
|
41
|
+
});
|
|
42
|
+
defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
|
|
43
|
+
MetadataRegistry.setModuleOptions(moduleClass, {
|
|
44
|
+
imports: options.imports ?? [],
|
|
45
|
+
exports: [
|
|
46
|
+
ConfigService,
|
|
47
|
+
CONFIG_OPTIONS
|
|
48
|
+
],
|
|
49
|
+
isGlobal: options.isGlobal
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
module: moduleClass,
|
|
53
|
+
providers: [
|
|
54
|
+
{
|
|
55
|
+
provide: CONFIG_OPTIONS,
|
|
56
|
+
useFactory: async (...args)=>{
|
|
57
|
+
const opts = await options.useFactory(...args);
|
|
58
|
+
return opts.validate ? opts.validate(opts.config) : opts.config;
|
|
59
|
+
},
|
|
60
|
+
inject: options.inject ?? []
|
|
61
|
+
},
|
|
62
|
+
ConfigService
|
|
63
|
+
],
|
|
64
|
+
...options.isGlobal ? {
|
|
65
|
+
global: true
|
|
66
|
+
} : {}
|
|
30
67
|
};
|
|
31
68
|
}
|
|
32
69
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare const METADATA_KEYS: {
|
|
|
3
3
|
readonly INJECT: "vela:inject";
|
|
4
4
|
readonly SCOPE: "vela:scope";
|
|
5
5
|
readonly MODULE: "vela:module";
|
|
6
|
+
readonly MODULE_OPTIONS: "vela:module-options";
|
|
6
7
|
readonly CONTROLLER: "vela:controller";
|
|
7
8
|
readonly ROUTES: "vela:routes";
|
|
8
9
|
readonly PARAMS: "vela:params";
|
|
@@ -26,7 +27,11 @@ export declare enum ParamType {
|
|
|
26
27
|
QUERY = "query",
|
|
27
28
|
PARAM = "param",
|
|
28
29
|
HEADERS = "headers",
|
|
29
|
-
REQUEST = "request"
|
|
30
|
+
REQUEST = "request",
|
|
31
|
+
RESPONSE = "response",
|
|
32
|
+
IP = "ip",
|
|
33
|
+
COOKIE = "cookie",
|
|
34
|
+
RAW_BODY = "raw_body"
|
|
30
35
|
}
|
|
31
36
|
export declare enum Scope {
|
|
32
37
|
SINGLETON = "singleton",
|
package/dist/constants.js
CHANGED
|
@@ -5,6 +5,7 @@ export const METADATA_KEYS = {
|
|
|
5
5
|
SCOPE: 'vela:scope',
|
|
6
6
|
// Module
|
|
7
7
|
MODULE: 'vela:module',
|
|
8
|
+
MODULE_OPTIONS: 'vela:module-options',
|
|
8
9
|
// HTTP
|
|
9
10
|
CONTROLLER: 'vela:controller',
|
|
10
11
|
ROUTES: 'vela:routes',
|
|
@@ -33,6 +34,10 @@ export var ParamType = /*#__PURE__*/ function(ParamType) {
|
|
|
33
34
|
ParamType["PARAM"] = "param";
|
|
34
35
|
ParamType["HEADERS"] = "headers";
|
|
35
36
|
ParamType["REQUEST"] = "request";
|
|
37
|
+
ParamType["RESPONSE"] = "response";
|
|
38
|
+
ParamType["IP"] = "ip";
|
|
39
|
+
ParamType["COOKIE"] = "cookie";
|
|
40
|
+
ParamType["RAW_BODY"] = "raw_body";
|
|
36
41
|
return ParamType;
|
|
37
42
|
}({});
|
|
38
43
|
export var Scope = /*#__PURE__*/ function(Scope) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Scope } from '../constants';
|
|
1
2
|
import type { ProviderOptions, Token, Type } from './types';
|
|
2
3
|
export declare class Container {
|
|
3
4
|
private providers;
|
|
@@ -9,6 +10,7 @@ export declare class Container {
|
|
|
9
10
|
private registerOptions;
|
|
10
11
|
resolve<T>(token: Token<T>): T;
|
|
11
12
|
has(token: Token): boolean;
|
|
13
|
+
getProviderScope(token: Token): Scope | undefined;
|
|
12
14
|
getTokens(): Token[];
|
|
13
15
|
/**
|
|
14
16
|
* Create a child container for request-scoped resolution.
|
|
@@ -16,10 +18,12 @@ export declare class Container {
|
|
|
16
18
|
* instances separately per child (per request).
|
|
17
19
|
*/
|
|
18
20
|
createChild(): Container;
|
|
21
|
+
createDetached(): Container;
|
|
19
22
|
clear(): void;
|
|
20
23
|
private resolveRegistration;
|
|
21
24
|
private resolveClass;
|
|
22
25
|
private resolveFactory;
|
|
23
26
|
resolveAsync<T>(token: Token<T>): Promise<T>;
|
|
27
|
+
private createLazyProxy;
|
|
24
28
|
private tokenToString;
|
|
25
29
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Scope } from "../constants.js";
|
|
2
2
|
import { getConstructorDependencies, getInjectMetadata, getScope, isInjectable } from "./decorators.js";
|
|
3
|
-
import { InjectionToken } from "./types.js";
|
|
3
|
+
import { ForwardRef, InjectionToken } from "./types.js";
|
|
4
4
|
export class Container {
|
|
5
5
|
providers = new Map();
|
|
6
6
|
resolutionStack = new Set();
|
|
@@ -20,18 +20,18 @@ export class Container {
|
|
|
20
20
|
}
|
|
21
21
|
const scope = getScope(target);
|
|
22
22
|
this.providers.set(target, {
|
|
23
|
-
|
|
23
|
+
provide: target,
|
|
24
24
|
scope,
|
|
25
25
|
useClass: target
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
registerOptions(options) {
|
|
29
|
-
const token = options.
|
|
29
|
+
const token = options.provide;
|
|
30
30
|
if (!token) {
|
|
31
31
|
throw new Error('Provider registration requires a token');
|
|
32
32
|
}
|
|
33
33
|
const registration = {
|
|
34
|
-
token,
|
|
34
|
+
provide: token,
|
|
35
35
|
scope: options.scope ?? Scope.SINGLETON
|
|
36
36
|
};
|
|
37
37
|
if (options.useValue !== undefined) {
|
|
@@ -40,6 +40,8 @@ export class Container {
|
|
|
40
40
|
} else if (options.useFactory) {
|
|
41
41
|
registration.useFactory = options.useFactory;
|
|
42
42
|
registration.inject = options.inject;
|
|
43
|
+
} else if (options.useClass) {
|
|
44
|
+
registration.useClass = options.useClass;
|
|
43
45
|
} else if (options.useExisting) {
|
|
44
46
|
registration.useExisting = options.useExisting;
|
|
45
47
|
} else if (typeof token === 'function') {
|
|
@@ -56,7 +58,7 @@ export class Container {
|
|
|
56
58
|
}
|
|
57
59
|
if (token instanceof InjectionToken && token.options?.factory) {
|
|
58
60
|
this.register({
|
|
59
|
-
token,
|
|
61
|
+
provide: token,
|
|
60
62
|
useFactory: token.options.factory
|
|
61
63
|
});
|
|
62
64
|
return this.resolve(token);
|
|
@@ -68,6 +70,9 @@ export class Container {
|
|
|
68
70
|
has(token) {
|
|
69
71
|
return this.providers.has(token);
|
|
70
72
|
}
|
|
73
|
+
getProviderScope(token) {
|
|
74
|
+
return this.providers.get(token)?.scope;
|
|
75
|
+
}
|
|
71
76
|
getTokens() {
|
|
72
77
|
return Array.from(this.providers.keys());
|
|
73
78
|
}
|
|
@@ -81,6 +86,11 @@ export class Container {
|
|
|
81
86
|
child.providers = this.providers; // share provider registrations
|
|
82
87
|
return child;
|
|
83
88
|
}
|
|
89
|
+
createDetached() {
|
|
90
|
+
const child = new Container();
|
|
91
|
+
child.providers = new Map(this.providers); // copy, not share
|
|
92
|
+
return child;
|
|
93
|
+
}
|
|
84
94
|
clear() {
|
|
85
95
|
this.providers.clear();
|
|
86
96
|
this.resolutionStack.clear();
|
|
@@ -99,19 +109,19 @@ export class Container {
|
|
|
99
109
|
}
|
|
100
110
|
// Request: return cached from this child's requestInstances
|
|
101
111
|
if (registration.scope === Scope.REQUEST) {
|
|
102
|
-
const cached = this.requestInstances.get(registration.
|
|
112
|
+
const cached = this.requestInstances.get(registration.provide);
|
|
103
113
|
if (cached !== undefined) {
|
|
104
114
|
return cached;
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
|
-
if (this.resolutionStack.has(registration.
|
|
117
|
+
if (this.resolutionStack.has(registration.provide)) {
|
|
108
118
|
const chain = [
|
|
109
119
|
...this.resolutionStack,
|
|
110
|
-
registration.
|
|
120
|
+
registration.provide
|
|
111
121
|
].map((t)=>this.tokenToString(t)).join(' -> ');
|
|
112
122
|
throw new Error(`Circular dependency detected: ${chain}`);
|
|
113
123
|
}
|
|
114
|
-
this.resolutionStack.add(registration.
|
|
124
|
+
this.resolutionStack.add(registration.provide);
|
|
115
125
|
try {
|
|
116
126
|
let instance;
|
|
117
127
|
if (registration.useFactory) {
|
|
@@ -119,16 +129,16 @@ export class Container {
|
|
|
119
129
|
} else if (registration.useClass) {
|
|
120
130
|
instance = this.resolveClass(registration.useClass);
|
|
121
131
|
} else {
|
|
122
|
-
throw new Error(`Invalid provider registration for: ${this.tokenToString(registration.
|
|
132
|
+
throw new Error(`Invalid provider registration for: ${this.tokenToString(registration.provide)}`);
|
|
123
133
|
}
|
|
124
134
|
if (registration.scope === Scope.SINGLETON) {
|
|
125
135
|
registration.instance = instance;
|
|
126
136
|
} else if (registration.scope === Scope.REQUEST) {
|
|
127
|
-
this.requestInstances.set(registration.
|
|
137
|
+
this.requestInstances.set(registration.provide, instance);
|
|
128
138
|
}
|
|
129
139
|
return instance;
|
|
130
140
|
} finally{
|
|
131
|
-
this.resolutionStack.delete(registration.
|
|
141
|
+
this.resolutionStack.delete(registration.provide);
|
|
132
142
|
}
|
|
133
143
|
}
|
|
134
144
|
resolveClass(target) {
|
|
@@ -136,13 +146,24 @@ export class Container {
|
|
|
136
146
|
const injectMetadata = getInjectMetadata(target);
|
|
137
147
|
const injectMap = new Map(injectMetadata.map((m)=>[
|
|
138
148
|
m.index,
|
|
139
|
-
m
|
|
149
|
+
m
|
|
140
150
|
]));
|
|
141
151
|
const dependencies = paramTypes.map((paramType, index)=>{
|
|
142
|
-
const
|
|
152
|
+
const meta = injectMap.get(index);
|
|
153
|
+
const rawToken = meta?.token;
|
|
154
|
+
const isForwardRef = rawToken instanceof ForwardRef;
|
|
155
|
+
const token = isForwardRef ? rawToken.factory() : rawToken ?? paramType;
|
|
143
156
|
if (!token || token === Object) {
|
|
157
|
+
if (meta?.optional) return undefined;
|
|
144
158
|
throw new Error(`Cannot resolve dependency at index ${index} for ${target.name}. ` + `Parameter type is undefined or Object. ` + `Use @Inject() to specify the token explicitly.`);
|
|
145
159
|
}
|
|
160
|
+
if (meta?.optional && !this.has(token)) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
// forwardRef with circular dep — break the cycle with a lazy Proxy
|
|
164
|
+
if (isForwardRef && this.resolutionStack.has(token)) {
|
|
165
|
+
return this.createLazyProxy(token);
|
|
166
|
+
}
|
|
146
167
|
return this.resolve(token);
|
|
147
168
|
});
|
|
148
169
|
return new target(...dependencies);
|
|
@@ -154,7 +175,7 @@ export class Container {
|
|
|
154
175
|
const dependencies = (registration.inject || []).map((token)=>this.resolve(token));
|
|
155
176
|
const result = registration.useFactory(...dependencies);
|
|
156
177
|
if (result instanceof Promise) {
|
|
157
|
-
throw new Error(`Async factory for ${this.tokenToString(registration.
|
|
178
|
+
throw new Error(`Async factory for ${this.tokenToString(registration.provide)} returned a Promise. ` + `Use resolveAsync() for async providers.`);
|
|
158
179
|
}
|
|
159
180
|
return result;
|
|
160
181
|
}
|
|
@@ -180,6 +201,22 @@ export class Container {
|
|
|
180
201
|
}
|
|
181
202
|
return this.resolve(token);
|
|
182
203
|
}
|
|
204
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
205
|
+
createLazyProxy(token) {
|
|
206
|
+
const container = this;
|
|
207
|
+
return new Proxy({}, {
|
|
208
|
+
get (_target, prop) {
|
|
209
|
+
const instance = container.resolve(token);
|
|
210
|
+
const value = instance[prop];
|
|
211
|
+
return typeof value === 'function' ? value.bind(instance) : value;
|
|
212
|
+
},
|
|
213
|
+
set (_target, prop, value) {
|
|
214
|
+
const instance = container.resolve(token);
|
|
215
|
+
instance[prop] = value;
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
183
220
|
tokenToString(token) {
|
|
184
221
|
if (token instanceof InjectionToken) {
|
|
185
222
|
return token.toString();
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Scope } from '../constants';
|
|
2
2
|
import type { InjectableOptions, InjectMetadata, Token } from './types';
|
|
3
|
+
import { ForwardRef } from './types';
|
|
3
4
|
export declare function Injectable(options?: InjectableOptions): ClassDecorator;
|
|
4
|
-
export declare function
|
|
5
|
+
export declare function Optional(): ParameterDecorator;
|
|
6
|
+
export declare function Inject(token: Token | ForwardRef): ParameterDecorator;
|
|
5
7
|
export declare function isInjectable(target: object): boolean;
|
|
6
8
|
export declare function getScope(target: object): Scope;
|
|
7
9
|
export declare function getConstructorDependencies(target: object): unknown[];
|
|
@@ -11,13 +11,34 @@ export function Injectable(options = {}) {
|
|
|
11
11
|
defineMetadata(METADATA_KEYS.SCOPE, scope, target);
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
+
export function Optional() {
|
|
15
|
+
return (target, _propertyKey, parameterIndex)=>{
|
|
16
|
+
const existing = getInjectMetadata(target);
|
|
17
|
+
const entry = existing.find((m)=>m.index === parameterIndex);
|
|
18
|
+
if (entry) {
|
|
19
|
+
entry.optional = true;
|
|
20
|
+
} else {
|
|
21
|
+
existing.push({
|
|
22
|
+
index: parameterIndex,
|
|
23
|
+
optional: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
MetadataRegistry.setInjectTokens(target, existing);
|
|
27
|
+
defineMetadata(METADATA_KEYS.INJECT, existing, target);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
14
30
|
export function Inject(token) {
|
|
15
31
|
return (target, _propertyKey, parameterIndex)=>{
|
|
16
|
-
const existing =
|
|
17
|
-
existing.
|
|
18
|
-
|
|
19
|
-
token
|
|
20
|
-
}
|
|
32
|
+
const existing = getInjectMetadata(target);
|
|
33
|
+
const entry = existing.find((m)=>m.index === parameterIndex);
|
|
34
|
+
if (entry) {
|
|
35
|
+
entry.token = token;
|
|
36
|
+
} else {
|
|
37
|
+
existing.push({
|
|
38
|
+
index: parameterIndex,
|
|
39
|
+
token
|
|
40
|
+
});
|
|
41
|
+
}
|
|
21
42
|
MetadataRegistry.setInjectTokens(target, existing);
|
|
22
43
|
defineMetadata(METADATA_KEYS.INJECT, existing, target);
|
|
23
44
|
};
|
|
@@ -32,5 +53,5 @@ export function getConstructorDependencies(target) {
|
|
|
32
53
|
return Reflect.getMetadata('design:paramtypes', target) || [];
|
|
33
54
|
}
|
|
34
55
|
export function getInjectMetadata(target) {
|
|
35
|
-
return MetadataRegistry.getInjectTokens(target) ??
|
|
56
|
+
return MetadataRegistry.getInjectTokens(target) ?? getMetadata(METADATA_KEYS.INJECT, target) ?? [];
|
|
36
57
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { Container } from './container';
|
|
2
|
-
export { Injectable, Inject, isInjectable, getScope } from './decorators';
|
|
3
|
-
export { InjectionToken } from './types';
|
|
2
|
+
export { Injectable, Inject, Optional, isInjectable, getScope } from './decorators';
|
|
3
|
+
export { InjectionToken, ForwardRef, forwardRef } from './types';
|
|
4
|
+
export { ModuleRef } from './module-ref';
|
|
5
|
+
export { mixin } from './mixin';
|
|
4
6
|
export type { Type, Token, InjectableOptions, InjectMetadata, ProviderOptions, ProviderRegistration, InjectionTokenOptions, } from './types';
|
package/dist/container/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { Container } from "./container.js";
|
|
2
|
-
export { Injectable, Inject, isInjectable, getScope } from "./decorators.js";
|
|
3
|
-
export { InjectionToken } from "./types.js";
|
|
2
|
+
export { Injectable, Inject, Optional, isInjectable, getScope } from "./decorators.js";
|
|
3
|
+
export { InjectionToken, ForwardRef, forwardRef } from "./types.js";
|
|
4
|
+
export { ModuleRef } from "./module-ref.js";
|
|
5
|
+
export { mixin } from "./mixin.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Type } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a reusable injectable mixin class from a base class.
|
|
4
|
+
* Applies @Injectable() so the returned class can be registered as a provider.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* function RoleGuardMixin(role: string) {
|
|
9
|
+
* class MixedRoleGuard implements CanActivate {
|
|
10
|
+
* canActivate(ctx: ExecutionContext) {
|
|
11
|
+
* return ctx.getRequest().headers.get('x-role') === role;
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* return mixin(MixedRoleGuard);
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* const AdminGuard = RoleGuardMixin('admin');
|
|
18
|
+
*
|
|
19
|
+
* @UseGuards(AdminGuard)
|
|
20
|
+
* @Get('/admin')
|
|
21
|
+
* adminOnly() { ... }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function mixin<T>(mixinClass: Type<T>): Type<T>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Injectable } from "./decorators.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a reusable injectable mixin class from a base class.
|
|
4
|
+
* Applies @Injectable() so the returned class can be registered as a provider.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* function RoleGuardMixin(role: string) {
|
|
9
|
+
* class MixedRoleGuard implements CanActivate {
|
|
10
|
+
* canActivate(ctx: ExecutionContext) {
|
|
11
|
+
* return ctx.getRequest().headers.get('x-role') === role;
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* return mixin(MixedRoleGuard);
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* const AdminGuard = RoleGuardMixin('admin');
|
|
18
|
+
*
|
|
19
|
+
* @UseGuards(AdminGuard)
|
|
20
|
+
* @Get('/admin')
|
|
21
|
+
* adminOnly() { ... }
|
|
22
|
+
* ```
|
|
23
|
+
*/ export function mixin(mixinClass) {
|
|
24
|
+
Injectable()(mixinClass);
|
|
25
|
+
return mixinClass;
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Token, Type } from './types';
|
|
2
|
+
import type { Container } from './container';
|
|
3
|
+
export declare class ModuleRef {
|
|
4
|
+
private readonly container;
|
|
5
|
+
constructor(container: Container);
|
|
6
|
+
/**
|
|
7
|
+
* Retrieve a provider instance from the DI container.
|
|
8
|
+
* Returns the existing singleton (or cached value) for the token.
|
|
9
|
+
*/
|
|
10
|
+
get<T>(token: Token<T>): T;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve a provider, creating a new instance for TRANSIENT-scoped providers.
|
|
13
|
+
* For SINGLETON-scoped providers this is equivalent to get().
|
|
14
|
+
*/
|
|
15
|
+
resolve<T>(token: Token<T>): T;
|
|
16
|
+
/**
|
|
17
|
+
* Instantiate a class outside of the DI container's singleton cache.
|
|
18
|
+
* Dependencies are resolved from the container. Each call returns a new instance.
|
|
19
|
+
*/
|
|
20
|
+
create<T>(type: Type<T>): T;
|
|
21
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
function _ts_metadata(k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
}
|
|
10
|
+
import { Scope } from "../constants.js";
|
|
11
|
+
import { Injectable } from "./decorators.js";
|
|
12
|
+
export class ModuleRef {
|
|
13
|
+
container;
|
|
14
|
+
constructor(container){
|
|
15
|
+
this.container = container;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Retrieve a provider instance from the DI container.
|
|
19
|
+
* Returns the existing singleton (or cached value) for the token.
|
|
20
|
+
*/ get(token) {
|
|
21
|
+
return this.container.resolve(token);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a provider, creating a new instance for TRANSIENT-scoped providers.
|
|
25
|
+
* For SINGLETON-scoped providers this is equivalent to get().
|
|
26
|
+
*/ resolve(token) {
|
|
27
|
+
return this.container.resolve(token);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Instantiate a class outside of the DI container's singleton cache.
|
|
31
|
+
* Dependencies are resolved from the container. Each call returns a new instance.
|
|
32
|
+
*/ create(type) {
|
|
33
|
+
const sandbox = this.container.createDetached();
|
|
34
|
+
sandbox.register({
|
|
35
|
+
provide: type,
|
|
36
|
+
useClass: type,
|
|
37
|
+
scope: Scope.TRANSIENT
|
|
38
|
+
});
|
|
39
|
+
return sandbox.resolve(type);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
ModuleRef = _ts_decorate([
|
|
43
|
+
Injectable(),
|
|
44
|
+
_ts_metadata("design:type", Function),
|
|
45
|
+
_ts_metadata("design:paramtypes", [
|
|
46
|
+
typeof Container === "undefined" ? Object : Container
|
|
47
|
+
])
|
|
48
|
+
], ModuleRef);
|
|
@@ -9,24 +9,31 @@ export declare class InjectionToken<T = unknown> {
|
|
|
9
9
|
constructor(description: string, options?: InjectionTokenOptions<T> | undefined);
|
|
10
10
|
toString(): string;
|
|
11
11
|
}
|
|
12
|
+
export declare class ForwardRef<T = unknown> {
|
|
13
|
+
readonly factory: () => Token<T>;
|
|
14
|
+
constructor(factory: () => Token<T>);
|
|
15
|
+
}
|
|
16
|
+
export declare function forwardRef<T>(factory: () => Token<T>): ForwardRef<T>;
|
|
12
17
|
export type Token<T = any> = Type<T> | InjectionToken<T> | string | symbol;
|
|
13
18
|
export interface InjectableOptions {
|
|
14
19
|
scope?: Scope;
|
|
15
20
|
}
|
|
16
21
|
export interface InjectMetadata {
|
|
17
22
|
index: number;
|
|
18
|
-
token
|
|
23
|
+
token?: Token | ForwardRef;
|
|
24
|
+
optional?: boolean;
|
|
19
25
|
}
|
|
20
26
|
export interface ProviderOptions<T = unknown> {
|
|
21
|
-
|
|
27
|
+
provide?: Token<T>;
|
|
22
28
|
scope?: Scope;
|
|
23
29
|
useValue?: T;
|
|
24
30
|
useFactory?: (...args: any[]) => T | Promise<T>;
|
|
31
|
+
useClass?: Type<T>;
|
|
25
32
|
inject?: Token[];
|
|
26
33
|
useExisting?: Token<T>;
|
|
27
34
|
}
|
|
28
35
|
export interface ProviderRegistration<T = unknown> {
|
|
29
|
-
|
|
36
|
+
provide: Token<T>;
|
|
30
37
|
scope: Scope;
|
|
31
38
|
instance?: T;
|
|
32
39
|
useValue?: T;
|
package/dist/container/types.js
CHANGED
|
@@ -9,3 +9,12 @@ export class InjectionToken {
|
|
|
9
9
|
return `InjectionToken(${this.description})`;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export class ForwardRef {
|
|
13
|
+
factory;
|
|
14
|
+
constructor(factory){
|
|
15
|
+
this.factory = factory;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function forwardRef(factory) {
|
|
19
|
+
return new ForwardRef(factory);
|
|
20
|
+
}
|