@riktajs/core 0.6.0 → 0.8.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/core/container/abstract-class.utils.d.ts +10 -0
- package/dist/core/container/abstract-class.utils.js +54 -0
- package/dist/core/container/container.d.ts +3 -0
- package/dist/core/container/container.js +46 -7
- package/dist/core/container/implements.decorator.d.ts +6 -0
- package/dist/core/container/implements.decorator.js +45 -0
- package/dist/core/container/index.d.ts +2 -0
- package/dist/core/container/index.js +2 -0
- package/dist/core/decorators/apply-decorators.d.ts +8 -0
- package/dist/core/decorators/apply-decorators.js +40 -0
- package/dist/core/decorators/autowired.decorator.d.ts +2 -1
- package/dist/core/decorators/autowired.decorator.js +3 -1
- package/dist/core/decorators/create-param-decorator.d.ts +11 -0
- package/dist/core/decorators/create-param-decorator.js +26 -0
- package/dist/core/decorators/index.d.ts +2 -0
- package/dist/core/decorators/index.js +2 -0
- package/dist/core/registry.d.ts +14 -0
- package/dist/core/registry.js +77 -0
- package/dist/core/router/router.d.ts +1 -1
- package/dist/core/router/router.js +33 -12
- package/dist/core/types.d.ts +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Constructor } from '../types';
|
|
2
|
+
export declare const IMPLEMENTS_METADATA: unique symbol;
|
|
3
|
+
export declare const PRIMARY_METADATA: unique symbol;
|
|
4
|
+
export declare function isAbstractClass(target: unknown): target is Constructor;
|
|
5
|
+
export declare function extendsFrom(derived: Constructor, base: Constructor): boolean;
|
|
6
|
+
export declare function getImplementedAbstract(target: Constructor): Constructor | undefined;
|
|
7
|
+
export declare function isPrimaryImplementation(target: Constructor): boolean;
|
|
8
|
+
export declare function setImplementsMetadata(target: Constructor, abstractClass: Constructor): void;
|
|
9
|
+
export declare function setPrimaryMetadata(target: Constructor): void;
|
|
10
|
+
export declare function markAsAbstract(target: Constructor): void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRIMARY_METADATA = exports.IMPLEMENTS_METADATA = void 0;
|
|
4
|
+
exports.isAbstractClass = isAbstractClass;
|
|
5
|
+
exports.extendsFrom = extendsFrom;
|
|
6
|
+
exports.getImplementedAbstract = getImplementedAbstract;
|
|
7
|
+
exports.isPrimaryImplementation = isPrimaryImplementation;
|
|
8
|
+
exports.setImplementsMetadata = setImplementsMetadata;
|
|
9
|
+
exports.setPrimaryMetadata = setPrimaryMetadata;
|
|
10
|
+
exports.markAsAbstract = markAsAbstract;
|
|
11
|
+
exports.IMPLEMENTS_METADATA = Symbol('rikta:implements');
|
|
12
|
+
exports.PRIMARY_METADATA = Symbol('rikta:primary');
|
|
13
|
+
function isAbstractClass(target) {
|
|
14
|
+
if (typeof target !== 'function') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const isMarkedAbstract = Reflect.getMetadata(exports.IMPLEMENTS_METADATA, target) === undefined
|
|
18
|
+
&& Reflect.hasMetadata('abstract:class', target);
|
|
19
|
+
if (isMarkedAbstract) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return Reflect.getMetadata('abstract:class', target) === true;
|
|
23
|
+
}
|
|
24
|
+
function extendsFrom(derived, base) {
|
|
25
|
+
if (!derived || !base) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (derived === base) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
let current = Object.getPrototypeOf(derived);
|
|
32
|
+
while (current && current !== Function.prototype) {
|
|
33
|
+
if (current === base) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
current = Object.getPrototypeOf(current);
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
function getImplementedAbstract(target) {
|
|
41
|
+
return Reflect.getMetadata(exports.IMPLEMENTS_METADATA, target);
|
|
42
|
+
}
|
|
43
|
+
function isPrimaryImplementation(target) {
|
|
44
|
+
return Reflect.getMetadata(exports.PRIMARY_METADATA, target) === true;
|
|
45
|
+
}
|
|
46
|
+
function setImplementsMetadata(target, abstractClass) {
|
|
47
|
+
Reflect.defineMetadata(exports.IMPLEMENTS_METADATA, abstractClass, target);
|
|
48
|
+
}
|
|
49
|
+
function setPrimaryMetadata(target) {
|
|
50
|
+
Reflect.defineMetadata(exports.PRIMARY_METADATA, true, target);
|
|
51
|
+
}
|
|
52
|
+
function markAsAbstract(target) {
|
|
53
|
+
Reflect.defineMetadata('abstract:class', true, target);
|
|
54
|
+
}
|
|
@@ -17,9 +17,12 @@ export declare class Container {
|
|
|
17
17
|
resolve<T>(token: Token<T>): T;
|
|
18
18
|
resolveOptional<T>(token: Token<T>): T | undefined;
|
|
19
19
|
private resolveToken;
|
|
20
|
+
private resolveAbstractClass;
|
|
20
21
|
private resolveProvider;
|
|
21
22
|
private resolveClass;
|
|
22
23
|
private injectProperties;
|
|
24
|
+
private resolveWithName;
|
|
25
|
+
private resolveWithNameOptional;
|
|
23
26
|
registerInstance<T>(target: Constructor<T>, instance: T): void;
|
|
24
27
|
getProviders(): Token[];
|
|
25
28
|
clearSingletons(): void;
|
|
@@ -4,6 +4,8 @@ exports.container = exports.Container = void 0;
|
|
|
4
4
|
require("reflect-metadata");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
const injection_token_1 = require("./injection-token");
|
|
7
|
+
const registry_1 = require("../registry");
|
|
8
|
+
const abstract_class_utils_1 = require("./abstract-class.utils");
|
|
7
9
|
class Container {
|
|
8
10
|
static instance;
|
|
9
11
|
singletons = new Map();
|
|
@@ -70,12 +72,20 @@ class Container {
|
|
|
70
72
|
}
|
|
71
73
|
const config = this.providers.get(token);
|
|
72
74
|
if (!config) {
|
|
75
|
+
if (typeof token === 'function') {
|
|
76
|
+
const implementation = this.resolveAbstractClass(token);
|
|
77
|
+
if (implementation) {
|
|
78
|
+
const instance = this.resolveClass(implementation);
|
|
79
|
+
this.singletons.set(token, instance);
|
|
80
|
+
return instance;
|
|
81
|
+
}
|
|
82
|
+
if (!(0, abstract_class_utils_1.isAbstractClass)(token)) {
|
|
83
|
+
return this.resolveClass(token);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
73
86
|
if (optional) {
|
|
74
87
|
return undefined;
|
|
75
88
|
}
|
|
76
|
-
if (typeof token === 'function') {
|
|
77
|
-
return this.resolveClass(token);
|
|
78
|
-
}
|
|
79
89
|
throw new Error(`No provider found for token: ${this.getTokenName(token)}`);
|
|
80
90
|
}
|
|
81
91
|
if (config.provider && typeof config.provider !== 'function') {
|
|
@@ -86,6 +96,16 @@ class Container {
|
|
|
86
96
|
}
|
|
87
97
|
throw new Error(`Cannot resolve token: ${this.getTokenName(token)}`);
|
|
88
98
|
}
|
|
99
|
+
resolveAbstractClass(abstractClass, name) {
|
|
100
|
+
if (name) {
|
|
101
|
+
return registry_1.registry.resolveImplementation(abstractClass, name);
|
|
102
|
+
}
|
|
103
|
+
const config = this.providers.get(abstractClass);
|
|
104
|
+
if (config?.provider && 'useClass' in config.provider) {
|
|
105
|
+
return config.provider.useClass;
|
|
106
|
+
}
|
|
107
|
+
return registry_1.registry.resolveImplementation(abstractClass);
|
|
108
|
+
}
|
|
89
109
|
resolveProvider(provider, scope) {
|
|
90
110
|
if (typeof provider === 'function') {
|
|
91
111
|
return this.resolveClass(provider);
|
|
@@ -142,6 +162,7 @@ class Container {
|
|
|
142
162
|
const injectOverride = injectMeta.find(m => m.index === index);
|
|
143
163
|
const token = injectOverride?.token ?? paramType;
|
|
144
164
|
const isOptional = injectOverride?.optional ?? false;
|
|
165
|
+
const name = injectOverride?.name;
|
|
145
166
|
if (!token) {
|
|
146
167
|
if (isOptional) {
|
|
147
168
|
dependencies.push(undefined);
|
|
@@ -159,8 +180,8 @@ class Container {
|
|
|
159
180
|
`Use @Autowired(token) decorator.`);
|
|
160
181
|
}
|
|
161
182
|
dependencies.push(isOptional
|
|
162
|
-
? this.
|
|
163
|
-
: this.
|
|
183
|
+
? this.resolveWithNameOptional(token, name)
|
|
184
|
+
: this.resolveWithName(token, name));
|
|
164
185
|
}
|
|
165
186
|
const instance = new target(...dependencies);
|
|
166
187
|
this.injectProperties(target, instance);
|
|
@@ -181,8 +202,8 @@ class Container {
|
|
|
181
202
|
const isOptional = meta.optional ?? false;
|
|
182
203
|
try {
|
|
183
204
|
const value = isOptional
|
|
184
|
-
? this.
|
|
185
|
-
: this.
|
|
205
|
+
? this.resolveWithNameOptional(meta.token, meta.name)
|
|
206
|
+
: this.resolveWithName(meta.token, meta.name);
|
|
186
207
|
instance[meta.propertyKey] = value;
|
|
187
208
|
}
|
|
188
209
|
catch (error) {
|
|
@@ -192,6 +213,24 @@ class Container {
|
|
|
192
213
|
}
|
|
193
214
|
}
|
|
194
215
|
}
|
|
216
|
+
resolveWithName(token, name) {
|
|
217
|
+
if (name && typeof token === 'function') {
|
|
218
|
+
const implementation = this.resolveAbstractClass(token, name);
|
|
219
|
+
if (implementation) {
|
|
220
|
+
return this.resolveClass(implementation);
|
|
221
|
+
}
|
|
222
|
+
throw new Error(`No implementation named '${name}' found for ${this.getTokenName(token)}`);
|
|
223
|
+
}
|
|
224
|
+
return this.resolve(token);
|
|
225
|
+
}
|
|
226
|
+
resolveWithNameOptional(token, name) {
|
|
227
|
+
try {
|
|
228
|
+
return this.resolveWithName(token, name);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
195
234
|
registerInstance(target, instance) {
|
|
196
235
|
this.providers.set(target, { scope: 'singleton' });
|
|
197
236
|
this.singletons.set(target, instance);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { AnyConstructor } from '../types';
|
|
3
|
+
export declare function Implements<T extends AnyConstructor>(abstractClass: T): ClassDecorator;
|
|
4
|
+
export declare function Primary(): ClassDecorator;
|
|
5
|
+
export declare function Named(name: string): ClassDecorator;
|
|
6
|
+
export declare function AbstractClass(): ClassDecorator;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Implements = Implements;
|
|
4
|
+
exports.Primary = Primary;
|
|
5
|
+
exports.Named = Named;
|
|
6
|
+
exports.AbstractClass = AbstractClass;
|
|
7
|
+
require("reflect-metadata");
|
|
8
|
+
const abstract_class_utils_1 = require("./abstract-class.utils");
|
|
9
|
+
const registry_1 = require("../registry");
|
|
10
|
+
function Implements(abstractClass) {
|
|
11
|
+
return (target) => {
|
|
12
|
+
(0, abstract_class_utils_1.setImplementsMetadata)(target, abstractClass);
|
|
13
|
+
(0, abstract_class_utils_1.markAsAbstract)(abstractClass);
|
|
14
|
+
const name = Reflect.getMetadata('rikta:named', target);
|
|
15
|
+
registry_1.registry.registerAbstractImplementation(abstractClass, target, name);
|
|
16
|
+
const isPrimary = Reflect.getMetadata('rikta:is-primary', target) === true;
|
|
17
|
+
if (isPrimary) {
|
|
18
|
+
registry_1.registry.setPrimaryImplementation(abstractClass, target);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function Primary() {
|
|
23
|
+
return (target) => {
|
|
24
|
+
(0, abstract_class_utils_1.setPrimaryMetadata)(target);
|
|
25
|
+
const abstractClass = Reflect.getMetadata(abstract_class_utils_1.IMPLEMENTS_METADATA, target);
|
|
26
|
+
if (abstractClass) {
|
|
27
|
+
registry_1.registry.setPrimaryImplementation(abstractClass, target);
|
|
28
|
+
}
|
|
29
|
+
Reflect.defineMetadata('rikta:is-primary', true, target);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function Named(name) {
|
|
33
|
+
return (target) => {
|
|
34
|
+
Reflect.defineMetadata('rikta:named', name, target);
|
|
35
|
+
const abstractClass = Reflect.getMetadata(abstract_class_utils_1.IMPLEMENTS_METADATA, target);
|
|
36
|
+
if (abstractClass) {
|
|
37
|
+
registry_1.registry.setImplementationName(abstractClass, target, name);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function AbstractClass() {
|
|
42
|
+
return (target) => {
|
|
43
|
+
(0, abstract_class_utils_1.markAsAbstract)(target);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./container"), exports);
|
|
18
18
|
__exportStar(require("./injection-token"), exports);
|
|
19
|
+
__exportStar(require("./abstract-class.utils"), exports);
|
|
20
|
+
__exportStar(require("./implements.decorator"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
|
|
3
|
+
type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
|
|
4
|
+
type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
|
|
5
|
+
type AnyDecorator = ClassDecorator | MethodDecorator | PropertyDecorator;
|
|
6
|
+
export declare function applyDecorators(...decorators: AnyDecorator[]): MethodDecorator & ClassDecorator & PropertyDecorator;
|
|
7
|
+
export declare function SetMetadata<T = unknown>(key: string | symbol, value: T): MethodDecorator & ClassDecorator;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyDecorators = applyDecorators;
|
|
4
|
+
exports.SetMetadata = SetMetadata;
|
|
5
|
+
require("reflect-metadata");
|
|
6
|
+
function applyDecorators(...decorators) {
|
|
7
|
+
return ((target, propertyKey, descriptor) => {
|
|
8
|
+
for (const decorator of decorators.reverse()) {
|
|
9
|
+
if (descriptor) {
|
|
10
|
+
const result = decorator(target, propertyKey, descriptor);
|
|
11
|
+
if (result) {
|
|
12
|
+
descriptor = result;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
else if (propertyKey !== undefined) {
|
|
16
|
+
decorator(target, propertyKey);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const result = decorator(target);
|
|
20
|
+
if (result) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (descriptor) {
|
|
26
|
+
return descriptor;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function SetMetadata(key, value) {
|
|
31
|
+
const decoratorFactory = (target, propertyKey, descriptor) => {
|
|
32
|
+
if (descriptor) {
|
|
33
|
+
Reflect.defineMetadata(key, value, target.constructor, propertyKey);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
Reflect.defineMetadata(key, value, target);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
return decoratorFactory;
|
|
40
|
+
}
|
|
@@ -5,6 +5,7 @@ export interface AutowiredMetadata {
|
|
|
5
5
|
index?: number;
|
|
6
6
|
propertyKey?: string;
|
|
7
7
|
optional?: boolean;
|
|
8
|
+
name?: string;
|
|
8
9
|
}
|
|
9
|
-
export declare function Autowired(token?: Token): ParameterDecorator & PropertyDecorator;
|
|
10
|
+
export declare function Autowired(token?: Token, name?: string): ParameterDecorator & PropertyDecorator;
|
|
10
11
|
export declare function Optional(): ParameterDecorator & PropertyDecorator;
|
|
@@ -4,7 +4,7 @@ exports.Autowired = Autowired;
|
|
|
4
4
|
exports.Optional = Optional;
|
|
5
5
|
require("reflect-metadata");
|
|
6
6
|
const constants_1 = require("../constants");
|
|
7
|
-
function Autowired(token) {
|
|
7
|
+
function Autowired(token, name) {
|
|
8
8
|
return (target, propertyKey, parameterIndex) => {
|
|
9
9
|
if (typeof parameterIndex === 'number') {
|
|
10
10
|
const paramTypes = Reflect.getMetadata('design:paramtypes', target) ?? [];
|
|
@@ -18,6 +18,7 @@ function Autowired(token) {
|
|
|
18
18
|
existingInjects.push({
|
|
19
19
|
token: resolvedToken,
|
|
20
20
|
index: parameterIndex,
|
|
21
|
+
name,
|
|
21
22
|
});
|
|
22
23
|
Reflect.defineMetadata(constants_1.INJECT_METADATA, existingInjects, target);
|
|
23
24
|
}
|
|
@@ -32,6 +33,7 @@ function Autowired(token) {
|
|
|
32
33
|
existingAutowires.push({
|
|
33
34
|
token: resolvedToken,
|
|
34
35
|
propertyKey: String(propertyKey),
|
|
36
|
+
name,
|
|
35
37
|
});
|
|
36
38
|
Reflect.defineMetadata(constants_1.AUTOWIRED_METADATA, existingAutowires, target.constructor);
|
|
37
39
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ExecutionContext } from '../guards/execution-context';
|
|
3
|
+
export declare const CUSTOM_PARAM_METADATA: unique symbol;
|
|
4
|
+
export interface CustomParamMetadata<T = unknown> {
|
|
5
|
+
index: number;
|
|
6
|
+
factory: CustomParamFactory<T, unknown>;
|
|
7
|
+
data?: T;
|
|
8
|
+
}
|
|
9
|
+
export type CustomParamFactory<TData = unknown, TResult = unknown> = (data: TData, ctx: ExecutionContext) => TResult | Promise<TResult>;
|
|
10
|
+
export declare function createParamDecorator<TData = unknown, TResult = unknown>(factory: CustomParamFactory<TData, TResult>): (data?: TData) => ParameterDecorator;
|
|
11
|
+
export declare function getCustomParamMetadata<T = unknown>(target: Function, propertyKey: string | symbol): CustomParamMetadata<T>[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CUSTOM_PARAM_METADATA = void 0;
|
|
4
|
+
exports.createParamDecorator = createParamDecorator;
|
|
5
|
+
exports.getCustomParamMetadata = getCustomParamMetadata;
|
|
6
|
+
require("reflect-metadata");
|
|
7
|
+
exports.CUSTOM_PARAM_METADATA = Symbol.for('rikta:custom:param:metadata');
|
|
8
|
+
function createParamDecorator(factory) {
|
|
9
|
+
return (data) => {
|
|
10
|
+
return (target, propertyKey, parameterIndex) => {
|
|
11
|
+
if (propertyKey === undefined)
|
|
12
|
+
return;
|
|
13
|
+
const existingParams = Reflect.getMetadata(exports.CUSTOM_PARAM_METADATA, target.constructor, propertyKey) ?? [];
|
|
14
|
+
const metadata = {
|
|
15
|
+
index: parameterIndex,
|
|
16
|
+
factory: factory,
|
|
17
|
+
data,
|
|
18
|
+
};
|
|
19
|
+
existingParams.push(metadata);
|
|
20
|
+
Reflect.defineMetadata(exports.CUSTOM_PARAM_METADATA, existingParams, target.constructor, propertyKey);
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function getCustomParamMetadata(target, propertyKey) {
|
|
25
|
+
return Reflect.getMetadata(exports.CUSTOM_PARAM_METADATA, target, propertyKey) ?? [];
|
|
26
|
+
}
|
|
@@ -21,3 +21,5 @@ __exportStar(require("./param.decorator"), exports);
|
|
|
21
21
|
__exportStar(require("./autowired.decorator"), exports);
|
|
22
22
|
__exportStar(require("./provider.decorator"), exports);
|
|
23
23
|
__exportStar(require("./config-property.decorator"), exports);
|
|
24
|
+
__exportStar(require("./create-param-decorator"), exports);
|
|
25
|
+
__exportStar(require("./apply-decorators"), exports);
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -3,12 +3,18 @@ interface ConfigProviderRegistration {
|
|
|
3
3
|
token: string;
|
|
4
4
|
providerClass: Constructor;
|
|
5
5
|
}
|
|
6
|
+
interface AbstractImplementation {
|
|
7
|
+
implementation: Constructor;
|
|
8
|
+
isPrimary: boolean;
|
|
9
|
+
name?: string;
|
|
10
|
+
}
|
|
6
11
|
declare class Registry {
|
|
7
12
|
private static instance;
|
|
8
13
|
private controllers;
|
|
9
14
|
private providers;
|
|
10
15
|
private customProviders;
|
|
11
16
|
private configProviderMap;
|
|
17
|
+
private abstractImplementations;
|
|
12
18
|
private constructor();
|
|
13
19
|
static getInstance(): Registry;
|
|
14
20
|
static reset(): void;
|
|
@@ -24,6 +30,14 @@ declare class Registry {
|
|
|
24
30
|
registerConfigProvider(token: string, providerClass: Constructor): void;
|
|
25
31
|
getConfigProviderRegistrations(): ConfigProviderRegistration[];
|
|
26
32
|
clearConfigProviders(): void;
|
|
33
|
+
registerAbstractImplementation(abstractClass: Constructor, implementation: Constructor, name?: string): void;
|
|
34
|
+
setImplementationName(abstractClass: Constructor, implementation: Constructor, name: string): void;
|
|
35
|
+
setPrimaryImplementation(abstractClass: Constructor, implementation: Constructor): void;
|
|
36
|
+
getImplementations(abstractClass: Constructor): AbstractImplementation[];
|
|
37
|
+
resolveImplementation(abstractClass: Constructor, name?: string): Constructor | undefined;
|
|
38
|
+
getNamedImplementation(abstractClass: Constructor, name: string): Constructor | undefined;
|
|
39
|
+
hasImplementation(abstractClass: Constructor): boolean;
|
|
40
|
+
clearAbstractImplementations(): void;
|
|
27
41
|
}
|
|
28
42
|
export declare const registry: Registry;
|
|
29
43
|
export { Registry };
|
package/dist/core/registry.js
CHANGED
|
@@ -8,6 +8,7 @@ class Registry {
|
|
|
8
8
|
providers = new Set();
|
|
9
9
|
customProviders = new Set();
|
|
10
10
|
configProviderMap = new Map();
|
|
11
|
+
abstractImplementations = new Map();
|
|
11
12
|
constructor() { }
|
|
12
13
|
static getInstance() {
|
|
13
14
|
if (!Registry.instance) {
|
|
@@ -62,6 +63,82 @@ class Registry {
|
|
|
62
63
|
clearConfigProviders() {
|
|
63
64
|
this.configProviderMap.clear();
|
|
64
65
|
}
|
|
66
|
+
registerAbstractImplementation(abstractClass, implementation, name) {
|
|
67
|
+
const implementations = this.abstractImplementations.get(abstractClass) ?? [];
|
|
68
|
+
const existing = implementations.find(i => i.implementation === implementation);
|
|
69
|
+
if (!existing) {
|
|
70
|
+
implementations.push({
|
|
71
|
+
implementation,
|
|
72
|
+
isPrimary: false,
|
|
73
|
+
name,
|
|
74
|
+
});
|
|
75
|
+
this.abstractImplementations.set(abstractClass, implementations);
|
|
76
|
+
}
|
|
77
|
+
else if (name && !existing.name) {
|
|
78
|
+
existing.name = name;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
setImplementationName(abstractClass, implementation, name) {
|
|
82
|
+
const implementations = this.abstractImplementations.get(abstractClass);
|
|
83
|
+
if (!implementations) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const impl = implementations.find(i => i.implementation === implementation);
|
|
87
|
+
if (impl) {
|
|
88
|
+
impl.name = name;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
setPrimaryImplementation(abstractClass, implementation) {
|
|
92
|
+
const implementations = this.abstractImplementations.get(abstractClass);
|
|
93
|
+
if (!implementations) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (const impl of implementations) {
|
|
97
|
+
impl.isPrimary = impl.implementation === implementation;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
getImplementations(abstractClass) {
|
|
101
|
+
return this.abstractImplementations.get(abstractClass) ?? [];
|
|
102
|
+
}
|
|
103
|
+
resolveImplementation(abstractClass, name) {
|
|
104
|
+
const implementations = this.abstractImplementations.get(abstractClass);
|
|
105
|
+
if (!implementations || implementations.length === 0) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (name) {
|
|
109
|
+
const named = implementations.find(i => i.name === name);
|
|
110
|
+
if (named) {
|
|
111
|
+
return named.implementation;
|
|
112
|
+
}
|
|
113
|
+
throw new Error(`No implementation named '${name}' found for abstract class ${abstractClass.name}. ` +
|
|
114
|
+
`Available names: ${implementations.filter(i => i.name).map(i => i.name).join(', ') || 'none'}`);
|
|
115
|
+
}
|
|
116
|
+
if (implementations.length === 1) {
|
|
117
|
+
return implementations[0].implementation;
|
|
118
|
+
}
|
|
119
|
+
const primary = implementations.find(i => i.isPrimary);
|
|
120
|
+
if (primary) {
|
|
121
|
+
return primary.implementation;
|
|
122
|
+
}
|
|
123
|
+
const implNames = implementations.map(i => i.implementation.name).join(', ');
|
|
124
|
+
throw new Error(`Multiple implementations found for abstract class ${abstractClass.name}: ${implNames}. ` +
|
|
125
|
+
`Use @Primary() to mark one as the default, or @Named() for qualified injection.`);
|
|
126
|
+
}
|
|
127
|
+
getNamedImplementation(abstractClass, name) {
|
|
128
|
+
const implementations = this.abstractImplementations.get(abstractClass);
|
|
129
|
+
if (!implementations) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const named = implementations.find(i => i.name === name);
|
|
133
|
+
return named?.implementation;
|
|
134
|
+
}
|
|
135
|
+
hasImplementation(abstractClass) {
|
|
136
|
+
const implementations = this.abstractImplementations.get(abstractClass);
|
|
137
|
+
return !!implementations && implementations.length > 0;
|
|
138
|
+
}
|
|
139
|
+
clearAbstractImplementations() {
|
|
140
|
+
this.abstractImplementations.clear();
|
|
141
|
+
}
|
|
65
142
|
}
|
|
66
143
|
exports.Registry = Registry;
|
|
67
144
|
exports.registry = Registry.getInstance();
|
|
@@ -13,7 +13,7 @@ export declare class Router {
|
|
|
13
13
|
private registerRoute;
|
|
14
14
|
private compileParamResolvers;
|
|
15
15
|
private createParamExtractor;
|
|
16
|
-
private
|
|
16
|
+
private resolveAllParams;
|
|
17
17
|
private resolveGuardInstances;
|
|
18
18
|
private executeGuardsOptimized;
|
|
19
19
|
private resolveMiddlewareInstances;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Router = void 0;
|
|
4
4
|
require("reflect-metadata");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
|
+
const create_param_decorator_1 = require("../decorators/create-param-decorator");
|
|
6
7
|
const validation_exception_1 = require("../exceptions/validation.exception");
|
|
7
8
|
const exceptions_1 = require("../exceptions/exceptions");
|
|
8
9
|
const execution_context_1 = require("../guards/execution-context");
|
|
@@ -38,28 +39,41 @@ class Router {
|
|
|
38
39
|
throw new Error(`Handler ${String(route.handlerName)} not found on ${controllerClass.name}`);
|
|
39
40
|
}
|
|
40
41
|
const paramsMeta = Reflect.getMetadata(constants_1.PARAM_METADATA, controllerClass, route.handlerName) ?? [];
|
|
42
|
+
const customParamsMeta = (0, create_param_decorator_1.getCustomParamMetadata)(controllerClass, route.handlerName);
|
|
41
43
|
const statusCode = Reflect.getMetadata(constants_1.HTTP_CODE_METADATA, controllerClass, route.handlerName);
|
|
42
44
|
const guards = (0, use_guards_decorator_1.getGuardsMetadata)(controllerClass, route.handlerName);
|
|
43
45
|
const middleware = (0, use_middleware_decorator_1.getMiddlewareMetadata)(controllerClass, route.handlerName);
|
|
44
46
|
const compiledResolvers = this.compileParamResolvers(paramsMeta);
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
+
const hasBuiltinParams = compiledResolvers.length > 0;
|
|
48
|
+
const hasCustomParams = customParamsMeta.length > 0;
|
|
49
|
+
const hasParams = hasBuiltinParams || hasCustomParams;
|
|
50
|
+
const allParamIndexes = [
|
|
51
|
+
...compiledResolvers.map(r => r.index),
|
|
52
|
+
...customParamsMeta.map(r => r.index)
|
|
53
|
+
];
|
|
54
|
+
const maxParamIndex = hasParams ? Math.max(...allParamIndexes) : -1;
|
|
47
55
|
const guardInstances = this.resolveGuardInstances(guards);
|
|
48
56
|
const hasGuards = guardInstances.length > 0;
|
|
49
57
|
const middlewareInstances = this.resolveMiddlewareInstances(middleware);
|
|
50
58
|
const hasMiddleware = middlewareInstances.length > 0;
|
|
51
|
-
const
|
|
59
|
+
const needsContext = hasGuards || hasCustomParams;
|
|
60
|
+
const createContext = needsContext
|
|
52
61
|
? (req, rep) => new execution_context_1.ExecutionContextImpl(req, rep, controllerClass, route.handlerName)
|
|
53
62
|
: null;
|
|
54
63
|
const routeHandler = async (request, reply) => {
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
const executionContext = createContext ? createContext(request, reply) : null;
|
|
65
|
+
if (hasGuards && executionContext) {
|
|
66
|
+
await this.executeGuardsOptimized(guardInstances, executionContext);
|
|
57
67
|
}
|
|
58
68
|
if (hasMiddleware) {
|
|
59
69
|
await this.executeMiddlewareChain(middlewareInstances, request, reply);
|
|
60
70
|
}
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
let args;
|
|
72
|
+
if (hasParams) {
|
|
73
|
+
args = await this.resolveAllParams(compiledResolvers, customParamsMeta, maxParamIndex, request, reply, executionContext);
|
|
74
|
+
}
|
|
75
|
+
const result = args
|
|
76
|
+
? await handler.apply(controllerInstance, args)
|
|
63
77
|
: await handler.call(controllerInstance);
|
|
64
78
|
if (statusCode)
|
|
65
79
|
reply.status(statusCode);
|
|
@@ -113,8 +127,8 @@ class Router {
|
|
|
113
127
|
return () => undefined;
|
|
114
128
|
}
|
|
115
129
|
}
|
|
116
|
-
|
|
117
|
-
const
|
|
130
|
+
async resolveAllParams(compiledResolvers, customParams, maxIndex, request, reply, executionContext) {
|
|
131
|
+
const routeContext = {
|
|
118
132
|
request,
|
|
119
133
|
reply,
|
|
120
134
|
params: request.params,
|
|
@@ -122,9 +136,9 @@ class Router {
|
|
|
122
136
|
body: request.body,
|
|
123
137
|
};
|
|
124
138
|
const args = new Array(maxIndex + 1);
|
|
125
|
-
for (let i = 0; i <
|
|
126
|
-
const resolver =
|
|
127
|
-
let value = resolver.extract(
|
|
139
|
+
for (let i = 0; i < compiledResolvers.length; i++) {
|
|
140
|
+
const resolver = compiledResolvers[i];
|
|
141
|
+
let value = resolver.extract(routeContext);
|
|
128
142
|
if (resolver.zodSchema) {
|
|
129
143
|
const result = resolver.zodSchema.safeParse(value);
|
|
130
144
|
if (!result.success) {
|
|
@@ -134,6 +148,13 @@ class Router {
|
|
|
134
148
|
}
|
|
135
149
|
args[resolver.index] = value;
|
|
136
150
|
}
|
|
151
|
+
if (customParams.length > 0) {
|
|
152
|
+
const ctx = executionContext ?? new execution_context_1.ExecutionContextImpl(request, reply, {}, '');
|
|
153
|
+
for (const customParam of customParams) {
|
|
154
|
+
const value = await customParam.factory(customParam.data, ctx);
|
|
155
|
+
args[customParam.index] = value;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
137
158
|
return args;
|
|
138
159
|
}
|
|
139
160
|
resolveGuardInstances(guards) {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
2
2
|
import { Container } from "@rikta/core/container";
|
|
3
3
|
export type Constructor<T = any> = new (...args: any[]) => T;
|
|
4
|
+
export type AbstractConstructor<T = any> = abstract new (...args: any[]) => T;
|
|
5
|
+
export type AnyConstructor<T = any> = Constructor<T> | AbstractConstructor<T>;
|
|
4
6
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
|
|
5
7
|
export interface RouteContext {
|
|
6
8
|
request: FastifyRequest;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riktajs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A fast and modern TypeScript backend framework with zero-config autowiring, powered by Fastify",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependency-injection",
|
|
27
27
|
"nestjs-like"
|
|
28
28
|
],
|
|
29
|
-
"author": "",
|
|
29
|
+
"author": "riktar",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"dotenv": "^17.2.3",
|