libp2p
Version:
JavaScript implementation of libp2p, a modular peer to peer network stack
120 lines • 3.94 kB
JavaScript
import { serviceCapabilities, serviceDependencies, isStartable } from '@libp2p/interface';
import { defaultLogger } from '@libp2p/logger';
import { MissingServiceError, UnmetServiceDependenciesError } from './errors.js';
class DefaultComponents {
components = {};
_started = false;
constructor(init = {}) {
this.components = {};
for (const [key, value] of Object.entries(init)) {
this.components[key] = value;
}
if (this.components.logger == null) {
this.components.logger = defaultLogger();
}
}
isStarted() {
return this._started;
}
async _invokeStartableMethod(methodName) {
await Promise.all(Object.values(this.components)
.filter(obj => isStartable(obj))
.map(async (startable) => {
await startable[methodName]?.();
}));
}
async beforeStart() {
await this._invokeStartableMethod('beforeStart');
}
async start() {
await this._invokeStartableMethod('start');
this._started = true;
}
async afterStart() {
await this._invokeStartableMethod('afterStart');
}
async beforeStop() {
await this._invokeStartableMethod('beforeStop');
}
async stop() {
await this._invokeStartableMethod('stop');
this._started = false;
}
async afterStop() {
await this._invokeStartableMethod('afterStop');
}
}
const OPTIONAL_SERVICES = [
'metrics',
'connectionProtector',
'dns'
];
const NON_SERVICE_PROPERTIES = [
'components',
'isStarted',
'beforeStart',
'start',
'afterStart',
'beforeStop',
'stop',
'afterStop',
'then',
'_invokeStartableMethod'
];
export function defaultComponents(init = {}) {
const components = new DefaultComponents(init);
const proxy = new Proxy(components, {
get(target, prop, receiver) {
if (typeof prop === 'string' && !NON_SERVICE_PROPERTIES.includes(prop)) {
const service = components.components[prop];
if (service == null && !OPTIONAL_SERVICES.includes(prop)) {
throw new MissingServiceError(`${prop} not set`);
}
return service;
}
return Reflect.get(target, prop, receiver);
},
set(target, prop, value) {
if (typeof prop === 'string') {
components.components[prop] = value;
}
else {
Reflect.set(target, prop, value);
}
return true;
}
});
// @ts-expect-error component keys are proxied
return proxy;
}
export function checkServiceDependencies(components) {
const serviceCapabilities = {};
for (const service of Object.values(components.components)) {
for (const capability of getServiceCapabilities(service)) {
serviceCapabilities[capability] = true;
}
}
for (const service of Object.values(components.components)) {
for (const capability of getServiceDependencies(service)) {
if (serviceCapabilities[capability] !== true) {
throw new UnmetServiceDependenciesError(`Service "${getServiceName(service)}" required capability "${capability}" but it was not provided by any component, you may need to add additional configuration when creating your node.`);
}
}
}
}
function getServiceCapabilities(service) {
if (Array.isArray(service?.[serviceCapabilities])) {
return service[serviceCapabilities];
}
return [];
}
function getServiceDependencies(service) {
if (Array.isArray(service?.[serviceDependencies])) {
return service[serviceDependencies];
}
return [];
}
function getServiceName(service) {
return service?.[Symbol.toStringTag] ?? service?.toString() ?? 'unknown';
}
//# sourceMappingURL=components.js.map