##// END OF EJS Templates
working on IoC container
cin -
r38:d3813a6cdb36 di-typescript
parent child
Show More
@@ -1,4 +1,15
1 {
1 {
2 "java.configuration.updateBuildConfiguration": "disabled",
2 "java.configuration.updateBuildConfiguration": "disabled",
3 "tslint.enable": true
3 "tslint.enable": true,
4 "search.exclude": {
5 "**/node_modules": true,
6 "**/bower_components": true,
7 "/build": true
8 },
9 "files.watcherExclude": {
10 "**/.git/objects/**": true,
11 "**/.git/subtree-cache/**": true,
12 "**/node_modules/**": true,
13 "/build": true
14 }
4 } No newline at end of file
15 }
@@ -1,11 +1,11
1 import { IActivationController, IActivatable, ICancellation } from '../interfaces';
1 import { IActivationController, IActivatable, ICancellation } from "../interfaces";
2 import { AsyncComponent } from './AsyncComponent';
2 import { AsyncComponent } from "./AsyncComponent";
3 import { Cancellation } from '../Cancellation';
3 import { Cancellation } from "../Cancellation";
4 import { TraceSource } from '../log/TraceSource';
4 import { TraceSource } from "../log/TraceSource";
5
5
6 type Constructor<T = {}> = new (...args: any[]) => T;
6 type Constructor<T = {}> = new (...args: any[]) => T;
7
7
8 const log = TraceSource.get('@implab/core/components/ActivatableMixin');
8 const log = TraceSource.get("@implab/core/components/ActivatableMixin");
9
9
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
11 return class extends Base implements IActivatable {
11 return class extends Base implements IActivatable {
@@ -77,7 +77,7 export function ActivatableMixin<TBase e
77 log.error("Suppressed onDeactivated error: {0}", e);
77 log.error("Suppressed onDeactivated error: {0}", e);
78 }
78 }
79 }
79 }
80 }
80 };
81 }
81 }
82
82
83 export const traceSource = log; No newline at end of file
83 export const traceSource = log;
@@ -2,7 +2,7 import { Descriptor } from "./interfaces
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3
3
4 export class AggregateDescriptor<T> implements Descriptor {
4 export class AggregateDescriptor<T> implements Descriptor {
5 _value: T
5 _value: T;
6
6
7 constructor(value: T) {
7 constructor(value: T) {
8
8
@@ -10,7 +10,7 export class AggregateDescriptor<T> impl
10
10
11 activate(context: ActivationContext, name: string) {
11 activate(context: ActivationContext, name: string) {
12 context.enter(name);
12 context.enter(name);
13 let v = context.parse(this._value, ".params");
13 const v = context.parse(this._value, ".params");
14 context.leave();
14 context.leave();
15 return v;
15 return v;
16 }
16 }
@@ -1,28 +1,26
1 declare function require(modules: string[], cb?: (...args: any[]) => any) : void;
2
3 declare function define(name:string, modules: string[], cb?: (...args: any[]) => any) : void;
4
5 import { Uuid } from "../Uuid";
6 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
7 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
8 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
9 import { isDescriptor, ActivationType } from "./interfaces";
4 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration } from "./interfaces";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
5 import { AggregateDescriptor } from "./AggregateDescriptor";
11 import { isPrimitive } from "../safe";
6 import { isPrimitive } from "../safe";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
7 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { ServiceDescriptor } from "./ServiceDescriptor";
8 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
14
9 import { ModuleResolverBase } from "./ModuleResolverBase";
10 import format = require("../text/format");
15
11
16 export class Container {
12 export class Container {
17 _services
13 _services: ServiceMap;
18
14
19 _cache
15 _cache: object;
16
17 _cleanup: (() => void)[];
20
18
21 _cleanup: any[]
19 _root: Container;
22
20
23 _root: Container
21 _parent: Container;
24
22
25 _parent: Container
23 _resolver: ModuleResolverBase;
26
24
27 constructor(parent?: Container) {
25 constructor(parent?: Container) {
28 this._parent = parent;
26 this._parent = parent;
@@ -42,7 +40,7 export class Container {
42 }
40 }
43
41
44 getService<T = any>(name: string, def?: T) {
42 getService<T = any>(name: string, def?: T) {
45 let d = this._services[name];
43 const d = this._services[name];
46 if (!d)
44 if (!d)
47 if (arguments.length > 1)
45 if (arguments.length > 1)
48 return def;
46 return def;
@@ -50,21 +48,21 export class Container {
50 throw new Error("Service '" + name + "' isn't found");
48 throw new Error("Service '" + name + "' isn't found");
51
49
52 if (d.isInstanceCreated())
50 if (d.isInstanceCreated())
53 return d.getInstance();
51 return d.getInstance() as T;
54
52
55 var context = new ActivationContext(this, this._services);
53 const context = new ActivationContext(this, this._services);
56
54
57 try {
55 try {
58 return d.activate(context, name);
56 return d.activate(context, name) as T;
59 } catch (error) {
57 } catch (error) {
60 throw new ActivationError(name, context.getStack(), error);
58 throw new ActivationError(name, context.getStack(), error);
61 }
59 }
62 }
60 }
63
61
64 register(nameOrCollection, service?) {
62 register(nameOrCollection, service?) {
65 if (arguments.length == 1) {
63 if (arguments.length === 1) {
66 var data = nameOrCollection;
64 const data = nameOrCollection;
67 for (let name in data)
65 for (const name in data)
68 this.register(name, data[name]);
66 this.register(name, data[name]);
69 } else {
67 } else {
70 if (!(isDescriptor(service)))
68 if (!(isDescriptor(service)))
@@ -82,8 +80,8 export class Container {
82
80
83 dispose() {
81 dispose() {
84 if (this._cleanup) {
82 if (this._cleanup) {
85 for (var i = 0; i < this._cleanup.length; i++)
83 for (const f of this._cleanup)
86 this._cleanup[i].call(null);
84 f();
87 this._cleanup = null;
85 this._cleanup = null;
88 }
86 }
89 }
87 }
@@ -98,36 +96,13 export class Container {
98 * The function which will be used to load a configuration or types for services.
96 * The function which will be used to load a configuration or types for services.
99 *
97 *
100 */
98 */
101 async configure(config, opts) {
99 async configure(config: string | object, opts?: object) {
102 var me = this,
103 contextRequire = (opts && opts.contextRequire);
104
105 if (typeof (config) === "string") {
100 if (typeof (config) === "string") {
106 let args;
101 const resolver = await this._resolver.createResolver(config, opts);
107 if (!contextRequire) {
102 const data = await this._resolver.loadModule(config);
108 var shim = [config, Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
103 return this._configure(data, { resolver });
109 args = await new Promise((resolve, reject) => {
110 define(shim, ["require", config], function (ctx, data) {
111 resolve([data, {
112 contextRequire: ctx
113 }]);
114 })
115 });
116 require([shim]);
117 } else {
104 } else {
118 // TODO how to get correct contextRequire for the relative config module?
105 return this._configure(config);
119 args = await new Promise((resolve, reject) => {
120 contextRequire([config], function (data) {
121 resolve([data, {
122 contextRequire: contextRequire
123 }]);
124 });
125 });
126 }
127
128 return me._configure.apply(me, args);
129 } else {
130 return me._configure(config, opts);
131 }
106 }
132 }
107 }
133
108
@@ -147,109 +122,108 export class Container {
147 return (this._cache[id] = value);
122 return (this._cache[id] = value);
148 }
123 }
149
124
150 async _configure(data, opts) {
125 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
151 var typemap = {},
126 const resolver = (opts && opts.resolver) || this._resolver;
152 me = this,
127
153 p,
128 const services: ServiceMap = {};
154 contextRequire = (opts && opts.contextRequire) || require;
155
129
156 var services = {};
130 resolver.beginBatch();
157
131
158 for (p in data) {
132 async function parse(k) {
159 var service = me._parse(data[p], typemap);
133 services[k] = await this._parse(data[k], resolver);
160 if (!(isDescriptor(service)))
161 service = new AggregateDescriptor(service);
162 services[p] = service;
163 }
134 }
164
135
165 me.register(services);
136 const batch = Object.keys(data).map(parse);
166
137
167 var names = [];
138 resolver.completeBatch();
168
169 for (p in typemap)
170 names.push(p);
171
139
172 return new Promise((resolve, reject) => {
140 await Promise.all(batch);
173 if (names.length) {
174 contextRequire(names, function () {
175 for (var i = 0; i < names.length; i++)
176 typemap[names[i]] = arguments[i];
177 resolve(me);
178 });
179 } else {
180 resolve(me);
181 }
182 });
183
141
142 this.register(services);
184 }
143 }
185
144
186 _parse(data, typemap) {
145 async _parse(registration: any, resolver: ModuleResolverBase) {
187 if (isPrimitive(data) || isDescriptor(data))
146 if (isPrimitive(registration) || isDescriptor(registration))
188 return data;
147 return registration;
189 if (data.$dependency) {
148
149 if (isDependencyRegistration(registration)) {
150
190 return new ReferenceDescriptor(
151 return new ReferenceDescriptor(
191 data.$dependency,
152 registration.$dependency,
192 data.lazy,
153 registration.lazy,
193 data.optional,
154 registration.optional,
194 data["default"],
155 registration["default"],
195 data.services && this._parseObject(data.services, typemap));
156 registration.services && this._parseObject(registration.services, resolver)
196 } else if (data.$value) {
157 );
197 return !data.parse ?
158
198 new ValueDescriptor(data.$value) :
159 } else if (isValueRegistration(registration)) {
199 new AggregateDescriptor(this._parse(data.$value, typemap));
160
200 } else if (data.$type || data.$factory) {
161 return !registration.parse ?
201 return this._parseService(data, typemap);
162 new ValueDescriptor(registration.$value) :
202 } else if (data instanceof Array) {
163 new AggregateDescriptor(this._parse(registration.$value, resolver));
203 return this._parseArray(data, typemap);
164
165 } else if (registration.$type || registration.$factory) {
166 return this._parseService(registration, resolver);
167 } else if (registration instanceof Array) {
168 return this._parseArray(registration, resolver);
204 }
169 }
205
170
206 return this._parseObject(data, typemap);
171 return this._parseObject(registration, resolver);
207 }
172 }
208
173
209 _parseService(data, typemap) {
174 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
210 var me = this,
175 const opts: ServiceDescriptorParams = {
211 opts: any = {
212 owner: this
176 owner: this
213 };
177 };
214 if (data.$type) {
215
178
216 opts.type = data.$type;
179 function guard<T>(fn: () => PromiseLike<T>) {
217
180 return fn();
218 if (typeof (data.$type) === "string") {
219 typemap[data.$type] = null;
220 opts.typeMap = typemap;
221 }
222 }
181 }
223
182
224 if (data.$factory)
183 if (data.$type) {
184 if (data.$type instanceof Function)
185 opts.type = data.$type;
186 else if (typeof data.$type === "string")
187 opts.type = await resolver.resolve(data.$type);
188 else
189 throw new Error(format("Unsupported type specification: {0:json}", data.$type));
190 } else {
191 if (data.$factory instanceof Function)
225 opts.factory = data.$factory;
192 opts.factory = data.$factory;
193 else if (typeof data.$factory === "string")
194 opts.factory = await resolver.resolve(data.$factory);
195 else
196 throw new Error(format("Unsupported factory specification: {0:json}", data.$factory));
197 }
226
198
227 if (data.services)
199 if (data.services)
228 opts.services = me._parseObject(data.services, typemap);
200 opts.services = await this._parseObject(data.services, resolver);
229 if (data.inject)
201
230 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
202 if (data.inject instanceof Array)
231 return me._parseObject(x, typemap);
203 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
232 }) : me._parseObject(data.inject, typemap);
204 else
205 opts.inject = this._parseObject(data.inject, resolver);
206
233 if (data.params)
207 if (data.params)
234 opts.params = me._parse(data.params, typemap);
208 opts.params = this._parse(data.params, resolver);
235
209
236 if (data.activation) {
210 if (data.activation) {
237 if (typeof (data.activation) === "string") {
211 if (typeof (data.activation) === "string") {
238 switch (data.activation.toLowerCase()) {
212 switch (data.activation.toLowerCase()) {
239 case "singleton":
213 case "singleton":
240 opts.activation = ActivationType.SINGLETON;
214 opts.activation = ActivationType.Singleton;
241 break;
215 break;
242 case "container":
216 case "container":
243 opts.activation = ActivationType.CONTAINER;
217 opts.activation = ActivationType.Container;
244 break;
218 break;
245 case "hierarchy":
219 case "hierarchy":
246 opts.activation = ActivationType.HIERARCHY;
220 opts.activation = ActivationType.Hierarchy;
247 break;
221 break;
248 case "context":
222 case "context":
249 opts.activation = ActivationType.CONTEXT;
223 opts.activation = ActivationType.Context;
250 break;
224 break;
251 case "call":
225 case "call":
252 opts.activation = ActivationType.CALL;
226 opts.activation = ActivationType.Call;
253 break;
227 break;
254 default:
228 default:
255 throw new Error("Unknown activation type: " +
229 throw new Error("Unknown activation type: " +
@@ -266,14 +240,14 export class Container {
266 return new ServiceDescriptor(opts);
240 return new ServiceDescriptor(opts);
267 }
241 }
268
242
269 _parseObject(data, typemap) {
243 _parseObject(data: any, typemap) {
270 if (data.constructor &&
244 if (data.constructor &&
271 data.constructor.prototype !== Object.prototype)
245 data.constructor.prototype !== Object.prototype)
272 return new ValueDescriptor(data);
246 return new ValueDescriptor(data);
273
247
274 var o = {};
248 const o = {};
275
249
276 for (var p in data)
250 for (const p in data)
277 o[p] = this._parse(data[p], typemap);
251 o[p] = this._parse(data[p], typemap);
278
252
279 return o;
253 return o;
@@ -284,9 +258,6 export class Container {
284 data.constructor.prototype !== Array.prototype)
258 data.constructor.prototype !== Array.prototype)
285 return new ValueDescriptor(data);
259 return new ValueDescriptor(data);
286
260
287 var me = this;
261 return data.map(x => this._parse(x, typemap));
288 return data.map(function (x) {
289 return me._parse(x, typemap);
290 });
291 }
262 }
292 } No newline at end of file
263 }
@@ -2,16 +2,21 import { argumentNotEmptyString, get } f
2
2
3 export abstract class ModuleResolverBase {
3 export abstract class ModuleResolverBase {
4
4
5
6 async resolve(typeName: string) {
5 async resolve(typeName: string) {
7 argumentNotEmptyString(typeName, "typeName");
6 argumentNotEmptyString(typeName, "typeName");
8 let [moduleName, localName] = typeName.split("#", 2);
7 const [moduleName, localName] = typeName.split("#", 2);
9
8
10 let moduleObject = await this.loadModule(moduleName);
9 const moduleObject = await this.loadModule(moduleName);
11 return localName ? get(localName, moduleObject) : moduleObject;
10 return localName ? get(localName, moduleObject) : moduleObject;
12 }
11 }
13
12
14 abstract loadModule(moduleName: string): PromiseLike<Object>
13 beginBatch() {
14 }
15
15
16 abstract createResolver(moduleName: string): PromiseLike<ModuleResolverBase>
16 completeBatch() {
17 } No newline at end of file
17 }
18
19 abstract loadModule(moduleName: string): PromiseLike<object>;
20
21 abstract createResolver(moduleName: string, opts?: object): PromiseLike<ModuleResolverBase>;
22 }
@@ -7,18 +7,18 declare function require(modules: string
7
7
8 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any): void;
8 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any): void;
9
9
10 class RequireJsResolverParams {
10 interface RequireJsResolverParams {
11 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void
11 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void;
12
12
13 base: string
13 base: string;
14 }
14 }
15
15
16 TraceSource.get("RequireJsResolver");
16 TraceSource.get("RequireJsResolver");
17
17
18 export class RequireJsResolver extends ModuleResolverBase {
18 export class RequireJsResolver extends ModuleResolverBase {
19 _contextRequire = require
19 _contextRequire = require;
20
20
21 _base: string
21 _base: string;
22
22
23 constructor(opts) {
23 constructor(opts) {
24 super();
24 super();
@@ -29,7 +29,7 export class RequireJsResolver extends M
29 this._contextRequire = opts.contextRequire;
29 this._contextRequire = opts.contextRequire;
30
30
31 if (opts.base) {
31 if (opts.base) {
32 if (opts.base.indexOf("./") == 0)
32 if (opts.base.indexOf("./") === 0)
33 throw new Error(`A module id should be an absolute: '${opts.base}'`);
33 throw new Error(`A module id should be an absolute: '${opts.base}'`);
34 this._base = opts.base;
34 this._base = opts.base;
35 }
35 }
@@ -40,8 +40,8 export class RequireJsResolver extends M
40 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
40 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
41 argumentNotEmptyString(moduleName, "moduleName");
41 argumentNotEmptyString(moduleName, "moduleName");
42
42
43 let parts = moduleName.split("/");
43 const parts = moduleName.split("/");
44 if (parts[0] == ".") {
44 if (parts[0] === ".") {
45 if (this._base)
45 if (this._base)
46 parts[0] = this._base;
46 parts[0] = this._base;
47 else
47 else
@@ -53,23 +53,22 export class RequireJsResolver extends M
53 else
53 else
54 parts.push(Uuid());
54 parts.push(Uuid());
55
55
56 var shim = parts.join('/');
56 const shim = parts.join("/");
57
57
58 let contextRequire = await new Promise((resolve, reject) => {
58 const contextRequire = await new Promise(
59 define(shim, ["require"], function (ctx) {
59 resolve => define(shim, ["require"], resolve)
60 resolve(ctx);
60 );
61 })
62 });
63
61
64 return new RequireJsResolver({
62 return new RequireJsResolver({
65 base: parts.slice(0,-1).join('/'),
63 base: parts.slice(0, -1).join("/"),
66 contextRequire: contextRequire
64 contextRequire
67 });
65 });
68 }
66 }
69
67
70 async loadModule(moduleName: string): Promise<Object> {
68 async loadModule(moduleName: string): Promise<object> {
71 return new Promise<Object>((resolve) => this._contextRequire.call(null, [moduleName], resolve)
69 return new Promise<object>(
70 resolve => this._contextRequire.call(null, [moduleName], resolve)
72 );
71 );
73 }
72 }
74
73
75 } No newline at end of file
74 }
@@ -2,11 +2,12 import { ActivationContext } from "./Act
2 import { Descriptor, ActivationType, ServiceMap, Constructor, Factory } from "./interfaces";
2 import { Descriptor, ActivationType, ServiceMap, Constructor, Factory } from "./interfaces";
3 import { Container } from "./Container";
3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, oid } from "../safe";
4 import { argumentNotNull, isPrimitive, oid } from "../safe";
5 import { ClientResponse } from "http";
5
6
6 let cacheId = 0;
7 let cacheId = 0;
7
8
8 function injectMethod(target, method, context, args) {
9 function injectMethod(target, method, context, args) {
9 var m = target[method];
10 const m = target[method];
10 if (!m)
11 if (!m)
11 throw new Error("Method '" + method + "' not found");
12 throw new Error("Method '" + method + "' not found");
12
13
@@ -14,60 +15,60 function injectMethod(target, method, co
14 m.apply(target, context.parse(args, "." + method));
15 m.apply(target, context.parse(args, "." + method));
15 else
16 else
16 m.call(target, context.parse(args, "." + method));
17 m.call(target, context.parse(args, "." + method));
17 };
18 }
18
19
19 function makeClenupCallback(target, method) {
20 function makeClenupCallback(target, method: (instance) => void | string) {
20 if (typeof (method) === "string") {
21 if (typeof (method) === "string") {
21 return function () {
22 return () => {
22 target[method]();
23 target[method]();
23 };
24 };
24 } else {
25 } else {
25 return function () {
26 return () => {
26 method(target);
27 method(target);
27 };
28 };
28 }
29 }
29 };
30 }
30
31
31 export interface ServiceDescriptorParams<T> {
32 export interface ServiceDescriptorParams<T = {}> {
32 activation: ActivationType
33 activation?: ActivationType;
33
34
34 owner: Container
35 owner: Container;
35
36
36 type: Constructor<T>
37 type?: Constructor<T>;
37
38
38 factory: Factory<T>
39 factory?: Factory<T>;
39
40
40 params
41 params?;
41
42
42 inject
43 inject?;
43
44
44 services: ServiceMap
45 services?: ServiceMap;
45
46
46 cleanup: (instance: T) => void
47 cleanup?: (instance: T) => void | string;
47 }
48 }
48
49
49 export class ServiceDescriptor<T> implements Descriptor {
50 export class ServiceDescriptor<T = {}> implements Descriptor {
50 _instance: T = null
51 _instance: T = null;
51
52
52 _hasInstance = false
53 _hasInstance = false;
53
54
54 _activationType = ActivationType.CALL
55 _activationType = ActivationType.Call;
55
56
56 _services: ServiceMap
57 _services: ServiceMap;
57
58
58 _type: Constructor<T> = null
59 _type: Constructor<T> = null;
59
60
60 _factory: Factory<T> = null
61 _factory: Factory<T> = null;
61
62
62 _params
63 _params;
63
64
64 _inject: Array<Object>
65 _inject: Array<object>;
65
66
66 _cleanup: (instance: T) => void
67 _cleanup: (instance: T) => void;
67
68
68 _cacheId: any
69 _cacheId: any;
69
70
70 _owner: Container
71 _owner: Container;
71
72
72 constructor(opts: ServiceDescriptorParams<T>) {
73 constructor(opts: ServiceDescriptorParams<T>) {
73 argumentNotNull(opts, "opts");
74 argumentNotNull(opts, "opts");
@@ -105,8 +106,8 export class ServiceDescriptor<T> implem
105 this._cleanup = opts.cleanup;
106 this._cleanup = opts.cleanup;
106 }
107 }
107
108
108 if (this._activationType == ActivationType.SINGLETON) {
109 if (this._activationType === ActivationType.Singleton) {
109 let tof = this._type || this._factory;
110 const tof = this._type || this._factory;
110
111
111 // create the persistent cache identifier for the type
112 // create the persistent cache identifier for the type
112 if (isPrimitive(tof))
113 if (isPrimitive(tof))
@@ -123,13 +124,13 export class ServiceDescriptor<T> implem
123 let instance;
124 let instance;
124
125
125 switch (this._activationType) {
126 switch (this._activationType) {
126 case ActivationType.SINGLETON: // SINGLETON
127 case ActivationType.Singleton: // SINGLETON
127 // if the value is cached return it
128 // if the value is cached return it
128 if (this._hasInstance)
129 if (this._hasInstance)
129 return this._instance;
130 return this._instance;
130
131
131 // singletons are bound to the root container
132 // singletons are bound to the root container
132 let container = context.container.getRootContainer();
133 const container = context.container.getRootContainer();
133
134
134 if (container.has(this._cacheId)) {
135 if (container.has(this._cacheId)) {
135 instance = container.get(this._cacheId);
136 instance = container.get(this._cacheId);
@@ -144,8 +145,9 export class ServiceDescriptor<T> implem
144 this._hasInstance = true;
145 this._hasInstance = true;
145 return (this._instance = instance);
146 return (this._instance = instance);
146
147
147 case ActivationType.CONTAINER: // CONTAINER
148 case ActivationType.Container: // CONTAINER
148 //return a cached value
149 // return a cached value
150
149 if (this._hasInstance)
151 if (this._hasInstance)
150 return this._instance;
152 return this._instance;
151
153
@@ -160,18 +162,19 export class ServiceDescriptor<T> implem
160 // cache and return the instance
162 // cache and return the instance
161 this._hasInstance = true;
163 this._hasInstance = true;
162 return (this._instance = instance);
164 return (this._instance = instance);
163 case ActivationType.CONTEXT: // CONTEXT
165 case ActivationType.Context: // CONTEXT
164 //return a cached value if one exists
166 // return a cached value if one exists
167
165 if (context.has(this._cacheId))
168 if (context.has(this._cacheId))
166 return context.get(this._cacheId);
169 return context.get(this._cacheId);
167 // context context activated instances are controlled by callers
170 // context context activated instances are controlled by callers
168 return context.store(this._cacheId, this._create(
171 return context.store(this._cacheId, this._create(
169 context,
172 context,
170 name));
173 name));
171 case ActivationType.CALL: // CALL
174 case ActivationType.Call: // CALL
172 // per-call created instances are controlled by callers
175 // per-call created instances are controlled by callers
173 return this._create(context, name);
176 return this._create(context, name);
174 case ActivationType.HIERARCHY: // HIERARCHY
177 case ActivationType.Hierarchy: // HIERARCHY
175 // hierarchy activated instances are behave much like container activated
178 // hierarchy activated instances are behave much like container activated
176 // except they are created and bound to the child container
179 // except they are created and bound to the child container
177
180
@@ -188,7 +191,7 export class ServiceDescriptor<T> implem
188
191
189 return context.container.store(this._cacheId, instance);
192 return context.container.store(this._cacheId, instance);
190 default:
193 default:
191 throw "Invalid activation type: " + this._activationType;
194 throw new Error("Invalid activation type: " + this._activationType);
192 }
195 }
193 }
196 }
194
197
@@ -203,35 +206,22 export class ServiceDescriptor<T> implem
203 _create(context, name) {
206 _create(context, name) {
204 context.enter(name, this, Boolean(this._services));
207 context.enter(name, this, Boolean(this._services));
205
208
206 if (this._activationType != ActivationType.CALL &&
209 if (this._activationType !== ActivationType.Call &&
207 context.visit(this._cacheId) > 0)
210 context.visit(this._cacheId) > 0)
208 throw new Error("Recursion detected");
211 throw new Error("Recursion detected");
209
212
210 if (this._services) {
213 if (this._services) {
211 for (var p in this._services)
214 for (const p in this._services)
212 context.register(p, this._services[p]);
215 context.register(p, this._services[p]);
213 }
216 }
214
217
215 var instance;
218 let instance;
216
219
217 if (!this._factory) {
220 if (!this._factory) {
218 var ctor = this._type;
221 const ctor = this._type;
219
222 this._factory = (...args) => {
220 if (this._params === undefined) {
223 return new ctor(...args);
221 this._factory = function () {
222 return new ctor();
223 };
224 };
224 } else if (this._params instanceof Array) {
225 this._factory = function () {
226 var inst = Object.create(ctor.prototype);
227 var ret = ctor.apply(inst, arguments);
228 return typeof (ret) === "object" ? ret : inst;
229 };
230 } else {
231 this._factory = function (param) {
232 return new ctor(param);
233 };
234 }
235 }
225 }
236
226
237 if (this._params === undefined) {
227 if (this._params === undefined) {
@@ -247,8 +237,8 export class ServiceDescriptor<T> implem
247 }
237 }
248
238
249 if (this._inject) {
239 if (this._inject) {
250 this._inject.forEach(function (spec) {
240 this._inject.forEach(spec => {
251 for (var m in spec)
241 for (const m in spec)
252 injectMethod(instance, m, context, spec[m]);
242 injectMethod(instance, m, context, spec[m]);
253 });
243 });
254 }
244 }
@@ -261,7 +251,7 export class ServiceDescriptor<T> implem
261 // @constructor {singleton} foo/bar/Baz
251 // @constructor {singleton} foo/bar/Baz
262 // @factory {singleton}
252 // @factory {singleton}
263 toString() {
253 toString() {
264 var parts = [];
254 const parts = [];
265
255
266 parts.push(this._type ? "@constructor" : "@factory");
256 parts.push(this._type ? "@constructor" : "@factory");
267
257
@@ -272,4 +262,4 export class ServiceDescriptor<T> implem
272
262
273 return parts.join(" ");
263 return parts.join(" ");
274 }
264 }
275 } No newline at end of file
265 }
@@ -3,26 +3,69 import { ActivationContext } from "./Act
3
3
4 export interface Descriptor {
4 export interface Descriptor {
5 activate(context: ActivationContext, name?: string);
5 activate(context: ActivationContext, name?: string);
6 isInstanceCreated(): boolean;
7 getInstance();
6 }
8 }
7
9
8 export type Constructor<T = {}> = new (...args: any[]) => T;
10 export type Constructor<T = {}> = new (...args: any[]) => T;
9
11
10
11 export type Factory<T = {}> = (...args: any[]) => T;
12 export type Factory<T = {}> = (...args: any[]) => T;
12
13
13 export function isDescriptor(instance): instance is Descriptor {
14 export function isDescriptor(instance): instance is Descriptor {
14 return (!isNull(instance)) &&
15 return (!isNull(instance)) &&
15 ('activate' in instance);
16 ("activate" in instance);
16 }
17 }
17
18
18 export interface ServiceMap {
19 export interface ServiceMap {
19 [s: string] : Descriptor
20 [s: string]: Descriptor;
20 }
21 }
21
22
22 export enum ActivationType {
23 export enum ActivationType {
23 SINGLETON,
24 Singleton,
24 CONTAINER,
25 Container,
25 HIERARCHY,
26 Hierarchy,
26 CONTEXT,
27 Context,
27 CALL
28 Call
28 } No newline at end of file
29 }
30
31 export interface RegistrationWithServices {
32 services?: object;
33 }
34
35 export interface ServiceRegistration extends RegistrationWithServices {
36 $type?: string | Constructor;
37
38 $factory?: string | Factory;
39
40 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
41
42 params?;
43
44 inject?: object | object[];
45
46 cleanup: (instance) => void | string;
47 }
48
49 export interface ValueRegistration {
50 $value;
51 parse?: boolean;
52 }
53
54 export interface DependencyRegistration extends RegistrationWithServices {
55 $dependency: string;
56 lazy?: boolean;
57 optional?: boolean;
58 default?;
59 }
60
61 export function isServiceRegistration(x): x is ServiceRegistration {
62 return x && ("$type" in x || "$factory" in x);
63 }
64
65 export function isValueRegistration(x): x is ValueRegistration {
66 return x && "$value" in x;
67 }
68
69 export function isDependencyRegistration(x): x is DependencyRegistration {
70 return x && "$depdendency" in x;
71 }
General Comments 0
You need to be logged in to leave comments. Login now