lavva.exalushome
Version:
Library implementing communication and abstraction layers for ExalusHome system
123 lines • 6.48 kB
JavaScript
import { LoggerService } from './Services/Logging/LoggerService';
import { TypedEvent } from './TypedEvent';
export class DependencyContainer {
constructor() {
this._services = new Map();
this._serviceRegistrationEvent = new TypedEvent();
this._pendingResolutions = new Map();
this._nextResolutionId = 1;
}
static get Timers() {
return globalThis;
}
static get Instance() {
const g = globalThis;
if (g.__dc_instance)
return g.__dc_instance;
const inst = new DependencyContainer();
g.__dc_instance = inst;
if (typeof window !== 'undefined' && !window.LavvaDI)
window.appservices = inst;
this.Log = new LoggerService();
this.Log.Warning(DependencyContainer.ServiceName, 'Initializing DependencyContainer');
inst.RegisterService(this.Log);
this.IsInitialized = true;
return inst;
}
RegisterService(srv) {
var _a;
const serviceName = srv.GetServiceName();
if (this._services.has(serviceName)) {
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Warning(DependencyContainer.ServiceName, `Service "${serviceName}" is being re-registered and the previous instance will be overwritten.`);
}
this._services.set(serviceName, srv);
this._serviceRegistrationEvent.Invoke(srv);
}
GetService(name) {
return this._services.get(name);
}
GetServiceAsync(name, token) {
var _a, _b;
if (this._services.has(name)) {
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Debug(DependencyContainer.ServiceName, `Resolved service "${name}" immediately.`);
return Promise.resolve(this._services.get(name));
}
const resolution = this.CreatePendingResolution(name);
this._pendingResolutions.set(resolution.Id, resolution);
(_b = DependencyContainer.Log) === null || _b === void 0 ? void 0 : _b.Debug(DependencyContainer.ServiceName, `Waiting for service "${name}" (request #${resolution.Id}). Registered services: ${this.GetRegisteredServiceNamesForLogs()}.`);
return new Promise((resolve, reject) => {
const h = (s) => {
var _a;
if (s.GetServiceName() === name && this.TryCompleteResolution(resolution.Id)) {
this._serviceRegistrationEvent.Unsubscribe(h);
token === null || token === void 0 ? void 0 : token.CancellationEvent.Unsubscribe(onCancel);
const waitedMs = Date.now() - resolution.StartedAt;
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Info(DependencyContainer.ServiceName, `Resolved service "${name}" after ${waitedMs}ms (request #${resolution.Id}).`);
resolve(s);
}
};
const onCancel = () => {
var _a;
this._serviceRegistrationEvent.Unsubscribe(h);
if (!this.TryCompleteResolution(resolution.Id))
return;
const waitedMs = Date.now() - resolution.StartedAt;
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Warning(DependencyContainer.ServiceName, `Cancelled waiting for service "${name}" after ${waitedMs}ms (request #${resolution.Id}).`);
reject(new Error('cancelled'));
};
token === null || token === void 0 ? void 0 : token.CancellationEvent.Subscribe(onCancel);
this._serviceRegistrationEvent.Subscribe(h);
});
}
GetServiceWithTimeoutAsync(name, timeout) {
var _a, _b;
if (this._services.has(name)) {
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Debug(DependencyContainer.ServiceName, `Resolved service "${name}" immediately.`);
return Promise.resolve(this._services.get(name));
}
const resolution = this.CreatePendingResolution(name);
this._pendingResolutions.set(resolution.Id, resolution);
(_b = DependencyContainer.Log) === null || _b === void 0 ? void 0 : _b.Debug(DependencyContainer.ServiceName, `Waiting for service "${name}" with timeout ${timeout}ms (request #${resolution.Id}). Registered services: ${this.GetRegisteredServiceNamesForLogs()}.`);
return new Promise((resolve, reject) => {
const h = (s) => {
var _a;
if (s.GetServiceName() === name && this.TryCompleteResolution(resolution.Id)) {
DependencyContainer.Timers.clearTimeout(tid);
this._serviceRegistrationEvent.Unsubscribe(h);
const waitedMs = Date.now() - resolution.StartedAt;
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Info(DependencyContainer.ServiceName, `Resolved service "${name}" after ${waitedMs}ms (request #${resolution.Id}).`);
resolve(s);
}
};
const tid = DependencyContainer.Timers.setTimeout(() => {
var _a;
this._serviceRegistrationEvent.Unsubscribe(h);
if (!this.TryCompleteResolution(resolution.Id))
return;
const waitedMs = Date.now() - resolution.StartedAt;
(_a = DependencyContainer.Log) === null || _a === void 0 ? void 0 : _a.Warning(DependencyContainer.ServiceName, `Timeout waiting for service "${name}" after ${waitedMs}ms (request #${resolution.Id}). Registered services: ${this.GetRegisteredServiceNamesForLogs()}.`);
reject(new Error(`timeout waiting for "${name}"`));
}, timeout);
this._serviceRegistrationEvent.Subscribe(h);
});
}
CreatePendingResolution(name) {
return {
Id: this._nextResolutionId++,
ServiceName: name,
StartedAt: Date.now(),
};
}
TryCompleteResolution(id) {
return this._pendingResolutions.delete(id);
}
GetRegisteredServiceNamesForLogs() {
const names = [...this._services.keys()].sort();
if (names.length === 0)
return "<none>";
return names.join(", ");
}
}
DependencyContainer.ServiceName = "DependencyContainer";
DependencyContainer.IsInitialized = false;
//# sourceMappingURL=DependencyContainer.js.map