context-tree
Version:
Simple hierarchical dependency injection library, as the part of Fractal Architecture
126 lines • 4.06 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Context = exports.ContextResolvers = exports.ContextResolver = void 0;
class ContextResolver {
constructor(context, resolver) {
this.context = context;
this.resolver = resolver;
}
}
exports.ContextResolver = ContextResolver;
class ContextResolvers {
constructor() {
this.resolvers = new Map();
}
addResolver(resolver) {
const { context, resolver: _resolver } = resolver;
this.resolvers.set(context, _resolver);
}
removeResolver(context) {
this.resolvers.delete(context);
}
findResolver(_context) {
let context = _context;
while (context) {
const resolver = this.resolvers.get(context);
if (resolver) {
return resolver;
}
context = context.parent;
}
return null;
}
}
exports.ContextResolvers = ContextResolvers;
const contextTypeResolversCache = new WeakMap();
class Context {
constructor(name, parent = null) {
this.parent = parent;
this.name = name;
}
static resolvers(resolvers) {
const contextResolvers = new ContextResolvers();
for (const resolver of resolvers) {
contextResolvers.addResolver(resolver);
}
return contextResolvers;
}
static checkRequired(instance) {
const constructor = instance.constructor;
const requiredContexts = instance.requiredContexts ?? constructor?.requiredContexts;
if (requiredContexts && Array.isArray(requiredContexts)) {
const missingContexts = requiredContexts.filter((context) => !context.findResolver(instance));
if (missingContexts.length > 0) {
const contextNames = missingContexts
.map((context) => context.name)
.join(", ");
throw new Error(`Missing required contexts for instance of class '${constructor.name}': ${contextNames}`);
}
}
}
partial(name) {
return new Context(name, this);
}
resolvesTo(resolver) {
return new ContextResolver(this, resolver);
}
resolve(instance) {
const resolver = this.findResolver(instance);
if (resolver) {
return resolver();
}
else {
throw new Error(`Cannot find resolver for context ${this.name}`);
}
}
resolveMaybe(instance) {
const resolver = this.findResolver(instance);
if (resolver) {
return resolver();
}
else {
return undefined;
}
}
findResolver(startInstance) {
const bfsQueue = new Set();
bfsQueue.add(startInstance);
let resolver = null;
for (const instance of bfsQueue) {
const resolver = this.contextTypeResolver(instance) ??
instance.contextResolvers?.findResolver(this);
if (resolver) {
return resolver;
}
const { context } = instance;
if (context) {
if (Array.isArray(context)) {
for (const parent of context) {
bfsQueue.add(parent);
}
}
else {
bfsQueue.add(context);
}
}
}
return null;
}
contextTypeResolver(instance) {
let context = this;
while (context) {
if (instance.contextType === context) {
let instanceResolver = contextTypeResolversCache.get(instance);
if (!instanceResolver) {
instanceResolver = () => instance;
contextTypeResolversCache.set(instance, instanceResolver);
}
return instanceResolver;
}
context = context.parent;
}
return null;
}
}
exports.Context = Context;
//# sourceMappingURL=index.js.map
;