mesh-ioc 0.1.2 β†’ 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,78 @@ Mesh IoC solves the problem of dependency management of application services. It
13
13
  - πŸ“ πŸ₯š Tolerates circular dependencies
14
14
  - πŸ•΅οΈβ€β™€οΈ Provides APIs for dependency analysis
15
15
 
16
+ ## Quick API Cheatsheet
17
+
18
+ ```ts
19
+ // Mesh is an IoC container that stores service bindings and instantiated objects
20
+ const mesh = new Mesh('someName');
21
+
22
+ // Bindings:
23
+
24
+ // 1. Service (zero-arg constructor) to itself
25
+ mesh.service(SomeDatabase);
26
+ // 2. Abstract class to service implementation
27
+ mesh.service(Logger, SomeLogger);
28
+ // 3. String key to service (πŸ‘Ž because of limited type support)
29
+ mesh.service('Something', Something);
30
+ // 4. Service to instance
31
+ mesh.constant(SomeService, someServiceInstance);
32
+ // 5. String key to arbitrary constant
33
+ mesh.constant('SessionId', 42);
34
+ // 6. Alias (key to another key)
35
+ mesh.alias('DB', SomeDatabase);
36
+
37
+ // Methods can also be chained
38
+ mesh.service(MyFoo)
39
+ .alias(Foo, MyFoo)
40
+ .constant('Secret', 'supersecret');
41
+
42
+ // Declarative bindings in services:
43
+
44
+ class SomeDatabase {
45
+ @dep() logger!: Logger; // auto-resolved by Mesh
46
+ }
47
+
48
+ // Manual resolution:
49
+
50
+ const db = mesh.resolve(SomeDatabase);
51
+
52
+ // Connect instances:
53
+
54
+ class User {
55
+ @dep() db!: MyDatabase;
56
+ }
57
+
58
+ const user = mesh.connect(new User()); // now user.db will also be resolved by Mesh
59
+
60
+ // Scopes:
61
+
62
+ // Declare scoped bindings
63
+ mesh.scope('request')
64
+ .service(UsersRouter)
65
+ .service(OrdersRouter);
66
+
67
+ // Create and use scope
68
+ const request = mesh.createScope('request')
69
+ // Bind scope-specific data
70
+ .constant(Request, req)
71
+ .constant(Response, res);
72
+
73
+ // Scoped services can use scope-specific data
74
+ class UsersRouter {
75
+ @dep() req!: Request;
76
+ @dep() res!: Response;
77
+ }
78
+
79
+ // Middleware:
80
+ mesh.use(instance => {
81
+ // Allows modifying instances as they are created or connected
82
+ // (useful for logging, stats or replacing instances with proxies)
83
+ console.log('Instantiated', instance);
84
+ return instance;
85
+ });
86
+ ```
87
+
16
88
  ## IoC Recap
17
89
 
