conteneur
Version:
TypeScript Inversion of Control container for Dependency Injection.
64 lines (62 loc) • 2.08 kB
JavaScript
// src/container/container.ts
var createContainer = (options) => createRegistry(options);
var createRegistry = (options) => {
const defaultStrategy = options?.defaultStrategy ?? "transient";
const parentRegistry = options?.parentRegistry;
const registrationMap = /* @__PURE__ */ new Map();
const resolutionCache = /* @__PURE__ */ new Map();
const resolutionStack = [];
const cradle = new Proxy({}, {
get: (_target, key) => resolve(key),
set: (_target, key) => {
throw new Error(`Direct assignment for ${key} is not allowed`);
}
});
const register = (entries) => {
for (const [key, [target, options2]] of Object.entries(entries)) {
registrationMap.set(key, {
target,
strategy: options2?.strategy ?? defaultStrategy
});
}
};
const resolve = (key) => {
if (resolutionStack.includes(key)) {
throw new Error(`Cyclic dependency detected: ${resolutionStack.join(" -> ")} -> ${String(key)}`);
}
resolutionStack.push(key);
const resolver = registrationMap.get(key) ?? parentRegistry?.registrationMap.get(key);
if (!resolver) {
resolutionStack.pop();
throw new Error(`Resolver for ${String(key)} not found`);
}
let resolved;
if (resolver.strategy === "singleton") {
let cached = resolutionCache.get(key);
if (cached === void 0) {
cached = inject(resolver.target);
resolutionCache.set(key, cached);
}
resolved = cached;
} else {
resolved = inject(resolver.target);
}
resolutionStack.pop();
return resolved;
};
const inject = (target) => {
if (typeof target !== "function") {
throw new Error("Injection target must be a function or a class");
}
if (!!target.prototype && typeof target.prototype === "object") {
return new target(cradle);
}
return target(cradle);
};
const createScope = () => createRegistry({
defaultStrategy,
parentRegistry: { registrationMap }
});
return { register, resolve, inject, createScope };
};
export { createContainer };