composed-di 0.0.1
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 +102 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/main.js +284 -0
- package/dist/serviceFactory.d.ts +25 -0
- package/dist/serviceFactory.d.ts.map +1 -0
- package/dist/serviceFactory.js +44 -0
- package/dist/serviceFactory.js.map +1 -0
- package/dist/serviceKey.d.ts +6 -0
- package/dist/serviceKey.d.ts.map +1 -0
- package/dist/serviceKey.js +12 -0
- package/dist/serviceKey.js.map +1 -0
- package/dist/serviceModule.d.ts +10 -0
- package/dist/serviceModule.d.ts.map +1 -0
- package/dist/serviceModule.js +70 -0
- package/dist/serviceModule.js.map +1 -0
- package/dist/serviceProvider.d.ts +5 -0
- package/dist/serviceProvider.d.ts.map +1 -0
- package/dist/serviceProvider.js +3 -0
- package/dist/serviceProvider.js.map +1 -0
- package/package.json +39 -0
- package/src/index.ts +4 -0
- package/src/serviceFactory.ts +69 -0
- package/src/serviceKey.ts +8 -0
- package/src/serviceModule.ts +91 -0
- package/src/serviceProvider.ts +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# composed-di
|
|
2
|
+
|
|
3
|
+
A tiny, type-friendly dependency injection helper for composing services via keys and factories.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
- ServiceKey<T>: a typed token used to identify a service
|
|
7
|
+
- ServiceFactory<T>: a contract to create a service (with helpers singletonFactory and oneShotFactory)
|
|
8
|
+
- ServiceModule: a resolver that wires factories, validates dependencies, and injects services
|
|
9
|
+
|
|
10
|
+
ServiceModule will:
|
|
11
|
+
- detect recursive dependencies (a factory that depends on its own key)
|
|
12
|
+
- detect missing dependencies (a factory that depends on keys that have no factory)
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
This repository is set up as a library. Build artifacts are generated under `dist/` by running:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Below is an example based on the previous sample `main.ts` usage pattern.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {
|
|
28
|
+
ServiceKey,
|
|
29
|
+
ServiceModule,
|
|
30
|
+
singletonFactory,
|
|
31
|
+
oneShotFactory,
|
|
32
|
+
} from 'composed-di'; // if used locally, import from the relative path to `src/index`
|
|
33
|
+
|
|
34
|
+
// 1) Define service types
|
|
35
|
+
interface Config {
|
|
36
|
+
baseUrl: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface Logger {
|
|
40
|
+
info: (msg: string) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class App {
|
|
44
|
+
constructor(private config: Config, private logger: Logger) {}
|
|
45
|
+
start() {
|
|
46
|
+
this.logger.info(`Starting with baseUrl=${this.config.baseUrl}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2) Create keys
|
|
51
|
+
const ConfigKey = new ServiceKey<Config>('Config');
|
|
52
|
+
const LoggerKey = new ServiceKey<Logger>('Logger');
|
|
53
|
+
const AppKey = new ServiceKey<App>('App');
|
|
54
|
+
|
|
55
|
+
// 3) Create factories (singleton or one-shot)
|
|
56
|
+
const configFactory = singletonFactory({
|
|
57
|
+
provides: ConfigKey,
|
|
58
|
+
dependsOn: [] as const,
|
|
59
|
+
async initialize() {
|
|
60
|
+
return { baseUrl: 'https://api.example.com' } satisfies Config;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const loggerFactory = singletonFactory({
|
|
65
|
+
provides: LoggerKey,
|
|
66
|
+
dependsOn: [] as const,
|
|
67
|
+
async initialize() {
|
|
68
|
+
return console as unknown as Logger;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const appFactory = oneShotFactory({
|
|
73
|
+
provides: AppKey,
|
|
74
|
+
dependsOn: [ConfigKey, LoggerKey] as const,
|
|
75
|
+
async initialize(config, logger) {
|
|
76
|
+
return new App(config, logger);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 4) Compose a module
|
|
81
|
+
const module = ServiceModule.from([configFactory, loggerFactory, appFactory]);
|
|
82
|
+
|
|
83
|
+
// 5) Inject and use
|
|
84
|
+
(async () => {
|
|
85
|
+
const app = await module.inject(AppKey);
|
|
86
|
+
app.start();
|
|
87
|
+
})();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Notes:
|
|
91
|
+
- Use `as const` on your `dependsOn` list to preserve tuple types and keep constructor parameters strongly typed.
|
|
92
|
+
- `ServiceModule.inject` resolves dependencies recursively, so factories can depend on other services.
|
|
93
|
+
- If a dependency is missing or recursive, `ServiceModule` throws with a helpful error message.
|
|
94
|
+
|
|
95
|
+
## API
|
|
96
|
+
|
|
97
|
+
- `class ServiceKey<T>(name: string)`
|
|
98
|
+
- `interface ServiceFactory<T, D extends ServiceKey<unknown>[]>` with `provides`, `dependsOn`, `initialize`, `dispose`
|
|
99
|
+
- `singletonFactory({ provides, dependsOn?, initialize, dispose? })`
|
|
100
|
+
- `oneShotFactory({ provides, dependsOn, initialize, dispose? })`
|
|
101
|
+
- `class ServiceModule` with `static from(factories)`, `inject(key)`
|
|
102
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ServiceKey } from './serviceKey';
|
|
2
|
+
export { ServiceModule } from './serviceModule';
|
|
3
|
+
export { type ServiceFactory, singletonFactory, oneShotFactory } from './serviceFactory';
|
|
4
|
+
export { type ServiceProvider } from './serviceProvider';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.oneShotFactory = exports.singletonFactory = exports.ServiceModule = exports.ServiceKey = void 0;
|
|
4
|
+
var serviceKey_1 = require("./serviceKey");
|
|
5
|
+
Object.defineProperty(exports, "ServiceKey", { enumerable: true, get: function () { return serviceKey_1.ServiceKey; } });
|
|
6
|
+
var serviceModule_1 = require("./serviceModule");
|
|
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; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,mDAAyF;AAA3D,kHAAA,gBAAgB,OAAA;AAAE,gHAAA,cAAc,OAAA"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// src/lib/serviceModule.ts
|
|
26
|
+
var ServiceModule = class _ServiceModule {
|
|
27
|
+
constructor(factories) {
|
|
28
|
+
this.factories = [];
|
|
29
|
+
this.factories = Array.from(factories);
|
|
30
|
+
this.factories.forEach((factory) => {
|
|
31
|
+
checkRecursiveDependencies(factory);
|
|
32
|
+
checkMissingDependencies(factory, this.factories);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async inject(key) {
|
|
36
|
+
const factory = this.factories.find((factory2) => {
|
|
37
|
+
return isSuitable(key, factory2);
|
|
38
|
+
});
|
|
39
|
+
if (!factory) {
|
|
40
|
+
throw new Error(`Could not find a suitable factory for ${key.name}`);
|
|
41
|
+
}
|
|
42
|
+
const dependencies = await Promise.all(
|
|
43
|
+
factory.dependsOn.map((dependencyKey) => {
|
|
44
|
+
return this.inject(dependencyKey);
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
return factory.initialize(...dependencies);
|
|
48
|
+
}
|
|
49
|
+
static from(factories) {
|
|
50
|
+
return new _ServiceModule(new Set(factories));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function checkRecursiveDependencies(factory) {
|
|
54
|
+
const recursive = factory.dependsOn.some((dependencyKey) => {
|
|
55
|
+
return dependencyKey === factory.provides;
|
|
56
|
+
});
|
|
57
|
+
if (recursive) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"Recursive dependency detected on: " + factory.provides.name
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function checkMissingDependencies(factory, factories) {
|
|
64
|
+
const missingDependencies = factory.dependsOn.filter(
|
|
65
|
+
(dependencyKey) => {
|
|
66
|
+
return !isRegistered(dependencyKey, factories);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
if (missingDependencies.length === 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const dependencyList = missingDependencies.map((dependencyKey) => ` -> ${dependencyKey.name}`).join("\n");
|
|
73
|
+
throw new Error(
|
|
74
|
+
`${factory.provides.name} will fail because it depends on:
|
|
75
|
+
${dependencyList}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function isRegistered(key, factories) {
|
|
79
|
+
return factories.some((factory) => factory.provides === key);
|
|
80
|
+
}
|
|
81
|
+
function isSuitable(key, factory) {
|
|
82
|
+
return factory?.provides === key;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/lib/serviceKey.ts
|
|
86
|
+
var ServiceKey = class {
|
|
87
|
+
constructor(name) {
|
|
88
|
+
this.name = name;
|
|
89
|
+
this.symbol = Symbol(name);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/lib/serviceFactory.ts
|
|
94
|
+
function singletonFactory({
|
|
95
|
+
provides,
|
|
96
|
+
dependsOn = [],
|
|
97
|
+
initialize,
|
|
98
|
+
dispose = () => {
|
|
99
|
+
}
|
|
100
|
+
}) {
|
|
101
|
+
let instance;
|
|
102
|
+
return {
|
|
103
|
+
provides,
|
|
104
|
+
dependsOn,
|
|
105
|
+
async initialize(...dependencies) {
|
|
106
|
+
if (instance) {
|
|
107
|
+
return instance;
|
|
108
|
+
}
|
|
109
|
+
instance = await initialize(...dependencies);
|
|
110
|
+
return instance;
|
|
111
|
+
},
|
|
112
|
+
dispose(serviceInstance) {
|
|
113
|
+
if (instance === serviceInstance) {
|
|
114
|
+
dispose(serviceInstance);
|
|
115
|
+
instance = void 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/core/sms/di/smsModule.ts
|
|
122
|
+
var import_node_process = __toESM(require("node:process"));
|
|
123
|
+
|
|
124
|
+
// src/core/sms/noOpSmsService.ts
|
|
125
|
+
var NoOpSmsService = class {
|
|
126
|
+
send(recipient, text) {
|
|
127
|
+
console.warn(`NoOpSmsService: Sending sms to ${recipient}: ${text}`);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/core/sms/tigoSmsService.ts
|
|
132
|
+
var TigoSmsService = class {
|
|
133
|
+
send(_recipient, _text) {
|
|
134
|
+
throw new Error("Not yet implemented");
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// src/core/logger/emailAlertLogger.ts
|
|
139
|
+
var EmailAlertLogger = class {
|
|
140
|
+
constructor(bufferSize = 50, recipients = []) {
|
|
141
|
+
this.buffer = [];
|
|
142
|
+
this.emailQueue = [];
|
|
143
|
+
this.bufferSize = bufferSize;
|
|
144
|
+
this.recipients = recipients;
|
|
145
|
+
}
|
|
146
|
+
addToBuffer(event) {
|
|
147
|
+
if (this.buffer.length >= this.bufferSize) {
|
|
148
|
+
this.buffer.shift();
|
|
149
|
+
}
|
|
150
|
+
this.buffer.push({ ...event, date: /* @__PURE__ */ new Date() });
|
|
151
|
+
}
|
|
152
|
+
info(message, payload) {
|
|
153
|
+
this.addToBuffer({ level: "INFO", message, payload });
|
|
154
|
+
}
|
|
155
|
+
error(message, error, payload) {
|
|
156
|
+
this.addToBuffer({ level: "ERROR", message, payload });
|
|
157
|
+
this.emailQueue.push(createTableString(this.buffer, error));
|
|
158
|
+
this.buffer.length = 0;
|
|
159
|
+
}
|
|
160
|
+
debug(message, payload) {
|
|
161
|
+
this.addToBuffer({ level: "DEBUG", message, payload });
|
|
162
|
+
}
|
|
163
|
+
async flush() {
|
|
164
|
+
for (const email of this.emailQueue) {
|
|
165
|
+
console.error(email);
|
|
166
|
+
}
|
|
167
|
+
this.buffer.length = 0;
|
|
168
|
+
this.emailQueue.length = 0;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
function createTableString(events, error) {
|
|
172
|
+
if (events.length === 0) {
|
|
173
|
+
return "No events to display";
|
|
174
|
+
}
|
|
175
|
+
const dateWidth = Math.max(
|
|
176
|
+
4,
|
|
177
|
+
...events.map((e) => e.date.toISOString().length)
|
|
178
|
+
);
|
|
179
|
+
const levelWidth = Math.max(
|
|
180
|
+
5,
|
|
181
|
+
...events.map((e) => e.level.length)
|
|
182
|
+
);
|
|
183
|
+
const messageWidth = Math.max(7, ...events.map((e) => e.message.length));
|
|
184
|
+
const payloadWidth = Math.max(
|
|
185
|
+
7,
|
|
186
|
+
...events.map((e) => e.payload ? JSON.stringify(e.payload).length : 0)
|
|
187
|
+
);
|
|
188
|
+
const header = [
|
|
189
|
+
"DATE".padEnd(dateWidth),
|
|
190
|
+
"LEVEL".padEnd(levelWidth),
|
|
191
|
+
"MESSAGE".padEnd(messageWidth),
|
|
192
|
+
"PAYLOAD".padEnd(payloadWidth)
|
|
193
|
+
].join(" | ");
|
|
194
|
+
const separator = [
|
|
195
|
+
"-".repeat(dateWidth),
|
|
196
|
+
"-".repeat(levelWidth),
|
|
197
|
+
"-".repeat(messageWidth),
|
|
198
|
+
"-".repeat(payloadWidth)
|
|
199
|
+
].join("-|-");
|
|
200
|
+
const rows = events.map((event) => {
|
|
201
|
+
const date = event.date.toISOString().padEnd(dateWidth);
|
|
202
|
+
const level = event.level.padEnd(levelWidth);
|
|
203
|
+
const message = event.message.padEnd(messageWidth);
|
|
204
|
+
const payload = (event.payload ? JSON.stringify(event.payload) : "").padEnd(
|
|
205
|
+
payloadWidth
|
|
206
|
+
);
|
|
207
|
+
return [date, level, message, payload].join(" | ");
|
|
208
|
+
});
|
|
209
|
+
const errorSection = error ? "\n\nError Details:\n" + "-".repeat(20) + "\n" + (error instanceof Error ? `${error.name}: ${error.message}
|
|
210
|
+
${error.stack || ""}` : String(error)) : "";
|
|
211
|
+
return [header, separator, ...rows].join("\n") + errorSection;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/core/logger/di/loggerModule.ts
|
|
215
|
+
var LOGGER = new ServiceKey("Logger");
|
|
216
|
+
var LoggerFactory = singletonFactory({
|
|
217
|
+
provides: LOGGER,
|
|
218
|
+
dependsOn: [],
|
|
219
|
+
initialize: () => {
|
|
220
|
+
return Promise.resolve(new EmailAlertLogger(100, ["juanhr454@gmail.com"]));
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// src/core/sms/di/smsModule.ts
|
|
225
|
+
var SMS_SERVICE = new ServiceKey("SmsService");
|
|
226
|
+
var SmsServiceFactory = singletonFactory({
|
|
227
|
+
provides: SMS_SERVICE,
|
|
228
|
+
dependsOn: [LOGGER],
|
|
229
|
+
initialize(logger) {
|
|
230
|
+
logger.info("Initializing SmsService");
|
|
231
|
+
if (import_node_process.default.env.environment === "dev") {
|
|
232
|
+
return Promise.resolve(new NoOpSmsService());
|
|
233
|
+
} else {
|
|
234
|
+
return Promise.resolve(new TigoSmsService());
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
var SmsServiceTestFactory = singletonFactory({
|
|
239
|
+
provides: SMS_SERVICE,
|
|
240
|
+
dependsOn: [],
|
|
241
|
+
initialize() {
|
|
242
|
+
return Promise.resolve(new NoOpSmsService());
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// src/main.ts
|
|
247
|
+
var authModule = ServiceModule.from([SmsServiceFactory, LoggerFactory]);
|
|
248
|
+
function module2(module3, fn) {
|
|
249
|
+
return (event, context) => {
|
|
250
|
+
fn(event, { services: module3, ...context });
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
var handler = module2(authModule, login);
|
|
254
|
+
(async function() {
|
|
255
|
+
await deeplyNestedFunction({ services: authModule });
|
|
256
|
+
const logger = await authModule.inject(LOGGER);
|
|
257
|
+
if (logger instanceof EmailAlertLogger) {
|
|
258
|
+
await logger.flush();
|
|
259
|
+
}
|
|
260
|
+
console.log("Done");
|
|
261
|
+
})();
|
|
262
|
+
async function deeplyNestedFunction(context) {
|
|
263
|
+
await login({}, context);
|
|
264
|
+
}
|
|
265
|
+
async function login(event, context) {
|
|
266
|
+
const state = authorize(event);
|
|
267
|
+
const logger = await context.services.inject(LOGGER);
|
|
268
|
+
if (state === "DEVICE_CHANGED") {
|
|
269
|
+
logger.info("Device changed", { "user": "asad" });
|
|
270
|
+
const sms = await context.services.inject(SMS_SERVICE);
|
|
271
|
+
logger.debug("Sending sms");
|
|
272
|
+
try {
|
|
273
|
+
sms.send(971233149, "Hola Juan, tu pin es 22222");
|
|
274
|
+
} catch (e) {
|
|
275
|
+
logger.error("Error sending sms", e);
|
|
276
|
+
}
|
|
277
|
+
const sms1 = await context.services.inject(SMS_SERVICE);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
function authorize(_request) {
|
|
282
|
+
return "DEVICE_CHANGED";
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2xpYi9zZXJ2aWNlTW9kdWxlLnRzIiwgIi4uL3NyYy9saWIvc2VydmljZUtleS50cyIsICIuLi9zcmMvbGliL3NlcnZpY2VGYWN0b3J5LnRzIiwgIi4uL3NyYy9jb3JlL3Ntcy9kaS9zbXNNb2R1bGUudHMiLCAiLi4vc3JjL2NvcmUvc21zL25vT3BTbXNTZXJ2aWNlLnRzIiwgIi4uL3NyYy9jb3JlL3Ntcy90aWdvU21zU2VydmljZS50cyIsICIuLi9zcmMvY29yZS9sb2dnZXIvZW1haWxBbGVydExvZ2dlci50cyIsICIuLi9zcmMvY29yZS9sb2dnZXIvZGkvbG9nZ2VyTW9kdWxlLnRzIiwgIi4uL3NyYy9tYWluLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgeyBTZXJ2aWNlS2V5IH0gZnJvbSAnLi9zZXJ2aWNlS2V5JztcclxuaW1wb3J0IHsgU2VydmljZUZhY3RvcnkgfSBmcm9tICcuL3NlcnZpY2VGYWN0b3J5JztcclxuaW1wb3J0IHsgU2VydmljZVByb3ZpZGVyIH0gZnJvbSAnLi9zZXJ2aWNlUHJvdmlkZXInO1xyXG5cclxuZXhwb3J0IGNsYXNzIFNlcnZpY2VNb2R1bGUgaW1wbGVtZW50cyBTZXJ2aWNlUHJvdmlkZXIge1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgZmFjdG9yaWVzOiBTZXJ2aWNlRmFjdG9yeTxhbnksIGFueT5bXSA9IFtdO1xyXG5cclxuICBjb25zdHJ1Y3RvcihmYWN0b3JpZXM6IFNldDxTZXJ2aWNlRmFjdG9yeTx1bmtub3duLCBTZXJ2aWNlS2V5PHVua25vd24+W10+Pikge1xyXG4gICAgdGhpcy5mYWN0b3JpZXMgPSBBcnJheS5mcm9tKGZhY3Rvcmllcyk7XHJcbiAgICB0aGlzLmZhY3Rvcmllcy5mb3JFYWNoKChmYWN0b3J5KSA9PiB7XHJcbiAgICAgIGNoZWNrUmVjdXJzaXZlRGVwZW5kZW5jaWVzKGZhY3RvcnkpO1xyXG4gICAgICBjaGVja01pc3NpbmdEZXBlbmRlbmNpZXMoZmFjdG9yeSwgdGhpcy5mYWN0b3JpZXMpO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgYXN5bmMgaW5qZWN0PFQ+KGtleTogU2VydmljZUtleTxUPik6IFByb21pc2U8VD4ge1xyXG4gICAgY29uc3QgZmFjdG9yeSA9IHRoaXMuZmFjdG9yaWVzLmZpbmQoKGZhY3RvcnkpID0+IHtcclxuICAgICAgcmV0dXJuIGlzU3VpdGFibGUoa2V5LCBmYWN0b3J5KTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIGEgZmFjdG9yeSB0byBzdXBwbHkgdGhlIHJlcXVlc3RlZCBrZXkgd2FzIG5vdCBmb3VuZFxyXG4gICAgaWYgKCFmYWN0b3J5KSB7XHJcbiAgICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGZpbmQgYSBzdWl0YWJsZSBmYWN0b3J5IGZvciAke2tleS5uYW1lfWApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlc29sdmUgYWxsIGRlcGVuZGVuY2llcyBmaXJzdFxyXG4gICAgY29uc3QgZGVwZW5kZW5jaWVzID0gYXdhaXQgUHJvbWlzZS5hbGwoXHJcbiAgICAgIGZhY3RvcnkuZGVwZW5kc09uLm1hcCgoZGVwZW5kZW5jeUtleTogU2VydmljZUtleTx1bmtub3duPikgPT4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmluamVjdChkZXBlbmRlbmN5S2V5KTtcclxuICAgICAgfSksXHJcbiAgICApO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZhY3RvcnkgdG8gcmV0cmlldmUgdGhlIGRlcGVuZGVuY3lcclxuICAgIHJldHVybiBmYWN0b3J5LmluaXRpYWxpemUoLi4uZGVwZW5kZW5jaWVzKTtcclxuICB9XHJcblxyXG4gIHN0YXRpYyBmcm9tKFxyXG4gICAgZmFjdG9yaWVzOiBTZXJ2aWNlRmFjdG9yeTx1bmtub3duLCBTZXJ2aWNlS2V5PHVua25vd24+W10+W10sXHJcbiAgKTogU2VydmljZU1vZHVsZSB7XHJcbiAgICByZXR1cm4gbmV3IFNlcnZpY2VNb2R1bGUobmV3IFNldChmYWN0b3JpZXMpKTtcclxuICB9XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGNoZWNrUmVjdXJzaXZlRGVwZW5kZW5jaWVzKFxyXG4gIGZhY3Rvcnk6IFNlcnZpY2VGYWN0b3J5PHVua25vd24sIFNlcnZpY2VLZXk8dW5rbm93bj5bXT4sXHJcbikge1xyXG4gIGNvbnN0IHJlY3Vyc2l2ZSA9IGZhY3RvcnkuZGVwZW5kc09uLnNvbWUoKGRlcGVuZGVuY3lLZXkpID0+IHtcclxuICAgIHJldHVybiBkZXBlbmRlbmN5S2V5ID09PSBmYWN0b3J5LnByb3ZpZGVzO1xyXG4gIH0pO1xyXG5cclxuICBpZiAocmVjdXJzaXZlKSB7XHJcbiAgICB0aHJvdyBuZXcgRXJyb3IoXHJcbiAgICAgICdSZWN1cnNpdmUgZGVwZW5kZW5jeSBkZXRlY3RlZCBvbjogJyArIGZhY3RvcnkucHJvdmlkZXMubmFtZSxcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG5mdW5jdGlvbiBjaGVja01pc3NpbmdEZXBlbmRlbmNpZXMoXHJcbiAgZmFjdG9yeTogU2VydmljZUZhY3Rvcnk8dW5rbm93biwgU2VydmljZUtleTx1bmtub3duPltdPixcclxuICBmYWN0b3JpZXM6IFNlcnZpY2VGYWN0b3J5PHVua25vd24+W10sXHJcbikge1xyXG4gIGNvbnN0IG1pc3NpbmdEZXBlbmRlbmNpZXMgPSBmYWN0b3J5LmRlcGVuZHNPbi5maWx0ZXIoXHJcbiAgICAoZGVwZW5kZW5jeUtleTogU2VydmljZUtleTxhbnk+KSA9PiB7XHJcbiAgICAgIHJldHVybiAhaXNSZWdpc3RlcmVkKGRlcGVuZGVuY3lLZXksIGZhY3Rvcmllcyk7XHJcbiAgICB9LFxyXG4gICk7XHJcbiAgaWYgKG1pc3NpbmdEZXBlbmRlbmNpZXMubGVuZ3RoID09PSAwKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICBjb25zdCBkZXBlbmRlbmN5TGlzdCA9IG1pc3NpbmdEZXBlbmRlbmNpZXNcclxuICAgIC5tYXAoKGRlcGVuZGVuY3lLZXkpID0+IGAgLT4gJHtkZXBlbmRlbmN5S2V5Lm5hbWV9YClcclxuICAgIC5qb2luKCdcXG4nKTtcclxuICB0aHJvdyBuZXcgRXJyb3IoXHJcbiAgICBgJHtmYWN0b3J5LnByb3ZpZGVzLm5hbWV9IHdpbGwgZmFpbCBiZWNhdXNlIGl0IGRlcGVuZHMgb246XFxuICR7ZGVwZW5kZW5jeUxpc3R9YCxcclxuICApO1xyXG59XHJcblxyXG5mdW5jdGlvbiBpc1JlZ2lzdGVyZWQoXHJcbiAga2V5OiBTZXJ2aWNlS2V5PHVua25vd24+LFxyXG4gIGZhY3RvcmllczogU2VydmljZUZhY3Rvcnk8dW5rbm93bj5bXSxcclxuKSB7XHJcbiAgcmV0dXJuIGZhY3Rvcmllcy5zb21lKChmYWN0b3J5KSA9PiBmYWN0b3J5LnByb3ZpZGVzID09PSBrZXkpO1xyXG59XHJcblxyXG5mdW5jdGlvbiBpc1N1aXRhYmxlPFQsIEQgZXh0ZW5kcyBTZXJ2aWNlS2V5PHVua25vd24+W10+KFxyXG4gIGtleTogU2VydmljZUtleTxUPixcclxuICBmYWN0b3J5OiBTZXJ2aWNlRmFjdG9yeTx1bmtub3duLCBEPixcclxuKTogZmFjdG9yeSBpcyBTZXJ2aWNlRmFjdG9yeTxULCBEPiB7XHJcbiAgcmV0dXJuIGZhY3Rvcnk/LnByb3ZpZGVzID09PSBrZXk7XHJcbn1cclxuIiwgIi8vIEB0cy1pZ25vcmVcclxuZXhwb3J0IGNsYXNzIFNlcnZpY2VLZXk8VD4ge1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgc3ltYm9sOiBzeW1ib2w7XHJcblxyXG4gIGNvbnN0cnVjdG9yKHB1YmxpYyByZWFkb25seSBuYW1lOiBzdHJpbmcpIHtcclxuICAgIHRoaXMuc3ltYm9sID0gU3ltYm9sKG5hbWUpO1xyXG4gIH1cclxufVxyXG4iLCAiaW1wb3J0IHsgU2VydmljZUtleSB9IGZyb20gJy4vc2VydmljZUtleSc7XHJcblxyXG4vLyBIZWxwZXIgdHlwZXMgdG8gZXh0cmFjdCB0aGUgdHlwZSBmcm9tIFNlcnZpY2VLZXlcclxudHlwZSBTZXJ2aWNlVHlwZTxUPiA9IFQgZXh0ZW5kcyBTZXJ2aWNlS2V5PGluZmVyIFU+ID8gVSA6IG5ldmVyO1xyXG5cclxuLy8gSGVscGVyIHR5cGVzIHRvIGNvbnZlcnQgYW4gYXJyYXkgb2YgU2VydmljZUtleSB0byB0dXBsZSBvZiB0aGVpciB0eXBlc1xyXG50eXBlIERlcGVuZGVuY3lUeXBlczxUIGV4dGVuZHMgU2VydmljZUtleTx1bmtub3duPltdPiA9IHtcclxuICBbSyBpbiBrZXlvZiBUXTogU2VydmljZVR5cGU8VFtLXT47XHJcbn07XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZpY2VGYWN0b3J5PFQsIEQgZXh0ZW5kcyBTZXJ2aWNlS2V5PHVua25vd24+W10gPSBbXT4ge1xyXG4gIHByb3ZpZGVzOiBTZXJ2aWNlS2V5PFQ+O1xyXG4gIGRlcGVuZHNPbjogRDtcclxuXHJcbiAgaW5pdGlhbGl6ZSguLi5kZXBlbmRlbmNpZXM6IERlcGVuZGVuY3lUeXBlczxEPik6IFByb21pc2U8VD47XHJcblxyXG4gIGRpc3Bvc2UoaW5zdGFuY2U6IFQpOiB2b2lkO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gc2luZ2xldG9uRmFjdG9yeTxULCBEIGV4dGVuZHMgU2VydmljZUtleTx1bmtub3duPltdID0gW10+KHtcclxuICBwcm92aWRlcyxcclxuICBkZXBlbmRzT24gPSBbXSBhcyB1bmtub3duIGFzIEQsXHJcbiAgaW5pdGlhbGl6ZSxcclxuICBkaXNwb3NlID0gKCkgPT4ge30sXHJcbn06IHtcclxuICBwcm92aWRlczogU2VydmljZUtleTxUPjtcclxuICBkZXBlbmRzT24/OiBEO1xyXG4gIGluaXRpYWxpemU6ICguLi5kZXBlbmRlbmNpZXM6IERlcGVuZGVuY3lUeXBlczxEPikgPT4gUHJvbWlzZTxUPjtcclxuICBkaXNwb3NlPzogKGluc3RhbmNlOiBUKSA9PiB2b2lkO1xyXG59KTogU2VydmljZUZhY3Rvcnk8VCwgRD4ge1xyXG4gIGxldCBpbnN0YW5jZTogVCB8IHVuZGVmaW5lZDtcclxuXHJcbiAgcmV0dXJuIHtcclxuICAgIHByb3ZpZGVzLFxyXG4gICAgZGVwZW5kc09uLFxyXG4gICAgYXN5bmMgaW5pdGlhbGl6ZSguLi5kZXBlbmRlbmNpZXM6IERlcGVuZGVuY3lUeXBlczxEPik6IFByb21pc2U8VD4ge1xyXG4gICAgICBpZiAoaW5zdGFuY2UpIHtcclxuICAgICAgICByZXR1cm4gaW5zdGFuY2U7XHJcbiAgICAgIH1cclxuICAgICAgaW5zdGFuY2UgPSBhd2FpdCBpbml0aWFsaXplKC4uLmRlcGVuZGVuY2llcyk7XHJcbiAgICAgIHJldHVybiBpbnN0YW5jZTtcclxuICAgIH0sXHJcbiAgICBkaXNwb3NlKHNlcnZpY2VJbnN0YW5jZTogVCk6IHZvaWQge1xyXG4gICAgICBpZiAoaW5zdGFuY2UgPT09IHNlcnZpY2VJbnN0YW5jZSkge1xyXG4gICAgICAgIGRpc3Bvc2Uoc2VydmljZUluc3RhbmNlKTtcclxuICAgICAgICBpbnN0YW5jZSA9IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfSxcclxuICB9O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gb25lU2hvdEZhY3Rvcnk8VCwgRCBleHRlbmRzIFNlcnZpY2VLZXk8dW5rbm93bj5bXSA9IFtdPih7XHJcbiAgcHJvdmlkZXMsXHJcbiAgZGVwZW5kc09uLFxyXG4gIGluaXRpYWxpemUsXHJcbiAgZGlzcG9zZSA9ICgpID0+IHt9LFxyXG59OiB7XHJcbiAgcHJvdmlkZXM6IFNlcnZpY2VLZXk8VD47XHJcbiAgZGVwZW5kc09uOiBEO1xyXG4gIGluaXRpYWxpemU6ICguLi5kZXBlbmRlbmNpZXM6IERlcGVuZGVuY3lUeXBlczxEPikgPT4gUHJvbWlzZTxUPjtcclxuICBkaXNwb3NlPzogKGluc3RhbmNlOiBUKSA9PiB2b2lkO1xyXG59KTogU2VydmljZUZhY3Rvcnk8VCwgRD4ge1xyXG4gIHJldHVybiB7XHJcbiAgICBwcm92aWRlcyxcclxuICAgIGRlcGVuZHNPbixcclxuICAgIGluaXRpYWxpemUsXHJcbiAgICBkaXNwb3NlLFxyXG4gIH07XHJcbn1cclxuIiwgImltcG9ydCB7IFNlcnZpY2VLZXkgfSBmcm9tICcuLi8uLi8uLi9saWIvc2VydmljZUtleSc7XG5pbXBvcnQgeyBTbXNTZXJ2aWNlIH0gZnJvbSAnLi4vc21zU2VydmljZSc7XG5pbXBvcnQgeyBzaW5nbGV0b25GYWN0b3J5IH0gZnJvbSAnLi4vLi4vLi4vbGliL3NlcnZpY2VGYWN0b3J5JztcbmltcG9ydCBwcm9jZXNzIGZyb20gJ25vZGU6cHJvY2Vzcyc7XG5pbXBvcnQgeyBOb09wU21zU2VydmljZSB9IGZyb20gJy4uL25vT3BTbXNTZXJ2aWNlJztcbmltcG9ydCB7IFRpZ29TbXNTZXJ2aWNlIH0gZnJvbSAnLi4vdGlnb1Ntc1NlcnZpY2UnO1xuaW1wb3J0IHsgTE9HR0VSIH0gZnJvbSAnLi4vLi4vbG9nZ2VyL2RpL2xvZ2dlck1vZHVsZSc7XG5pbXBvcnQgeyBMb2dnZXIgfSBmcm9tICcuLi8uLi9sb2dnZXIvbG9nZ2VyJztcblxuZXhwb3J0IGNvbnN0IFNNU19TRVJWSUNFID0gbmV3IFNlcnZpY2VLZXk8U21zU2VydmljZT4oJ1Ntc1NlcnZpY2UnKTtcblxuZXhwb3J0IGNvbnN0IFNtc1NlcnZpY2VGYWN0b3J5ID0gc2luZ2xldG9uRmFjdG9yeSh7XG4gIHByb3ZpZGVzOiBTTVNfU0VSVklDRSxcbiAgZGVwZW5kc09uOiBbTE9HR0VSXSxcbiAgaW5pdGlhbGl6ZShsb2dnZXI6IExvZ2dlcik6IFByb21pc2U8U21zU2VydmljZT4ge1xuICAgIGxvZ2dlci5pbmZvKCdJbml0aWFsaXppbmcgU21zU2VydmljZScpO1xuICAgIC8vIFNpbmNlIHRoZSBjcmVhdGUgbWV0aG9kIGlzIGFzeW5jLCB3ZSBjb3VsZCBpbnN0YW50aWF0ZSBhIGRpZmZlcmVudFxuICAgIC8vIGltcGxlbWVudGF0aW9uIG9uIGRlbWFuZCBieSByZXRyaWV2aW5nIHNvbWUgY29uZmlndXJhdGlvbiBmcm9tIGEgZGJcbiAgICAvLyBvciBhIGNvbmZpZyBmaWxlLlxuICAgIGlmIChwcm9jZXNzLmVudi5lbnZpcm9ubWVudCA9PT0gJ2RldicpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobmV3IE5vT3BTbXNTZXJ2aWNlKCkpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG5ldyBUaWdvU21zU2VydmljZSgpKTtcbiAgICB9XG4gIH0sXG59KTtcblxuZXhwb3J0IGNvbnN0IFNtc1NlcnZpY2VUZXN0RmFjdG9yeSA9IHNpbmdsZXRvbkZhY3Rvcnkoe1xuICBwcm92aWRlczogU01TX1NFUlZJQ0UsXG4gIGRlcGVuZHNPbjogW10sXG4gIGluaXRpYWxpemUoKTogUHJvbWlzZTxTbXNTZXJ2aWNlPiB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShuZXcgTm9PcFNtc1NlcnZpY2UoKSk7XG4gIH0sXG59KSIsICJpbXBvcnQgeyBTbXNTZXJ2aWNlIH0gZnJvbSAnLi9zbXNTZXJ2aWNlJztcclxuXHJcbmV4cG9ydCBjbGFzcyBOb09wU21zU2VydmljZSBpbXBsZW1lbnRzIFNtc1NlcnZpY2Uge1xyXG4gIHNlbmQocmVjaXBpZW50OiBudW1iZXIsIHRleHQ6IHN0cmluZykge1xyXG4gICAgY29uc29sZS53YXJuKGBOb09wU21zU2VydmljZTogU2VuZGluZyBzbXMgdG8gJHtyZWNpcGllbnR9OiAke3RleHR9YCk7XHJcbiAgfVxyXG59XHJcbiIsICJpbXBvcnQgeyBTbXNTZXJ2aWNlIH0gZnJvbSAnLi9zbXNTZXJ2aWNlJztcclxuXHJcbmV4cG9ydCBjbGFzcyBUaWdvU21zU2VydmljZSBpbXBsZW1lbnRzIFNtc1NlcnZpY2Uge1xyXG4gIHNlbmQoX3JlY2lwaWVudDogbnVtYmVyLCBfdGV4dDogc3RyaW5nKSB7XHJcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCB5ZXQgaW1wbGVtZW50ZWQnKTtcclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IExvZ2dlciwgTG9nRXZlbnQsIExvZ0V2ZW50UGF5bG9hZCB9IGZyb20gJy4vbG9nZ2VyJztcclxuXHJcbmV4cG9ydCBjbGFzcyBFbWFpbEFsZXJ0TG9nZ2VyIGltcGxlbWVudHMgTG9nZ2VyIHtcclxuICBwcml2YXRlIHJlYWRvbmx5IGJ1ZmZlcjogTG9nRXZlbnRbXSA9IFtdO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgYnVmZmVyU2l6ZTogbnVtYmVyO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgcmVjaXBpZW50czogc3RyaW5nW107XHJcbiAgcHJpdmF0ZSByZWFkb25seSBlbWFpbFF1ZXVlOiBzdHJpbmdbXSA9IFtdO1xyXG5cclxuICBjb25zdHJ1Y3RvcihidWZmZXJTaXplOiBudW1iZXIgPSA1MCwgcmVjaXBpZW50czogc3RyaW5nW10gPSBbXSkge1xyXG4gICAgdGhpcy5idWZmZXJTaXplID0gYnVmZmVyU2l6ZTtcclxuICAgIHRoaXMucmVjaXBpZW50cyA9IHJlY2lwaWVudHM7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGFkZFRvQnVmZmVyKGV2ZW50OiBPbWl0PExvZ0V2ZW50LCAnZGF0ZSc+KTogdm9pZCB7XHJcbiAgICAvLyBLZWVwIHRoZSBidWZmZXIgdG8gdGhlIHNwZWNpZmllZCBzaXplXHJcbiAgICBpZiAodGhpcy5idWZmZXIubGVuZ3RoID49IHRoaXMuYnVmZmVyU2l6ZSkge1xyXG4gICAgICB0aGlzLmJ1ZmZlci5zaGlmdCgpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5idWZmZXIucHVzaCh7IC4uLmV2ZW50LCBkYXRlOiBuZXcgRGF0ZSgpIH0pO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIGluZm8obWVzc2FnZTogc3RyaW5nLCBwYXlsb2FkPzogTG9nRXZlbnRQYXlsb2FkKTogdm9pZCB7XHJcbiAgICB0aGlzLmFkZFRvQnVmZmVyKHsgbGV2ZWw6ICdJTkZPJywgbWVzc2FnZSwgcGF5bG9hZCB9KTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBlcnJvcihcclxuICAgIG1lc3NhZ2U6IHN0cmluZyxcclxuICAgIGVycm9yPzogdW5rbm93bixcclxuICAgIHBheWxvYWQ/OiBMb2dFdmVudFBheWxvYWQsXHJcbiAgKTogdm9pZCB7XHJcbiAgICB0aGlzLmFkZFRvQnVmZmVyKHsgbGV2ZWw6ICdFUlJPUicsIG1lc3NhZ2UsIHBheWxvYWQgfSk7XHJcbiAgICB0aGlzLmVtYWlsUXVldWUucHVzaChjcmVhdGVUYWJsZVN0cmluZyh0aGlzLmJ1ZmZlciwgZXJyb3IpKTtcclxuICAgIHRoaXMuYnVmZmVyLmxlbmd0aCA9IDA7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgZGVidWcobWVzc2FnZTogc3RyaW5nLCBwYXlsb2FkPzogTG9nRXZlbnRQYXlsb2FkKTogdm9pZCB7XHJcbiAgICB0aGlzLmFkZFRvQnVmZmVyKHsgbGV2ZWw6ICdERUJVRycsIG1lc3NhZ2UsIHBheWxvYWQgfSk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgYXN5bmMgZmx1c2goKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBmb3IgKGNvbnN0IGVtYWlsIG9mIHRoaXMuZW1haWxRdWV1ZSkge1xyXG4gICAgICBjb25zb2xlLmVycm9yKGVtYWlsKTtcclxuICAgIH1cclxuICAgIC8vIENsZWFyIGFsbCBidWZmZXJzXHJcbiAgICB0aGlzLmJ1ZmZlci5sZW5ndGggPSAwO1xyXG4gICAgdGhpcy5lbWFpbFF1ZXVlLmxlbmd0aCA9IDA7XHJcbiAgfVxyXG59XHJcblxyXG5mdW5jdGlvbiBjcmVhdGVUYWJsZVN0cmluZyhldmVudHM6IExvZ0V2ZW50W10sIGVycm9yPzogdW5rbm93bik6IHN0cmluZyB7XHJcbiAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApIHtcclxuICAgIHJldHVybiAnTm8gZXZlbnRzIHRvIGRpc3BsYXknO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsY3VsYXRlIGNvbHVtbiB3aWR0aHNcclxuICBjb25zdCBkYXRlV2lkdGggPSBNYXRoLm1heChcclxuICAgIDQsXHJcbiAgICAuLi5ldmVudHMubWFwKChlKSA9PiBlLmRhdGUudG9JU09TdHJpbmcoKS5sZW5ndGgpLFxyXG4gICk7XHJcbiAgY29uc3QgbGV2ZWxXaWR0aCA9IE1hdGgubWF4KFxyXG4gICAgNSwgLy8gXCJMZXZlbFwiIGhlYWRlciBsZW5ndGhcclxuICAgIC4uLmV2ZW50cy5tYXAoKGUpID0+IGUubGV2ZWwubGVuZ3RoKSxcclxuICApO1xyXG4gIGNvbnN0IG1lc3NhZ2VXaWR0aCA9IE1hdGgubWF4KDcsIC4uLmV2ZW50cy5tYXAoKGUpID0+IGUubWVzc2FnZS5sZW5ndGgpKTtcclxuICBjb25zdCBwYXlsb2FkV2lkdGggPSBNYXRoLm1heChcclxuICAgIDcsXHJcbiAgICAuLi5ldmVudHMubWFwKChlKSA9PiAoZS5wYXlsb2FkID8gSlNPTi5zdHJpbmdpZnkoZS5wYXlsb2FkKS5sZW5ndGggOiAwKSksXHJcbiAgKTtcclxuXHJcbiAgLy8gQ3JlYXRlIGhlYWRlclxyXG4gIGNvbnN0IGhlYWRlciA9IFtcclxuICAgICdEQVRFJy5wYWRFbmQoZGF0ZVdpZHRoKSxcclxuICAgICdMRVZFTCcucGFkRW5kKGxldmVsV2lkdGgpLFxyXG4gICAgJ01FU1NBR0UnLnBhZEVuZChtZXNzYWdlV2lkdGgpLFxyXG4gICAgJ1BBWUxPQUQnLnBhZEVuZChwYXlsb2FkV2lkdGgpLFxyXG4gIF0uam9pbignIHwgJyk7XHJcblxyXG4gIC8vIENyZWF0ZSBzZXBhcmF0b3JcclxuICBjb25zdCBzZXBhcmF0b3IgPSBbXHJcbiAgICAnLScucmVwZWF0KGRhdGVXaWR0aCksXHJcbiAgICAnLScucmVwZWF0KGxldmVsV2lkdGgpLFxyXG4gICAgJy0nLnJlcGVhdChtZXNzYWdlV2lkdGgpLFxyXG4gICAgJy0nLnJlcGVhdChwYXlsb2FkV2lkdGgpLFxyXG4gIF0uam9pbignLXwtJyk7XHJcblxyXG4gIC8vIENyZWF0ZSByb3dzXHJcbiAgY29uc3Qgcm93cyA9IGV2ZW50cy5tYXAoKGV2ZW50KSA9PiB7XHJcbiAgICBjb25zdCBkYXRlID0gZXZlbnQuZGF0ZS50b0lTT1N0cmluZygpLnBhZEVuZChkYXRlV2lkdGgpO1xyXG4gICAgY29uc3QgbGV2ZWwgPSBldmVudC5sZXZlbC5wYWRFbmQobGV2ZWxXaWR0aCk7XHJcbiAgICBjb25zdCBtZXNzYWdlID0gZXZlbnQubWVzc2FnZS5wYWRFbmQobWVzc2FnZVdpZHRoKTtcclxuICAgIGNvbnN0IHBheWxvYWQgPSAoZXZlbnQucGF5bG9hZCA/IEpTT04uc3RyaW5naWZ5KGV2ZW50LnBheWxvYWQpIDogJycpLnBhZEVuZChcclxuICAgICAgcGF5bG9hZFdpZHRoLFxyXG4gICAgKTtcclxuICAgIHJldHVybiBbZGF0ZSwgbGV2ZWwsIG1lc3NhZ2UsIHBheWxvYWRdLmpvaW4oJyB8ICcpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBDcmVhdGUgZXJyb3Igc2VjdGlvbiBpZiBlcnJvciBleGlzdHNcclxuICBjb25zdCBlcnJvclNlY3Rpb24gPSBlcnJvclxyXG4gICAgPyAnXFxuXFxuRXJyb3IgRGV0YWlsczpcXG4nICtcclxuICAgICAgJy0nLnJlcGVhdCgyMCkgK1xyXG4gICAgICAnXFxuJyArXHJcbiAgICAgIChlcnJvciBpbnN0YW5jZW9mIEVycm9yXHJcbiAgICAgICAgPyBgJHtlcnJvci5uYW1lfTogJHtlcnJvci5tZXNzYWdlfVxcbiR7ZXJyb3Iuc3RhY2sgfHwgJyd9YFxyXG4gICAgICAgIDogU3RyaW5nKGVycm9yKSlcclxuICAgIDogJyc7XHJcblxyXG4gIC8vIENvbWJpbmUgYWxsIHBhcnRzXHJcbiAgcmV0dXJuIFtoZWFkZXIsIHNlcGFyYXRvciwgLi4ucm93c10uam9pbignXFxuJykgKyBlcnJvclNlY3Rpb247XHJcbn1cclxuIiwgImltcG9ydCB7IHNpbmdsZXRvbkZhY3RvcnkgfSBmcm9tICcuLi8uLi8uLi9saWIvc2VydmljZUZhY3RvcnknO1xyXG5pbXBvcnQgeyBTZXJ2aWNlS2V5IH0gZnJvbSAnLi4vLi4vLi4vbGliL3NlcnZpY2VLZXknO1xyXG5pbXBvcnQgeyBFbWFpbEFsZXJ0TG9nZ2VyIH0gZnJvbSAnLi4vZW1haWxBbGVydExvZ2dlcic7XHJcbmltcG9ydCB7IExvZ2dlciB9IGZyb20gJy4uL2xvZ2dlcic7XHJcbmltcG9ydCB7IFNNU19TRVJWSUNFIH0gZnJvbSAnLi4vLi4vc21zL2RpL3Ntc01vZHVsZSc7XHJcbmltcG9ydCB7IFNtc1NlcnZpY2UgfSBmcm9tICcuLi8uLi9zbXMvc21zU2VydmljZSc7XHJcblxyXG5leHBvcnQgY29uc3QgTE9HR0VSID0gbmV3IFNlcnZpY2VLZXk8TG9nZ2VyPignTG9nZ2VyJyk7XHJcblxyXG5leHBvcnQgY29uc3QgTG9nZ2VyRmFjdG9yeSA9IHNpbmdsZXRvbkZhY3Rvcnkoe1xyXG4gIHByb3ZpZGVzOiBMT0dHRVIsXHJcbiAgZGVwZW5kc09uOiBbXSxcclxuICBpbml0aWFsaXplOiAoKSA9PiB7XHJcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG5ldyBFbWFpbEFsZXJ0TG9nZ2VyKDEwMCwgWydqdWFuaHI0NTRAZ21haWwuY29tJ10pKTtcclxuICB9LFxyXG59KTtcclxuIiwgImltcG9ydCB7IFNlcnZpY2VNb2R1bGUgfSBmcm9tICcuL2xpYi9zZXJ2aWNlTW9kdWxlJztcclxuaW1wb3J0IHsgU2VydmljZVByb3ZpZGVyIH0gZnJvbSAnLi9saWIvc2VydmljZVByb3ZpZGVyJztcclxuaW1wb3J0IHsgU01TX1NFUlZJQ0UsIFNtc1NlcnZpY2VGYWN0b3J5IH0gZnJvbSAnLi9jb3JlL3Ntcy9kaS9zbXNNb2R1bGUnO1xyXG5pbXBvcnQgeyBMT0dHRVIsIExvZ2dlckZhY3RvcnkgfSBmcm9tICcuL2NvcmUvbG9nZ2VyL2RpL2xvZ2dlck1vZHVsZSc7XHJcbmltcG9ydCB7IEVtYWlsQWxlcnRMb2dnZXIgfSBmcm9tICcuL2NvcmUvbG9nZ2VyL2VtYWlsQWxlcnRMb2dnZXInO1xyXG5cclxuY29uc3QgYXV0aE1vZHVsZSA9IFNlcnZpY2VNb2R1bGUuZnJvbShbU21zU2VydmljZUZhY3RvcnksIExvZ2dlckZhY3RvcnldKTtcclxuXHJcbmZ1bmN0aW9uIG1vZHVsZShcclxuICBtb2R1bGU6IFNlcnZpY2VNb2R1bGUsXHJcbiAgZm46IChldmVudDogb2JqZWN0LCBjb250ZXh0OiBDb250ZXh0KSA9PiB2b2lkLFxyXG4pIHtcclxuICByZXR1cm4gKGV2ZW50OiBvYmplY3QsIGNvbnRleHQ6IG9iamVjdCkgPT4ge1xyXG4gICAgZm4oZXZlbnQsIHsgc2VydmljZXM6IG1vZHVsZSwgLi4uY29udGV4dCB9KTtcclxuICB9O1xyXG59XHJcblxyXG5jb25zdCBoYW5kbGVyID0gbW9kdWxlKGF1dGhNb2R1bGUsIGxvZ2luKTtcclxuXHJcbihhc3luYyBmdW5jdGlvbiAoKSB7XHJcbiAgYXdhaXQgZGVlcGx5TmVzdGVkRnVuY3Rpb24oeyBzZXJ2aWNlczogYXV0aE1vZHVsZSB9KTtcclxuICBjb25zdCBsb2dnZXIgPSBhd2FpdCBhdXRoTW9kdWxlLmluamVjdChMT0dHRVIpO1xyXG4gIGlmIChsb2dnZXIgaW5zdGFuY2VvZiBFbWFpbEFsZXJ0TG9nZ2VyKSB7XHJcbiAgICBhd2FpdCBsb2dnZXIuZmx1c2goKTtcclxuICB9XHJcbiAgY29uc29sZS5sb2coJ0RvbmUnKTtcclxufSkoKTtcclxuXHJcbmFzeW5jIGZ1bmN0aW9uIGRlZXBseU5lc3RlZEZ1bmN0aW9uKGNvbnRleHQ6IENvbnRleHQpIHtcclxuICBhd2FpdCBsb2dpbih7fSwgY29udGV4dCk7XHJcbn1cclxuXHJcbmludGVyZmFjZSBDb250ZXh0IHtcclxuICBzZXJ2aWNlczogU2VydmljZVByb3ZpZGVyO1xyXG59XHJcblxyXG5hc3luYyBmdW5jdGlvbiBsb2dpbihldmVudDogYW55LCBjb250ZXh0OiBDb250ZXh0KSB7XHJcbiAgY29uc3Qgc3RhdGUgPSBhdXRob3JpemUoZXZlbnQpO1xyXG4gIGNvbnN0IGxvZ2dlciA9IGF3YWl0IGNvbnRleHQuc2VydmljZXMuaW5qZWN0KExPR0dFUik7XHJcbiAgaWYgKHN0YXRlID09PSAnREVWSUNFX0NIQU5HRUQnKSB7XHJcbiAgICBsb2dnZXIuaW5mbygnRGV2aWNlIGNoYW5nZWQnLCB7J3VzZXInOiAnYXNhZCd9KTtcclxuICAgIC8vIFdpdGggdGhlIHNlcnZpY2Uga2V5IHdlIGdldCB0eXBlIHNhZmV0eS9pbmZlcmVuY2UgYW5kIG5hdmlnYXRlIHRvIGRlZmluaXRpb24gZm9yIGZyZWUsXHJcbiAgICAvLyB0aGVyZSBpcyBubyBtYWdpYyBubyBhbm5vdGF0aW9ucywgd2UgY2FuIHNlZSBhbGwgdGhlXHJcbiAgICAvLyBmYWN0b3JpZXMgdGhhdCBwcm92aWRlIHRoaXMgZGVwZW5kZW5jeSBhbmQgc2VlIGFsbCBwbGFjZXMgd2hlcmUgdGhpcyBpcyByZXRyaWV2ZWQuXHJcbiAgICBjb25zdCBzbXMgPSBhd2FpdCBjb250ZXh0LnNlcnZpY2VzLmluamVjdChTTVNfU0VSVklDRSk7XHJcbiAgICBsb2dnZXIuZGVidWcoJ1NlbmRpbmcgc21zJyk7XHJcbiAgICB0cnkge1xyXG4gICAgICBzbXMuc2VuZCg5NzEyMzMxNDksICdIb2xhIEp1YW4sIHR1IHBpbiBlcyAyMjIyMicpO1xyXG4gICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIHNlbmRpbmcgc21zJywgZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0cmlldmluZyB0aGUgc2VydmljZSBhIHNlY29uZCB0aW1lIHdvdWxkIGhhdmUgdmlydHVhbGx5IHplcm8gY29zdCBpZiB0aGVcclxuICAgIC8vIHVuZGVybHlpbmcgZmFjdG9yeSBwcm92aWRlcyBhIHNpbmdsZXRvbi5cclxuICAgIGNvbnN0IHNtczEgPSBhd2FpdCBjb250ZXh0LnNlcnZpY2VzLmluamVjdChTTVNfU0VSVklDRSk7XHJcbiAgfVxyXG5cclxuICByZXR1cm47XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGF1dGhvcml6ZShfcmVxdWVzdDogYW55KTogJ09LJyB8ICdERVZJQ0VfQ0hBTkdFRCcge1xyXG4gIHJldHVybiAnREVWSUNFX0NIQU5HRUQnO1xyXG59XHJcbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFJTyxJQUFNLGdCQUFOLE1BQU0sZUFBeUM7QUFBQSxFQUdwRCxZQUFZLFdBQWdFO0FBRjVFLFNBQWlCLFlBQXdDLENBQUM7QUFH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
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ServiceKey } from './serviceKey';
|
|
2
|
+
type ServiceType<T> = T extends ServiceKey<infer U> ? U : never;
|
|
3
|
+
type DependencyTypes<T extends ServiceKey<unknown>[]> = {
|
|
4
|
+
[K in keyof T]: ServiceType<T[K]>;
|
|
5
|
+
};
|
|
6
|
+
export interface ServiceFactory<T, D extends ServiceKey<unknown>[] = []> {
|
|
7
|
+
provides: ServiceKey<T>;
|
|
8
|
+
dependsOn: D;
|
|
9
|
+
initialize(...dependencies: DependencyTypes<D>): Promise<T>;
|
|
10
|
+
dispose(instance: T): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function singletonFactory<T, D extends ServiceKey<unknown>[] = []>({ provides, dependsOn, initialize, dispose, }: {
|
|
13
|
+
provides: ServiceKey<T>;
|
|
14
|
+
dependsOn?: D;
|
|
15
|
+
initialize: (...dependencies: DependencyTypes<D>) => Promise<T>;
|
|
16
|
+
dispose?: (instance: T) => void;
|
|
17
|
+
}): ServiceFactory<T, D>;
|
|
18
|
+
export declare function oneShotFactory<T, D extends ServiceKey<unknown>[] = []>({ provides, dependsOn, initialize, dispose, }: {
|
|
19
|
+
provides: ServiceKey<T>;
|
|
20
|
+
dependsOn: D;
|
|
21
|
+
initialize: (...dependencies: DependencyTypes<D>) => Promise<T>;
|
|
22
|
+
dispose?: (instance: T) => void;
|
|
23
|
+
}): ServiceFactory<T, D>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=serviceFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI;KACrD,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,WAAW,cAAc,CAAC,CAAC,EAAE,CAAC,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE;IACrE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACxB,SAAS,EAAE,CAAC,CAAC;IAEb,UAAU,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE5D,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,EACxE,QAAQ,EACR,SAA8B,EAC9B,UAAU,EACV,OAAkB,GACnB,EAAE;IACD,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,CAAC,CAAC;IACd,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;CACjC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAoBvB;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,EACtE,QAAQ,EACR,SAAS,EACT,UAAU,EACV,OAAkB,GACnB,EAAE;IACD,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACxB,SAAS,EAAE,CAAC,CAAC;IACb,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;CACjC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAOvB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.singletonFactory = singletonFactory;
|
|
13
|
+
exports.oneShotFactory = oneShotFactory;
|
|
14
|
+
function singletonFactory({ provides, dependsOn = [], initialize, dispose = () => { }, }) {
|
|
15
|
+
let instance;
|
|
16
|
+
return {
|
|
17
|
+
provides,
|
|
18
|
+
dependsOn,
|
|
19
|
+
initialize(...dependencies) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
if (instance) {
|
|
22
|
+
return instance;
|
|
23
|
+
}
|
|
24
|
+
instance = yield initialize(...dependencies);
|
|
25
|
+
return instance;
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
dispose(serviceInstance) {
|
|
29
|
+
if (instance === serviceInstance) {
|
|
30
|
+
dispose(serviceInstance);
|
|
31
|
+
instance = undefined;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function oneShotFactory({ provides, dependsOn, initialize, dispose = () => { }, }) {
|
|
37
|
+
return {
|
|
38
|
+
provides,
|
|
39
|
+
dependsOn,
|
|
40
|
+
initialize,
|
|
41
|
+
dispose,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=serviceFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceFactory.js","sourceRoot":"","sources":["../src/serviceFactory.ts"],"names":[],"mappings":";;;;;;;;;;;AAmBA,4CA8BC;AAED,wCAiBC;AAjDD,SAAgB,gBAAgB,CAA0C,EACxE,QAAQ,EACR,SAAS,GAAG,EAAkB,EAC9B,UAAU,EACV,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,GAMnB;IACC,IAAI,QAAuB,CAAC;IAE5B,OAAO;QACL,QAAQ;QACR,SAAS;QACH,UAAU,CAAC,GAAG,YAAgC;;gBAClD,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,QAAQ,CAAC;gBAClB,CAAC;gBACD,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,YAAY,CAAC,CAAC;gBAC7C,OAAO,QAAQ,CAAC;YAClB,CAAC;SAAA;QACD,OAAO,CAAC,eAAkB;YACxB,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;gBACjC,OAAO,CAAC,eAAe,CAAC,CAAC;gBACzB,QAAQ,GAAG,SAAS,CAAC;YACvB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAA0C,EACtE,QAAQ,EACR,SAAS,EACT,UAAU,EACV,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,GAMnB;IACC,OAAO;QACL,QAAQ;QACR,SAAS;QACT,UAAU;QACV,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServiceKey = void 0;
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
class ServiceKey {
|
|
6
|
+
constructor(name) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.symbol = Symbol(name);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.ServiceKey = ServiceKey;
|
|
12
|
+
//# sourceMappingURL=serviceKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ServiceKey } from './serviceKey';
|
|
2
|
+
import { ServiceFactory } from './serviceFactory';
|
|
3
|
+
import { ServiceProvider } from './serviceProvider';
|
|
4
|
+
export declare class ServiceModule implements ServiceProvider {
|
|
5
|
+
private readonly factories;
|
|
6
|
+
constructor(factories: Set<ServiceFactory<unknown, ServiceKey<unknown>[]>>);
|
|
7
|
+
inject<T>(key: ServiceKey<T>): Promise<T>;
|
|
8
|
+
static from(factories: ServiceFactory<unknown, ServiceKey<unknown>[]>[]): ServiceModule;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=serviceModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,qBAAa,aAAc,YAAW,eAAe;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAEhD,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAQ7D,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBtD,MAAM,CAAC,IAAI,CACT,SAAS,EAAE,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,GAC1D,aAAa;CAGjB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ServiceModule = void 0;
|
|
13
|
+
class ServiceModule {
|
|
14
|
+
constructor(factories) {
|
|
15
|
+
this.factories = [];
|
|
16
|
+
this.factories = Array.from(factories);
|
|
17
|
+
this.factories.forEach((factory) => {
|
|
18
|
+
checkRecursiveDependencies(factory);
|
|
19
|
+
checkMissingDependencies(factory, this.factories);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
inject(key) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const factory = this.factories.find((factory) => {
|
|
25
|
+
return isSuitable(key, factory);
|
|
26
|
+
});
|
|
27
|
+
// Check if a factory to supply the requested key was not found
|
|
28
|
+
if (!factory) {
|
|
29
|
+
throw new Error(`Could not find a suitable factory for ${key.name}`);
|
|
30
|
+
}
|
|
31
|
+
// Resolve all dependencies first
|
|
32
|
+
const dependencies = yield Promise.all(factory.dependsOn.map((dependencyKey) => {
|
|
33
|
+
return this.inject(dependencyKey);
|
|
34
|
+
}));
|
|
35
|
+
// Call the factory to retrieve the dependency
|
|
36
|
+
return factory.initialize(...dependencies);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
static from(factories) {
|
|
40
|
+
return new ServiceModule(new Set(factories));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ServiceModule = ServiceModule;
|
|
44
|
+
function checkRecursiveDependencies(factory) {
|
|
45
|
+
const recursive = factory.dependsOn.some((dependencyKey) => {
|
|
46
|
+
return dependencyKey === factory.provides;
|
|
47
|
+
});
|
|
48
|
+
if (recursive) {
|
|
49
|
+
throw new Error('Recursive dependency detected on: ' + factory.provides.name);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function checkMissingDependencies(factory, factories) {
|
|
53
|
+
const missingDependencies = factory.dependsOn.filter((dependencyKey) => {
|
|
54
|
+
return !isRegistered(dependencyKey, factories);
|
|
55
|
+
});
|
|
56
|
+
if (missingDependencies.length === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const dependencyList = missingDependencies
|
|
60
|
+
.map((dependencyKey) => ` -> ${dependencyKey.name}`)
|
|
61
|
+
.join('\n');
|
|
62
|
+
throw new Error(`${factory.provides.name} will fail because it depends on:\n ${dependencyList}`);
|
|
63
|
+
}
|
|
64
|
+
function isRegistered(key, factories) {
|
|
65
|
+
return factories.some((factory) => factory.provides === key);
|
|
66
|
+
}
|
|
67
|
+
function isSuitable(key, factory) {
|
|
68
|
+
return (factory === null || factory === void 0 ? void 0 : factory.provides) === key;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=serviceModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceModule.js","sourceRoot":"","sources":["../src/serviceModule.ts"],"names":[],"mappings":";;;;;;;;;;;;AAIA,MAAa,aAAa;IAGxB,YAAY,SAA8D;QAFzD,cAAS,GAA+B,EAAE,CAAC;QAG1D,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjC,0BAA0B,CAAC,OAAO,CAAC,CAAC;YACpC,wBAAwB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC;IAEY,MAAM,CAAI,GAAkB;;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC9C,OAAO,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;YAEH,+DAA+D;YAC/D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,yCAAyC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACvE,CAAC;YAED,iCAAiC;YACjC,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,GAAG,CACpC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,aAAkC,EAAE,EAAE;gBAC3D,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACpC,CAAC,CAAC,CACH,CAAC;YAEF,8CAA8C;YAC9C,OAAO,OAAO,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC,CAAC;QAC7C,CAAC;KAAA;IAED,MAAM,CAAC,IAAI,CACT,SAA2D;QAE3D,OAAO,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/C,CAAC;CACF;AArCD,sCAqCC;AAED,SAAS,0BAA0B,CACjC,OAAuD;IAEvD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE;QACzD,OAAO,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,oCAAoC,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB,CAC/B,OAAuD,EACvD,SAAoC;IAEpC,MAAM,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAClD,CAAC,aAA8B,EAAE,EAAE;QACjC,OAAO,CAAC,YAAY,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IACjD,CAAC,CACF,CAAC;IACF,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,cAAc,GAAG,mBAAmB;SACvC,GAAG,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,OAAO,aAAa,CAAC,IAAI,EAAE,CAAC;SACnD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,uCAAuC,cAAc,EAAE,CAChF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,GAAwB,EACxB,SAAoC;IAEpC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,UAAU,CACjB,GAAkB,EAClB,OAAmC;IAEnC,OAAO,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,MAAK,GAAG,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceProvider.d.ts","sourceRoot":"","sources":["../src/serviceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceProvider.js","sourceRoot":"","sources":["../src/serviceProvider.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "composed-di",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"author": "Juan Herrera juanhr454@gmail.com",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"keywords": [
|
|
21
|
+
"dependency injection",
|
|
22
|
+
"di"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/imherrera/composed-di"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc --build",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@tsconfig/recommended": "^1.0.10",
|
|
35
|
+
"@types/node": "24.0.10",
|
|
36
|
+
"prettier": "^3.3.1",
|
|
37
|
+
"typescript": "^5.2.2"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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 of ServiceKey to tuple of their types
|
|
7
|
+
type DependencyTypes<T extends ServiceKey<unknown>[]> = {
|
|
8
|
+
[K in keyof T]: ServiceType<T[K]>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface ServiceFactory<T, D extends ServiceKey<unknown>[] = []> {
|
|
12
|
+
provides: ServiceKey<T>;
|
|
13
|
+
dependsOn: D;
|
|
14
|
+
|
|
15
|
+
initialize(...dependencies: DependencyTypes<D>): Promise<T>;
|
|
16
|
+
|
|
17
|
+
dispose(instance: T): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function singletonFactory<T, D extends ServiceKey<unknown>[] = []>({
|
|
21
|
+
provides,
|
|
22
|
+
dependsOn = [] as unknown as D,
|
|
23
|
+
initialize,
|
|
24
|
+
dispose = () => {},
|
|
25
|
+
}: {
|
|
26
|
+
provides: ServiceKey<T>;
|
|
27
|
+
dependsOn?: D;
|
|
28
|
+
initialize: (...dependencies: DependencyTypes<D>) => Promise<T>;
|
|
29
|
+
dispose?: (instance: T) => void;
|
|
30
|
+
}): ServiceFactory<T, D> {
|
|
31
|
+
let instance: T | undefined;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
provides,
|
|
35
|
+
dependsOn,
|
|
36
|
+
async initialize(...dependencies: DependencyTypes<D>): Promise<T> {
|
|
37
|
+
if (instance) {
|
|
38
|
+
return instance;
|
|
39
|
+
}
|
|
40
|
+
instance = await initialize(...dependencies);
|
|
41
|
+
return instance;
|
|
42
|
+
},
|
|
43
|
+
dispose(serviceInstance: T): void {
|
|
44
|
+
if (instance === serviceInstance) {
|
|
45
|
+
dispose(serviceInstance);
|
|
46
|
+
instance = undefined;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function oneShotFactory<T, D extends ServiceKey<unknown>[] = []>({
|
|
53
|
+
provides,
|
|
54
|
+
dependsOn,
|
|
55
|
+
initialize,
|
|
56
|
+
dispose = () => {},
|
|
57
|
+
}: {
|
|
58
|
+
provides: ServiceKey<T>;
|
|
59
|
+
dependsOn: D;
|
|
60
|
+
initialize: (...dependencies: DependencyTypes<D>) => Promise<T>;
|
|
61
|
+
dispose?: (instance: T) => void;
|
|
62
|
+
}): ServiceFactory<T, D> {
|
|
63
|
+
return {
|
|
64
|
+
provides,
|
|
65
|
+
dependsOn,
|
|
66
|
+
initialize,
|
|
67
|
+
dispose,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ServiceKey } from './serviceKey';
|
|
2
|
+
import { ServiceFactory } from './serviceFactory';
|
|
3
|
+
import { ServiceProvider } from './serviceProvider';
|
|
4
|
+
|
|
5
|
+
export class ServiceModule implements ServiceProvider {
|
|
6
|
+
private readonly factories: ServiceFactory<any, any>[] = [];
|
|
7
|
+
|
|
8
|
+
constructor(factories: Set<ServiceFactory<unknown, ServiceKey<unknown>[]>>) {
|
|
9
|
+
this.factories = Array.from(factories);
|
|
10
|
+
this.factories.forEach((factory) => {
|
|
11
|
+
checkRecursiveDependencies(factory);
|
|
12
|
+
checkMissingDependencies(factory, this.factories);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public async inject<T>(key: ServiceKey<T>): Promise<T> {
|
|
17
|
+
const factory = this.factories.find((factory) => {
|
|
18
|
+
return isSuitable(key, factory);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Check if a factory to supply the requested key was not found
|
|
22
|
+
if (!factory) {
|
|
23
|
+
throw new Error(`Could not find a suitable factory for ${key.name}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Resolve all dependencies first
|
|
27
|
+
const dependencies = await Promise.all(
|
|
28
|
+
factory.dependsOn.map((dependencyKey: ServiceKey<unknown>) => {
|
|
29
|
+
return this.inject(dependencyKey);
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Call the factory to retrieve the dependency
|
|
34
|
+
return factory.initialize(...dependencies);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static from(
|
|
38
|
+
factories: ServiceFactory<unknown, ServiceKey<unknown>[]>[],
|
|
39
|
+
): ServiceModule {
|
|
40
|
+
return new ServiceModule(new Set(factories));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function checkRecursiveDependencies(
|
|
45
|
+
factory: ServiceFactory<unknown, ServiceKey<unknown>[]>,
|
|
46
|
+
) {
|
|
47
|
+
const recursive = factory.dependsOn.some((dependencyKey) => {
|
|
48
|
+
return dependencyKey === factory.provides;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (recursive) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'Recursive dependency detected on: ' + factory.provides.name,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function checkMissingDependencies(
|
|
59
|
+
factory: ServiceFactory<unknown, ServiceKey<unknown>[]>,
|
|
60
|
+
factories: ServiceFactory<unknown>[],
|
|
61
|
+
) {
|
|
62
|
+
const missingDependencies = factory.dependsOn.filter(
|
|
63
|
+
(dependencyKey: ServiceKey<any>) => {
|
|
64
|
+
return !isRegistered(dependencyKey, factories);
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
if (missingDependencies.length === 0) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const dependencyList = missingDependencies
|
|
72
|
+
.map((dependencyKey) => ` -> ${dependencyKey.name}`)
|
|
73
|
+
.join('\n');
|
|
74
|
+
throw new Error(
|
|
75
|
+
`${factory.provides.name} will fail because it depends on:\n ${dependencyList}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isRegistered(
|
|
80
|
+
key: ServiceKey<unknown>,
|
|
81
|
+
factories: ServiceFactory<unknown>[],
|
|
82
|
+
) {
|
|
83
|
+
return factories.some((factory) => factory.provides === key);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isSuitable<T, D extends ServiceKey<unknown>[]>(
|
|
87
|
+
key: ServiceKey<T>,
|
|
88
|
+
factory: ServiceFactory<unknown, D>,
|
|
89
|
+
): factory is ServiceFactory<T, D> {
|
|
90
|
+
return factory?.provides === key;
|
|
91
|
+
}
|