18
90
  [IoC](https://en.wikipedia.org/wiki/Inversion_of_control) when applied to class dependency management states that each component should not resolve their dependencies β€” instead the dependencies should be provided to each component.
@@ -80,15 +152,19 @@ class Redis {
80
152
 
81
153
  constructor() {
82
154
  this.redis = new RedisClient(/* ... */);
83
- this.redis.on('connect', () => this.logger.log('Connected to Redis'));
155
+ this.redis.on('connect', () => {
156
+ // Logger can now be used by this class transparently
157
+ this.logger.log('Connected to Redis');
158
+ });
84
159
  }
85
160
  }
86
161
 
87
162
  // app.ts
88
163
 
89
164
  class AppMesh extends Mesh {
90
- this.bind(Logger, ConsoleLogger);
91
- this.bind(Redis);
165
+ // List all services so that mesh connects them together
166
+ this.service(Redis);
167
+ this.service(Logger, ConsoleLogger);
92
168
  }
93
169
  ```
94
170
 
@@ -111,3 +187,149 @@ There are several aspects that differentiate Mesh IoC from the rest of the DI li
111
187
  - Mesh can handle circular dependencies, all thanks to on-demand resolution. However, due to how Node.js module loading works, `@dep` will be unable to infer _one_ of the service keys (depending on which module happened to load first). It's a good practice to use explicit service keys on both sides of circular dependencies.
112
188
 
113
189
  - Constant values can be bound to mesh. Those could be instances of other classes.
190
+
191
+ **Important!** Mesh should be used to track _services_. We defined services as classes with **zero-argument constructors** (this is also enforced by TypeScript). However, there are multiple patterns to support constructor arguments, read on!
192
+
193
+ ## Application Architecture Guide
194
+
195
+ This short guide briefly explains the basic concepts of a good application architecture where all components are loosely coupled, dependencies are easy to reason about and are not mixed with the actual data arguments.
196
+
197
+ 1. Identify the layers of your application. Oftentimes different components have different lifespans or, as we tend to refer to it, scopes:
198
+
199
+ - **Application scope**: things like database connection pools, servers and other global components are scoped to entire application; their instances are effectively singletons (i.e. you don't want to establish a new database connection each time you query it).
200
+ - **Request/session scope**: things like traditional HTTP routers will depend on `request` and `response` objects; the same reasoning can be applied to other scenarios, for example, web socket server may need functionality per each connected client β€”Β such components will depend on client socket.
201
+ - **Short-lived per-instance scope**: if you use "fat" classes (e.g. Active Record pattern) then each entity instances should be conceptually "connected" to the rest of the application (e.g. `instance.save()` should somehow know about the database)
202
+
203
+ 2. Build the mesh hierarchy, starting from application scope, descending into more granular scopes.
204
+
205
+ ```ts
206
+ // app.ts
207
+ export class App {
208
+ // You can either inherit from Mesh or store it as a field.
209
+ // Name parameter is optional, but can be useful for debugging.
210
+ mesh: Mesh;
211
+
212
+ constructor() {
213
+ this.mesh = new Mesh('App')
214
+ // Define your application-scoped services
215
+ .constant(App, this)
216
+ .service(Logger, GlobalLogger);
217
+ .service(MyDatabase);
218
+ .service(MyServer);
219
+ // Define your session-scoped services
220
+ .scope('session')
221
+ .service(Logger, SessionLogger)
222
+ .service(SessionScopedService);
223
+ }
224
+
225
+ start() {
226
+ // Define logic for application startup
227
+ // (e.g. connect to databases, start listening to servers, etc)
228
+ this.mesh.resolve(MyServer).listen();
229
+ }
230
+
231
+ startSession(req: Request, res: Response) {
232
+ // Bind session-specific data (e.g. request, response, client web socket, session id, etc)
233
+ const sessionMesh = this.mesh.createScope('session')
234
+ // Session-specific data can be bound by session-scoped services
235
+ .constant(Request, req)
236
+ .constant(Response, res);
237
+ // Define logic for session initialisation
238
+ sessionMesh.resolve(SessionScopedService).doStuff();
239
+ }
240
+ }
241
+ ```
242
+
243
+ 3. Create an application entrypoint (advice: never mix modules that export classes with entrypoint modules!):
244
+
245
+ ```ts
246
+ // bin/run.ts
247
+
248
+ const app = new App();
249
+ app.start();
250
+ ```
251
+
252
+ 4. Identify the proper component for session entrypoint:
253
+
254
+ ```ts
255
+ export class MyServer {
256
+ @dep() app!: App;
257
+
258
+ // The actual server (e.g. http server or web socket server)
259
+ server: Server;
260
+
261
+ constructor() {
262
+ this.server = new Server((req, res) => {
263
+ // This is the actual entrypoint of Session
264
+ app.startSession(req, res);
265
+ });
266
+ }
267
+ }
268
+ ```
269
+
270
+ 5. Use `@dep()` to transparently inject dependencies in your services:
271
+
272
+ ```ts
273
+ export class SessionScopedService {
274
+
275
+ @dep() database!: Database;
276
+ @dep() req!: Request;
277
+ @dep() res!: Response;
278
+
279
+ // ...
280
+ }
281
+ ```
282
+
283
+ 6. Come up with conventions and document them, for example:
284
+
285
+ - create different directories for services with different scopes
286
+ - separate entrypoints from the rest of the modules
287
+ - entrypoints only import, instantiate and invoke methods (think "runnable from CLI")
288
+ - all other modules only export stuff
289
+
290
+ You can take those further and adapt to your own needs. Meshes are composable and the underlying mechanics are quite simple. Start using it and you'll get a better understanding of how to adapt it to the needs of your particular case.
291
+
292
+ ## Advanced
293
+
294
+ ### Connecting "guest" instances
295
+
296
+ Mesh IoC allows connecting an arbitrary instance to the mesh, so that the `@dep` can be used in it.
297
+
298
+ For example:
299
+
300
+ ```ts
301
+ // This entity class is not managed by Mesh directly, instead it's instantiated by UserService
302
+ class User {
303
+ @dep() database!: Database;
304
+
305
+ // Note: constructor can have arbitrary arguments in this case,
306
+ // because the instantiation isn't controlled by Mesh
307
+ constructor(
308
+ public firstName = '',
309
+ public lastName = '',
310
+ public email = '',
311
+ // ...
312
+ ) {}
313
+
314
+ async save() {
315
+ await this.database.save(this);
316
+ }
317
+ }
318
+
319
+ class UserService {
320
+ // Note: Mesh is automatically available in all connected services
321
+ @dep() mesh!: Mesh;
322
+
323
+ createUser(firstName = '', lastName = '', email = '', /*...*/) {
324
+ const user = new User();
325
+ // Now connect it to mesh, so that User can access its services via `@dep`
326
+ return this.mesh.connect(user);
327
+ }
328
+ }
329
+ ```
330
+
331
+ Note: the important limitation of this approach is that `@dep` are not available in entity constructors (e.g. `database` cannot be resolved in `User` constructor, because by the time the instance is instantiated it's not yet connected to the mesh).
332
+
333
+ ## License
334
+
335
+ [ISC](https://en.wikipedia.org/wiki/ISC_license) Β© Boris Okunskiy
@@ -1,24 +1,14 @@
1
- import { Mesh } from './mesh';
2
1
  import { ServiceConstructor } from './types';
3
- export declare abstract class Binding<T> {
4
- readonly mesh: Mesh;
5
- readonly key: string;
6
- constructor(mesh: Mesh, key: string);
7
- abstract get(): T;
8
- }
9
- export declare class ConstantBinding<T> extends Binding<T> {
2
+ export declare type Binding<T> = ConstantBinding<T> | ServiceBinding<T> | AliasBinding;
3
+ export declare type ConstantBinding<T> = {
4
+ type: 'constant';
10
5
  value: T;
11
- constructor(mesh: Mesh, key: string, value: T);
12
- get(): T;
13
- }
14
- export declare class ServiceBinding<T> extends Binding<T> {
15
- readonly ctor: ServiceConstructor<T>;
16
- instance: T | undefined;
17
- constructor(mesh: Mesh, key: string, ctor: ServiceConstructor<T>);
18
- get(): T;
19
- }
20
- export declare class ProxyBinding<T> extends Binding<T> {
21
- readonly alias: string;
22
- constructor(mesh: Mesh, key: string, alias: string);
23
- get(): T;
24
- }
6
+ };
7
+ export declare type ServiceBinding<T> = {
8
+ type: 'service';
9
+ class: ServiceConstructor<T>;
10
+ };
11
+ export declare type AliasBinding = {
12
+ type: 'alias';
13
+ key: string;
14
+ };
@@ -1,45 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ProxyBinding = exports.ServiceBinding = exports.ConstantBinding = exports.Binding = void 0;
4
- class Binding {
5
- constructor(mesh, key) {
6
- this.mesh = mesh;
7
- this.key = key;
8
- }
9
- }
10
- exports.Binding = Binding;
11
- class ConstantBinding extends Binding {
12
- constructor(mesh, key, value) {
13
- super(mesh, key);
14
- this.value = value;
15
- this.mesh.connect(this.value);
16
- }
17
- get() {
18
- return this.value;
19
- }
20
- }
21
- exports.ConstantBinding = ConstantBinding;
22
- class ServiceBinding extends Binding {
23
- constructor(mesh, key, ctor) {
24
- super(mesh, key);
25
- this.ctor = ctor;
26
- }
27
- get() {
28
- if (!this.instance) {
29
- this.instance = new this.ctor();
30
- this.mesh.connect(this.instance);
31
- }
32
- return this.instance;
33
- }
34
- }
35
- exports.ServiceBinding = ServiceBinding;
36
- class ProxyBinding extends Binding {
37
- constructor(mesh, key, alias) {
38
- super(mesh, key);
39
- this.alias = alias;
40
- }
41
- get() {
42
- return this.mesh.resolve(this.alias);
43
- }
44
- }
45
- exports.ProxyBinding = ProxyBinding;
@@ -11,10 +11,10 @@ export declare class DepInstanceNotConnected extends BaseError {
11
11
  propertyName: string;
12
12
  constructor(className: string, propertyName: string);
13
13
  }
14
- export declare class MeshServiceNotFound extends BaseError {
14
+ export declare class MeshBindingNotFound extends BaseError {
15
15
  constructor(meshName: string, serviceKey: string);
16
16
  }
17
- export declare class MeshInvalidServiceBinding extends BaseError {
17
+ export declare class MeshInvalidBinding extends BaseError {
18
18
  constructor(key: string);
19
19
  }
20
20
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MeshInvalidServiceBinding = exports.MeshServiceNotFound = exports.DepInstanceNotConnected = exports.DepKeyNotInferred = void 0;
3
+ exports.MeshInvalidBinding = exports.MeshBindingNotFound = exports.DepInstanceNotConnected = exports.DepKeyNotInferred = void 0;
4
4
  class BaseError extends Error {
5
5
  constructor() {
6
6
  super(...arguments);
@@ -26,18 +26,18 @@ class DepInstanceNotConnected extends BaseError {
26
26
  }
27
27
  }
28
28
  exports.DepInstanceNotConnected = DepInstanceNotConnected;
29
- class MeshServiceNotFound extends BaseError {
29
+ class MeshBindingNotFound extends BaseError {
30
30
  constructor(meshName, serviceKey) {
31
- super(`Service "${serviceKey}" not found in Mesh "${meshName}"`);
31
+ super(`"${serviceKey}" not found in Mesh "${meshName}"`);
32
32
  }
33
33
  }
34
- exports.MeshServiceNotFound = MeshServiceNotFound;
35
- class MeshInvalidServiceBinding extends BaseError {
34
+ exports.MeshBindingNotFound = MeshBindingNotFound;
35
+ class MeshInvalidBinding extends BaseError {
36
36
  constructor(key) {
37
- super(`Invalid service binding "${key}". Valid bindings are: ` +
37
+ super(`Invalid binding "${key}". Valid bindings are: ` +
38
38
  `string to constructor e.g. ("MyService", MyService) or ` +
39
39
  `abstract class to constructor e.g. (MyService, MyServiceImpl) or` +
40
40
  `constructor to self e.g. (MyService)`);
41
41
  }
42
42
  }
43
- exports.MeshInvalidServiceBinding = MeshInvalidServiceBinding;
43
+ exports.MeshInvalidBinding = MeshInvalidBinding;
@@ -1,19 +1,25 @@
1
1
  import { Binding } from './bindings';
2
- import { AbstractService, ServiceConstructor } from './types';
2
+ import { Scope } from './scope';
3
+ import { AbstractClass, Middleware, ServiceConstructor, ServiceKey } from './types';
3
4
  export declare const MESH_REF: unique symbol;
4
- export declare type ServiceKey<T> = ServiceConstructor<T> | AbstractService<T> | string;
5
5
  export declare class Mesh {
6
6
  name: string;
7
7
  parent: Mesh | undefined;
8
- bindings: Map<string, Binding<any>>;
8
+ currentScope: Scope;
9
+ childScopes: Map<string, Scope>;
10
+ instances: Map<string, any>;
11
+ middlewares: Middleware[];
9
12
  constructor(name?: string, parent?: Mesh | undefined);
10
- bind<T>(impl: ServiceConstructor<T>): Binding<T>;
11
- bind<T>(key: AbstractService<T> | string, impl: ServiceConstructor<T>): Binding<T>;
12
- constant<T>(key: ServiceKey<T>, value: T): Binding<T>;
13
- alias<T>(key: AbstractService<T> | string, referenceKey: AbstractService<T> | string): Binding<T>;
13
+ service<T>(impl: ServiceConstructor<T>): this;
14
+ service<T>(key: AbstractClass<T> | string, impl: ServiceConstructor<T>): this;
15
+ constant<T>(key: ServiceKey<T>, value: T): this;
16
+ alias<T>(key: AbstractClass<T> | string, referenceKey: AbstractClass<T> | string): this;
14
17
  resolve<T>(key: ServiceKey<T>): T;
15
- connect(value: any): void;
16
- protected _bindService<T>(key: string, impl: ServiceConstructor<T>): Binding<T>;
17
- protected _add<T>(binding: Binding<any>): Binding<T>;
18
- protected _addMeshRef(value: any): void;
18
+ connect<T>(value: T): T;
19
+ use(fn: Middleware): this;
20
+ scope(scopeId: string): Scope;
21
+ createScope(scopeId: string, scopeName?: string): Mesh;
22
+ protected instantiate<T>(binding: Binding<T>): T;
23
+ protected applyMiddleware<T>(value: T): T;
24
+ protected injectRef(value: any): void;
19
25
  }
package/out/main/mesh.js CHANGED
@@ -1,58 +1,103 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Mesh = exports.MESH_REF = void 0;
4
- const bindings_1 = require("./bindings");
5
4
  const errors_1 = require("./errors");
5
+ const scope_1 = require("./scope");
6
+ const util_1 = require("./util");
6
7
  exports.MESH_REF = Symbol.for('MESH_REF');
7
8
  class Mesh {
8
9
  constructor(name = 'default', parent = undefined) {
9
10
  this.name = name;
10
11
  this.parent = parent;
11
- this.bindings = new Map();
12
- this.constant('Mesh', this);
12
+ this.childScopes = new Map();
13
+ this.instances = new Map();
14
+ this.middlewares = [];
15
+ this.currentScope = new scope_1.Scope(name);
16
+ this.currentScope.constant('Mesh', this);
13
17
  }
14
- bind(key, impl) {
15
- const k = keyToString(key);
16
- if (typeof impl === 'function') {
17
- return this._bindService(k, impl);
18
- }
19
- else if (typeof key === 'function') {
20
- return this._bindService(k, key);
21
- }
22
- throw new errors_1.MeshInvalidServiceBinding(String(key));
18
+ service(key, impl) {
19
+ this.currentScope.service(key, impl);
20
+ return this;
23
21
  }
24
22
  constant(key, value) {
25
- const k = keyToString(key);
26
- return this._add(new bindings_1.ConstantBinding(this, k, value));
23
+ this.currentScope.constant(key, value);
24
+ return this;
27
25
  }
28
26
  alias(key, referenceKey) {
29
- const k = keyToString(key);
30
- const refK = typeof referenceKey === 'string' ? referenceKey : referenceKey.name;
31
- return this._add(new bindings_1.ProxyBinding(this, k, refK));
27
+ this.currentScope.alias(key, referenceKey);
28
+ return this;
32
29
  }
33
30
  resolve(key) {
34
- const k = keyToString(key);
35
- const binding = this.bindings.get(k);
31
+ const k = util_1.keyToString(key);
32
+ let instance = this.instances.get(k);
33
+ if (instance) {
34
+ return instance;
35
+ }
36
+ const binding = this.currentScope.bindings.get(k);
36
37
  if (binding) {
37
- return binding.get();
38
+ instance = this.instantiate(binding);
39
+ instance = this.connect(instance);
40
+ this.instances.set(k, instance);
41
+ return instance;
38
42
  }
39
43
  if (this.parent) {
40
44
  return this.parent.resolve(key);
41
45
  }
42
- throw new errors_1.MeshServiceNotFound(this.name, k);
46
+ throw new errors_1.MeshBindingNotFound(this.name, k);
43
47
  }
44
48
  connect(value) {
45
- // TODO apply middlewares
46
- this._addMeshRef(value);
49
+ const res = this.applyMiddleware(value);
50
+ this.injectRef(res);
51
+ return res;
52
+ }
53
+ use(fn) {
54
+ this.middlewares.push(fn);
55
+ return this;
56
+ }
57
+ scope(scopeId) {
58
+ let scope = this.childScopes.get(scopeId);
59
+ if (!scope) {
60
+ scope = new scope_1.Scope(scopeId);
61
+ this.childScopes.set(scopeId, scope);
62
+ }
63
+ return scope;
47
64
  }
48
- _bindService(key, impl) {
49
- return this._add(new bindings_1.ServiceBinding(this, key, impl));
65
+ createScope(scopeId, scopeName = scopeId) {
66
+ const childScope = this.childScopes.get(scopeId);
67
+ const newScope = new scope_1.Scope(scopeName, childScope !== null && childScope !== void 0 ? childScope : []);
68
+ const mesh = new Mesh(scopeId, this);
69
+ mesh.currentScope = newScope;
70
+ return mesh;
50
71
  }
51
- _add(binding) {
52
- this.bindings.set(binding.key, binding);
53
- return new bindings_1.ProxyBinding(this, binding.key, binding.key);
72
+ instantiate(binding) {
73
+ switch (binding.type) {
74
+ case 'alias':
75
+ return this.resolve(binding.key);
76
+ case 'service': {
77
+ // A fake derived class is created with Mesh attached to its prototype.
78
+ // This allows accessing deps in constructor whilst preserving instanceof.
79
+ const ctor = binding.class;
80
+ const derived = class extends ctor {
81
+ };
82
+ Object.defineProperty(derived, 'name', { value: ctor.name });
83
+ this.injectRef(derived.prototype);
84
+ return new derived();
85
+ }
86
+ case 'constant':
87
+ return binding.value;
88
+ }
89
+ }
90
+ applyMiddleware(value) {
91
+ let res = value;
92
+ for (const middleware of this.middlewares) {
93
+ res = middleware(res);
94
+ }
95
+ if (this.parent) {
96
+ res = this.parent.applyMiddleware(res);
97
+ }
98
+ return res;
54
99
  }
55
- _addMeshRef(value) {
100
+ injectRef(value) {
56
101
  if (typeof value !== 'object') {
57
102
  return;
58
103
  }
@@ -64,6 +109,3 @@ class Mesh {
64
109
  }
65
110
  }
66
111
  exports.Mesh = Mesh;
67
- function keyToString(key) {
68
- return typeof key === 'string' ? key : key.name;
69
- }
@@ -0,0 +1,13 @@
1
+ import { Binding } from './bindings';
2
+ import { AbstractClass, ServiceConstructor, ServiceKey } from './types';
3
+ export declare const MESH_REF: unique symbol;
4
+ export declare class Scope {
5
+ readonly name: string;
6
+ bindings: Map<string, Binding<any>>;
7
+ constructor(name: string, bindings?: Iterable<[string, Binding<any>]>);
8
+ [Symbol.iterator](): Generator<[string, Binding<any>], void, undefined>;
9
+ service<T>(impl: ServiceConstructor<T>): this;
10
+ service<T>(key: AbstractClass<T> | string, impl: ServiceConstructor<T>): this;
11
+ constant<T>(key: ServiceKey<T>, value: T): this;
12
+ alias<T>(key: AbstractClass<T> | string, referenceKey: AbstractClass<T> | string): this;
13
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Scope = exports.MESH_REF = void 0;
4
+ const errors_1 = require("./errors");
5
+ const util_1 = require("./util");
6
+ exports.MESH_REF = Symbol.for('MESH_REF');
7
+ class Scope {
8
+ constructor(name, bindings = []) {
9
+ this.name = name;
10
+ this.bindings = new Map();
11
+ for (const [k, v] of bindings) {
12
+ this.bindings.set(k, v);
13
+ }
14
+ }
15
+ *[Symbol.iterator]() {
16
+ yield* this.bindings.entries();
17
+ }
18
+ service(key, impl) {
19
+ const k = util_1.keyToString(key);
20
+ if (typeof impl === 'function') {
21
+ this.bindings.set(k, { type: 'service', class: impl });
22
+ return this;
23
+ }
24
+ else if (typeof key === 'function') {
25
+ this.bindings.set(k, { type: 'service', class: key });
26
+ return this;
27
+ }
28
+ throw new errors_1.MeshInvalidBinding(String(key));
29
+ }
30
+ constant(key, value) {
31
+ const k = util_1.keyToString(key);
32
+ this.bindings.set(k, { type: 'constant', value });
33
+ return this;
34
+ }
35
+ alias(key, referenceKey) {
36
+ const k = util_1.keyToString(key);
37
+ const refK = util_1.keyToString(referenceKey);
38
+ this.bindings.set(k, { type: 'alias', key: refK });
39
+ return this;
40
+ }
41
+ }
42
+ exports.Scope = Scope;
@@ -4,7 +4,9 @@ export declare type Constructor<T> = {
4
4
  export declare type ServiceConstructor<T> = {
5
5
  new (): T;
6
6
  };
7
- export declare type AbstractService<T> = {
7
+ export declare type AbstractClass<T> = {
8
8
  name: string;
9
9
  prototype: T;
10
10
  };
11
+ export declare type ServiceKey<T> = ServiceConstructor<T> | AbstractClass<T> | string;
12
+ export declare type Middleware = (instance: any) => any;
@@ -0,0 +1,2 @@
1
+ import { ServiceKey } from './types';
2
+ export declare function keyToString<T>(key: ServiceKey<T>): string;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.keyToString = void 0;
4
+ function keyToString(key) {
5
+ return typeof key === 'string' ? key : key.name;
6
+ }
7
+ exports.keyToString = keyToString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mesh-ioc",
3
- "version": "0.1.2",
3
+ "version": "1.0.0",
4
4
  "description": "Mesh: Powerful and Lightweight IoC Library",
5
5
  "main": "out/main/index.js",
6
6
  "types": "out/main/index.d.ts",