mesh-ioc 1.3.0 β 2.1.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 +166 -107
- package/out/main/index.d.ts +0 -1
- package/out/main/index.js +0 -1
- package/out/main/mesh.d.ts +17 -8
- package/out/main/mesh.js +43 -28
- package/package.json +1 -1
- package/out/main/decorators/service.d.ts +0 -7
- package/out/main/decorators/service.js +0 -10
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Powerful and lightweight alternative to Dependency Injection (DI) solutions like [Inversify](https://inversify.io/).
|
|
4
4
|
|
|
5
|
-
Mesh IoC solves the problem of dependency management of application services. It wires
|
|
5
|
+
Mesh IoC solves the problem of dependency management of application services. It wires application services togethert (i.e. singletons instantiated once and scoped to an entire application) and contextual services (e.g. services scoped to a particular HTTP request, WebSocket connection, etc.)
|
|
6
6
|
|
|
7
7
|
## Key features
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ 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
|
-
##
|
|
16
|
+
## API Cheatsheet
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
// Mesh is an IoC container that stores service bindings and instantiated objects
|
|
@@ -57,25 +57,6 @@ class User {
|
|
|
57
57
|
|
|
58
58
|
const user = mesh.connect(new User()); // now user.db will also be resolved by Mesh
|
|
59
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
60
|
// Middleware:
|
|
80
61
|
mesh.use(instance => {
|
|
81
62
|
// Allows modifying instances as they are created or connected
|
|
@@ -161,16 +142,20 @@ class Redis {
|
|
|
161
142
|
|
|
162
143
|
// app.ts
|
|
163
144
|
|
|
164
|
-
class
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
145
|
+
class App {
|
|
146
|
+
mesh = new Mesh('App'); // Optional string identifier helps with debugging resolution problems
|
|
147
|
+
|
|
148
|
+
constructor() {
|
|
149
|
+
// List all services so that mesh connects them together
|
|
150
|
+
this.mesh.service(Redis);
|
|
151
|
+
this.mesh.service(Logger, ConsoleLogger);
|
|
152
|
+
}
|
|
168
153
|
}
|
|
169
154
|
```
|
|
170
155
|
|
|
171
156
|
There are several aspects that differentiate Mesh IoC from the rest of the DI libraries.
|
|
172
157
|
|
|
173
|
-
-
|
|
158
|
+
- Service instances are cached in mesh. If you only have a single mesh instance, the services will effectively be singletons. However, if multiple mesh instances are created, then each mesh will track its own service instances.
|
|
174
159
|
|
|
175
160
|
- If you're familiar with Inversify, this effectively makes all bindings `inSingletonScope`.
|
|
176
161
|
|
|
@@ -186,110 +171,176 @@ There are several aspects that differentiate Mesh IoC from the rest of the DI li
|
|
|
186
171
|
|
|
187
172
|
- 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.
|
|
188
173
|
|
|
189
|
-
- Constant values can be bound to mesh. Those could be instances of other classes.
|
|
174
|
+
- Constant values can be bound to mesh. Those could be instances of other classes or just arbitrary values bound by string keys.
|
|
190
175
|
|
|
191
|
-
**Important!** Mesh should be used to track _services_. We
|
|
176
|
+
**Important!** Mesh should be used to track _services_. We define 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
177
|
|
|
193
178
|
## Application Architecture Guide
|
|
194
179
|
|
|
195
|
-
This short guide briefly explains the basic concepts of a good application architecture where
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
}
|
|
180
|
+
This short guide briefly explains the basic concepts of a good application architecture where the components are loosely coupled, dependencies are easy to reason about and are not mixed with the actual data arguments.
|
|
181
|
+
|
|
182
|
+
### Scopes
|
|
183
|
+
|
|
184
|
+
Oftentimes different application components have different lifespans or scopes.
|
|
185
|
+
|
|
186
|
+
For example:
|
|
187
|
+
|
|
188
|
+
- **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).
|
|
189
|
+
|
|
190
|
+
- **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.
|
|
191
|
+
|
|
192
|
+
- **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).
|
|
193
|
+
|
|
194
|
+
In the following sections we'll explore the best practices to organise the application components and to achieve a clear demarcation of scopes.
|
|
195
|
+
|
|
196
|
+
### Composition Root and Entrypoint
|
|
197
|
+
|
|
198
|
+
The term "app" is often very ambiguous when it comes to module organisation. For example, most http backend frameworks consider an "app" to be component responsible for routing and listening to HTTP requests, whereas most frontend frameworks would consider an "app" to be the root component with some setup.
|
|
199
|
+
|
|
200
|
+
It is also a common practice to mix the "app" with the actual entrypoint, i.e. the app module would be an equivalent of the "main" function that gets executed as soon as the script is parsed. This later results in problems with unit testing (i.e. the test runtime cannot use the "app" part separately from the "entrypoint" which typically results in the decisions like "unit test applications by sending HTTP requests to them").
|
|
201
|
+
|
|
202
|
+
With IoC the "app" term gets a well-defined meaning: `App` is a composition root, a "centralized registry" of application-scoped components.
|
|
203
|
+
|
|
204
|
+
For application scope one must make sure that only a single `mesh` instance is maintained throughout a lifecycle:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
// src/main/app.ts
|
|
208
|
+
export class App {
|
|
209
|
+
mesh = new Mesh('App');
|
|
210
|
+
|
|
211
|
+
constuctor() {
|
|
212
|
+
// Add application-scoped (singleton) bindings, e.g.
|
|
213
|
+
this.mesh.service(Logger, GlobalLogger);
|
|
214
|
+
this.mesh.service(MyDatabase);
|
|
215
|
+
this.mesh.service(MyHttpServer);
|
|
216
|
+
// ...
|
|
240
217
|
}
|
|
241
|
-
```
|
|
242
218
|
|
|
243
|
-
|
|
219
|
+
async start() {
|
|
220
|
+
// Define logic for application startup, e.g.
|
|
221
|
+
await this.mesh.resolve(MyDatabase).connect();
|
|
222
|
+
await this.mesh.resolve(MyHttpServer).listen();
|
|
223
|
+
// ...
|
|
224
|
+
}
|
|
244
225
|
|
|
245
|
-
|
|
246
|
-
|
|
226
|
+
async stop() {
|
|
227
|
+
// Define logic for application shutdown, e.g.
|
|
228
|
+
await this.mesh.resolve(MyHttpServer).close();
|
|
229
|
+
await this.mesh.resolve(MyDatabase).close();
|
|
230
|
+
// ...
|
|
231
|
+
}
|
|
247
232
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
```
|
|
233
|
+
}
|
|
234
|
+
```
|
|
251
235
|
|
|
252
|
-
|
|
236
|
+
The entrypoint would be an actual "main" function that instantiates the `App` class and calls its `start` method:
|
|
253
237
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
238
|
+
```ts
|
|
239
|
+
// src/bin/serve.ts
|
|
240
|
+
import { App } from '../main/app.js';
|
|
241
|
+
|
|
242
|
+
const app = new App();
|
|
243
|
+
app.start()
|
|
244
|
+
.catch(err => {
|
|
245
|
+
// Setup app initialization error handling
|
|
246
|
+
process.exit(1);
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Test runtime setup
|
|
251
|
+
|
|
252
|
+
Test runtime can create a fresh `App` instance on every test case, e.g.:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
// src/test/runtime.ts
|
|
256
|
+
import { App } from '../main/app.js';
|
|
257
257
|
|
|
258
|
-
|
|
259
|
-
server: Server;
|
|
258
|
+
let app: TestApp;
|
|
260
259
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
260
|
+
beforeEach(() => {
|
|
261
|
+
app = new TestApp();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
export class TestApp extends App {
|
|
265
|
+
|
|
266
|
+
constructor() {
|
|
267
|
+
super();
|
|
268
|
+
// Setup test application
|
|
269
|
+
// e.g. one can substitute real components with mocks
|
|
270
|
+
this.mesh.service(ThirdPartyServiceMock);
|
|
271
|
+
this.mesh.alias(ThirdPartyService, ThirdPartyServiceMock);
|
|
272
|
+
// ...
|
|
267
273
|
}
|
|
268
|
-
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Each test would then be able to access `app` and re-bind any of the dependencies without affecting other test cases.
|
|
269
278
|
|
|
270
|
-
|
|
279
|
+
### Creating request/response scopes
|
|
271
280
|
|
|
272
|
-
|
|
273
|
-
export class SessionScopedService {
|
|
281
|
+
Mesh IoC opts for maximum simplicity by following a straighforward model: each mesh instance is a scope.
|
|
274
282
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
Therefore, to create a scope, say, per HTTP request we need to simply create a mesh instance inside the HTTP handler. It's also a good idea to keep the logic of creating HTTP-scoped mesh inside the composition root, so that it can be overridden in tests.
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
// src/main/app.ts
|
|
278
287
|
|
|
288
|
+
export class App {
|
|
289
|
+
mesh = new Mesh('App');
|
|
290
|
+
|
|
291
|
+
constructor() {
|
|
279
292
|
// ...
|
|
293
|
+
this.mesh.service(MyServer);
|
|
294
|
+
// Bind the function that creates a scoped mesh, so that the server could use it.
|
|
295
|
+
this.mesh.constant('createRequestScope', (req: Request, res: Response) => this.createRequestScope());
|
|
280
296
|
}
|
|
281
|
-
```
|
|
282
297
|
|
|
283
|
-
|
|
298
|
+
protected createRequestScope(req: Request, res: Response) {
|
|
299
|
+
const mesh = new Mesh('Request');
|
|
300
|
+
// Allow request-scoped classes to also resolve app-scoped dependencies
|
|
301
|
+
mesh.parent = this.mesh;
|
|
302
|
+
// Scoped variables (req, res) can be bound as constants
|
|
303
|
+
mesh.constant(Request, req);
|
|
304
|
+
mesh.constant(Response, res);
|
|
305
|
+
// Create request-scoped bindings
|
|
306
|
+
mesh.service(Router, MyRouter);
|
|
307
|
+
// ...
|
|
308
|
+
return mesh;
|
|
309
|
+
}
|
|
284
310
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Then in `MyServer`:
|
|
289
315
|
|
|
290
|
-
|
|
316
|
+
```ts
|
|
317
|
+
export class MyServer {
|
|
291
318
|
|
|
292
|
-
|
|
319
|
+
@dep({ key: 'createRequestScope' }) createRequestScope!: (req: Request, res: Response) => Mesh;
|
|
320
|
+
|
|
321
|
+
handleRequest(req: Request, res: Response) {
|
|
322
|
+
// This is an "entrypoint" of request scope
|
|
323
|
+
const mesh = this.createRequestScope(req, res);
|
|
324
|
+
// For example, let's delegate request handling to the Router
|
|
325
|
+
const router = mesh.resolve(Router);
|
|
326
|
+
router.handle();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
In our example, `Router` is a request-scoped class and thus can access `req` and `res`:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
export class MyRouter extends Router {
|
|
335
|
+
|
|
336
|
+
@dep() req!: Request;
|
|
337
|
+
@dep() res!: Response;
|
|
338
|
+
|
|
339
|
+
async handle() {
|
|
340
|
+
// ...
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
293
344
|
|
|
294
345
|
### Connecting "guest" instances
|
|
295
346
|
|
|
@@ -330,6 +381,14 @@ class UserService {
|
|
|
330
381
|
|
|
331
382
|
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
383
|
|
|
384
|
+
## Tips and tricks
|
|
385
|
+
|
|
386
|
+
- Use classes as service keys whenever possible, this adds an additional compile-time type check to ensure that the implementation matches the declaration.
|
|
387
|
+
|
|
388
|
+
- TypeScript `interface` cannot be a service key, because it does not exist at runtime. Use `abstract class` instead.
|
|
389
|
+
|
|
390
|
+
- Don't be too dogmatic about interface/implementation split. For example, if all class methods are its public interface, there is no point in splitting it into an "interface" and its single implementation (e.g. ` abstract Config` and `ConfigImpl`) β this provides close to zero practical value and contributes towards people disliking the OOP paradigm for its verbosity and cargo culting. Instead just bind the concrete class to itself.
|
|
391
|
+
|
|
333
392
|
## License
|
|
334
393
|
|
|
335
394
|
[ISC](https://en.wikipedia.org/wiki/ISC_license) Β© Boris Okunskiy
|
package/out/main/index.d.ts
CHANGED
package/out/main/index.js
CHANGED
|
@@ -13,5 +13,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
__exportStar(require("./decorators/dep"), exports);
|
|
14
14
|
__exportStar(require("./errors"), exports);
|
|
15
15
|
__exportStar(require("./mesh"), exports);
|
|
16
|
-
__exportStar(require("./scope"), exports);
|
|
17
16
|
__exportStar(require("./types"), exports);
|
package/out/main/mesh.d.ts
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
import { Scope } from './scope';
|
|
2
1
|
import { AbstractClass, Binding, Middleware, ServiceConstructor, ServiceKey } from './types';
|
|
3
2
|
export declare const MESH_REF: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* An IoC container.
|
|
5
|
+
*
|
|
6
|
+
* Encapsulates bindings β a map that allows to associate a _service key_ with a way to obtain an instance.
|
|
7
|
+
*
|
|
8
|
+
* Three binding types are supported via corresponding methods:
|
|
9
|
+
*
|
|
10
|
+
* - `service` β a zero-arg constructor mapping; these will be instantiated on demand and cached in this mesh
|
|
11
|
+
* - `constant` β an instance of a class (can be bound by using class as service name) or an arbitrary value bound by a string key
|
|
12
|
+
* - `alias` β a "redirect" mapping, useful in tests
|
|
13
|
+
*/
|
|
4
14
|
export declare class Mesh {
|
|
5
15
|
name: string;
|
|
6
16
|
parent: Mesh | undefined;
|
|
7
|
-
|
|
8
|
-
childScopes: Map<string, Scope>;
|
|
17
|
+
bindings: Map<string, Binding<any>>;
|
|
9
18
|
instances: Map<string, any>;
|
|
10
19
|
middlewares: Middleware[];
|
|
11
|
-
constructor(name?: string, parent?: Mesh | undefined
|
|
20
|
+
constructor(name?: string, parent?: Mesh | undefined);
|
|
21
|
+
[Symbol.iterator](): Generator<[string, Binding<any>], void, undefined>;
|
|
22
|
+
clone(): Mesh;
|
|
12
23
|
service<T>(impl: ServiceConstructor<T>): this;
|
|
13
24
|
service<T>(key: AbstractClass<T> | string, impl: ServiceConstructor<T>): this;
|
|
14
25
|
constant<T>(key: ServiceKey<T>, value: T): this;
|
|
15
26
|
alias<T>(key: AbstractClass<T> | string, referenceKey: AbstractClass<T> | string): this;
|
|
16
|
-
resolve<T>(key: ServiceKey<T
|
|
17
|
-
tryResolve<T>(key: ServiceKey<T
|
|
27
|
+
resolve<T>(key: ServiceKey<T>, recursive?: boolean): T;
|
|
28
|
+
tryResolve<T>(key: ServiceKey<T>, recursive?: boolean): T | undefined;
|
|
18
29
|
connect<T>(value: T): T;
|
|
19
30
|
use(fn: Middleware): this;
|
|
20
|
-
scope(scopeId: string): Scope;
|
|
21
|
-
createScope(scopeId: string, scopeName?: string): Mesh;
|
|
22
31
|
protected instantiate<T>(binding: Binding<T>): T;
|
|
23
32
|
protected applyMiddleware<T>(value: T): T;
|
|
24
33
|
protected injectRef(value: any): void;
|
package/out/main/mesh.js
CHANGED
|
@@ -2,53 +2,82 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Mesh = exports.MESH_REF = void 0;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
-
const scope_1 = require("./scope");
|
|
6
5
|
const util_1 = require("./util");
|
|
7
6
|
exports.MESH_REF = Symbol.for('MESH_REF');
|
|
7
|
+
/**
|
|
8
|
+
* An IoC container.
|
|
9
|
+
*
|
|
10
|
+
* Encapsulates bindings β a map that allows to associate a _service key_ with a way to obtain an instance.
|
|
11
|
+
*
|
|
12
|
+
* Three binding types are supported via corresponding methods:
|
|
13
|
+
*
|
|
14
|
+
* - `service` β a zero-arg constructor mapping; these will be instantiated on demand and cached in this mesh
|
|
15
|
+
* - `constant` β an instance of a class (can be bound by using class as service name) or an arbitrary value bound by a string key
|
|
16
|
+
* - `alias` β a "redirect" mapping, useful in tests
|
|
17
|
+
*/
|
|
8
18
|
class Mesh {
|
|
9
|
-
constructor(name = 'default', parent = undefined
|
|
19
|
+
constructor(name = 'default', parent = undefined) {
|
|
10
20
|
this.name = name;
|
|
11
21
|
this.parent = parent;
|
|
12
|
-
this.
|
|
22
|
+
this.bindings = new Map();
|
|
13
23
|
this.instances = new Map();
|
|
14
24
|
this.middlewares = [];
|
|
15
|
-
this.
|
|
16
|
-
|
|
25
|
+
this.constant(Mesh, this);
|
|
26
|
+
}
|
|
27
|
+
*[Symbol.iterator]() {
|
|
28
|
+
yield* this.bindings.entries();
|
|
29
|
+
}
|
|
30
|
+
clone() {
|
|
31
|
+
const clone = new Mesh();
|
|
32
|
+
clone.parent = this.parent;
|
|
33
|
+
clone.bindings = new Map(this.bindings);
|
|
34
|
+
return clone;
|
|
17
35
|
}
|
|
18
36
|
service(key, impl) {
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
const k = util_1.keyToString(key);
|
|
38
|
+
if (typeof impl === 'function') {
|
|
39
|
+
this.bindings.set(k, { type: 'service', class: impl });
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
else if (typeof key === 'function') {
|
|
43
|
+
this.bindings.set(k, { type: 'service', class: key });
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
throw new errors_1.MeshInvalidBinding(String(key));
|
|
21
47
|
}
|
|
22
48
|
constant(key, value) {
|
|
23
|
-
|
|
49
|
+
const k = util_1.keyToString(key);
|
|
50
|
+
this.bindings.set(k, { type: 'constant', value });
|
|
24
51
|
return this;
|
|
25
52
|
}
|
|
26
53
|
alias(key, referenceKey) {
|
|
27
|
-
|
|
54
|
+
const k = util_1.keyToString(key);
|
|
55
|
+
const refK = util_1.keyToString(referenceKey);
|
|
56
|
+
this.bindings.set(k, { type: 'alias', key: refK });
|
|
28
57
|
return this;
|
|
29
58
|
}
|
|
30
|
-
resolve(key) {
|
|
31
|
-
const instance = this.tryResolve(key);
|
|
59
|
+
resolve(key, recursive = true) {
|
|
60
|
+
const instance = this.tryResolve(key, recursive);
|
|
32
61
|
if (instance === undefined) {
|
|
33
62
|
const k = util_1.keyToString(key);
|
|
34
63
|
throw new errors_1.MeshBindingNotFound(this.name, k);
|
|
35
64
|
}
|
|
36
65
|
return instance;
|
|
37
66
|
}
|
|
38
|
-
tryResolve(key) {
|
|
67
|
+
tryResolve(key, recursive = true) {
|
|
39
68
|
const k = util_1.keyToString(key);
|
|
40
69
|
let instance = this.instances.get(k);
|
|
41
70
|
if (instance) {
|
|
42
71
|
return instance;
|
|
43
72
|
}
|
|
44
|
-
const binding = this.
|
|
73
|
+
const binding = this.bindings.get(k);
|
|
45
74
|
if (binding) {
|
|
46
75
|
instance = this.instantiate(binding);
|
|
47
76
|
instance = this.connect(instance);
|
|
48
77
|
this.instances.set(k, instance);
|
|
49
78
|
return instance;
|
|
50
79
|
}
|
|
51
|
-
if (this.parent) {
|
|
80
|
+
if (recursive && this.parent) {
|
|
52
81
|
return this.parent.tryResolve(key);
|
|
53
82
|
}
|
|
54
83
|
return undefined;
|
|
@@ -62,20 +91,6 @@ class Mesh {
|
|
|
62
91
|
this.middlewares.push(fn);
|
|
63
92
|
return this;
|
|
64
93
|
}
|
|
65
|
-
scope(scopeId) {
|
|
66
|
-
let scope = this.childScopes.get(scopeId);
|
|
67
|
-
if (!scope) {
|
|
68
|
-
scope = new scope_1.Scope(scopeId);
|
|
69
|
-
this.childScopes.set(scopeId, scope);
|
|
70
|
-
}
|
|
71
|
-
return scope;
|
|
72
|
-
}
|
|
73
|
-
createScope(scopeId, scopeName = scopeId) {
|
|
74
|
-
const childScope = this.childScopes.get(scopeId);
|
|
75
|
-
const newScope = new scope_1.Scope(scopeName, childScope !== null && childScope !== void 0 ? childScope : []);
|
|
76
|
-
const mesh = new Mesh(scopeId, this, newScope);
|
|
77
|
-
return mesh;
|
|
78
|
-
}
|
|
79
94
|
instantiate(binding) {
|
|
80
95
|
switch (binding.type) {
|
|
81
96
|
case 'alias':
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.service = exports.serviceMetadata = void 0;
|
|
4
|
-
exports.serviceMetadata = [];
|
|
5
|
-
function service(options = {}) {
|
|
6
|
-
return function (target) {
|
|
7
|
-
exports.serviceMetadata.push(Object.assign({ class: target }, options));
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
exports.service = service;
|