@alterior/runtime 3.5.3 → 3.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -4
- package/src/app-options.ts +72 -0
- package/src/application.ts +207 -0
- package/src/args.ts +9 -0
- package/src/expose.ts +46 -0
- package/src/index.ts +9 -0
- package/src/lifecycle.ts +26 -0
- package/src/modules.ts +325 -0
- package/src/reflector.ts +433 -0
- package/src/roles.service.ts +171 -0
- package/src/service.ts +34 -0
- package/dist.esm/module.test.js +0 -374
- package/dist.esm/test.js +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alterior/runtime",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.6",
|
|
4
4
|
"description": "Core runtime for Alterior apps",
|
|
5
5
|
"author": "The Alterior Project (https://github.com/alterior-mvc)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,14 +37,15 @@
|
|
|
37
37
|
"docs": "typedoc ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@alterior/annotations": "^3.
|
|
40
|
+
"@alterior/annotations": "^3.5.6",
|
|
41
41
|
"@alterior/common": "^3.4.0",
|
|
42
|
-
"@alterior/di": "^3.5.
|
|
42
|
+
"@alterior/di": "^3.5.6",
|
|
43
43
|
"tslib": "^2.3.1"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"reflect-metadata": "^0.1.13",
|
|
47
47
|
"zone.js": "^0.11.4"
|
|
48
48
|
},
|
|
49
|
-
"
|
|
49
|
+
"lernaKick": 1,
|
|
50
|
+
"gitHead": "220e169fc8c855dc0935620bdb5efb1e9a2b86a7"
|
|
50
51
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Annotation, AnnotationDecorator, MetadataName } from '@alterior/annotations';
|
|
2
|
+
|
|
3
|
+
export interface ApplicationOptions {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Specify a human readable name for your application.
|
|
7
|
+
*/
|
|
8
|
+
name? : string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Version of the service
|
|
12
|
+
*/
|
|
13
|
+
version? : string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The computer-readable name for your application. Should match your NPM package name.
|
|
17
|
+
*/
|
|
18
|
+
packageName? : string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A long-form description for your application, when necessary. If you implement only one,
|
|
22
|
+
* implement summary instead.
|
|
23
|
+
*/
|
|
24
|
+
description? : string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A shorter-form description for your application, when necessary. If you implement only one,
|
|
28
|
+
* implement this instead of description.
|
|
29
|
+
*/
|
|
30
|
+
summary? : string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A set of string tags related to your application.
|
|
34
|
+
*/
|
|
35
|
+
tags? : string[];
|
|
36
|
+
|
|
37
|
+
group? : string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Enable verbose console logging for Alterior
|
|
41
|
+
*/
|
|
42
|
+
verbose? : boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Whether to start the service immediately on startup.
|
|
46
|
+
* Defaults to true.
|
|
47
|
+
*/
|
|
48
|
+
autostart? : boolean;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Turn off all console output
|
|
52
|
+
*/
|
|
53
|
+
silent? : boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Used to attach an ApplicationOptions object onto a class definition.
|
|
58
|
+
*/
|
|
59
|
+
@MetadataName('@alterior/di:Application')
|
|
60
|
+
export class AppOptionsAnnotation extends Annotation {
|
|
61
|
+
constructor(readonly options? : ApplicationOptions) {
|
|
62
|
+
super();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Use this decorator to define the options for your application,
|
|
68
|
+
* either on the entry module, or service class when using `@alterior/web-server`.
|
|
69
|
+
*/
|
|
70
|
+
export const AppOptions = AppOptionsAnnotation.decorator({
|
|
71
|
+
validTargets: ['class']
|
|
72
|
+
});
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
|
|
3
|
+
import { ApplicationOptions, AppOptionsAnnotation } from './app-options';
|
|
4
|
+
import { ReflectiveInjector, Provider, Injectable,
|
|
5
|
+
ModuleAnnotation, Injector } from '@alterior/di';
|
|
6
|
+
import { Runtime } from './modules';
|
|
7
|
+
import { ApplicationArgs } from './args';
|
|
8
|
+
import { RolesService } from './roles.service';
|
|
9
|
+
import { Environment, Time } from '@alterior/common';
|
|
10
|
+
|
|
11
|
+
declare let module : never;
|
|
12
|
+
|
|
13
|
+
export class ApplicationOptionsRef {
|
|
14
|
+
constructor(
|
|
15
|
+
options : ApplicationOptions
|
|
16
|
+
) {
|
|
17
|
+
this.options = Object.assign({}, options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
readonly options : ApplicationOptions;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Represents the current runtime execution context.
|
|
25
|
+
* This is exposed via a zone-local variable, and the Runtime
|
|
26
|
+
* populates it with useful information as it becomes available.
|
|
27
|
+
*/
|
|
28
|
+
export class ExecutionContext {
|
|
29
|
+
/**
|
|
30
|
+
* Retrieve the Alterior application which is currently being executed.
|
|
31
|
+
* If an application has not been bootstrapped yet, the value is null.
|
|
32
|
+
*/
|
|
33
|
+
public application : Application = null;
|
|
34
|
+
|
|
35
|
+
static readonly ZONE_LOCAL_NAME = '@alterior/runtime:ExecutionContext';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the current execution context, if any.
|
|
39
|
+
*/
|
|
40
|
+
public static get current(): ExecutionContext {
|
|
41
|
+
return Zone.current.get(ExecutionContext.ZONE_LOCAL_NAME);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Execute the given function in a new zone which has
|
|
46
|
+
* this ExecutionContext instance as the current execution context.
|
|
47
|
+
*/
|
|
48
|
+
public async run<T>(callback : () => Promise<T>): Promise<T> {
|
|
49
|
+
let zone = Zone.current.fork({
|
|
50
|
+
name: `AlteriorExecutionContext`,
|
|
51
|
+
properties: {
|
|
52
|
+
[ExecutionContext.ZONE_LOCAL_NAME]: this
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return await zone.run(() => callback());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public runSync<T>(callback : () => T): T {
|
|
60
|
+
let zone = Zone.current.fork({
|
|
61
|
+
name: `AlteriorExecutionContext`,
|
|
62
|
+
properties: {
|
|
63
|
+
[ExecutionContext.ZONE_LOCAL_NAME]: this
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return zone.run(() => callback());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Handles bootstrapping the application.
|
|
73
|
+
*/
|
|
74
|
+
@Injectable()
|
|
75
|
+
export class Application {
|
|
76
|
+
constructor(
|
|
77
|
+
readonly runtime? : Runtime,
|
|
78
|
+
private _optionsRef? : ApplicationOptionsRef,
|
|
79
|
+
private _args? : ApplicationArgs
|
|
80
|
+
) {
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public start() {
|
|
85
|
+
this.runtime.start();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public stop() {
|
|
89
|
+
this.runtime.stop();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get injector() {
|
|
93
|
+
return this.runtime.injector;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
inject<T>(ctor : { new(...args) : T }, notFoundValue? : T): T {
|
|
97
|
+
return this.injector.get(ctor, notFoundValue);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get args() : string[] {
|
|
101
|
+
return this._args.get();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get options() : ApplicationOptions {
|
|
105
|
+
return this._optionsRef.options;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private static loadOptions(entryModule : Function, bootstrapOptions : ApplicationOptions): ApplicationOptions {
|
|
109
|
+
// Read an @AppOptions() decorator if any, and merge providers from it
|
|
110
|
+
// into the bootstrapped providers
|
|
111
|
+
|
|
112
|
+
let appOptionsAnnotation = AppOptionsAnnotation.getForClass(entryModule);
|
|
113
|
+
let appProvidedOptions : ApplicationOptions = appOptionsAnnotation ? appOptionsAnnotation.options : {} || {};
|
|
114
|
+
|
|
115
|
+
return Object.assign(
|
|
116
|
+
<ApplicationOptions>{
|
|
117
|
+
version: '0.0.0',
|
|
118
|
+
verbose: false,
|
|
119
|
+
silent: false,
|
|
120
|
+
autostart: true,
|
|
121
|
+
providers: []
|
|
122
|
+
},
|
|
123
|
+
appProvidedOptions,
|
|
124
|
+
bootstrapOptions
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private static validateEntryModule(module : Function) {
|
|
129
|
+
if (typeof module !== 'function') {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`You must pass a Module class as the first parameter `
|
|
132
|
+
+ `to bootstrap(). You provided: `
|
|
133
|
+
+ `${typeof module} with value '${module}'`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let moduleMetadata = ModuleAnnotation.getForClass(module);
|
|
138
|
+
if (!moduleMetadata)
|
|
139
|
+
throw new Error(`You must pass a module class decorated by @Module()`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private _bootstrapped : boolean = false;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Bootstrap an Alterior application.
|
|
146
|
+
*/
|
|
147
|
+
public static bootstrap(entryModule : Function, options? : ApplicationOptions): Application {
|
|
148
|
+
let executionContext = new ExecutionContext();
|
|
149
|
+
return executionContext.runSync(() => {
|
|
150
|
+
this.validateEntryModule(entryModule);
|
|
151
|
+
|
|
152
|
+
options = this.loadOptions(entryModule, options);
|
|
153
|
+
|
|
154
|
+
let runtime = new Runtime(entryModule);
|
|
155
|
+
|
|
156
|
+
let providers : Provider[] = [
|
|
157
|
+
ApplicationArgs,
|
|
158
|
+
RolesService,
|
|
159
|
+
Environment,
|
|
160
|
+
Time
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
runtime.contributeProviders(providers);
|
|
164
|
+
providers.push(
|
|
165
|
+
{
|
|
166
|
+
provide: ApplicationOptionsRef,
|
|
167
|
+
useValue: new ApplicationOptionsRef(options)
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
providers.push(Application);
|
|
171
|
+
|
|
172
|
+
runtime.providers = providers;
|
|
173
|
+
|
|
174
|
+
let injector : ReflectiveInjector;
|
|
175
|
+
try {
|
|
176
|
+
injector = ReflectiveInjector.resolveAndCreate(providers);
|
|
177
|
+
} catch (e) {
|
|
178
|
+
console.error(`Failed to resolve injector:`);
|
|
179
|
+
console.error(e);
|
|
180
|
+
console.error(`Providers:`);
|
|
181
|
+
console.dir(providers);
|
|
182
|
+
console.error(`Modules:`);
|
|
183
|
+
console.dir(runtime.definitions);
|
|
184
|
+
throw e;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
(<RolesService>injector.get(RolesService)).silent = options.silent;
|
|
188
|
+
|
|
189
|
+
runtime.load(injector);
|
|
190
|
+
executionContext.application = runtime.getService(Application);
|
|
191
|
+
|
|
192
|
+
runtime.fireEvent('OnInit');
|
|
193
|
+
runtime.configure();
|
|
194
|
+
|
|
195
|
+
if (runtime.selfTest) {
|
|
196
|
+
console.log(`[Self Test] ✔ Looks good!`);
|
|
197
|
+
process.exit(0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (options.autostart)
|
|
201
|
+
runtime.start();
|
|
202
|
+
|
|
203
|
+
return executionContext.application;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
}
|
package/src/args.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstracts the fetching of the application's arguments. This is most useful for testing,
|
|
3
|
+
* but could also be used if trying to host Alterior in a strange environment.
|
|
4
|
+
*/
|
|
5
|
+
export class ApplicationArgs {
|
|
6
|
+
public get() {
|
|
7
|
+
return process.argv.slice(2);
|
|
8
|
+
}
|
|
9
|
+
}
|
package/src/expose.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { shallowClone } from '@alterior/common';
|
|
2
|
+
|
|
3
|
+
export interface ExposeOptions {
|
|
4
|
+
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface Exposure {
|
|
8
|
+
propertyName: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const EXPOSURES_PROPERTY = 'alterior:service:exposures';
|
|
12
|
+
|
|
13
|
+
export function Expose() {
|
|
14
|
+
return function (target: Object, propertyName: string, descriptor: PropertyDescriptor) {
|
|
15
|
+
if (!target.hasOwnProperty(EXPOSURES_PROPERTY)) {
|
|
16
|
+
Object.defineProperty(target, EXPOSURES_PROPERTY, {
|
|
17
|
+
enumerable: false,
|
|
18
|
+
value: []
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let routes = target[EXPOSURES_PROPERTY] || [];
|
|
23
|
+
routes.push(<Exposure>{
|
|
24
|
+
propertyName
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export class ExposureReflector {
|
|
31
|
+
constructor(type : Function) {
|
|
32
|
+
this.exposures = this.getExposuresFromType(type).map(x => shallowClone(x));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private getExposuresFromType(type : Function) {
|
|
36
|
+
let parentPrototype = Object.getPrototypeOf(type.prototype);
|
|
37
|
+
let exposures : Exposure[] = (type.prototype[EXPOSURES_PROPERTY] || []);
|
|
38
|
+
if (parentPrototype) {
|
|
39
|
+
return [].concat(...this.getExposuresFromType(parentPrototype.constructor), ...exposures);
|
|
40
|
+
} else {
|
|
41
|
+
return [].concat(...exposures);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public exposures : Exposure[];
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './app-options';
|
|
2
|
+
export * from './lifecycle';
|
|
3
|
+
export * from './args';
|
|
4
|
+
export * from './application';
|
|
5
|
+
export * from './modules';
|
|
6
|
+
export * from './reflector';
|
|
7
|
+
export * from './roles.service';
|
|
8
|
+
export * from './service';
|
|
9
|
+
export * from './expose';
|
package/src/lifecycle.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specifies that the implementer understands the Alterior OnInit lifecycle event.
|
|
3
|
+
* Implementing this interface is a best practice to ensure that implementations
|
|
4
|
+
* are well-formed.
|
|
5
|
+
*/
|
|
6
|
+
export interface OnInit {
|
|
7
|
+
altOnInit();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Specifies that the implementer understands the Alterior OnStart lifecycle event.
|
|
12
|
+
* Implementing this interface is a best practice to ensure that implementations
|
|
13
|
+
* are well-formed.
|
|
14
|
+
*/
|
|
15
|
+
export interface OnStart {
|
|
16
|
+
altOnStart();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Specifies that the implementer understands the Alterior OnStop lifecycle event.
|
|
21
|
+
* Implementing this interface is a best practice to ensure that implementations
|
|
22
|
+
* are well-formed.
|
|
23
|
+
*/
|
|
24
|
+
export interface OnStop {
|
|
25
|
+
altOnStop();
|
|
26
|
+
}
|