composed-di 0.0.4-alpha → 0.0.5-alpha
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 +65 -26
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -8
- package/dist/index.js.map +1 -1
- package/dist/serviceFactory.d.ts +27 -19
- package/dist/serviceFactory.d.ts.map +1 -1
- package/dist/serviceFactory.js +40 -30
- package/dist/serviceFactory.js.map +1 -1
- package/dist/serviceKey.d.ts +1 -1
- package/dist/serviceKey.d.ts.map +1 -1
- package/dist/serviceKey.js +1 -1
- package/dist/serviceKey.js.map +1 -1
- package/dist/serviceModule.d.ts +5 -6
- package/dist/serviceModule.d.ts.map +1 -1
- package/dist/serviceModule.js +1 -1
- package/dist/serviceModule.js.map +1 -1
- package/dist/utils.d.ts +26 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +107 -0
- package/dist/utils.js.map +1 -0
- package/package.json +1 -1
- package/src/ServiceFactory.ts +83 -0
- package/src/{serviceModule.ts → ServiceModule.ts} +102 -103
- package/src/index.ts +4 -4
- package/src/utils.ts +141 -0
- package/src/main.ts +0 -19
- package/src/serviceFactory.ts +0 -78
- package/src/serviceProvider.ts +0 -5
- /package/src/{serviceKey.ts → ServiceKey.ts} +0 -0
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ A tiny, type-friendly dependency injection helper for composing services via key
|
|
|
4
4
|
|
|
5
5
|
It provides:
|
|
6
6
|
- ServiceKey<T>: a typed token used to identify a service
|
|
7
|
-
- ServiceFactory<T>: a contract to create a service (with
|
|
8
|
-
- ServiceModule: a resolver that wires factories, validates dependencies, and
|
|
7
|
+
- ServiceFactory<T>: a contract to create a service (with static methods `singleton` and `oneShot`)
|
|
8
|
+
- ServiceModule: a resolver that wires factories, validates dependencies, and provides services
|
|
9
9
|
|
|
10
10
|
ServiceModule will:
|
|
11
11
|
- detect recursive dependencies (a factory that depends on its own key)
|
|
@@ -13,7 +13,13 @@ ServiceModule will:
|
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Install from npm:
|
|
17
|
+
|
|
18
|
+
- npm: `npm install composed-di`
|
|
19
|
+
- pnpm: `pnpm add composed-di`
|
|
20
|
+
- yarn: `yarn add composed-di`
|
|
21
|
+
|
|
22
|
+
If you're working on this repo locally, build artifacts are generated under `dist/` by running:
|
|
17
23
|
|
|
18
24
|
```
|
|
19
25
|
npm run build
|
|
@@ -21,15 +27,14 @@ npm run build
|
|
|
21
27
|
|
|
22
28
|
## Usage
|
|
23
29
|
|
|
24
|
-
Below is
|
|
30
|
+
Below is a minimal example using the public API.
|
|
25
31
|
|
|
26
32
|
```ts
|
|
27
33
|
import {
|
|
28
34
|
ServiceKey,
|
|
29
35
|
ServiceModule,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from 'composed-di'; // if used locally, import from the relative path to `src/index`
|
|
36
|
+
ServiceFactory,
|
|
37
|
+
} from 'composed-di'; // when developing this repo locally, import from './src'
|
|
33
38
|
|
|
34
39
|
// 1) Define service types
|
|
35
40
|
interface Config {
|
|
@@ -53,50 +58,84 @@ const LoggerKey = new ServiceKey<Logger>('Logger');
|
|
|
53
58
|
const AppKey = new ServiceKey<App>('App');
|
|
54
59
|
|
|
55
60
|
// 3) Create factories (singleton or one-shot)
|
|
56
|
-
const configFactory =
|
|
61
|
+
const configFactory = ServiceFactory.singleton({
|
|
57
62
|
provides: ConfigKey,
|
|
58
|
-
dependsOn: []
|
|
59
|
-
|
|
63
|
+
dependsOn: [],
|
|
64
|
+
initialize: () => {
|
|
60
65
|
return { baseUrl: 'https://api.example.com' } satisfies Config;
|
|
61
66
|
},
|
|
62
67
|
});
|
|
63
68
|
|
|
64
|
-
const loggerFactory =
|
|
69
|
+
const loggerFactory = ServiceFactory.singleton({
|
|
65
70
|
provides: LoggerKey,
|
|
66
|
-
dependsOn: []
|
|
67
|
-
|
|
71
|
+
dependsOn: [],
|
|
72
|
+
initialize: () => {
|
|
68
73
|
return console as unknown as Logger;
|
|
69
74
|
},
|
|
70
75
|
});
|
|
71
76
|
|
|
72
|
-
const appFactory =
|
|
77
|
+
const appFactory = ServiceFactory.oneShot({
|
|
73
78
|
provides: AppKey,
|
|
74
|
-
dependsOn: [ConfigKey, LoggerKey]
|
|
75
|
-
|
|
79
|
+
dependsOn: [ConfigKey, LoggerKey],
|
|
80
|
+
initialize: (config, logger) => {
|
|
76
81
|
return new App(config, logger);
|
|
77
82
|
},
|
|
78
83
|
});
|
|
79
84
|
|
|
80
|
-
// 4) Compose a module
|
|
85
|
+
// 4) Compose a module (you can pass factories and/or other ServiceModule instances)
|
|
81
86
|
const module = ServiceModule.from([configFactory, loggerFactory, appFactory]);
|
|
82
87
|
|
|
83
|
-
// 5)
|
|
88
|
+
// 5) Resolve and use
|
|
84
89
|
(async () => {
|
|
85
|
-
const app = await module.
|
|
90
|
+
const app = await module.get(AppKey);
|
|
86
91
|
app.start();
|
|
87
92
|
})();
|
|
88
93
|
```
|
|
89
94
|
|
|
90
95
|
Notes:
|
|
91
|
-
-
|
|
92
|
-
- `ServiceModule.inject` resolves dependencies recursively, so factories can depend on other services.
|
|
96
|
+
- `ServiceModule.get` resolves dependencies recursively, so factories can depend on other services.
|
|
93
97
|
- If a dependency is missing or recursive, `ServiceModule` throws with a helpful error message.
|
|
94
98
|
|
|
99
|
+
## Visualizing Dependencies
|
|
100
|
+
|
|
101
|
+
The library includes utilities to generate a DOT graph representation of your service dependencies, which can be visualized using Graphviz tools.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { ServiceModule, printDotGraph, createDotGraph } from 'composed-di';
|
|
105
|
+
|
|
106
|
+
// After creating your ServiceModule
|
|
107
|
+
const module = ServiceModule.from([configFactory, loggerFactory, appFactory]);
|
|
108
|
+
|
|
109
|
+
// Option 1: Print the DOT graph to console with instructions
|
|
110
|
+
printDotGraph(module);
|
|
111
|
+
|
|
112
|
+
// Option 2: Generate DOT graph with custom options
|
|
113
|
+
const dotGraph = createDotGraph(module, {
|
|
114
|
+
direction: 'LR', // 'TB' (top-bottom), 'LR' (left-right), 'BT', 'RL'
|
|
115
|
+
title: 'My Service Dependencies',
|
|
116
|
+
highlightLeaves: true, // Highlight services with no dependencies (green)
|
|
117
|
+
highlightRoots: true, // Highlight services with no dependents (orange)
|
|
118
|
+
});
|
|
119
|
+
console.log(dotGraph);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The generated DOT notation can be visualized using:
|
|
123
|
+
- [GraphvizOnline](https://dreampuf.github.io/GraphvizOnline/)
|
|
124
|
+
- [Edotor](https://edotor.net/)
|
|
125
|
+
|
|
95
126
|
## API
|
|
96
127
|
|
|
97
|
-
- `class ServiceKey<T>(name: string)`
|
|
98
|
-
- `
|
|
99
|
-
- `
|
|
100
|
-
- `
|
|
101
|
-
- `
|
|
128
|
+
- `class ServiceKey<T>(name: string)` - Creates a typed token to identify a service
|
|
129
|
+
- `abstract class ServiceFactory<T, D extends readonly ServiceKey<unknown>[]>` with:
|
|
130
|
+
- `provides: ServiceKey<T>` - The service key this factory provides
|
|
131
|
+
- `dependsOn: D` - Array of service keys this factory depends on
|
|
132
|
+
- `initialize(...dependencies)` - Creates the service instance
|
|
133
|
+
- `dispose(instance)` - Cleans up the service instance
|
|
134
|
+
- `static singleton({ provides, dependsOn?, initialize, dispose? })` - Creates a singleton factory
|
|
135
|
+
- `static oneShot({ provides, dependsOn, initialize, dispose? })` - Creates a one-shot factory
|
|
136
|
+
- `class ServiceModule` with:
|
|
137
|
+
- `static from(factoriesOrModules)` - Creates a module from factories and/or other modules
|
|
138
|
+
- `async get(key)` - Resolves and returns a service by its key
|
|
139
|
+
- `createDotGraph(module, options?)` - Generates DOT notation graph from a ServiceModule
|
|
140
|
+
- `printDotGraph(module)` - Prints DOT graph to console with visualization instructions
|
|
102
141
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export * from './ServiceKey';
|
|
2
|
+
export * from './ServiceModule';
|
|
3
|
+
export * from './ServiceFactory';
|
|
4
|
+
export * from './utils';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Object.defineProperty(exports, "ServiceModule", { enumerable: true, get: function () { return serviceModule_1.ServiceModule; } });
|
|
8
|
-
var serviceFactory_1 = require("./serviceFactory");
|
|
9
|
-
Object.defineProperty(exports, "singletonFactory", { enumerable: true, get: function () { return serviceFactory_1.singletonFactory; } });
|
|
10
|
-
Object.defineProperty(exports, "oneShotFactory", { enumerable: true, get: function () { return serviceFactory_1.oneShotFactory; } });
|
|
17
|
+
__exportStar(require("./ServiceKey"), exports);
|
|
18
|
+
__exportStar(require("./ServiceModule"), exports);
|
|
19
|
+
__exportStar(require("./ServiceFactory"), exports);
|
|
20
|
+
__exportStar(require("./utils"), exports);
|
|
11
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC;AAChC,mDAAiC;AACjC,0CAAuB"}
|
package/dist/serviceFactory.d.ts
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
import { ServiceKey } from './
|
|
1
|
+
import { ServiceKey } from './ServiceKey';
|
|
2
2
|
type ServiceType<T> = T extends ServiceKey<infer U> ? U : never;
|
|
3
3
|
type DependencyTypes<T extends readonly ServiceKey<unknown>[]> = {
|
|
4
4
|
[K in keyof T]: ServiceType<T[K]>;
|
|
5
5
|
};
|
|
6
|
-
export
|
|
7
|
-
provides: ServiceKey<T>;
|
|
8
|
-
dependsOn: D;
|
|
9
|
-
initialize(...dependencies: DependencyTypes<D>): T | Promise<T>;
|
|
10
|
-
dispose(instance: T): void;
|
|
6
|
+
export declare abstract class ServiceFactory<const T, const D extends readonly ServiceKey<unknown>[] = []> {
|
|
7
|
+
abstract provides: ServiceKey<T>;
|
|
8
|
+
abstract dependsOn: D;
|
|
9
|
+
abstract initialize(...dependencies: DependencyTypes<D>): T | Promise<T>;
|
|
10
|
+
abstract dispose(instance: T): void;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a singleton service factory that ensures a single instance of the provided service is initialized
|
|
13
|
+
* and used throughout its lifecycle.
|
|
14
|
+
*/
|
|
15
|
+
static singleton<const T, const D extends readonly ServiceKey<unknown>[] = []>({ provides, dependsOn, initialize, dispose, }: {
|
|
16
|
+
provides: ServiceKey<T>;
|
|
17
|
+
dependsOn?: D;
|
|
18
|
+
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
19
|
+
dispose?: (instance: T) => void;
|
|
20
|
+
}): ServiceFactory<T, D>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a one-shot service factory that initializes a new instance of the provided service
|
|
23
|
+
* every time it is requested.
|
|
24
|
+
*/
|
|
25
|
+
static oneShot<const T, const D extends readonly ServiceKey<unknown>[] = []>({ provides, dependsOn, initialize, dispose, }: {
|
|
26
|
+
provides: ServiceKey<T>;
|
|
27
|
+
dependsOn: D;
|
|
28
|
+
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
29
|
+
dispose?: (instance: T) => void;
|
|
30
|
+
}): ServiceFactory<T, D>;
|
|
11
31
|
}
|
|
12
|
-
export declare function singletonFactory<T, D extends readonly ServiceKey<unknown>[] = []>({ provides, dependsOn, initialize, dispose, }: {
|
|
13
|
-
provides: ServiceKey<T>;
|
|
14
|
-
dependsOn?: D;
|
|
15
|
-
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
16
|
-
dispose?: (instance: T) => void;
|
|
17
|
-
}): ServiceFactory<T, D>;
|
|
18
|
-
export declare function oneShotFactory<T, D extends readonly ServiceKey<unknown>[] = []>({ provides, dependsOn, initialize, dispose, }: {
|
|
19
|
-
provides: ServiceKey<T>;
|
|
20
|
-
dependsOn: D;
|
|
21
|
-
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
22
|
-
dispose?: (instance: T) => void;
|
|
23
|
-
}): ServiceFactory<T, D>;
|
|
24
32
|
export {};
|
|
25
|
-
//# sourceMappingURL=
|
|
33
|
+
//# sourceMappingURL=ServiceFactory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"ServiceFactory.d.ts","sourceRoot":"","sources":["../src/ServiceFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAGhE,KAAK,eAAe,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI;KAC9D,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAClC,CAAC;AAEF,8BAAsB,cAAc,CAClC,KAAK,CAAC,CAAC,EACP,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE;IAEnD,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IAEtB,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAExE,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI;IAEnC;;;OAGG;IACH,MAAM,CAAC,SAAS,CACd,KAAK,CAAC,CAAC,EACP,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EACnD,EACA,QAAQ,EACR,SAA8B,EAC9B,UAAU,EACV,OAAkB,GACnB,EAAE;QACD,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;KACjC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;IAsBxB;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,EAC3E,QAAQ,EACR,SAAS,EACT,UAAU,EACV,OAAkB,GACnB,EAAE;QACD,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC;QACb,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;KACjC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;CAQzB"}
|
package/dist/serviceFactory.js
CHANGED
|
@@ -9,36 +9,46 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
exports.ServiceFactory = void 0;
|
|
13
|
+
class ServiceFactory {
|
|
14
|
+
/**
|
|
15
|
+
* Creates a singleton service factory that ensures a single instance of the provided service is initialized
|
|
16
|
+
* and used throughout its lifecycle.
|
|
17
|
+
*/
|
|
18
|
+
static singleton({ provides, dependsOn = [], initialize, dispose = () => { }, }) {
|
|
19
|
+
let instance;
|
|
20
|
+
return {
|
|
21
|
+
provides,
|
|
22
|
+
dependsOn,
|
|
23
|
+
initialize(...dependencies) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
if (instance) {
|
|
26
|
+
return instance;
|
|
27
|
+
}
|
|
28
|
+
instance = yield initialize(...dependencies);
|
|
22
29
|
return instance;
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
dispose(serviceInstance) {
|
|
33
|
+
if (instance === serviceInstance) {
|
|
34
|
+
dispose(serviceInstance);
|
|
35
|
+
instance = undefined;
|
|
23
36
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates a one-shot service factory that initializes a new instance of the provided service
|
|
42
|
+
* every time it is requested.
|
|
43
|
+
*/
|
|
44
|
+
static oneShot({ provides, dependsOn, initialize, dispose = () => { }, }) {
|
|
45
|
+
return {
|
|
46
|
+
provides,
|
|
47
|
+
dependsOn,
|
|
48
|
+
initialize,
|
|
49
|
+
dispose,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
35
52
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
provides,
|
|
39
|
-
dependsOn,
|
|
40
|
-
initialize,
|
|
41
|
-
dispose,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=serviceFactory.js.map
|
|
53
|
+
exports.ServiceFactory = ServiceFactory;
|
|
54
|
+
//# sourceMappingURL=ServiceFactory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"ServiceFactory.js","sourceRoot":"","sources":["../src/ServiceFactory.ts"],"names":[],"mappings":";;;;;;;;;;;;AAUA,MAAsB,cAAc;IAWlC;;;OAGG;IACH,MAAM,CAAC,SAAS,CAGd,EACA,QAAQ,EACR,SAAS,GAAG,EAAkB,EAC9B,UAAU,EACV,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,GAMnB;QACC,IAAI,QAAuB,CAAC;QAE5B,OAAO;YACL,QAAQ;YACR,SAAS;YACH,UAAU,CAAC,GAAG,YAAgC;;oBAClD,IAAI,QAAQ,EAAE,CAAC;wBACb,OAAO,QAAQ,CAAC;oBAClB,CAAC;oBACD,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,YAAY,CAAC,CAAC;oBAC7C,OAAO,QAAQ,CAAC;gBAClB,CAAC;aAAA;YACD,OAAO,CAAC,eAAkB;gBACxB,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;oBACjC,OAAO,CAAC,eAAe,CAAC,CAAC;oBACzB,QAAQ,GAAG,SAAS,CAAC;gBACvB,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,OAAO,CAA+D,EAC3E,QAAQ,EACR,SAAS,EACT,UAAU,EACV,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,GAMnB;QACC,OAAO;YACL,QAAQ;YACR,SAAS;YACT,UAAU;YACV,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AAxED,wCAwEC"}
|
package/dist/serviceKey.d.ts
CHANGED
package/dist/serviceKey.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"ServiceKey.d.ts","sourceRoot":"","sources":["../src/ServiceKey.ts"],"names":[],"mappings":"AACA,qBAAa,UAAU,CAAC,CAAC;aAGK,IAAI,EAAE,MAAM;IAFxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEJ,IAAI,EAAE,MAAM;CAGzC"}
|
package/dist/serviceKey.js
CHANGED
package/dist/serviceKey.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"ServiceKey.js","sourceRoot":"","sources":["../src/ServiceKey.ts"],"names":[],"mappings":";;;AAAA,aAAa;AACb,MAAa,UAAU;IAGrB,YAA4B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF;AAND,gCAMC"}
|
package/dist/serviceModule.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { ServiceKey } from './
|
|
2
|
-
import { ServiceFactory } from './
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
private readonly factories;
|
|
1
|
+
import { ServiceKey } from './ServiceKey';
|
|
2
|
+
import { ServiceFactory } from './ServiceFactory';
|
|
3
|
+
export declare class ServiceModule {
|
|
4
|
+
readonly factories: ServiceFactory<any, any>[];
|
|
6
5
|
constructor(factories: Set<ServiceFactory<unknown, readonly ServiceKey<unknown>[]>>);
|
|
7
6
|
get<T>(key: ServiceKey<T>): Promise<T>;
|
|
8
7
|
static from(entries: (ServiceModule | ServiceFactory<unknown, readonly ServiceKey<unknown>[]>)[]): ServiceModule;
|
|
9
8
|
}
|
|
10
|
-
//# sourceMappingURL=
|
|
9
|
+
//# sourceMappingURL=ServiceModule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"ServiceModule.d.ts","sourceRoot":"","sources":["../src/ServiceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,qBAAa,aAAa;IACxB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAM;gBAGlD,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAS5D,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBnD,MAAM,CAAC,IAAI,CACT,OAAO,EAAE,CAAC,aAAa,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,GACnF,aAAa;CAajB"}
|
package/dist/serviceModule.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"ServiceModule.js","sourceRoot":"","sources":["../src/ServiceModule.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ServiceModule } from './ServiceModule';
|
|
2
|
+
export interface DotGraphOptions {
|
|
3
|
+
/** Graph direction: 'TB' (top-bottom), 'LR' (left-right), 'BT' (bottom-top), 'RL' (right-left) */
|
|
4
|
+
direction?: 'TB' | 'LR' | 'BT' | 'RL';
|
|
5
|
+
/** Title for the graph */
|
|
6
|
+
title?: string;
|
|
7
|
+
/** Show nodes with no dependencies in a different color */
|
|
8
|
+
highlightLeaves?: boolean;
|
|
9
|
+
/** Show nodes with no dependents in a different color */
|
|
10
|
+
highlightRoots?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Generates a DOT notation graph from a ServiceModule.
|
|
14
|
+
* The output can be visualized using Graphviz tools or online viewers like:
|
|
15
|
+
* - https://dreampuf.github.io/GraphvizOnline/
|
|
16
|
+
* - https://edotor.net/
|
|
17
|
+
*
|
|
18
|
+
* Arrows point from dependencies to dependents (from what is needed to what needs it).
|
|
19
|
+
*
|
|
20
|
+
* @param module - The ServiceModule to convert to DOT notation
|
|
21
|
+
* @param options - Optional configuration for the graph appearance
|
|
22
|
+
* @returns A string containing the DOT notation graph
|
|
23
|
+
*/
|
|
24
|
+
export declare function createDotGraph(module: ServiceModule, { direction, title, highlightLeaves, highlightRoots }?: DotGraphOptions): string;
|
|
25
|
+
export declare function printDotGraph(module: ServiceModule): void;
|
|
26
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,MAAM,WAAW,eAAe;IAC9B,kGAAkG;IAClG,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACtC,0BAA0B;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,GAAE,eAKtD,GACA,MAAM,CA6FR;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,QAIlD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDotGraph = createDotGraph;
|
|
4
|
+
exports.printDotGraph = printDotGraph;
|
|
5
|
+
/**
|
|
6
|
+
* Escapes special characters in strings for DOT notation
|
|
7
|
+
*/
|
|
8
|
+
function escapeDotString(str) {
|
|
9
|
+
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generates a DOT notation graph from a ServiceModule.
|
|
13
|
+
* The output can be visualized using Graphviz tools or online viewers like:
|
|
14
|
+
* - https://dreampuf.github.io/GraphvizOnline/
|
|
15
|
+
* - https://edotor.net/
|
|
16
|
+
*
|
|
17
|
+
* Arrows point from dependencies to dependents (from what is needed to what needs it).
|
|
18
|
+
*
|
|
19
|
+
* @param module - The ServiceModule to convert to DOT notation
|
|
20
|
+
* @param options - Optional configuration for the graph appearance
|
|
21
|
+
* @returns A string containing the DOT notation graph
|
|
22
|
+
*/
|
|
23
|
+
function createDotGraph(module, { direction, title, highlightLeaves, highlightRoots } = {
|
|
24
|
+
direction: 'BT',
|
|
25
|
+
title: 'Service Dependency Graph',
|
|
26
|
+
highlightLeaves: true,
|
|
27
|
+
highlightRoots: true,
|
|
28
|
+
}) {
|
|
29
|
+
const factories = module.factories;
|
|
30
|
+
const lines = [];
|
|
31
|
+
// Start the digraph
|
|
32
|
+
lines.push('digraph ServiceDependencies {');
|
|
33
|
+
lines.push(` label="${title}";`);
|
|
34
|
+
lines.push(' labelloc="t";');
|
|
35
|
+
lines.push(' fontsize=16;');
|
|
36
|
+
lines.push(` rankdir=${direction};`);
|
|
37
|
+
lines.push('');
|
|
38
|
+
// Default node styling
|
|
39
|
+
lines.push(' node [');
|
|
40
|
+
lines.push(' shape=box,');
|
|
41
|
+
lines.push(' style="rounded,filled",');
|
|
42
|
+
lines.push(' fillcolor="#e1f5ff",');
|
|
43
|
+
lines.push(' color="#0288d1",');
|
|
44
|
+
lines.push(' fontname="Arial",');
|
|
45
|
+
lines.push(' fontsize=12');
|
|
46
|
+
lines.push(' ];');
|
|
47
|
+
lines.push('');
|
|
48
|
+
// Default edge styling
|
|
49
|
+
lines.push(' edge [');
|
|
50
|
+
lines.push(' color="#666666",');
|
|
51
|
+
lines.push(' arrowsize=0.8');
|
|
52
|
+
lines.push(' ];');
|
|
53
|
+
lines.push('');
|
|
54
|
+
// Build dependency maps to identify leaves and roots
|
|
55
|
+
const hasDependencies = new Set();
|
|
56
|
+
const hasDependents = new Set();
|
|
57
|
+
factories.forEach((factory) => {
|
|
58
|
+
const serviceName = factory.provides.name;
|
|
59
|
+
if (factory.dependsOn.length > 0) {
|
|
60
|
+
hasDependencies.add(serviceName);
|
|
61
|
+
}
|
|
62
|
+
factory.dependsOn.forEach((dependency) => {
|
|
63
|
+
hasDependents.add(dependency.name);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
// Define nodes with special styling for leaves and roots
|
|
67
|
+
const nodeIds = new Map();
|
|
68
|
+
let nodeCounter = 0;
|
|
69
|
+
factories.forEach((factory) => {
|
|
70
|
+
const serviceName = factory.provides.name;
|
|
71
|
+
const nodeId = `node${nodeCounter++}`;
|
|
72
|
+
nodeIds.set(serviceName, nodeId);
|
|
73
|
+
const isLeaf = !hasDependencies.has(serviceName);
|
|
74
|
+
const isRoot = !hasDependents.has(serviceName);
|
|
75
|
+
let nodeStyle = '';
|
|
76
|
+
if (highlightLeaves && isLeaf) {
|
|
77
|
+
nodeStyle = ' [fillcolor="#c8e6c9", color="#388e3c"]';
|
|
78
|
+
}
|
|
79
|
+
else if (highlightRoots && isRoot) {
|
|
80
|
+
nodeStyle = ' [fillcolor="#ffccbc", color="#d84315"]';
|
|
81
|
+
}
|
|
82
|
+
lines.push(` ${nodeId} [label="${escapeDotString(serviceName)}"]${nodeStyle};`);
|
|
83
|
+
});
|
|
84
|
+
lines.push('');
|
|
85
|
+
// Define edges (dependencies)
|
|
86
|
+
factories.forEach((factory) => {
|
|
87
|
+
const serviceName = factory.provides.name;
|
|
88
|
+
const serviceNodeId = nodeIds.get(serviceName);
|
|
89
|
+
factory.dependsOn.forEach((dependency) => {
|
|
90
|
+
const depName = dependency.name;
|
|
91
|
+
const depNodeId = nodeIds.get(depName);
|
|
92
|
+
if (depNodeId) {
|
|
93
|
+
// Arrow points from dependency to dependent (what provides -> what needs it)
|
|
94
|
+
lines.push(` ${depNodeId} -> ${serviceNodeId};`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
// Close the digraph
|
|
99
|
+
lines.push('}');
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
function printDotGraph(module) {
|
|
103
|
+
console.log(createDotGraph(module));
|
|
104
|
+
console.log('\n\nCopy the DOT output above and paste it into:');
|
|
105
|
+
console.log('https://dreampuf.github.io/GraphvizOnline/');
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ServiceKey } from './ServiceKey';
|
|
2
|
+
|
|
3
|
+
// Helper types to extract the type from ServiceKey
|
|
4
|
+
type ServiceType<T> = T extends ServiceKey<infer U> ? U : never;
|
|
5
|
+
|
|
6
|
+
// Helper types to convert an array/tuple of ServiceKey to tuple of their types
|
|
7
|
+
type DependencyTypes<T extends readonly ServiceKey<unknown>[]> = {
|
|
8
|
+
[K in keyof T]: ServiceType<T[K]>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export abstract class ServiceFactory<
|
|
12
|
+
const T,
|
|
13
|
+
const D extends readonly ServiceKey<unknown>[] = [],
|
|
14
|
+
> {
|
|
15
|
+
abstract provides: ServiceKey<T>;
|
|
16
|
+
abstract dependsOn: D;
|
|
17
|
+
|
|
18
|
+
abstract initialize(...dependencies: DependencyTypes<D>): T | Promise<T>;
|
|
19
|
+
|
|
20
|
+
abstract dispose(instance: T): void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a singleton service factory that ensures a single instance of the provided service is initialized
|
|
24
|
+
* and used throughout its lifecycle.
|
|
25
|
+
*/
|
|
26
|
+
static singleton<
|
|
27
|
+
const T,
|
|
28
|
+
const D extends readonly ServiceKey<unknown>[] = [],
|
|
29
|
+
>({
|
|
30
|
+
provides,
|
|
31
|
+
dependsOn = [] as unknown as D,
|
|
32
|
+
initialize,
|
|
33
|
+
dispose = () => {},
|
|
34
|
+
}: {
|
|
35
|
+
provides: ServiceKey<T>;
|
|
36
|
+
dependsOn?: D;
|
|
37
|
+
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
38
|
+
dispose?: (instance: T) => void;
|
|
39
|
+
}): ServiceFactory<T, D> {
|
|
40
|
+
let instance: T | undefined;
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
provides,
|
|
44
|
+
dependsOn,
|
|
45
|
+
async initialize(...dependencies: DependencyTypes<D>): Promise<T> {
|
|
46
|
+
if (instance) {
|
|
47
|
+
return instance;
|
|
48
|
+
}
|
|
49
|
+
instance = await initialize(...dependencies);
|
|
50
|
+
return instance;
|
|
51
|
+
},
|
|
52
|
+
dispose(serviceInstance: T): void {
|
|
53
|
+
if (instance === serviceInstance) {
|
|
54
|
+
dispose(serviceInstance);
|
|
55
|
+
instance = undefined;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Creates a one-shot service factory that initializes a new instance of the provided service
|
|
63
|
+
* every time it is requested.
|
|
64
|
+
*/
|
|
65
|
+
static oneShot<const T, const D extends readonly ServiceKey<unknown>[] = []>({
|
|
66
|
+
provides,
|
|
67
|
+
dependsOn,
|
|
68
|
+
initialize,
|
|
69
|
+
dispose = () => {},
|
|
70
|
+
}: {
|
|
71
|
+
provides: ServiceKey<T>;
|
|
72
|
+
dependsOn: D;
|
|
73
|
+
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
74
|
+
dispose?: (instance: T) => void;
|
|
75
|
+
}): ServiceFactory<T, D> {
|
|
76
|
+
return {
|
|
77
|
+
provides,
|
|
78
|
+
dependsOn,
|
|
79
|
+
initialize,
|
|
80
|
+
dispose,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,103 +1,102 @@
|
|
|
1
|
-
import { ServiceKey } from './
|
|
2
|
-
import { ServiceFactory } from './
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.factories
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
1
|
+
import { ServiceKey } from './ServiceKey';
|
|
2
|
+
import { ServiceFactory } from './ServiceFactory';
|
|
3
|
+
|
|
4
|
+
export class ServiceModule {
|
|
5
|
+
readonly factories: ServiceFactory<any, any>[] = [];
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
factories: Set<ServiceFactory<unknown, readonly ServiceKey<unknown>[]>>,
|
|
9
|
+
) {
|
|
10
|
+
this.factories = Array.from(factories);
|
|
11
|
+
this.factories.forEach((factory) => {
|
|
12
|
+
checkRecursiveDependencies(factory);
|
|
13
|
+
checkMissingDependencies(factory, this.factories);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public async get<T>(key: ServiceKey<T>): Promise<T> {
|
|
18
|
+
const factory = this.factories.find((factory) => {
|
|
19
|
+
return isSuitable(key, factory);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Check if a factory to supply the requested key was not found
|
|
23
|
+
if (!factory) {
|
|
24
|
+
throw new Error(`Could not find a suitable factory for ${key.name}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Resolve all dependencies first
|
|
28
|
+
const dependencies = await Promise.all(
|
|
29
|
+
factory.dependsOn.map((dependencyKey: ServiceKey<unknown>) => {
|
|
30
|
+
return this.get(dependencyKey);
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Call the factory to retrieve the dependency
|
|
35
|
+
return factory.initialize(...dependencies);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static from(
|
|
39
|
+
entries: (ServiceModule | ServiceFactory<unknown, readonly ServiceKey<unknown>[]>)[],
|
|
40
|
+
): ServiceModule {
|
|
41
|
+
return new ServiceModule(
|
|
42
|
+
new Set(
|
|
43
|
+
entries.flatMap((e) => {
|
|
44
|
+
if (e instanceof ServiceModule) {
|
|
45
|
+
return e.factories;
|
|
46
|
+
} else {
|
|
47
|
+
return [e];
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function checkRecursiveDependencies(
|
|
56
|
+
factory: ServiceFactory<unknown, readonly ServiceKey<unknown>[]>,
|
|
57
|
+
) {
|
|
58
|
+
const recursive = factory.dependsOn.some((dependencyKey) => {
|
|
59
|
+
return dependencyKey === factory.provides;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (recursive) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
'Recursive dependency detected on: ' + factory.provides.name,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function checkMissingDependencies(
|
|
70
|
+
factory: ServiceFactory<unknown, readonly ServiceKey<unknown>[]>,
|
|
71
|
+
factories: ServiceFactory<unknown>[],
|
|
72
|
+
) {
|
|
73
|
+
const missingDependencies = factory.dependsOn.filter(
|
|
74
|
+
(dependencyKey: ServiceKey<any>) => {
|
|
75
|
+
return !isRegistered(dependencyKey, factories);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
if (missingDependencies.length === 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const dependencyList = missingDependencies
|
|
83
|
+
.map((dependencyKey) => ` -> ${dependencyKey.name}`)
|
|
84
|
+
.join('\n');
|
|
85
|
+
throw new Error(
|
|
86
|
+
`${factory.provides.name} will fail because it depends on:\n ${dependencyList}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isRegistered(
|
|
91
|
+
key: ServiceKey<unknown>,
|
|
92
|
+
factories: ServiceFactory<unknown>[],
|
|
93
|
+
) {
|
|
94
|
+
return factories.some((factory) => factory.provides === key);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isSuitable<T, D extends readonly ServiceKey<unknown>[]>(
|
|
98
|
+
key: ServiceKey<T>,
|
|
99
|
+
factory: ServiceFactory<unknown, D>,
|
|
100
|
+
): factory is ServiceFactory<T, D> {
|
|
101
|
+
return factory?.provides === key;
|
|
102
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export * from './ServiceKey';
|
|
2
|
+
export * from './ServiceModule';
|
|
3
|
+
export * from './ServiceFactory';
|
|
4
|
+
export * from './utils'
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ServiceModule } from './ServiceModule';
|
|
2
|
+
import { ServiceKey } from './ServiceKey';
|
|
3
|
+
|
|
4
|
+
export interface DotGraphOptions {
|
|
5
|
+
/** Graph direction: 'TB' (top-bottom), 'LR' (left-right), 'BT' (bottom-top), 'RL' (right-left) */
|
|
6
|
+
direction?: 'TB' | 'LR' | 'BT' | 'RL';
|
|
7
|
+
/** Title for the graph */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** Show nodes with no dependencies in a different color */
|
|
10
|
+
highlightLeaves?: boolean;
|
|
11
|
+
/** Show nodes with no dependents in a different color */
|
|
12
|
+
highlightRoots?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Escapes special characters in strings for DOT notation
|
|
17
|
+
*/
|
|
18
|
+
function escapeDotString(str: string): string {
|
|
19
|
+
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generates a DOT notation graph from a ServiceModule.
|
|
24
|
+
* The output can be visualized using Graphviz tools or online viewers like:
|
|
25
|
+
* - https://dreampuf.github.io/GraphvizOnline/
|
|
26
|
+
* - https://edotor.net/
|
|
27
|
+
*
|
|
28
|
+
* Arrows point from dependencies to dependents (from what is needed to what needs it).
|
|
29
|
+
*
|
|
30
|
+
* @param module - The ServiceModule to convert to DOT notation
|
|
31
|
+
* @param options - Optional configuration for the graph appearance
|
|
32
|
+
* @returns A string containing the DOT notation graph
|
|
33
|
+
*/
|
|
34
|
+
export function createDotGraph(
|
|
35
|
+
module: ServiceModule,
|
|
36
|
+
{ direction, title, highlightLeaves, highlightRoots }: DotGraphOptions = {
|
|
37
|
+
direction: 'BT',
|
|
38
|
+
title: 'Service Dependency Graph',
|
|
39
|
+
highlightLeaves: true,
|
|
40
|
+
highlightRoots: true,
|
|
41
|
+
},
|
|
42
|
+
): string {
|
|
43
|
+
const factories = module.factories;
|
|
44
|
+
const lines: string[] = [];
|
|
45
|
+
|
|
46
|
+
// Start the digraph
|
|
47
|
+
lines.push('digraph ServiceDependencies {');
|
|
48
|
+
lines.push(` label="${title}";`);
|
|
49
|
+
lines.push(' labelloc="t";');
|
|
50
|
+
lines.push(' fontsize=16;');
|
|
51
|
+
lines.push(` rankdir=${direction};`);
|
|
52
|
+
lines.push('');
|
|
53
|
+
|
|
54
|
+
// Default node styling
|
|
55
|
+
lines.push(' node [');
|
|
56
|
+
lines.push(' shape=box,');
|
|
57
|
+
lines.push(' style="rounded,filled",');
|
|
58
|
+
lines.push(' fillcolor="#e1f5ff",');
|
|
59
|
+
lines.push(' color="#0288d1",');
|
|
60
|
+
lines.push(' fontname="Arial",');
|
|
61
|
+
lines.push(' fontsize=12');
|
|
62
|
+
lines.push(' ];');
|
|
63
|
+
lines.push('');
|
|
64
|
+
|
|
65
|
+
// Default edge styling
|
|
66
|
+
lines.push(' edge [');
|
|
67
|
+
lines.push(' color="#666666",');
|
|
68
|
+
lines.push(' arrowsize=0.8');
|
|
69
|
+
lines.push(' ];');
|
|
70
|
+
lines.push('');
|
|
71
|
+
|
|
72
|
+
// Build dependency maps to identify leaves and roots
|
|
73
|
+
const hasDependencies = new Set<string>();
|
|
74
|
+
const hasDependents = new Set<string>();
|
|
75
|
+
|
|
76
|
+
factories.forEach((factory) => {
|
|
77
|
+
const serviceName = factory.provides.name;
|
|
78
|
+
|
|
79
|
+
if (factory.dependsOn.length > 0) {
|
|
80
|
+
hasDependencies.add(serviceName);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
factory.dependsOn.forEach((dependency: ServiceKey<unknown>) => {
|
|
84
|
+
hasDependents.add(dependency.name);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Define nodes with special styling for leaves and roots
|
|
89
|
+
const nodeIds = new Map<string, string>();
|
|
90
|
+
let nodeCounter = 0;
|
|
91
|
+
|
|
92
|
+
factories.forEach((factory) => {
|
|
93
|
+
const serviceName = factory.provides.name;
|
|
94
|
+
const nodeId = `node${nodeCounter++}`;
|
|
95
|
+
nodeIds.set(serviceName, nodeId);
|
|
96
|
+
|
|
97
|
+
const isLeaf = !hasDependencies.has(serviceName);
|
|
98
|
+
const isRoot = !hasDependents.has(serviceName);
|
|
99
|
+
|
|
100
|
+
let nodeStyle = '';
|
|
101
|
+
|
|
102
|
+
if (highlightLeaves && isLeaf) {
|
|
103
|
+
nodeStyle = ' [fillcolor="#c8e6c9", color="#388e3c"]';
|
|
104
|
+
} else if (highlightRoots && isRoot) {
|
|
105
|
+
nodeStyle = ' [fillcolor="#ffccbc", color="#d84315"]';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
lines.push(
|
|
109
|
+
` ${nodeId} [label="${escapeDotString(serviceName)}"]${nodeStyle};`,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
lines.push('');
|
|
114
|
+
|
|
115
|
+
// Define edges (dependencies)
|
|
116
|
+
factories.forEach((factory) => {
|
|
117
|
+
const serviceName = factory.provides.name;
|
|
118
|
+
const serviceNodeId = nodeIds.get(serviceName)!;
|
|
119
|
+
|
|
120
|
+
factory.dependsOn.forEach((dependency: ServiceKey<unknown>) => {
|
|
121
|
+
const depName = dependency.name;
|
|
122
|
+
const depNodeId = nodeIds.get(depName);
|
|
123
|
+
|
|
124
|
+
if (depNodeId) {
|
|
125
|
+
// Arrow points from dependency to dependent (what provides -> what needs it)
|
|
126
|
+
lines.push(` ${depNodeId} -> ${serviceNodeId};`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Close the digraph
|
|
132
|
+
lines.push('}');
|
|
133
|
+
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function printDotGraph(module: ServiceModule) {
|
|
138
|
+
console.log(createDotGraph(module));
|
|
139
|
+
console.log('\n\nCopy the DOT output above and paste it into:');
|
|
140
|
+
console.log('https://dreampuf.github.io/GraphvizOnline/');
|
|
141
|
+
}
|
package/src/main.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { ServiceKey } from './serviceKey';
|
|
2
|
-
import { singletonFactory } from './serviceFactory';
|
|
3
|
-
|
|
4
|
-
const TEST_SERVICE_KEY = new ServiceKey<TestService>('testService');
|
|
5
|
-
const FOO_KEY = new ServiceKey<string>('foo');
|
|
6
|
-
const BAR_KEY = new ServiceKey<number>('bar');
|
|
7
|
-
|
|
8
|
-
interface TestService {
|
|
9
|
-
foo: string;
|
|
10
|
-
bar: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const TestServiceFactory = singletonFactory({
|
|
14
|
-
provides: TEST_SERVICE_KEY,
|
|
15
|
-
dependsOn: [FOO_KEY, BAR_KEY] as const,
|
|
16
|
-
initialize: (foo, bar) => {
|
|
17
|
-
return { foo, bar };
|
|
18
|
-
},
|
|
19
|
-
});
|
package/src/serviceFactory.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { ServiceKey } from './serviceKey';
|
|
2
|
-
|
|
3
|
-
// Helper types to extract the type from ServiceKey
|
|
4
|
-
type ServiceType<T> = T extends ServiceKey<infer U> ? U : never;
|
|
5
|
-
|
|
6
|
-
// Helper types to convert an array/tuple of ServiceKey to tuple of their types
|
|
7
|
-
type DependencyTypes<T extends readonly ServiceKey<unknown>[]> = {
|
|
8
|
-
[K in keyof T]: ServiceType<T[K]>;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export interface ServiceFactory<
|
|
12
|
-
T,
|
|
13
|
-
D extends readonly ServiceKey<unknown>[] = [],
|
|
14
|
-
> {
|
|
15
|
-
provides: ServiceKey<T>;
|
|
16
|
-
dependsOn: D;
|
|
17
|
-
|
|
18
|
-
initialize(...dependencies: DependencyTypes<D>): T | Promise<T>;
|
|
19
|
-
|
|
20
|
-
dispose(instance: T): void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function singletonFactory<
|
|
24
|
-
T,
|
|
25
|
-
D extends readonly ServiceKey<unknown>[] = [],
|
|
26
|
-
>({
|
|
27
|
-
provides,
|
|
28
|
-
dependsOn = [] as unknown as D,
|
|
29
|
-
initialize,
|
|
30
|
-
dispose = () => {},
|
|
31
|
-
}: {
|
|
32
|
-
provides: ServiceKey<T>;
|
|
33
|
-
dependsOn?: D;
|
|
34
|
-
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
35
|
-
dispose?: (instance: T) => void;
|
|
36
|
-
}): ServiceFactory<T, D> {
|
|
37
|
-
let instance: T | undefined;
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
provides,
|
|
41
|
-
dependsOn,
|
|
42
|
-
async initialize(...dependencies: DependencyTypes<D>): Promise<T> {
|
|
43
|
-
if (instance) {
|
|
44
|
-
return instance;
|
|
45
|
-
}
|
|
46
|
-
instance = await initialize(...dependencies);
|
|
47
|
-
return instance;
|
|
48
|
-
},
|
|
49
|
-
dispose(serviceInstance: T): void {
|
|
50
|
-
if (instance === serviceInstance) {
|
|
51
|
-
dispose(serviceInstance);
|
|
52
|
-
instance = undefined;
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function oneShotFactory<
|
|
59
|
-
T,
|
|
60
|
-
D extends readonly ServiceKey<unknown>[] = [],
|
|
61
|
-
>({
|
|
62
|
-
provides,
|
|
63
|
-
dependsOn,
|
|
64
|
-
initialize,
|
|
65
|
-
dispose = () => {},
|
|
66
|
-
}: {
|
|
67
|
-
provides: ServiceKey<T>;
|
|
68
|
-
dependsOn: D;
|
|
69
|
-
initialize: (...dependencies: DependencyTypes<D>) => T | Promise<T>;
|
|
70
|
-
dispose?: (instance: T) => void;
|
|
71
|
-
}): ServiceFactory<T, D> {
|
|
72
|
-
return {
|
|
73
|
-
provides,
|
|
74
|
-
dependsOn,
|
|
75
|
-
initialize,
|
|
76
|
-
dispose,
|
|
77
|
-
};
|
|
78
|
-
}
|
package/src/serviceProvider.ts
DELETED
|
File without changes
|