proxydi 0.0.3 → 0.0.4

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.
@@ -0,0 +1,3 @@
1
+ import { ServiceId } from './types';
2
+ export declare const makeProxy: <T>(serviceId: ServiceId, instance: any) => T;
3
+ export declare function isProxy(value: any): boolean;
package/dist/ProxyDI.d.ts CHANGED
@@ -1,32 +1,27 @@
1
+ import { ProxyDI as IProxyDI, ProxydiedInstance } from './types';
1
2
  import { ProxyDISettings, ServiceClass, ServiceId } from './types';
2
- export declare const PROXYDI: unique symbol;
3
- export declare const PROXYDI_ID: unique symbol;
4
- export declare class ProxyDI {
3
+ export declare class ProxyDI implements IProxyDI {
5
4
  private static idCounter;
6
5
  readonly id: number;
7
6
  readonly parent?: ProxyDI;
8
7
  private children;
9
- private instances;
10
- private classes;
11
- private proxies;
12
- private proxyFactory;
13
- protected allowRewriteClasses: boolean;
14
- protected allowRewriteInstances: boolean;
15
- constructor(settings?: ProxyDISettings);
8
+ private serviceInstances;
9
+ private serviceClasses;
10
+ private settings;
11
+ constructor(settings?: ProxyDISettings, parent?: ProxyDI);
16
12
  registerInstance<T>(serviceId: ServiceId, instance: T extends {
17
13
  new (...args: any[]): any;
18
14
  } ? never : T): void;
19
15
  registerClass<T>(serviceId: ServiceId, serviceClass: ServiceClass<T>): void;
20
- private findInstance;
21
16
  isKnown(serviceId: ServiceId): boolean;
22
17
  resolve<T>(serviceId: ServiceId): T;
23
- private findDefiner;
24
18
  injectDependencies(instance: any): void;
25
- private getProxy;
26
- createChildContainer(settings?: ProxyDISettings): ProxyDI;
27
- removeInstance(serviceId: ServiceId): void;
19
+ createChildContainer(): ProxyDI;
20
+ removeInstance(serviceId: ServiceId | ProxydiedInstance): void;
28
21
  removeClass(serviceId: ServiceId): void;
29
22
  destroy(): void;
30
- addChild(child: ProxyDI): void;
23
+ private findInstance;
24
+ private findServiceClass;
25
+ private addChild;
31
26
  private removeChild;
32
27
  }
package/dist/index.cjs CHANGED
@@ -1,122 +1,119 @@
1
1
  'use strict';
2
2
 
3
3
  const INJECTS = Symbol('injects');
4
- const inject = (serviceId) => {
5
- return function (_value, context) {
6
- if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
4
+ const PROXYDI = Symbol('ProxyDI');
5
+ const SERVICE_ID = Symbol('ServiceId');
6
+ const IS_PROXY = Symbol('isProxy');
7
+ const INSTANCE = Symbol('instance');
8
+
9
+ const injectableClasses = {};
10
+ const injectable = (serviceId) => {
11
+ return function (value, context) {
12
+ if ((context === null || context === undefined ? undefined : context.kind) === 'class') {
7
13
  const name = serviceId ? serviceId : context.name;
8
- const inject = {
9
- property: context.name,
10
- serviceId: name,
11
- set: context.access.set,
12
- };
13
- context.addInitializer(function () {
14
- if (!this[INJECTS]) {
15
- this[INJECTS] = [];
16
- }
17
- this[INJECTS].push(inject);
18
- });
14
+ if (injectableClasses[name]) {
15
+ throw new Error(`ProxyDI injectable classes already has service ID: ${name}`);
16
+ }
17
+ injectableClasses[name] = value;
18
+ }
19
+ else {
20
+ throw new Error('@injectable decorator should decorate classes');
19
21
  }
20
22
  };
21
23
  };
22
24
 
23
- const injectableClasses = {};
25
+ const DEFAULT_SETTINGS = {
26
+ allowRegisterAnythingAsInstance: false,
27
+ allowRewriteClasses: false,
28
+ allowRewriteInstances: false,
29
+ };
24
30
 
25
- const IS_PROXY = Symbol('isProxy');
26
- class ProxyFactory {
27
- constructor(container) {
28
- this.container = container;
29
- }
30
- makeProxy(serviceId) {
31
- const self = this;
32
- const proxy = new Proxy({ [IS_PROXY]: true }, {
33
- get: function (target, prop, receiver) {
34
- if (target[prop]) {
35
- return target[prop];
36
- }
37
- if (self.container.isKnown(serviceId)) {
38
- const instance = self.container.resolve(serviceId);
39
- return Reflect.get(instance, prop, receiver);
40
- }
41
- else {
42
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
43
- }
44
- },
45
- set: function (target, prop, value) {
46
- if (self.container.isKnown(serviceId)) {
47
- const instance = self.container.resolve(serviceId);
48
- return Reflect.set(instance, prop, value);
49
- }
50
- else {
51
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
52
- }
53
- },
54
- has: function (target, prop) {
55
- if (self.container.isKnown(serviceId)) {
56
- const instance = self.container.resolve(serviceId);
57
- return Reflect.has(instance, prop);
58
- }
59
- else {
60
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
61
- }
62
- },
63
- });
64
- return proxy;
31
+ var _a;
32
+ class InstanceProxy {
33
+ constructor(instance) {
34
+ this[_a] = true;
35
+ this[INSTANCE] = instance;
65
36
  }
66
37
  }
38
+ _a = IS_PROXY;
39
+ const makeProxy = (serviceId, instance) => {
40
+ return new Proxy(new InstanceProxy(instance), {
41
+ get: function (target, prop, receiver) {
42
+ if (target[prop]) {
43
+ return target[prop];
44
+ }
45
+ const container = target[INSTANCE][PROXYDI];
46
+ if (container.isKnown(serviceId)) {
47
+ const instance = container.resolve(serviceId);
48
+ return Reflect.get(instance, prop, receiver);
49
+ }
50
+ else {
51
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
52
+ }
53
+ },
54
+ set: function (target, prop, value) {
55
+ const container = target[INSTANCE][PROXYDI];
56
+ if (container.isKnown(serviceId)) {
57
+ const instance = container.resolve(serviceId);
58
+ return Reflect.set(instance, prop, value);
59
+ }
60
+ else {
61
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
62
+ }
63
+ },
64
+ has: function (target, prop) {
65
+ const container = target[INSTANCE][PROXYDI];
66
+ if (container.isKnown(serviceId)) {
67
+ const instance = container.resolve(serviceId);
68
+ return Reflect.has(instance, prop);
69
+ }
70
+ else {
71
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
72
+ }
73
+ },
74
+ });
75
+ };
67
76
 
68
- const PROXYDI = Symbol('ProxyDI');
69
- const PROXYDI_ID = Symbol('ProxyDI_ID');
70
77
  class ProxyDI {
71
- constructor(settings) {
78
+ constructor(settings, parent) {
72
79
  this.children = {};
73
- this.instances = {};
74
- this.classes = {};
75
- this.proxies = {};
76
- this.allowRewriteClasses = false;
77
- this.allowRewriteInstances = false;
78
- this.proxyFactory = new ProxyFactory(this);
80
+ this.serviceInstances = {};
81
+ this.serviceClasses = {};
79
82
  this.id = ProxyDI.idCounter++;
80
- if (settings === null || settings === undefined ? undefined : settings.parent) {
81
- this.parent = settings.parent;
83
+ if (parent) {
84
+ this.parent = parent;
82
85
  this.parent.addChild(this);
83
86
  }
84
- if ((settings === null || settings === undefined ? undefined : settings.allowRewriteClasses) !== undefined) {
85
- this.allowRewriteClasses = settings.allowRewriteClasses;
86
- }
87
- if ((settings === null || settings === undefined ? undefined : settings.allowRewriteInstances) !== undefined) {
88
- this.allowRewriteInstances = settings.allowRewriteInstances;
89
- }
87
+ this.settings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), settings);
90
88
  }
91
89
  registerInstance(serviceId, instance) {
92
- if (this.instances[serviceId]) {
93
- if (!this.allowRewriteInstances) {
90
+ if (this.serviceInstances[serviceId]) {
91
+ if (!this.settings.allowRewriteInstances) {
94
92
  throw new Error(`ProxyDI already has registered instance for ${serviceId}`);
95
93
  }
96
94
  }
95
+ const isObject = typeof instance === 'object';
96
+ if (!isObject && !this.settings.allowRegisterAnythingAsInstance) {
97
+ throw new Error(`Can't register as instance (allowRegisterAnythingAsInstance is off for this ProxyDI contatiner): ${instance}`);
98
+ }
97
99
  this.injectDependencies(instance);
98
- if (typeof instance === 'object') {
99
- instance[PROXYDI_ID] = serviceId;
100
+ if (isObject) {
101
+ instance[SERVICE_ID] = serviceId;
100
102
  }
101
- this.instances[serviceId] = instance;
103
+ this.serviceInstances[serviceId] = instance;
102
104
  }
103
105
  registerClass(serviceId, serviceClass) {
104
- if (this.classes[serviceId]) {
105
- if (!this.allowRewriteClasses) {
106
+ if (this.serviceClasses[serviceId]) {
107
+ if (!this.settings.allowRewriteClasses) {
106
108
  throw new Error(`ProxyDI already has registered class for ${serviceId}`);
107
109
  }
108
110
  }
109
- this.classes[serviceId] = serviceClass;
110
- }
111
- findInstance(serviceId) {
112
- const instance = this.instances[serviceId];
113
- if (!instance && this.parent) {
114
- return this.parent.findInstance(serviceId);
115
- }
116
- return instance;
111
+ this.serviceClasses[serviceId] = serviceClass;
117
112
  }
118
113
  isKnown(serviceId) {
119
- return !!(this.findInstance(serviceId) || this.findDefiner(serviceId));
114
+ return !!(this.findInstance(serviceId) ||
115
+ this.findServiceClass(serviceId) ||
116
+ injectableClasses[serviceId]);
120
117
  }
121
118
  resolve(serviceId) {
122
119
  if (!this.isKnown(serviceId)) {
@@ -126,34 +123,21 @@ class ProxyDI {
126
123
  if (instance) {
127
124
  return instance;
128
125
  }
129
- const definer = this.findDefiner(serviceId);
130
- if (definer) {
131
- let instance = new definer();
132
- this.injectDependencies(instance);
133
- instance[PROXYDI_ID] = serviceId;
134
- this.instances[serviceId] = instance;
135
- return instance;
136
- }
137
126
  else {
138
- return this.getProxy(serviceId);
139
- }
140
- }
141
- findDefiner(serviceId) {
142
- let definer = this.classes[serviceId];
143
- if (!definer && this.parent) {
144
- return this.parent.findDefiner(serviceId);
145
- }
146
- if (!definer) {
147
- return injectableClasses[serviceId];
127
+ const ServiceClass = this.findServiceClass(serviceId);
128
+ const newInstance = new ServiceClass();
129
+ this.registerInstance(serviceId, newInstance);
130
+ return newInstance;
148
131
  }
149
- return definer;
150
132
  }
151
133
  injectDependencies(instance) {
152
134
  const serviceInjects = instance[INJECTS] || [];
153
135
  serviceInjects.forEach((inject) => {
136
+ // TODO: Return only proxies
137
+ //const value = makeProxy(inject.serviceId, instance);
154
138
  let value = this.findInstance(inject.serviceId);
155
139
  if (!value) {
156
- value = this.getProxy(inject.serviceId);
140
+ value = makeProxy(inject.serviceId, instance);
157
141
  }
158
142
  inject.set(instance, value);
159
143
  });
@@ -161,56 +145,62 @@ class ProxyDI {
161
145
  instance[PROXYDI] = this;
162
146
  }
163
147
  }
164
- getProxy(serviceId) {
165
- let proxy = this.proxies[serviceId];
166
- if (!proxy) {
167
- proxy = this.proxyFactory.makeProxy(serviceId);
168
- this.proxies[serviceId] = proxy;
169
- }
170
- return proxy;
171
- }
172
- createChildContainer(settings) {
173
- const childSettings = Object.assign(Object.assign({}, settings), { parent: this });
174
- return new ProxyDI(childSettings);
148
+ createChildContainer() {
149
+ return new ProxyDI(this.settings, this);
175
150
  }
176
151
  removeInstance(serviceId) {
177
- const id = serviceId[PROXYDI_ID]
178
- ? serviceId[PROXYDI_ID]
179
- : serviceId;
180
- const instance = this.instances[id];
152
+ const id = isInstance(serviceId) ? serviceId[SERVICE_ID] : serviceId;
153
+ const instance = this.serviceInstances[id];
181
154
  if (instance) {
182
155
  const serviceInjects = instance[INJECTS] || [];
183
156
  serviceInjects.forEach((inject) => {
184
157
  inject.set(instance, undefined);
185
158
  });
186
159
  delete instance[PROXYDI];
187
- delete instance[PROXYDI_ID];
188
- delete this.instances[serviceId];
160
+ delete instance[SERVICE_ID];
161
+ delete this.serviceInstances[id];
189
162
  }
190
163
  }
191
164
  removeClass(serviceId) {
192
- delete this.classes[serviceId];
165
+ delete this.serviceClasses[serviceId];
193
166
  }
194
167
  destroy() {
195
- const allServices = Object.keys(this.instances);
168
+ const allServices = Object.keys(this.serviceInstances);
196
169
  for (const serviceId of allServices) {
197
- const instance = this.instances[serviceId];
170
+ const instance = this.serviceInstances[serviceId];
198
171
  this.removeInstance(instance);
199
172
  }
200
- this.instances = {};
201
- this.proxies = {};
202
- this.classes = {};
173
+ this.serviceInstances = {};
174
+ this.serviceClasses = {};
203
175
  for (const id of Object.keys(this.children)) {
204
176
  const child = this.children[+id];
205
177
  child.destroy();
206
178
  }
207
179
  this.children = {};
208
- this.proxyFactory = undefined;
209
180
  if (this.parent) {
210
181
  this.parent.removeChild(this.id);
211
182
  this.parent = undefined;
212
183
  }
213
184
  }
185
+ findInstance(serviceId) {
186
+ const instance = this.serviceInstances[serviceId];
187
+ if (!instance && this.parent) {
188
+ const parentInstance = this.parent.findInstance(serviceId);
189
+ // TODO: Make copy, inject container dependencies?
190
+ return parentInstance;
191
+ }
192
+ return instance;
193
+ }
194
+ findServiceClass(serviceId) {
195
+ let ServiceClass = this.serviceClasses[serviceId];
196
+ if (!ServiceClass && !!this.parent) {
197
+ return this.parent.findServiceClass(serviceId);
198
+ }
199
+ if (!ServiceClass) {
200
+ return injectableClasses[serviceId];
201
+ }
202
+ return ServiceClass;
203
+ }
214
204
  addChild(child) {
215
205
  if (this.children[child.id]) {
216
206
  throw new Error(`ProxyDI already has child with id ${child.id}`);
@@ -225,6 +215,33 @@ class ProxyDI {
225
215
  }
226
216
  }
227
217
  ProxyDI.idCounter = 0;
218
+ function isInstance(service) {
219
+ return (typeof service === 'object' &&
220
+ !!service[SERVICE_ID]);
221
+ }
222
+
223
+ const inject = (serviceId) => {
224
+ return function (_value, context) {
225
+ if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
226
+ const name = serviceId ? serviceId : context.name;
227
+ const inject = {
228
+ property: context.name,
229
+ serviceId: name,
230
+ set: context.access.set,
231
+ };
232
+ context.addInitializer(function () {
233
+ if (!this[INJECTS]) {
234
+ this[INJECTS] = [];
235
+ }
236
+ this[INJECTS].push(inject);
237
+ });
238
+ }
239
+ else {
240
+ throw new Error('@inject decorator should decorate fields');
241
+ }
242
+ };
243
+ };
228
244
 
229
245
  exports.ProxyDI = ProxyDI;
230
246
  exports.inject = inject;
247
+ exports.injectable = injectable;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { ProxyDI } from './ProxyDI';
2
2
  export { inject } from './inject';
3
+ export { injectable } from './injectable';
3
4
  export { Inject, ServiceId, ServiceClass, ProxyDISettings } from './types';
package/dist/index.js CHANGED
@@ -1,120 +1,117 @@
1
1
  const INJECTS = Symbol('injects');
2
- const inject = (serviceId) => {
3
- return function (_value, context) {
4
- if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
2
+ const PROXYDI = Symbol('ProxyDI');
3
+ const SERVICE_ID = Symbol('ServiceId');
4
+ const IS_PROXY = Symbol('isProxy');
5
+ const INSTANCE = Symbol('instance');
6
+
7
+ const injectableClasses = {};
8
+ const injectable = (serviceId) => {
9
+ return function (value, context) {
10
+ if ((context === null || context === undefined ? undefined : context.kind) === 'class') {
5
11
  const name = serviceId ? serviceId : context.name;
6
- const inject = {
7
- property: context.name,
8
- serviceId: name,
9
- set: context.access.set,
10
- };
11
- context.addInitializer(function () {
12
- if (!this[INJECTS]) {
13
- this[INJECTS] = [];
14
- }
15
- this[INJECTS].push(inject);
16
- });
12
+ if (injectableClasses[name]) {
13
+ throw new Error(`ProxyDI injectable classes already has service ID: ${name}`);
14
+ }
15
+ injectableClasses[name] = value;
16
+ }
17
+ else {
18
+ throw new Error('@injectable decorator should decorate classes');
17
19
  }
18
20
  };
19
21
  };
20
22
 
21
- const injectableClasses = {};
23
+ const DEFAULT_SETTINGS = {
24
+ allowRegisterAnythingAsInstance: false,
25
+ allowRewriteClasses: false,
26
+ allowRewriteInstances: false,
27
+ };
22
28
 
23
- const IS_PROXY = Symbol('isProxy');
24
- class ProxyFactory {
25
- constructor(container) {
26
- this.container = container;
27
- }
28
- makeProxy(serviceId) {
29
- const self = this;
30
- const proxy = new Proxy({ [IS_PROXY]: true }, {
31
- get: function (target, prop, receiver) {
32
- if (target[prop]) {
33
- return target[prop];
34
- }
35
- if (self.container.isKnown(serviceId)) {
36
- const instance = self.container.resolve(serviceId);
37
- return Reflect.get(instance, prop, receiver);
38
- }
39
- else {
40
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
41
- }
42
- },
43
- set: function (target, prop, value) {
44
- if (self.container.isKnown(serviceId)) {
45
- const instance = self.container.resolve(serviceId);
46
- return Reflect.set(instance, prop, value);
47
- }
48
- else {
49
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
50
- }
51
- },
52
- has: function (target, prop) {
53
- if (self.container.isKnown(serviceId)) {
54
- const instance = self.container.resolve(serviceId);
55
- return Reflect.has(instance, prop);
56
- }
57
- else {
58
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
59
- }
60
- },
61
- });
62
- return proxy;
29
+ var _a;
30
+ class InstanceProxy {
31
+ constructor(instance) {
32
+ this[_a] = true;
33
+ this[INSTANCE] = instance;
63
34
  }
64
35
  }
36
+ _a = IS_PROXY;
37
+ const makeProxy = (serviceId, instance) => {
38
+ return new Proxy(new InstanceProxy(instance), {
39
+ get: function (target, prop, receiver) {
40
+ if (target[prop]) {
41
+ return target[prop];
42
+ }
43
+ const container = target[INSTANCE][PROXYDI];
44
+ if (container.isKnown(serviceId)) {
45
+ const instance = container.resolve(serviceId);
46
+ return Reflect.get(instance, prop, receiver);
47
+ }
48
+ else {
49
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
50
+ }
51
+ },
52
+ set: function (target, prop, value) {
53
+ const container = target[INSTANCE][PROXYDI];
54
+ if (container.isKnown(serviceId)) {
55
+ const instance = container.resolve(serviceId);
56
+ return Reflect.set(instance, prop, value);
57
+ }
58
+ else {
59
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
60
+ }
61
+ },
62
+ has: function (target, prop) {
63
+ const container = target[INSTANCE][PROXYDI];
64
+ if (container.isKnown(serviceId)) {
65
+ const instance = container.resolve(serviceId);
66
+ return Reflect.has(instance, prop);
67
+ }
68
+ else {
69
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
70
+ }
71
+ },
72
+ });
73
+ };
65
74
 
66
- const PROXYDI = Symbol('ProxyDI');
67
- const PROXYDI_ID = Symbol('ProxyDI_ID');
68
75
  class ProxyDI {
69
- constructor(settings) {
76
+ constructor(settings, parent) {
70
77
  this.children = {};
71
- this.instances = {};
72
- this.classes = {};
73
- this.proxies = {};
74
- this.allowRewriteClasses = false;
75
- this.allowRewriteInstances = false;
76
- this.proxyFactory = new ProxyFactory(this);
78
+ this.serviceInstances = {};
79
+ this.serviceClasses = {};
77
80
  this.id = ProxyDI.idCounter++;
78
- if (settings === null || settings === undefined ? undefined : settings.parent) {
79
- this.parent = settings.parent;
81
+ if (parent) {
82
+ this.parent = parent;
80
83
  this.parent.addChild(this);
81
84
  }
82
- if ((settings === null || settings === undefined ? undefined : settings.allowRewriteClasses) !== undefined) {
83
- this.allowRewriteClasses = settings.allowRewriteClasses;
84
- }
85
- if ((settings === null || settings === undefined ? undefined : settings.allowRewriteInstances) !== undefined) {
86
- this.allowRewriteInstances = settings.allowRewriteInstances;
87
- }
85
+ this.settings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), settings);
88
86
  }
89
87
  registerInstance(serviceId, instance) {
90
- if (this.instances[serviceId]) {
91
- if (!this.allowRewriteInstances) {
88
+ if (this.serviceInstances[serviceId]) {
89
+ if (!this.settings.allowRewriteInstances) {
92
90
  throw new Error(`ProxyDI already has registered instance for ${serviceId}`);
93
91
  }
94
92
  }
93
+ const isObject = typeof instance === 'object';
94
+ if (!isObject && !this.settings.allowRegisterAnythingAsInstance) {
95
+ throw new Error(`Can't register as instance (allowRegisterAnythingAsInstance is off for this ProxyDI contatiner): ${instance}`);
96
+ }
95
97
  this.injectDependencies(instance);
96
- if (typeof instance === 'object') {
97
- instance[PROXYDI_ID] = serviceId;
98
+ if (isObject) {
99
+ instance[SERVICE_ID] = serviceId;
98
100
  }
99
- this.instances[serviceId] = instance;
101
+ this.serviceInstances[serviceId] = instance;
100
102
  }
101
103
  registerClass(serviceId, serviceClass) {
102
- if (this.classes[serviceId]) {
103
- if (!this.allowRewriteClasses) {
104
+ if (this.serviceClasses[serviceId]) {
105
+ if (!this.settings.allowRewriteClasses) {
104
106
  throw new Error(`ProxyDI already has registered class for ${serviceId}`);
105
107
  }
106
108
  }
107
- this.classes[serviceId] = serviceClass;
108
- }
109
- findInstance(serviceId) {
110
- const instance = this.instances[serviceId];
111
- if (!instance && this.parent) {
112
- return this.parent.findInstance(serviceId);
113
- }
114
- return instance;
109
+ this.serviceClasses[serviceId] = serviceClass;
115
110
  }
116
111
  isKnown(serviceId) {
117
- return !!(this.findInstance(serviceId) || this.findDefiner(serviceId));
112
+ return !!(this.findInstance(serviceId) ||
113
+ this.findServiceClass(serviceId) ||
114
+ injectableClasses[serviceId]);
118
115
  }
119
116
  resolve(serviceId) {
120
117
  if (!this.isKnown(serviceId)) {
@@ -124,34 +121,21 @@ class ProxyDI {
124
121
  if (instance) {
125
122
  return instance;
126
123
  }
127
- const definer = this.findDefiner(serviceId);
128
- if (definer) {
129
- let instance = new definer();
130
- this.injectDependencies(instance);
131
- instance[PROXYDI_ID] = serviceId;
132
- this.instances[serviceId] = instance;
133
- return instance;
134
- }
135
124
  else {
136
- return this.getProxy(serviceId);
137
- }
138
- }
139
- findDefiner(serviceId) {
140
- let definer = this.classes[serviceId];
141
- if (!definer && this.parent) {
142
- return this.parent.findDefiner(serviceId);
143
- }
144
- if (!definer) {
145
- return injectableClasses[serviceId];
125
+ const ServiceClass = this.findServiceClass(serviceId);
126
+ const newInstance = new ServiceClass();
127
+ this.registerInstance(serviceId, newInstance);
128
+ return newInstance;
146
129
  }
147
- return definer;
148
130
  }
149
131
  injectDependencies(instance) {
150
132
  const serviceInjects = instance[INJECTS] || [];
151
133
  serviceInjects.forEach((inject) => {
134
+ // TODO: Return only proxies
135
+ //const value = makeProxy(inject.serviceId, instance);
152
136
  let value = this.findInstance(inject.serviceId);
153
137
  if (!value) {
154
- value = this.getProxy(inject.serviceId);
138
+ value = makeProxy(inject.serviceId, instance);
155
139
  }
156
140
  inject.set(instance, value);
157
141
  });
@@ -159,56 +143,62 @@ class ProxyDI {
159
143
  instance[PROXYDI] = this;
160
144
  }
161
145
  }
162
- getProxy(serviceId) {
163
- let proxy = this.proxies[serviceId];
164
- if (!proxy) {
165
- proxy = this.proxyFactory.makeProxy(serviceId);
166
- this.proxies[serviceId] = proxy;
167
- }
168
- return proxy;
169
- }
170
- createChildContainer(settings) {
171
- const childSettings = Object.assign(Object.assign({}, settings), { parent: this });
172
- return new ProxyDI(childSettings);
146
+ createChildContainer() {
147
+ return new ProxyDI(this.settings, this);
173
148
  }
174
149
  removeInstance(serviceId) {
175
- const id = serviceId[PROXYDI_ID]
176
- ? serviceId[PROXYDI_ID]
177
- : serviceId;
178
- const instance = this.instances[id];
150
+ const id = isInstance(serviceId) ? serviceId[SERVICE_ID] : serviceId;
151
+ const instance = this.serviceInstances[id];
179
152
  if (instance) {
180
153
  const serviceInjects = instance[INJECTS] || [];
181
154
  serviceInjects.forEach((inject) => {
182
155
  inject.set(instance, undefined);
183
156
  });
184
157
  delete instance[PROXYDI];
185
- delete instance[PROXYDI_ID];
186
- delete this.instances[serviceId];
158
+ delete instance[SERVICE_ID];
159
+ delete this.serviceInstances[id];
187
160
  }
188
161
  }
189
162
  removeClass(serviceId) {
190
- delete this.classes[serviceId];
163
+ delete this.serviceClasses[serviceId];
191
164
  }
192
165
  destroy() {
193
- const allServices = Object.keys(this.instances);
166
+ const allServices = Object.keys(this.serviceInstances);
194
167
  for (const serviceId of allServices) {
195
- const instance = this.instances[serviceId];
168
+ const instance = this.serviceInstances[serviceId];
196
169
  this.removeInstance(instance);
197
170
  }
198
- this.instances = {};
199
- this.proxies = {};
200
- this.classes = {};
171
+ this.serviceInstances = {};
172
+ this.serviceClasses = {};
201
173
  for (const id of Object.keys(this.children)) {
202
174
  const child = this.children[+id];
203
175
  child.destroy();
204
176
  }
205
177
  this.children = {};
206
- this.proxyFactory = undefined;
207
178
  if (this.parent) {
208
179
  this.parent.removeChild(this.id);
209
180
  this.parent = undefined;
210
181
  }
211
182
  }
183
+ findInstance(serviceId) {
184
+ const instance = this.serviceInstances[serviceId];
185
+ if (!instance && this.parent) {
186
+ const parentInstance = this.parent.findInstance(serviceId);
187
+ // TODO: Make copy, inject container dependencies?
188
+ return parentInstance;
189
+ }
190
+ return instance;
191
+ }
192
+ findServiceClass(serviceId) {
193
+ let ServiceClass = this.serviceClasses[serviceId];
194
+ if (!ServiceClass && !!this.parent) {
195
+ return this.parent.findServiceClass(serviceId);
196
+ }
197
+ if (!ServiceClass) {
198
+ return injectableClasses[serviceId];
199
+ }
200
+ return ServiceClass;
201
+ }
212
202
  addChild(child) {
213
203
  if (this.children[child.id]) {
214
204
  throw new Error(`ProxyDI already has child with id ${child.id}`);
@@ -223,5 +213,31 @@ class ProxyDI {
223
213
  }
224
214
  }
225
215
  ProxyDI.idCounter = 0;
216
+ function isInstance(service) {
217
+ return (typeof service === 'object' &&
218
+ !!service[SERVICE_ID]);
219
+ }
220
+
221
+ const inject = (serviceId) => {
222
+ return function (_value, context) {
223
+ if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
224
+ const name = serviceId ? serviceId : context.name;
225
+ const inject = {
226
+ property: context.name,
227
+ serviceId: name,
228
+ set: context.access.set,
229
+ };
230
+ context.addInitializer(function () {
231
+ if (!this[INJECTS]) {
232
+ this[INJECTS] = [];
233
+ }
234
+ this[INJECTS].push(inject);
235
+ });
236
+ }
237
+ else {
238
+ throw new Error('@inject decorator should decorate fields');
239
+ }
240
+ };
241
+ };
226
242
 
227
- export { ProxyDI, inject };
243
+ export { ProxyDI, inject, injectable };
package/dist/index.umd.js CHANGED
@@ -5,122 +5,119 @@
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
7
  const INJECTS = Symbol('injects');
8
- const inject = (serviceId) => {
9
- return function (_value, context) {
10
- if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
8
+ const PROXYDI = Symbol('ProxyDI');
9
+ const SERVICE_ID = Symbol('ServiceId');
10
+ const IS_PROXY = Symbol('isProxy');
11
+ const INSTANCE = Symbol('instance');
12
+
13
+ const injectableClasses = {};
14
+ const injectable = (serviceId) => {
15
+ return function (value, context) {
16
+ if ((context === null || context === undefined ? undefined : context.kind) === 'class') {
11
17
  const name = serviceId ? serviceId : context.name;
12
- const inject = {
13
- property: context.name,
14
- serviceId: name,
15
- set: context.access.set,
16
- };
17
- context.addInitializer(function () {
18
- if (!this[INJECTS]) {
19
- this[INJECTS] = [];
20
- }
21
- this[INJECTS].push(inject);
22
- });
18
+ if (injectableClasses[name]) {
19
+ throw new Error(`ProxyDI injectable classes already has service ID: ${name}`);
20
+ }
21
+ injectableClasses[name] = value;
22
+ }
23
+ else {
24
+ throw new Error('@injectable decorator should decorate classes');
23
25
  }
24
26
  };
25
27
  };
26
28
 
27
- const injectableClasses = {};
29
+ const DEFAULT_SETTINGS = {
30
+ allowRegisterAnythingAsInstance: false,
31
+ allowRewriteClasses: false,
32
+ allowRewriteInstances: false,
33
+ };
28
34
 
29
- const IS_PROXY = Symbol('isProxy');
30
- class ProxyFactory {
31
- constructor(container) {
32
- this.container = container;
33
- }
34
- makeProxy(serviceId) {
35
- const self = this;
36
- const proxy = new Proxy({ [IS_PROXY]: true }, {
37
- get: function (target, prop, receiver) {
38
- if (target[prop]) {
39
- return target[prop];
40
- }
41
- if (self.container.isKnown(serviceId)) {
42
- const instance = self.container.resolve(serviceId);
43
- return Reflect.get(instance, prop, receiver);
44
- }
45
- else {
46
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
47
- }
48
- },
49
- set: function (target, prop, value) {
50
- if (self.container.isKnown(serviceId)) {
51
- const instance = self.container.resolve(serviceId);
52
- return Reflect.set(instance, prop, value);
53
- }
54
- else {
55
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
56
- }
57
- },
58
- has: function (target, prop) {
59
- if (self.container.isKnown(serviceId)) {
60
- const instance = self.container.resolve(serviceId);
61
- return Reflect.has(instance, prop);
62
- }
63
- else {
64
- throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
65
- }
66
- },
67
- });
68
- return proxy;
35
+ var _a;
36
+ class InstanceProxy {
37
+ constructor(instance) {
38
+ this[_a] = true;
39
+ this[INSTANCE] = instance;
69
40
  }
70
41
  }
42
+ _a = IS_PROXY;
43
+ const makeProxy = (serviceId, instance) => {
44
+ return new Proxy(new InstanceProxy(instance), {
45
+ get: function (target, prop, receiver) {
46
+ if (target[prop]) {
47
+ return target[prop];
48
+ }
49
+ const container = target[INSTANCE][PROXYDI];
50
+ if (container.isKnown(serviceId)) {
51
+ const instance = container.resolve(serviceId);
52
+ return Reflect.get(instance, prop, receiver);
53
+ }
54
+ else {
55
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
56
+ }
57
+ },
58
+ set: function (target, prop, value) {
59
+ const container = target[INSTANCE][PROXYDI];
60
+ if (container.isKnown(serviceId)) {
61
+ const instance = container.resolve(serviceId);
62
+ return Reflect.set(instance, prop, value);
63
+ }
64
+ else {
65
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
66
+ }
67
+ },
68
+ has: function (target, prop) {
69
+ const container = target[INSTANCE][PROXYDI];
70
+ if (container.isKnown(serviceId)) {
71
+ const instance = container.resolve(serviceId);
72
+ return Reflect.has(instance, prop);
73
+ }
74
+ else {
75
+ throw new Error(`Unknown ProxyDI-service: ${serviceId}`);
76
+ }
77
+ },
78
+ });
79
+ };
71
80
 
72
- const PROXYDI = Symbol('ProxyDI');
73
- const PROXYDI_ID = Symbol('ProxyDI_ID');
74
81
  class ProxyDI {
75
- constructor(settings) {
82
+ constructor(settings, parent) {
76
83
  this.children = {};
77
- this.instances = {};
78
- this.classes = {};
79
- this.proxies = {};
80
- this.allowRewriteClasses = false;
81
- this.allowRewriteInstances = false;
82
- this.proxyFactory = new ProxyFactory(this);
84
+ this.serviceInstances = {};
85
+ this.serviceClasses = {};
83
86
  this.id = ProxyDI.idCounter++;
84
- if (settings === null || settings === undefined ? undefined : settings.parent) {
85
- this.parent = settings.parent;
87
+ if (parent) {
88
+ this.parent = parent;
86
89
  this.parent.addChild(this);
87
90
  }
88
- if ((settings === null || settings === undefined ? undefined : settings.allowRewriteClasses) !== undefined) {
89
- this.allowRewriteClasses = settings.allowRewriteClasses;
90
- }
91
- if ((settings === null || settings === undefined ? undefined : settings.allowRewriteInstances) !== undefined) {
92
- this.allowRewriteInstances = settings.allowRewriteInstances;
93
- }
91
+ this.settings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), settings);
94
92
  }
95
93
  registerInstance(serviceId, instance) {
96
- if (this.instances[serviceId]) {
97
- if (!this.allowRewriteInstances) {
94
+ if (this.serviceInstances[serviceId]) {
95
+ if (!this.settings.allowRewriteInstances) {
98
96
  throw new Error(`ProxyDI already has registered instance for ${serviceId}`);
99
97
  }
100
98
  }
99
+ const isObject = typeof instance === 'object';
100
+ if (!isObject && !this.settings.allowRegisterAnythingAsInstance) {
101
+ throw new Error(`Can't register as instance (allowRegisterAnythingAsInstance is off for this ProxyDI contatiner): ${instance}`);
102
+ }
101
103
  this.injectDependencies(instance);
102
- if (typeof instance === 'object') {
103
- instance[PROXYDI_ID] = serviceId;
104
+ if (isObject) {
105
+ instance[SERVICE_ID] = serviceId;
104
106
  }
105
- this.instances[serviceId] = instance;
107
+ this.serviceInstances[serviceId] = instance;
106
108
  }
107
109
  registerClass(serviceId, serviceClass) {
108
- if (this.classes[serviceId]) {
109
- if (!this.allowRewriteClasses) {
110
+ if (this.serviceClasses[serviceId]) {
111
+ if (!this.settings.allowRewriteClasses) {
110
112
  throw new Error(`ProxyDI already has registered class for ${serviceId}`);
111
113
  }
112
114
  }
113
- this.classes[serviceId] = serviceClass;
114
- }
115
- findInstance(serviceId) {
116
- const instance = this.instances[serviceId];
117
- if (!instance && this.parent) {
118
- return this.parent.findInstance(serviceId);
119
- }
120
- return instance;
115
+ this.serviceClasses[serviceId] = serviceClass;
121
116
  }
122
117
  isKnown(serviceId) {
123
- return !!(this.findInstance(serviceId) || this.findDefiner(serviceId));
118
+ return !!(this.findInstance(serviceId) ||
119
+ this.findServiceClass(serviceId) ||
120
+ injectableClasses[serviceId]);
124
121
  }
125
122
  resolve(serviceId) {
126
123
  if (!this.isKnown(serviceId)) {
@@ -130,34 +127,21 @@
130
127
  if (instance) {
131
128
  return instance;
132
129
  }
133
- const definer = this.findDefiner(serviceId);
134
- if (definer) {
135
- let instance = new definer();
136
- this.injectDependencies(instance);
137
- instance[PROXYDI_ID] = serviceId;
138
- this.instances[serviceId] = instance;
139
- return instance;
140
- }
141
130
  else {
142
- return this.getProxy(serviceId);
143
- }
144
- }
145
- findDefiner(serviceId) {
146
- let definer = this.classes[serviceId];
147
- if (!definer && this.parent) {
148
- return this.parent.findDefiner(serviceId);
149
- }
150
- if (!definer) {
151
- return injectableClasses[serviceId];
131
+ const ServiceClass = this.findServiceClass(serviceId);
132
+ const newInstance = new ServiceClass();
133
+ this.registerInstance(serviceId, newInstance);
134
+ return newInstance;
152
135
  }
153
- return definer;
154
136
  }
155
137
  injectDependencies(instance) {
156
138
  const serviceInjects = instance[INJECTS] || [];
157
139
  serviceInjects.forEach((inject) => {
140
+ // TODO: Return only proxies
141
+ //const value = makeProxy(inject.serviceId, instance);
158
142
  let value = this.findInstance(inject.serviceId);
159
143
  if (!value) {
160
- value = this.getProxy(inject.serviceId);
144
+ value = makeProxy(inject.serviceId, instance);
161
145
  }
162
146
  inject.set(instance, value);
163
147
  });
@@ -165,56 +149,62 @@
165
149
  instance[PROXYDI] = this;
166
150
  }
167
151
  }
168
- getProxy(serviceId) {
169
- let proxy = this.proxies[serviceId];
170
- if (!proxy) {
171
- proxy = this.proxyFactory.makeProxy(serviceId);
172
- this.proxies[serviceId] = proxy;
173
- }
174
- return proxy;
175
- }
176
- createChildContainer(settings) {
177
- const childSettings = Object.assign(Object.assign({}, settings), { parent: this });
178
- return new ProxyDI(childSettings);
152
+ createChildContainer() {
153
+ return new ProxyDI(this.settings, this);
179
154
  }
180
155
  removeInstance(serviceId) {
181
- const id = serviceId[PROXYDI_ID]
182
- ? serviceId[PROXYDI_ID]
183
- : serviceId;
184
- const instance = this.instances[id];
156
+ const id = isInstance(serviceId) ? serviceId[SERVICE_ID] : serviceId;
157
+ const instance = this.serviceInstances[id];
185
158
  if (instance) {
186
159
  const serviceInjects = instance[INJECTS] || [];
187
160
  serviceInjects.forEach((inject) => {
188
161
  inject.set(instance, undefined);
189
162
  });
190
163
  delete instance[PROXYDI];
191
- delete instance[PROXYDI_ID];
192
- delete this.instances[serviceId];
164
+ delete instance[SERVICE_ID];
165
+ delete this.serviceInstances[id];
193
166
  }
194
167
  }
195
168
  removeClass(serviceId) {
196
- delete this.classes[serviceId];
169
+ delete this.serviceClasses[serviceId];
197
170
  }
198
171
  destroy() {
199
- const allServices = Object.keys(this.instances);
172
+ const allServices = Object.keys(this.serviceInstances);
200
173
  for (const serviceId of allServices) {
201
- const instance = this.instances[serviceId];
174
+ const instance = this.serviceInstances[serviceId];
202
175
  this.removeInstance(instance);
203
176
  }
204
- this.instances = {};
205
- this.proxies = {};
206
- this.classes = {};
177
+ this.serviceInstances = {};
178
+ this.serviceClasses = {};
207
179
  for (const id of Object.keys(this.children)) {
208
180
  const child = this.children[+id];
209
181
  child.destroy();
210
182
  }
211
183
  this.children = {};
212
- this.proxyFactory = undefined;
213
184
  if (this.parent) {
214
185
  this.parent.removeChild(this.id);
215
186
  this.parent = undefined;
216
187
  }
217
188
  }
189
+ findInstance(serviceId) {
190
+ const instance = this.serviceInstances[serviceId];
191
+ if (!instance && this.parent) {
192
+ const parentInstance = this.parent.findInstance(serviceId);
193
+ // TODO: Make copy, inject container dependencies?
194
+ return parentInstance;
195
+ }
196
+ return instance;
197
+ }
198
+ findServiceClass(serviceId) {
199
+ let ServiceClass = this.serviceClasses[serviceId];
200
+ if (!ServiceClass && !!this.parent) {
201
+ return this.parent.findServiceClass(serviceId);
202
+ }
203
+ if (!ServiceClass) {
204
+ return injectableClasses[serviceId];
205
+ }
206
+ return ServiceClass;
207
+ }
218
208
  addChild(child) {
219
209
  if (this.children[child.id]) {
220
210
  throw new Error(`ProxyDI already has child with id ${child.id}`);
@@ -229,8 +219,35 @@
229
219
  }
230
220
  }
231
221
  ProxyDI.idCounter = 0;
222
+ function isInstance(service) {
223
+ return (typeof service === 'object' &&
224
+ !!service[SERVICE_ID]);
225
+ }
226
+
227
+ const inject = (serviceId) => {
228
+ return function (_value, context) {
229
+ if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
230
+ const name = serviceId ? serviceId : context.name;
231
+ const inject = {
232
+ property: context.name,
233
+ serviceId: name,
234
+ set: context.access.set,
235
+ };
236
+ context.addInitializer(function () {
237
+ if (!this[INJECTS]) {
238
+ this[INJECTS] = [];
239
+ }
240
+ this[INJECTS].push(inject);
241
+ });
242
+ }
243
+ else {
244
+ throw new Error('@inject decorator should decorate fields');
245
+ }
246
+ };
247
+ };
232
248
 
233
249
  exports.ProxyDI = ProxyDI;
234
250
  exports.inject = inject;
251
+ exports.injectable = injectable;
235
252
 
236
253
  }));
package/dist/inject.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  import { ServiceId } from './types';
2
- export declare const INJECTS: unique symbol;
3
2
  export declare const inject: (serviceId?: ServiceId) => (_value: unknown, context: ClassFieldDecoratorContext) => void;
@@ -0,0 +1,3 @@
1
+ import { ProxyDISettings } from './types';
2
+ export declare const DEFAULT_SETTINGS: Required<ProxyDISettings>;
3
+ export declare const TESTS_SETTINGS: Required<ProxyDISettings>;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { ProxyDI } from './ProxyDI';
2
1
  export type ServiceId = any;
3
2
  export type ServiceConstructor<T extends unknown> = new () => T;
4
3
  export type Setter = (object: unknown, value: unknown) => void;
@@ -7,9 +6,40 @@ export type Inject = {
7
6
  serviceId: ServiceId;
8
7
  set: Setter;
9
8
  };
9
+ export type ProxyDI = {
10
+ isKnown: (serviceId: ServiceId) => boolean;
11
+ injectDependencies: (instance: any) => void;
12
+ registerInstance: <T>(serviceId: ServiceId, instance: T extends {
13
+ new (...args: any[]): any;
14
+ } ? never : T) => void;
15
+ registerClass: <T>(serviceId: ServiceId, serviceClass: ServiceClass<T>) => void;
16
+ resolve: <T>(serviceId: ServiceId) => T;
17
+ createChildContainer: () => ProxyDI;
18
+ removeInstance: (serviceId: ServiceId | ProxydiedInstance) => void;
19
+ removeClass: (serviceId: ServiceId) => void;
20
+ destroy: () => void;
21
+ };
22
+ export declare const INJECTS: unique symbol;
23
+ export declare const PROXYDI: unique symbol;
24
+ export declare const SERVICE_ID: unique symbol;
25
+ export type ReadyForProxidyInstance = {
26
+ [INJECTS]: Inject[];
27
+ };
28
+ export type InjectedInstance = ReadyForProxidyInstance & {
29
+ [PROXYDI]: ProxyDI;
30
+ };
31
+ export type ProxydiedInstance = InjectedInstance & {
32
+ [SERVICE_ID]: ServiceId;
33
+ };
10
34
  export type ServiceClass<T> = new (...args: any[]) => T;
11
35
  export type ProxyDISettings = {
12
- parent?: ProxyDI;
36
+ allowRegisterAnythingAsInstance?: boolean;
13
37
  allowRewriteClasses?: boolean;
14
38
  allowRewriteInstances?: boolean;
15
39
  };
40
+ export declare const IS_PROXY: unique symbol;
41
+ export declare const INSTANCE: unique symbol;
42
+ export type InstanceProxy = {
43
+ [IS_PROXY]: true;
44
+ [INSTANCE]: ProxydiedInstance;
45
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proxydi",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "A typed DI container that resolves circular dependencies via Proxy",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",