@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alterior/runtime",
3
- "version": "3.5.3",
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.4.0",
40
+ "@alterior/annotations": "^3.5.6",
41
41
  "@alterior/common": "^3.4.0",
42
- "@alterior/di": "^3.5.3",
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
- "gitHead": "808d85ec078d91261d8388c717d77c02aefc568a"
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';
@@ -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
+ }