@frontmcp/di 1.12.0 → 1.12.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/esm/index.mjs +1 -1
- package/esm/package.json +2 -2
- package/index.js +1 -1
- package/package.json +2 -2
- package/registry/container.d.ts +1 -0
package/esm/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
function createTokenFactory(options={}){const prefix=options.prefix??"DI";return{type:name=>Symbol.for(`${prefix}:type:${name}`),meta:name=>Symbol.for(`${prefix}:meta:${name}`)}}var DiTokens=createTokenFactory({prefix:"DI"});var DESIGN_PARAMTYPES="design:paramtypes";var META_ASYNC_WITH=Symbol("di:async-with");var META_ASYNC_WITH_TOKENS=Symbol("di:async-with-tokens");var ProviderKind=(ProviderKind2=>{ProviderKind2["CLASS_TOKEN"]="CLASS_TOKEN";ProviderKind2["CLASS"]="CLASS";ProviderKind2["VALUE"]="VALUE";ProviderKind2["FACTORY"]="FACTORY";ProviderKind2["INJECTED"]="INJECTED";return ProviderKind2})(ProviderKind||{});var ProviderScope=(ProviderScope2=>{ProviderScope2["GLOBAL"]="global";ProviderScope2["CONTEXT"]="context";ProviderScope2["SESSION"]="session";ProviderScope2["REQUEST"]="request";return ProviderScope2})(ProviderScope||{});import{z}from"@frontmcp/lazy-zod";var providerMetadataSchema=z.object({id:z.string().optional(),name:z.string().min(1).optional(),description:z.string().optional(),scope:z.nativeEnum(ProviderScope).optional().default("global")}).passthrough();import"reflect-metadata";function getMetadata(key,target,propertyKey){return propertyKey!==void 0?Reflect.getMetadata(key,target,propertyKey):Reflect.getMetadata(key,target)}function setMetadata(key,value,target,propertyKey){if(propertyKey!==void 0){Reflect.defineMetadata(key,value,target,propertyKey)}else{Reflect.defineMetadata(key,value,target)}}function hasAsyncWith(klass){return!!getMetadata(META_ASYNC_WITH,klass)}var tokenName=t=>{if(typeof t==="string")return t;if(typeof t==="symbol")return t.description||"[ref]";return t?.name??"[ref]"};var isClass=x=>typeof x==="function"&&!!x.prototype;var isPromise=v=>!!v&&typeof v.then==="function";function getAsyncWithTokens(klass){const resolver=getMetadata(META_ASYNC_WITH_TOKENS,klass);if(!resolver)return null;const arr=resolver()??[];return arr.filter(isClass)}function readWithParamTypes(klass,forWhat){const lazy=getAsyncWithTokens(klass);if(lazy)return lazy;const meta=getMetadata(DESIGN_PARAMTYPES,klass,"with");if(meta===void 0){const where=`${klass.name}.with(...)`;const hint="Add @AsyncWith(() => [DepA, DepB]) to lazily declare deps, or decorate 'with' so TypeScript emits design:paramtypes.";throw new Error(`Cannot discover ${forWhat} deps for ${where}. ${hint}`)}const tdzIdx=meta.findIndex(t=>t===void 0);if(tdzIdx!==-1){const where=`${klass.name}.with(...)`;throw new Error(`Unresolved dependency at param #${tdzIdx} in ${where} (ESM circular import / TDZ).
|
|
2
2
|
Fix: add @AsyncWith(() => [/* deps */]) on ${klass.name} to avoid TDZ, or refactor the import cycle.`)}return meta.filter(isClass)}function depsOfClass(klass,phase){if(hasAsyncWith(klass))return readWithParamTypes(klass,phase);const ctorMeta=getMetadata(DESIGN_PARAMTYPES,klass)??[];const tdzIdx=ctorMeta.findIndex(t=>t===void 0);if(tdzIdx!==-1){throw new Error(`Unresolved constructor dependency in ${klass.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
3
3
|
Fix: convert this provider to @AsyncWith(() => [/* deps */]) and use a static with(...), or break the import cycle.`)}return ctorMeta.filter(isClass)}function depsOfFunc(fn,phase){const[_ignoreInput,...fnMeta]=getMetadata(DESIGN_PARAMTYPES,fn)??[];const tdzIdx=fnMeta.findIndex(t=>t===void 0);if(tdzIdx!==-1){throw new Error(`Unresolved function dependency in ${fn.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
4
|
-
`)}return fnMeta.filter(isClass)}function createProviderNormalizer(options){const{tokens}=options;return function normalizeProvider(item){if(isClass(item)){const isProviderClass=getMetadata(tokens.type,item);if(isProviderClass){const metadata={name:getMetadata(tokens.name,item)??item.name??"UnknownProvider",scope:getMetadata(tokens.scope,item),description:tokens.description?getMetadata(tokens.description,item):void 0,id:tokens.id?getMetadata(tokens.id,item):void 0};return{kind:"CLASS_TOKEN",provide:item,metadata}}const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj||"useFactory"in obj||"useClass"in obj){if(!obj.provide){const name=obj.name??"[object]";throw new Error(`Provider '${name}' is missing 'provide'.`)}}if("useValue"in obj){return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}let itemStr;try{itemStr=JSON.stringify(item)}catch{itemStr=String(item)}throw new Error(`Cannot normalize provider: unrecognized format ${itemStr}`)}}function providerDiscoveryDeps(rec,localTokens,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":{const all=[...rec.inject()];return all.filter(d=>localTokens.has(d))}case"CLASS":return depsOfClassFn(rec.useClass,"discovery");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"discovery")}}function providerInvocationTokens(rec,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":return[...rec.inject()];case"CLASS":return depsOfClassFn(rec.useClass,"invocation");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"invocation")}}var RegistryAbstract=class{asyncTimeoutMs=3e4;ready;providers;list;tokens;defs;graph;instances=new Map;constructor(name,providers,metadata,auto=true){if(providers&&typeof providers.addRegistry==="function"){providers.addRegistry(name,this)}this.providers=providers;this.list=metadata;const{tokens,defs,graph}=this.buildMap(metadata);this.tokens=tokens;this.defs=defs;this.graph=graph;if(auto){this.buildGraph();this.ready=this.initialize()}else{this.ready=Promise.resolve()}}hasAny(){return this.instances.size>0}getAllInstances(){return this.instances}};import"reflect-metadata";var DiContainer=class extends RegistryAbstract{order=new Set;normalizeProvider;parentContainer;constructor(providers,parent,options={}){const normalizer=options.providerTokens?createProviderNormalizer({tokens:options.providerTokens}):createBasicNormalizer();super("DiContainer",parent,providers,false);this.normalizeProvider=normalizer;this.parentContainer=parent;if(options.asyncTimeoutMs){this.asyncTimeoutMs=options.asyncTimeoutMs}this.buildGraph();this.topoSort();this.ready=this.initialize()}lookupDefInHierarchy(token){if(this.defs.has(token)){return{registry:this,rec:this.defs.get(token)}}return this.parentContainer?.lookupDefInHierarchy(token)}resolveDefaultFromHierarchy(token){const found=this.lookupDefInHierarchy(token);if(!found){throw new Error(`Cannot resolve token ${tokenName(token)}: not registered in hierarchy.`)}const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Dependency ${tokenName(token)} is scoped (${sc}) in ${registry.constructor.name}; cannot use as GLOBAL.`)}const inst=registry.instances.get(token);if(inst===void 0){throw new Error(`Dependency ${tokenName(token)} (GLOBAL) is not instantiated in ${registry.constructor.name}`)}return inst}buildMap(list){const tokens=new Set;const defs=new Map;const graph=new Map;const normalize=this.normalizeProvider??createBasicNormalizer();for(const raw of list){const rec=normalize(raw);const provide=rec.provide;tokens.add(provide);defs.set(provide,rec);graph.set(provide,new Set)}return{tokens,defs,graph}}buildGraph(){for(const token of this.tokens){const rec=this.defs.get(token);const deps=this.discoveryDeps(rec);for(const d of deps){const isLocal=this.tokens.has(d);const up=isLocal?void 0:this.lookupDefInHierarchy(d);if(!isLocal&&!up){throw new Error(`Provider ${tokenName(token)} depends on ${tokenName(d)}, which is not registered (local or parent).`)}const depRec=isLocal?this.defs.get(d):up.rec;const depScope=isLocal?this.getProviderScope(depRec):up.registry.getProviderScope(depRec);if(this.getProviderScope(rec)==="global"&&depScope!=="global"){throw new Error(`Invalid dependency: GLOBAL-scoped provider ${tokenName(token)} cannot depend on scoped provider ${tokenName(d)}.`)}if(isLocal){this.graph.get(token).add(d)}}}}topoSort(){const WHITE=0,GRAY=1,BLACK=2;const color=new Map;const order=new Set;const path=[];const dfs=n=>{color.set(n,GRAY);path.push(n);for(const dep of this.graph.get(n)??[]){const c=color.get(dep)??WHITE;if(c===GRAY){const idx=path.findIndex(p=>p===dep);const cycle=[...path.slice(idx),dep].map(t=>tokenName(t)).join(" -> ");throw new Error(`Dependency cycle detected: ${cycle}`)}if(c===WHITE)dfs(dep)}color.set(n,BLACK);order.add(n);path.pop()};for(const n of this.graph.keys()){if((color.get(n)??WHITE)===WHITE)dfs(n)}this.order=order}async initialize(opts){const force=!!opts?.force;const only=opts?.onlyTokens?new Set(opts.onlyTokens):null;for(const token of this.order){const rec=this.defs.get(token);if(this.getProviderScope(rec)!=="global")continue;if(only&&!only.has(token))continue;if(!force&&this.instances.has(token))continue;if(force&&this.instances.has(token)){this.instances.delete(token)}try{await this.instantiateOne(token,rec)}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing ${tokenName(token)}: ${msg}`)}}}async withTimeout(p,ms,label){if(ms<=0||!Number.isFinite(ms))return p;let timer;try{const tm=new Promise((_,rej)=>timer=setTimeout(()=>rej(new Error(`Timeout after ${ms}ms: ${label}`)),ms));return await Promise.race([p,tm])}finally{clearTimeout(timer)}}async resolveFactoryArg(t,store){if(store.has(t))return store.get(t);if(this.instances.has(t))return this.instances.get(t);const rec=this.defs.get(t);if(rec){const sc=this.getProviderScope(rec);if(sc==="global"){if(!this.instances.has(t)){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated`)}return this.instances.get(t)}else{await this.buildIntoStore(t,rec,store);return store.get(t)}}const up=this.lookupDefInHierarchy(t);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const inst=up.registry.instances.get(t);if(inst===void 0){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated in parent`)}return inst}else{await up.registry.buildIntoStore(t,up.rec,store);return store.get(t)}}if(isClass(t)){if(hasAsyncWith(t)){const out=t.with();return isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${t.name}.with(...)`):out}const instance=new t;const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${t.name}.init()`)}return instance}throw new Error(`Cannot resolve token ${tokenName(t)}: not registered in hierarchy and not constructable.`)}async instantiateOne(token,rec){switch(rec.kind){case"VALUE":{this.instances.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,this.instances))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;this.instances.set(token,val);return}case"CLASS":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.useClass;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}case"CLASS_TOKEN":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}}}async buildIntoStore(token,rec,store){if(store.has(token))return;try{switch(rec.kind){case"VALUE":{store.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,store))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;store.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveManagedForClass(d,store))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;store.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}store.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (scoped) ${tokenName(token)}: ${msg}`)}}async resolveManagedForClass(d,store){if(store.has(d))return store.get(d);if(this.instances.has(d))return this.instances.get(d);const depRec=this.defs.get(d);if(depRec){const sc=this.getProviderScope(depRec);if(sc==="global"){const v=this.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated`);return v}else{await this.buildIntoStore(d,depRec,store);return store.get(d)}}const up=this.lookupDefInHierarchy(d);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated in parent`);return v}else{await up.registry.buildIntoStore(d,up.rec,store);return store.get(d)}}throw new Error(`${tokenName(d)} is not instantiated`)}get(token){if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec&&this.getProviderScope(rec)!=="global"){throw new Error(`Provider ${tokenName(token)} is scoped (${this.getProviderScope(rec)}). Use getScoped(token, views).`)}if(this.parentContainer)return this.parentContainer.get(token);throw new Error(`Provider ${tokenName(token)} is not available in local or parent registries`)}resolve(cls){const found=this.lookupDefInHierarchy(cls);if(found){const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Provider ${tokenName(cls)} is scoped (${sc}). Use getScoped(token, views) or buildViews(...).`)}if(!registry.instances.has(cls)){throw new Error(`Provider ${tokenName(cls)} (GLOBAL scope) is not instantiated. Call await container.ready first.`)}return registry.instances.get(cls)}if(isClass(cls)){const instance=new cls;const init=instance?.init;if(typeof init==="function"){const result=init.call(instance);if(isPromise(result)){throw new Error(`Cannot use resolve() with async init() for ${cls.name??"unknown"}. Use get() after initialize() instead.`)}}return instance}throw new Error(`Cannot resolve ${tokenName(cls)}: not a registered GLOBAL provider and not a constructable class.`)}tryGet(token){try{return this.get(token)}catch{return void 0}}normalizeScope(scope){switch(scope){case"session":case"request":return"context";default:return scope}}getProviderScope(rec){const rawScope=rec.metadata?.scope??"global";return this.normalizeScope(rawScope)}discoveryDeps(rec){return providerDiscoveryDeps(rec,this.tokens,(k,phase)=>depsOfClass(k,phase))}invocationTokens(_token,rec){return providerInvocationTokens(rec,(k,phase)=>depsOfClass(k,phase))}getAllSingletons(){return this.instances}async buildViews(sessionKey,contextProviders){const global=this.getAllSingletons();const contextStore=new Map(contextProviders);for(const token of this.order){const rec=this.defs.get(token);if(!rec)continue;if(this.getProviderScope(rec)!=="context")continue;if(contextStore.has(token))continue;await this.buildIntoStoreWithViews(token,rec,contextStore,global)}return{global,context:contextStore}}async buildIntoStoreWithViews(token,rec,contextStore,globalStore){if(contextStore.has(token))return;try{switch(rec.kind){case"VALUE":{contextStore.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFromViews(d,contextStore,globalStore))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;contextStore.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveFromViews(d,contextStore,globalStore))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;contextStore.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}contextStore.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (context-scoped) ${tokenName(token)}: ${msg}`)}}async resolveFromViews(token,contextStore,globalStore){if(contextStore.has(token))return contextStore.get(token);if(globalStore.has(token))return globalStore.get(token);if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec){const scope=this.getProviderScope(rec);if(scope==="global"){throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated`)}else{await this.buildIntoStoreWithViews(token,rec,contextStore,globalStore);return contextStore.get(token)}}const up=this.lookupDefInHierarchy(token);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(token);if(v!==void 0)return v;throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated in parent`)}}throw new Error(`Cannot resolve dependency ${tokenName(token)} from views`)}getScoped(token,views){if(views.context.has(token))return views.context.get(token);if(views.global.has(token))return views.global.get(token);if(this.instances.has(token))return this.instances.get(token);throw new Error(`Provider ${tokenName(token)} not found in views. Ensure it was built via buildViews().`)}injectProvider(injected){const rec={...injected,kind:"INJECTED"};this.tokens.add(rec.provide);this.defs.set(rec.provide,rec);this.graph.set(rec.provide,new Set);this.instances.set(rec.provide,rec.value)}};function createBasicNormalizer(){return function normalizeProvider(item){if(isClass(item)){const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj){if(!obj.provide){throw new Error(`Provider with useValue must have a 'provide' field`)}return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){if(!obj.provide){throw new Error(`Provider with useFactory must have a 'provide' field`)}return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){if(!obj.provide){throw new Error(`Provider with useClass must have a 'provide' field`)}return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}throw new Error(`Cannot normalize provider: unrecognized format`)}}var IndexedRegistry=class extends RegistryAbstract{localRows=[];adopted=new Map;children=new Set;childSubscriptions=new Map;byQualifiedId=new Map;byName=new Map;byOwner=new Map;byOwnerAndName=new Map;version=0;emitter;createEmitter(){const handlers=new Set;return{emit:event=>handlers.forEach(h=>h(event)),on:handler=>{handlers.add(handler);return()=>handlers.delete(handler)}}}constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto);this.emitter=this.createEmitter()}adoptFromChild(child,childOwner){const childRows=child.listAllIndexed();const adoptedRows=childRows.map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,adoptedRows);this.children.add(child);const unsubscribe=child.subscribe({immediate:false},()=>{const latest=child.listAllIndexed().map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,latest);this.reindex();this.bump("reset")});this.childSubscriptions.set(child,unsubscribe);this.reindex();this.bump("reset")}removeChild(child){const unsub=this.childSubscriptions.get(child);if(unsub)unsub();this.childSubscriptions.delete(child);this.children.delete(child);this.adopted.delete(child);this.reindex();this.bump("reset")}relineage(row,prepend){const merged=[...prepend,...row.lineage];const lineage=this.dedupLineage(merged);const ownerKey=this.ownerKeyOf(lineage);const qualifiedName=this.qualifiedNameOf(row.baseName,lineage);const qualifiedId=`${ownerKey}:${row.token.toString()}`;return{...row,lineage,ownerKey,qualifiedName,qualifiedId}}dedupLineage(lineage){const result=[];for(const seg of lineage){const last=result[result.length-1];if(!last||last.type!==seg.type||last.id!==seg.id){result.push(seg)}}return result}ownerKeyOf(lineage){return lineage.map(s=>`${s.type}:${s.id}`).join("/")}qualifiedNameOf(baseName,lineage){const prefix=lineage.map(s=>s.name||s.id).join("/");return prefix?`${prefix}/${baseName}`:baseName}reindex(){const allRows=[...this.localRows];for(const rows of this.adopted.values()){allRows.push(...rows)}this.byQualifiedId.clear();this.byName.clear();this.byOwner.clear();this.byOwnerAndName.clear();for(const row of allRows){this.byQualifiedId.set(row.qualifiedId,row);const nameList=this.byName.get(row.baseName)??[];nameList.push(row);this.byName.set(row.baseName,nameList);const ownerList=this.byOwner.get(row.ownerKey)??[];ownerList.push(row);this.byOwner.set(row.ownerKey,ownerList);const ownerNameKey=`${row.ownerKey}:${row.baseName}`;this.byOwnerAndName.set(ownerNameKey,row)}this.buildIndexes(allRows)}subscribe(opts,callback){if(opts.immediate){callback({kind:"reset",changeScope:"global",version:this.version,snapshot:this.listAllIndexed()})}return this.emitter.on(e=>{if(opts.filter){const filtered=e.snapshot.filter(row=>opts.filter(row.instance));callback({...e,snapshot:filtered})}else{callback(e)}})}bump(kind){const version=++this.version;this.emitter.emit({kind,changeScope:"global",version,snapshot:this.listAllIndexed()})}findByName(name){const rows=this.byName.get(name);return rows?.[0]?.instance}findAllByName(name){const rows=this.byName.get(name);return rows?.map(r=>r.instance)??[]}findByQualifiedId(qualifiedId){return this.byQualifiedId.get(qualifiedId)?.instance}listByOwner(ownerKey){return this.byOwner.get(ownerKey)??[]}listAllIndexed(){const all=[...this.localRows];for(const rows of this.adopted.values()){all.push(...rows)}return all}listAllInstances(){return this.listAllIndexed().map(r=>r.instance)}hasAny(){return this.localRows.length>0||this.adopted.size>0}dispose(){for(const unsub of this.childSubscriptions.values()){unsub()}this.childSubscriptions.clear();this.children.clear();this.adopted.clear()}};var SimpleRegistry=class extends RegistryAbstract{constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto)}getAll(){return[...this.instances.values()]}count(){return this.instances.size}};import"reflect-metadata";function AsyncWith(tokensFactory){return target=>{Reflect.defineMetadata(META_ASYNC_WITH,true,target);Reflect.defineMetadata(META_ASYNC_WITH_TOKENS,tokensFactory,target)}}export{AsyncWith,DESIGN_PARAMTYPES,DiContainer,DiTokens,IndexedRegistry,META_ASYNC_WITH,META_ASYNC_WITH_TOKENS,ProviderKind,ProviderScope,RegistryAbstract,SimpleRegistry,createProviderNormalizer,createTokenFactory,depsOfClass,depsOfFunc,getAsyncWithTokens,getMetadata,hasAsyncWith,isClass,isPromise,providerDiscoveryDeps,providerInvocationTokens,providerMetadataSchema,readWithParamTypes,setMetadata,tokenName};
|
|
4
|
+
`)}return fnMeta.filter(isClass)}function createProviderNormalizer(options){const{tokens}=options;return function normalizeProvider(item){if(isClass(item)){const isProviderClass=getMetadata(tokens.type,item);if(isProviderClass){const metadata={name:getMetadata(tokens.name,item)??item.name??"UnknownProvider",scope:getMetadata(tokens.scope,item),description:tokens.description?getMetadata(tokens.description,item):void 0,id:tokens.id?getMetadata(tokens.id,item):void 0};return{kind:"CLASS_TOKEN",provide:item,metadata}}const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj||"useFactory"in obj||"useClass"in obj){if(!obj.provide){const name=obj.name??"[object]";throw new Error(`Provider '${name}' is missing 'provide'.`)}}if("useValue"in obj){return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}let itemStr;try{itemStr=JSON.stringify(item)}catch{itemStr=String(item)}throw new Error(`Cannot normalize provider: unrecognized format ${itemStr}`)}}function providerDiscoveryDeps(rec,localTokens,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":{const all=[...rec.inject()];return all.filter(d=>localTokens.has(d))}case"CLASS":return depsOfClassFn(rec.useClass,"discovery");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"discovery")}}function providerInvocationTokens(rec,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":return[...rec.inject()];case"CLASS":return depsOfClassFn(rec.useClass,"invocation");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"invocation")}}var RegistryAbstract=class{asyncTimeoutMs=3e4;ready;providers;list;tokens;defs;graph;instances=new Map;constructor(name,providers,metadata,auto=true){if(providers&&typeof providers.addRegistry==="function"){providers.addRegistry(name,this)}this.providers=providers;this.list=metadata;const{tokens,defs,graph}=this.buildMap(metadata);this.tokens=tokens;this.defs=defs;this.graph=graph;if(auto){this.buildGraph();this.ready=this.initialize()}else{this.ready=Promise.resolve()}}hasAny(){return this.instances.size>0}getAllInstances(){return this.instances}};import"reflect-metadata";var DiContainer=class extends RegistryAbstract{order=new Set;normalizeProvider;parentContainer;constructor(providers,parent,options={}){const normalizer=options.providerTokens?createProviderNormalizer({tokens:options.providerTokens}):createBasicNormalizer();super("DiContainer",parent,[],false);this.normalizeProvider=normalizer;this.parentContainer=parent;this.list=providers;const{tokens,defs,graph}=this.buildMap(providers);this.tokens=tokens;this.defs=defs;this.graph=graph;if(options.asyncTimeoutMs){this.asyncTimeoutMs=options.asyncTimeoutMs}this.buildGraph();this.topoSort();this.ready=this.initialize()}lookupDefInHierarchy(token){if(this.defs.has(token)){return{registry:this,rec:this.defs.get(token)}}return this.parentContainer?.lookupDefInHierarchy(token)}resolveDefaultFromHierarchy(token){const found=this.lookupDefInHierarchy(token);if(!found){throw new Error(`Cannot resolve token ${tokenName(token)}: not registered in hierarchy.`)}const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Dependency ${tokenName(token)} is scoped (${sc}) in ${registry.constructor.name}; cannot use as GLOBAL.`)}const inst=registry.instances.get(token);if(inst===void 0){throw new Error(`Dependency ${tokenName(token)} (GLOBAL) is not instantiated in ${registry.constructor.name}`)}return inst}buildMap(list){const tokens=new Set;const defs=new Map;const graph=new Map;const normalize=this.normalizeProvider??createBasicNormalizer();for(const raw of list){const rec=normalize(raw);const provide=rec.provide;tokens.add(provide);defs.set(provide,rec);graph.set(provide,new Set)}return{tokens,defs,graph}}buildGraph(){for(const token of this.tokens){const rec=this.defs.get(token);const deps=this.discoveryDeps(rec);for(const d of deps){const isLocal=this.tokens.has(d);const up=isLocal?void 0:this.lookupDefInHierarchy(d);if(!isLocal&&!up){throw new Error(`Provider ${tokenName(token)} depends on ${tokenName(d)}, which is not registered (local or parent).`)}const depRec=isLocal?this.defs.get(d):up.rec;const depScope=isLocal?this.getProviderScope(depRec):up.registry.getProviderScope(depRec);if(this.getProviderScope(rec)==="global"&&depScope!=="global"){throw new Error(`Invalid dependency: GLOBAL-scoped provider ${tokenName(token)} cannot depend on scoped provider ${tokenName(d)}.`)}if(isLocal){this.graph.get(token).add(d)}}}}topoSort(){const WHITE=0,GRAY=1,BLACK=2;const color=new Map;const order=new Set;const path=[];const dfs=n=>{color.set(n,GRAY);path.push(n);for(const dep of this.graph.get(n)??[]){const c=color.get(dep)??WHITE;if(c===GRAY){const idx=path.findIndex(p=>p===dep);const cycle=[...path.slice(idx),dep].map(t=>tokenName(t)).join(" -> ");throw new Error(`Dependency cycle detected: ${cycle}`)}if(c===WHITE)dfs(dep)}color.set(n,BLACK);order.add(n);path.pop()};for(const n of this.graph.keys()){if((color.get(n)??WHITE)===WHITE)dfs(n)}this.order=order}async initialize(opts){const force=!!opts?.force;const only=opts?.onlyTokens?new Set(opts.onlyTokens):null;for(const token of this.order){const rec=this.defs.get(token);if(this.getProviderScope(rec)!=="global")continue;if(only&&!only.has(token))continue;if(!force&&this.instances.has(token))continue;if(force&&this.instances.has(token)){this.instances.delete(token)}try{await this.instantiateOne(token,rec)}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing ${tokenName(token)}: ${msg}`)}}}async withTimeout(p,ms,label){if(ms<=0||!Number.isFinite(ms))return p;let timer;try{const tm=new Promise((_,rej)=>timer=setTimeout(()=>rej(new Error(`Timeout after ${ms}ms: ${label}`)),ms));return await Promise.race([p,tm])}finally{clearTimeout(timer)}}async resolveFactoryArg(t,store){if(store.has(t))return store.get(t);if(this.instances.has(t))return this.instances.get(t);const rec=this.defs.get(t);if(rec){const sc=this.getProviderScope(rec);if(sc==="global"){if(!this.instances.has(t)){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated`)}return this.instances.get(t)}else{await this.buildIntoStore(t,rec,store);return store.get(t)}}const up=this.lookupDefInHierarchy(t);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const inst=up.registry.instances.get(t);if(inst===void 0){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated in parent`)}return inst}else{await up.registry.buildIntoStore(t,up.rec,store);return store.get(t)}}if(isClass(t)){if(hasAsyncWith(t)){const out=t.with();return isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${t.name}.with(...)`):out}const instance=new t;const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${t.name}.init()`)}return instance}throw new Error(`Cannot resolve token ${tokenName(t)}: not registered in hierarchy and not constructable.`)}async instantiateOne(token,rec){switch(rec.kind){case"VALUE":{this.instances.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,this.instances))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;this.instances.set(token,val);return}case"CLASS":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.useClass;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}case"CLASS_TOKEN":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}}}async buildIntoStore(token,rec,store){if(store.has(token))return;try{switch(rec.kind){case"VALUE":{store.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,store))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;store.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveManagedForClass(d,store))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;store.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}store.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (scoped) ${tokenName(token)}: ${msg}`)}}async resolveManagedForClass(d,store){if(store.has(d))return store.get(d);if(this.instances.has(d))return this.instances.get(d);const depRec=this.defs.get(d);if(depRec){const sc=this.getProviderScope(depRec);if(sc==="global"){const v=this.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated`);return v}else{await this.buildIntoStore(d,depRec,store);return store.get(d)}}const up=this.lookupDefInHierarchy(d);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated in parent`);return v}else{await up.registry.buildIntoStore(d,up.rec,store);return store.get(d)}}throw new Error(`${tokenName(d)} is not instantiated`)}get(token){if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec&&this.getProviderScope(rec)!=="global"){throw new Error(`Provider ${tokenName(token)} is scoped (${this.getProviderScope(rec)}). Use getScoped(token, views).`)}if(this.parentContainer)return this.parentContainer.get(token);throw new Error(`Provider ${tokenName(token)} is not available in local or parent registries`)}resolve(cls){const found=this.lookupDefInHierarchy(cls);if(found){const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Provider ${tokenName(cls)} is scoped (${sc}). Use getScoped(token, views) or buildViews(...).`)}if(!registry.instances.has(cls)){throw new Error(`Provider ${tokenName(cls)} (GLOBAL scope) is not instantiated. Call await container.ready first.`)}return registry.instances.get(cls)}if(isClass(cls)){const instance=new cls;const init=instance?.init;if(typeof init==="function"){const result=init.call(instance);if(isPromise(result)){throw new Error(`Cannot use resolve() with async init() for ${cls.name??"unknown"}. Use get() after initialize() instead.`)}}return instance}throw new Error(`Cannot resolve ${tokenName(cls)}: not a registered GLOBAL provider and not a constructable class.`)}tryGet(token){try{return this.get(token)}catch{return void 0}}normalizeScope(scope){switch(scope){case"session":case"request":return"context";default:return scope}}getProviderScope(rec){const rawScope=rec.metadata?.scope??"global";return this.normalizeScope(rawScope)}discoveryDeps(rec){if(rec.kind==="FACTORY"){return rec.inject().filter(dep=>this.tokens.has(dep)||this.lookupDefInHierarchy(dep)!==void 0)}return providerDiscoveryDeps(rec,this.tokens,(k,phase)=>depsOfClass(k,phase))}invocationTokens(_token,rec){return providerInvocationTokens(rec,(k,phase)=>depsOfClass(k,phase))}getAllSingletons(){return this.instances}async buildViews(sessionKey,contextProviders){const global=this.getAllSingletons();const contextStore=new Map(contextProviders);for(const token of this.order){const rec=this.defs.get(token);if(!rec)continue;if(this.getProviderScope(rec)!=="context")continue;if(contextStore.has(token))continue;await this.buildIntoStoreWithViews(token,rec,contextStore,global)}return{global,context:contextStore}}async buildIntoStoreWithViews(token,rec,contextStore,globalStore){if(contextStore.has(token))return;try{switch(rec.kind){case"VALUE":{contextStore.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFromViews(d,contextStore,globalStore))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;contextStore.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveFromViews(d,contextStore,globalStore))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;contextStore.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}contextStore.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (context-scoped) ${tokenName(token)}: ${msg}`)}}async resolveFromViews(token,contextStore,globalStore){if(contextStore.has(token))return contextStore.get(token);if(globalStore.has(token))return globalStore.get(token);if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec){const scope=this.getProviderScope(rec);if(scope==="global"){throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated`)}else{await this.buildIntoStoreWithViews(token,rec,contextStore,globalStore);return contextStore.get(token)}}const up=this.lookupDefInHierarchy(token);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(token);if(v!==void 0)return v;throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated in parent`)}}throw new Error(`Cannot resolve dependency ${tokenName(token)} from views`)}getScoped(token,views){if(views.context.has(token))return views.context.get(token);if(views.global.has(token))return views.global.get(token);if(this.instances.has(token))return this.instances.get(token);throw new Error(`Provider ${tokenName(token)} not found in views. Ensure it was built via buildViews().`)}injectProvider(injected){const rec={...injected,kind:"INJECTED"};this.tokens.add(rec.provide);this.defs.set(rec.provide,rec);this.graph.set(rec.provide,new Set);this.instances.set(rec.provide,rec.value)}};function createBasicNormalizer(){return function normalizeProvider(item){if(isClass(item)){const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj){if(!obj.provide){throw new Error(`Provider with useValue must have a 'provide' field`)}return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){if(!obj.provide){throw new Error(`Provider with useFactory must have a 'provide' field`)}return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){if(!obj.provide){throw new Error(`Provider with useClass must have a 'provide' field`)}return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}throw new Error(`Cannot normalize provider: unrecognized format`)}}var IndexedRegistry=class extends RegistryAbstract{localRows=[];adopted=new Map;children=new Set;childSubscriptions=new Map;byQualifiedId=new Map;byName=new Map;byOwner=new Map;byOwnerAndName=new Map;version=0;emitter;createEmitter(){const handlers=new Set;return{emit:event=>handlers.forEach(h=>h(event)),on:handler=>{handlers.add(handler);return()=>handlers.delete(handler)}}}constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto);this.emitter=this.createEmitter()}adoptFromChild(child,childOwner){const childRows=child.listAllIndexed();const adoptedRows=childRows.map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,adoptedRows);this.children.add(child);const unsubscribe=child.subscribe({immediate:false},()=>{const latest=child.listAllIndexed().map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,latest);this.reindex();this.bump("reset")});this.childSubscriptions.set(child,unsubscribe);this.reindex();this.bump("reset")}removeChild(child){const unsub=this.childSubscriptions.get(child);if(unsub)unsub();this.childSubscriptions.delete(child);this.children.delete(child);this.adopted.delete(child);this.reindex();this.bump("reset")}relineage(row,prepend){const merged=[...prepend,...row.lineage];const lineage=this.dedupLineage(merged);const ownerKey=this.ownerKeyOf(lineage);const qualifiedName=this.qualifiedNameOf(row.baseName,lineage);const qualifiedId=`${ownerKey}:${row.token.toString()}`;return{...row,lineage,ownerKey,qualifiedName,qualifiedId}}dedupLineage(lineage){const result=[];for(const seg of lineage){const last=result[result.length-1];if(!last||last.type!==seg.type||last.id!==seg.id){result.push(seg)}}return result}ownerKeyOf(lineage){return lineage.map(s=>`${s.type}:${s.id}`).join("/")}qualifiedNameOf(baseName,lineage){const prefix=lineage.map(s=>s.name||s.id).join("/");return prefix?`${prefix}/${baseName}`:baseName}reindex(){const allRows=[...this.localRows];for(const rows of this.adopted.values()){allRows.push(...rows)}this.byQualifiedId.clear();this.byName.clear();this.byOwner.clear();this.byOwnerAndName.clear();for(const row of allRows){this.byQualifiedId.set(row.qualifiedId,row);const nameList=this.byName.get(row.baseName)??[];nameList.push(row);this.byName.set(row.baseName,nameList);const ownerList=this.byOwner.get(row.ownerKey)??[];ownerList.push(row);this.byOwner.set(row.ownerKey,ownerList);const ownerNameKey=`${row.ownerKey}:${row.baseName}`;this.byOwnerAndName.set(ownerNameKey,row)}this.buildIndexes(allRows)}subscribe(opts,callback){if(opts.immediate){callback({kind:"reset",changeScope:"global",version:this.version,snapshot:this.listAllIndexed()})}return this.emitter.on(e=>{if(opts.filter){const filtered=e.snapshot.filter(row=>opts.filter(row.instance));callback({...e,snapshot:filtered})}else{callback(e)}})}bump(kind){const version=++this.version;this.emitter.emit({kind,changeScope:"global",version,snapshot:this.listAllIndexed()})}findByName(name){const rows=this.byName.get(name);return rows?.[0]?.instance}findAllByName(name){const rows=this.byName.get(name);return rows?.map(r=>r.instance)??[]}findByQualifiedId(qualifiedId){return this.byQualifiedId.get(qualifiedId)?.instance}listByOwner(ownerKey){return this.byOwner.get(ownerKey)??[]}listAllIndexed(){const all=[...this.localRows];for(const rows of this.adopted.values()){all.push(...rows)}return all}listAllInstances(){return this.listAllIndexed().map(r=>r.instance)}hasAny(){return this.localRows.length>0||this.adopted.size>0}dispose(){for(const unsub of this.childSubscriptions.values()){unsub()}this.childSubscriptions.clear();this.children.clear();this.adopted.clear()}};var SimpleRegistry=class extends RegistryAbstract{constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto)}getAll(){return[...this.instances.values()]}count(){return this.instances.size}};import"reflect-metadata";function AsyncWith(tokensFactory){return target=>{Reflect.defineMetadata(META_ASYNC_WITH,true,target);Reflect.defineMetadata(META_ASYNC_WITH_TOKENS,tokensFactory,target)}}export{AsyncWith,DESIGN_PARAMTYPES,DiContainer,DiTokens,IndexedRegistry,META_ASYNC_WITH,META_ASYNC_WITH_TOKENS,ProviderKind,ProviderScope,RegistryAbstract,SimpleRegistry,createProviderNormalizer,createTokenFactory,depsOfClass,depsOfFunc,getAsyncWithTokens,getMetadata,hasAsyncWith,isClass,isPromise,providerDiscoveryDeps,providerInvocationTokens,providerMetadataSchema,readWithParamTypes,setMetadata,tokenName};
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/di",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "Generic dependency injection container and registry utilities for TypeScript applications",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=24.0.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@frontmcp/lazy-zod": "1.12.
|
|
51
|
+
"@frontmcp/lazy-zod": "1.12.1",
|
|
52
52
|
"reflect-metadata": "^0.2.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var index_exports={};__export(index_exports,{AsyncWith:()=>AsyncWith,DESIGN_PARAMTYPES:()=>DESIGN_PARAMTYPES,DiContainer:()=>DiContainer,DiTokens:()=>DiTokens,IndexedRegistry:()=>IndexedRegistry,META_ASYNC_WITH:()=>META_ASYNC_WITH,META_ASYNC_WITH_TOKENS:()=>META_ASYNC_WITH_TOKENS,ProviderKind:()=>ProviderKind,ProviderScope:()=>ProviderScope,RegistryAbstract:()=>RegistryAbstract,SimpleRegistry:()=>SimpleRegistry,createProviderNormalizer:()=>createProviderNormalizer,createTokenFactory:()=>createTokenFactory,depsOfClass:()=>depsOfClass,depsOfFunc:()=>depsOfFunc,getAsyncWithTokens:()=>getAsyncWithTokens,getMetadata:()=>getMetadata,hasAsyncWith:()=>hasAsyncWith,isClass:()=>isClass,isPromise:()=>isPromise,providerDiscoveryDeps:()=>providerDiscoveryDeps,providerInvocationTokens:()=>providerInvocationTokens,providerMetadataSchema:()=>providerMetadataSchema,readWithParamTypes:()=>readWithParamTypes,setMetadata:()=>setMetadata,tokenName:()=>tokenName});module.exports=__toCommonJS(index_exports);function createTokenFactory(options={}){const prefix=options.prefix??"DI";return{type:name=>Symbol.for(`${prefix}:type:${name}`),meta:name=>Symbol.for(`${prefix}:meta:${name}`)}}var DiTokens=createTokenFactory({prefix:"DI"});var DESIGN_PARAMTYPES="design:paramtypes";var META_ASYNC_WITH=Symbol("di:async-with");var META_ASYNC_WITH_TOKENS=Symbol("di:async-with-tokens");var ProviderKind=(ProviderKind2=>{ProviderKind2["CLASS_TOKEN"]="CLASS_TOKEN";ProviderKind2["CLASS"]="CLASS";ProviderKind2["VALUE"]="VALUE";ProviderKind2["FACTORY"]="FACTORY";ProviderKind2["INJECTED"]="INJECTED";return ProviderKind2})(ProviderKind||{});var ProviderScope=(ProviderScope2=>{ProviderScope2["GLOBAL"]="global";ProviderScope2["CONTEXT"]="context";ProviderScope2["SESSION"]="session";ProviderScope2["REQUEST"]="request";return ProviderScope2})(ProviderScope||{});var import_lazy_zod=require("@frontmcp/lazy-zod");var providerMetadataSchema=import_lazy_zod.z.object({id:import_lazy_zod.z.string().optional(),name:import_lazy_zod.z.string().min(1).optional(),description:import_lazy_zod.z.string().optional(),scope:import_lazy_zod.z.nativeEnum(ProviderScope).optional().default("global")}).passthrough();var import_reflect_metadata=require("reflect-metadata");function getMetadata(key,target,propertyKey){return propertyKey!==void 0?Reflect.getMetadata(key,target,propertyKey):Reflect.getMetadata(key,target)}function setMetadata(key,value,target,propertyKey){if(propertyKey!==void 0){Reflect.defineMetadata(key,value,target,propertyKey)}else{Reflect.defineMetadata(key,value,target)}}function hasAsyncWith(klass){return!!getMetadata(META_ASYNC_WITH,klass)}var tokenName=t=>{if(typeof t==="string")return t;if(typeof t==="symbol")return t.description||"[ref]";return t?.name??"[ref]"};var isClass=x=>typeof x==="function"&&!!x.prototype;var isPromise=v=>!!v&&typeof v.then==="function";function getAsyncWithTokens(klass){const resolver=getMetadata(META_ASYNC_WITH_TOKENS,klass);if(!resolver)return null;const arr=resolver()??[];return arr.filter(isClass)}function readWithParamTypes(klass,forWhat){const lazy=getAsyncWithTokens(klass);if(lazy)return lazy;const meta=getMetadata(DESIGN_PARAMTYPES,klass,"with");if(meta===void 0){const where=`${klass.name}.with(...)`;const hint="Add @AsyncWith(() => [DepA, DepB]) to lazily declare deps, or decorate 'with' so TypeScript emits design:paramtypes.";throw new Error(`Cannot discover ${forWhat} deps for ${where}. ${hint}`)}const tdzIdx=meta.findIndex(t=>t===void 0);if(tdzIdx!==-1){const where=`${klass.name}.with(...)`;throw new Error(`Unresolved dependency at param #${tdzIdx} in ${where} (ESM circular import / TDZ).
|
|
2
2
|
Fix: add @AsyncWith(() => [/* deps */]) on ${klass.name} to avoid TDZ, or refactor the import cycle.`)}return meta.filter(isClass)}function depsOfClass(klass,phase){if(hasAsyncWith(klass))return readWithParamTypes(klass,phase);const ctorMeta=getMetadata(DESIGN_PARAMTYPES,klass)??[];const tdzIdx=ctorMeta.findIndex(t=>t===void 0);if(tdzIdx!==-1){throw new Error(`Unresolved constructor dependency in ${klass.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
3
3
|
Fix: convert this provider to @AsyncWith(() => [/* deps */]) and use a static with(...), or break the import cycle.`)}return ctorMeta.filter(isClass)}function depsOfFunc(fn,phase){const[_ignoreInput,...fnMeta]=getMetadata(DESIGN_PARAMTYPES,fn)??[];const tdzIdx=fnMeta.findIndex(t=>t===void 0);if(tdzIdx!==-1){throw new Error(`Unresolved function dependency in ${fn.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
4
|
-
`)}return fnMeta.filter(isClass)}function createProviderNormalizer(options){const{tokens}=options;return function normalizeProvider(item){if(isClass(item)){const isProviderClass=getMetadata(tokens.type,item);if(isProviderClass){const metadata={name:getMetadata(tokens.name,item)??item.name??"UnknownProvider",scope:getMetadata(tokens.scope,item),description:tokens.description?getMetadata(tokens.description,item):void 0,id:tokens.id?getMetadata(tokens.id,item):void 0};return{kind:"CLASS_TOKEN",provide:item,metadata}}const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj||"useFactory"in obj||"useClass"in obj){if(!obj.provide){const name=obj.name??"[object]";throw new Error(`Provider '${name}' is missing 'provide'.`)}}if("useValue"in obj){return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}let itemStr;try{itemStr=JSON.stringify(item)}catch{itemStr=String(item)}throw new Error(`Cannot normalize provider: unrecognized format ${itemStr}`)}}function providerDiscoveryDeps(rec,localTokens,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":{const all=[...rec.inject()];return all.filter(d=>localTokens.has(d))}case"CLASS":return depsOfClassFn(rec.useClass,"discovery");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"discovery")}}function providerInvocationTokens(rec,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":return[...rec.inject()];case"CLASS":return depsOfClassFn(rec.useClass,"invocation");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"invocation")}}var RegistryAbstract=class{asyncTimeoutMs=3e4;ready;providers;list;tokens;defs;graph;instances=new Map;constructor(name,providers,metadata,auto=true){if(providers&&typeof providers.addRegistry==="function"){providers.addRegistry(name,this)}this.providers=providers;this.list=metadata;const{tokens,defs,graph}=this.buildMap(metadata);this.tokens=tokens;this.defs=defs;this.graph=graph;if(auto){this.buildGraph();this.ready=this.initialize()}else{this.ready=Promise.resolve()}}hasAny(){return this.instances.size>0}getAllInstances(){return this.instances}};var import_reflect_metadata2=require("reflect-metadata");var DiContainer=class extends RegistryAbstract{order=new Set;normalizeProvider;parentContainer;constructor(providers,parent,options={}){const normalizer=options.providerTokens?createProviderNormalizer({tokens:options.providerTokens}):createBasicNormalizer();super("DiContainer",parent,providers,false);this.normalizeProvider=normalizer;this.parentContainer=parent;if(options.asyncTimeoutMs){this.asyncTimeoutMs=options.asyncTimeoutMs}this.buildGraph();this.topoSort();this.ready=this.initialize()}lookupDefInHierarchy(token){if(this.defs.has(token)){return{registry:this,rec:this.defs.get(token)}}return this.parentContainer?.lookupDefInHierarchy(token)}resolveDefaultFromHierarchy(token){const found=this.lookupDefInHierarchy(token);if(!found){throw new Error(`Cannot resolve token ${tokenName(token)}: not registered in hierarchy.`)}const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Dependency ${tokenName(token)} is scoped (${sc}) in ${registry.constructor.name}; cannot use as GLOBAL.`)}const inst=registry.instances.get(token);if(inst===void 0){throw new Error(`Dependency ${tokenName(token)} (GLOBAL) is not instantiated in ${registry.constructor.name}`)}return inst}buildMap(list){const tokens=new Set;const defs=new Map;const graph=new Map;const normalize=this.normalizeProvider??createBasicNormalizer();for(const raw of list){const rec=normalize(raw);const provide=rec.provide;tokens.add(provide);defs.set(provide,rec);graph.set(provide,new Set)}return{tokens,defs,graph}}buildGraph(){for(const token of this.tokens){const rec=this.defs.get(token);const deps=this.discoveryDeps(rec);for(const d of deps){const isLocal=this.tokens.has(d);const up=isLocal?void 0:this.lookupDefInHierarchy(d);if(!isLocal&&!up){throw new Error(`Provider ${tokenName(token)} depends on ${tokenName(d)}, which is not registered (local or parent).`)}const depRec=isLocal?this.defs.get(d):up.rec;const depScope=isLocal?this.getProviderScope(depRec):up.registry.getProviderScope(depRec);if(this.getProviderScope(rec)==="global"&&depScope!=="global"){throw new Error(`Invalid dependency: GLOBAL-scoped provider ${tokenName(token)} cannot depend on scoped provider ${tokenName(d)}.`)}if(isLocal){this.graph.get(token).add(d)}}}}topoSort(){const WHITE=0,GRAY=1,BLACK=2;const color=new Map;const order=new Set;const path=[];const dfs=n=>{color.set(n,GRAY);path.push(n);for(const dep of this.graph.get(n)??[]){const c=color.get(dep)??WHITE;if(c===GRAY){const idx=path.findIndex(p=>p===dep);const cycle=[...path.slice(idx),dep].map(t=>tokenName(t)).join(" -> ");throw new Error(`Dependency cycle detected: ${cycle}`)}if(c===WHITE)dfs(dep)}color.set(n,BLACK);order.add(n);path.pop()};for(const n of this.graph.keys()){if((color.get(n)??WHITE)===WHITE)dfs(n)}this.order=order}async initialize(opts){const force=!!opts?.force;const only=opts?.onlyTokens?new Set(opts.onlyTokens):null;for(const token of this.order){const rec=this.defs.get(token);if(this.getProviderScope(rec)!=="global")continue;if(only&&!only.has(token))continue;if(!force&&this.instances.has(token))continue;if(force&&this.instances.has(token)){this.instances.delete(token)}try{await this.instantiateOne(token,rec)}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing ${tokenName(token)}: ${msg}`)}}}async withTimeout(p,ms,label){if(ms<=0||!Number.isFinite(ms))return p;let timer;try{const tm=new Promise((_,rej)=>timer=setTimeout(()=>rej(new Error(`Timeout after ${ms}ms: ${label}`)),ms));return await Promise.race([p,tm])}finally{clearTimeout(timer)}}async resolveFactoryArg(t,store){if(store.has(t))return store.get(t);if(this.instances.has(t))return this.instances.get(t);const rec=this.defs.get(t);if(rec){const sc=this.getProviderScope(rec);if(sc==="global"){if(!this.instances.has(t)){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated`)}return this.instances.get(t)}else{await this.buildIntoStore(t,rec,store);return store.get(t)}}const up=this.lookupDefInHierarchy(t);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const inst=up.registry.instances.get(t);if(inst===void 0){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated in parent`)}return inst}else{await up.registry.buildIntoStore(t,up.rec,store);return store.get(t)}}if(isClass(t)){if(hasAsyncWith(t)){const out=t.with();return isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${t.name}.with(...)`):out}const instance=new t;const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${t.name}.init()`)}return instance}throw new Error(`Cannot resolve token ${tokenName(t)}: not registered in hierarchy and not constructable.`)}async instantiateOne(token,rec){switch(rec.kind){case"VALUE":{this.instances.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,this.instances))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;this.instances.set(token,val);return}case"CLASS":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.useClass;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}case"CLASS_TOKEN":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}}}async buildIntoStore(token,rec,store){if(store.has(token))return;try{switch(rec.kind){case"VALUE":{store.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,store))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;store.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveManagedForClass(d,store))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;store.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}store.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (scoped) ${tokenName(token)}: ${msg}`)}}async resolveManagedForClass(d,store){if(store.has(d))return store.get(d);if(this.instances.has(d))return this.instances.get(d);const depRec=this.defs.get(d);if(depRec){const sc=this.getProviderScope(depRec);if(sc==="global"){const v=this.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated`);return v}else{await this.buildIntoStore(d,depRec,store);return store.get(d)}}const up=this.lookupDefInHierarchy(d);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated in parent`);return v}else{await up.registry.buildIntoStore(d,up.rec,store);return store.get(d)}}throw new Error(`${tokenName(d)} is not instantiated`)}get(token){if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec&&this.getProviderScope(rec)!=="global"){throw new Error(`Provider ${tokenName(token)} is scoped (${this.getProviderScope(rec)}). Use getScoped(token, views).`)}if(this.parentContainer)return this.parentContainer.get(token);throw new Error(`Provider ${tokenName(token)} is not available in local or parent registries`)}resolve(cls){const found=this.lookupDefInHierarchy(cls);if(found){const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Provider ${tokenName(cls)} is scoped (${sc}). Use getScoped(token, views) or buildViews(...).`)}if(!registry.instances.has(cls)){throw new Error(`Provider ${tokenName(cls)} (GLOBAL scope) is not instantiated. Call await container.ready first.`)}return registry.instances.get(cls)}if(isClass(cls)){const instance=new cls;const init=instance?.init;if(typeof init==="function"){const result=init.call(instance);if(isPromise(result)){throw new Error(`Cannot use resolve() with async init() for ${cls.name??"unknown"}. Use get() after initialize() instead.`)}}return instance}throw new Error(`Cannot resolve ${tokenName(cls)}: not a registered GLOBAL provider and not a constructable class.`)}tryGet(token){try{return this.get(token)}catch{return void 0}}normalizeScope(scope){switch(scope){case"session":case"request":return"context";default:return scope}}getProviderScope(rec){const rawScope=rec.metadata?.scope??"global";return this.normalizeScope(rawScope)}discoveryDeps(rec){return providerDiscoveryDeps(rec,this.tokens,(k,phase)=>depsOfClass(k,phase))}invocationTokens(_token,rec){return providerInvocationTokens(rec,(k,phase)=>depsOfClass(k,phase))}getAllSingletons(){return this.instances}async buildViews(sessionKey,contextProviders){const global=this.getAllSingletons();const contextStore=new Map(contextProviders);for(const token of this.order){const rec=this.defs.get(token);if(!rec)continue;if(this.getProviderScope(rec)!=="context")continue;if(contextStore.has(token))continue;await this.buildIntoStoreWithViews(token,rec,contextStore,global)}return{global,context:contextStore}}async buildIntoStoreWithViews(token,rec,contextStore,globalStore){if(contextStore.has(token))return;try{switch(rec.kind){case"VALUE":{contextStore.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFromViews(d,contextStore,globalStore))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;contextStore.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveFromViews(d,contextStore,globalStore))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;contextStore.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}contextStore.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (context-scoped) ${tokenName(token)}: ${msg}`)}}async resolveFromViews(token,contextStore,globalStore){if(contextStore.has(token))return contextStore.get(token);if(globalStore.has(token))return globalStore.get(token);if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec){const scope=this.getProviderScope(rec);if(scope==="global"){throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated`)}else{await this.buildIntoStoreWithViews(token,rec,contextStore,globalStore);return contextStore.get(token)}}const up=this.lookupDefInHierarchy(token);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(token);if(v!==void 0)return v;throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated in parent`)}}throw new Error(`Cannot resolve dependency ${tokenName(token)} from views`)}getScoped(token,views){if(views.context.has(token))return views.context.get(token);if(views.global.has(token))return views.global.get(token);if(this.instances.has(token))return this.instances.get(token);throw new Error(`Provider ${tokenName(token)} not found in views. Ensure it was built via buildViews().`)}injectProvider(injected){const rec={...injected,kind:"INJECTED"};this.tokens.add(rec.provide);this.defs.set(rec.provide,rec);this.graph.set(rec.provide,new Set);this.instances.set(rec.provide,rec.value)}};function createBasicNormalizer(){return function normalizeProvider(item){if(isClass(item)){const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj){if(!obj.provide){throw new Error(`Provider with useValue must have a 'provide' field`)}return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){if(!obj.provide){throw new Error(`Provider with useFactory must have a 'provide' field`)}return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){if(!obj.provide){throw new Error(`Provider with useClass must have a 'provide' field`)}return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}throw new Error(`Cannot normalize provider: unrecognized format`)}}var IndexedRegistry=class extends RegistryAbstract{localRows=[];adopted=new Map;children=new Set;childSubscriptions=new Map;byQualifiedId=new Map;byName=new Map;byOwner=new Map;byOwnerAndName=new Map;version=0;emitter;createEmitter(){const handlers=new Set;return{emit:event=>handlers.forEach(h=>h(event)),on:handler=>{handlers.add(handler);return()=>handlers.delete(handler)}}}constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto);this.emitter=this.createEmitter()}adoptFromChild(child,childOwner){const childRows=child.listAllIndexed();const adoptedRows=childRows.map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,adoptedRows);this.children.add(child);const unsubscribe=child.subscribe({immediate:false},()=>{const latest=child.listAllIndexed().map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,latest);this.reindex();this.bump("reset")});this.childSubscriptions.set(child,unsubscribe);this.reindex();this.bump("reset")}removeChild(child){const unsub=this.childSubscriptions.get(child);if(unsub)unsub();this.childSubscriptions.delete(child);this.children.delete(child);this.adopted.delete(child);this.reindex();this.bump("reset")}relineage(row,prepend){const merged=[...prepend,...row.lineage];const lineage=this.dedupLineage(merged);const ownerKey=this.ownerKeyOf(lineage);const qualifiedName=this.qualifiedNameOf(row.baseName,lineage);const qualifiedId=`${ownerKey}:${row.token.toString()}`;return{...row,lineage,ownerKey,qualifiedName,qualifiedId}}dedupLineage(lineage){const result=[];for(const seg of lineage){const last=result[result.length-1];if(!last||last.type!==seg.type||last.id!==seg.id){result.push(seg)}}return result}ownerKeyOf(lineage){return lineage.map(s=>`${s.type}:${s.id}`).join("/")}qualifiedNameOf(baseName,lineage){const prefix=lineage.map(s=>s.name||s.id).join("/");return prefix?`${prefix}/${baseName}`:baseName}reindex(){const allRows=[...this.localRows];for(const rows of this.adopted.values()){allRows.push(...rows)}this.byQualifiedId.clear();this.byName.clear();this.byOwner.clear();this.byOwnerAndName.clear();for(const row of allRows){this.byQualifiedId.set(row.qualifiedId,row);const nameList=this.byName.get(row.baseName)??[];nameList.push(row);this.byName.set(row.baseName,nameList);const ownerList=this.byOwner.get(row.ownerKey)??[];ownerList.push(row);this.byOwner.set(row.ownerKey,ownerList);const ownerNameKey=`${row.ownerKey}:${row.baseName}`;this.byOwnerAndName.set(ownerNameKey,row)}this.buildIndexes(allRows)}subscribe(opts,callback){if(opts.immediate){callback({kind:"reset",changeScope:"global",version:this.version,snapshot:this.listAllIndexed()})}return this.emitter.on(e=>{if(opts.filter){const filtered=e.snapshot.filter(row=>opts.filter(row.instance));callback({...e,snapshot:filtered})}else{callback(e)}})}bump(kind){const version=++this.version;this.emitter.emit({kind,changeScope:"global",version,snapshot:this.listAllIndexed()})}findByName(name){const rows=this.byName.get(name);return rows?.[0]?.instance}findAllByName(name){const rows=this.byName.get(name);return rows?.map(r=>r.instance)??[]}findByQualifiedId(qualifiedId){return this.byQualifiedId.get(qualifiedId)?.instance}listByOwner(ownerKey){return this.byOwner.get(ownerKey)??[]}listAllIndexed(){const all=[...this.localRows];for(const rows of this.adopted.values()){all.push(...rows)}return all}listAllInstances(){return this.listAllIndexed().map(r=>r.instance)}hasAny(){return this.localRows.length>0||this.adopted.size>0}dispose(){for(const unsub of this.childSubscriptions.values()){unsub()}this.childSubscriptions.clear();this.children.clear();this.adopted.clear()}};var SimpleRegistry=class extends RegistryAbstract{constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto)}getAll(){return[...this.instances.values()]}count(){return this.instances.size}};var import_reflect_metadata3=require("reflect-metadata");function AsyncWith(tokensFactory){return target=>{Reflect.defineMetadata(META_ASYNC_WITH,true,target);Reflect.defineMetadata(META_ASYNC_WITH_TOKENS,tokensFactory,target)}}0&&(module.exports={AsyncWith,DESIGN_PARAMTYPES,DiContainer,DiTokens,IndexedRegistry,META_ASYNC_WITH,META_ASYNC_WITH_TOKENS,ProviderKind,ProviderScope,RegistryAbstract,SimpleRegistry,createProviderNormalizer,createTokenFactory,depsOfClass,depsOfFunc,getAsyncWithTokens,getMetadata,hasAsyncWith,isClass,isPromise,providerDiscoveryDeps,providerInvocationTokens,providerMetadataSchema,readWithParamTypes,setMetadata,tokenName});
|
|
4
|
+
`)}return fnMeta.filter(isClass)}function createProviderNormalizer(options){const{tokens}=options;return function normalizeProvider(item){if(isClass(item)){const isProviderClass=getMetadata(tokens.type,item);if(isProviderClass){const metadata={name:getMetadata(tokens.name,item)??item.name??"UnknownProvider",scope:getMetadata(tokens.scope,item),description:tokens.description?getMetadata(tokens.description,item):void 0,id:tokens.id?getMetadata(tokens.id,item):void 0};return{kind:"CLASS_TOKEN",provide:item,metadata}}const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj||"useFactory"in obj||"useClass"in obj){if(!obj.provide){const name=obj.name??"[object]";throw new Error(`Provider '${name}' is missing 'provide'.`)}}if("useValue"in obj){return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}let itemStr;try{itemStr=JSON.stringify(item)}catch{itemStr=String(item)}throw new Error(`Cannot normalize provider: unrecognized format ${itemStr}`)}}function providerDiscoveryDeps(rec,localTokens,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":{const all=[...rec.inject()];return all.filter(d=>localTokens.has(d))}case"CLASS":return depsOfClassFn(rec.useClass,"discovery");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"discovery")}}function providerInvocationTokens(rec,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":return[...rec.inject()];case"CLASS":return depsOfClassFn(rec.useClass,"invocation");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"invocation")}}var RegistryAbstract=class{asyncTimeoutMs=3e4;ready;providers;list;tokens;defs;graph;instances=new Map;constructor(name,providers,metadata,auto=true){if(providers&&typeof providers.addRegistry==="function"){providers.addRegistry(name,this)}this.providers=providers;this.list=metadata;const{tokens,defs,graph}=this.buildMap(metadata);this.tokens=tokens;this.defs=defs;this.graph=graph;if(auto){this.buildGraph();this.ready=this.initialize()}else{this.ready=Promise.resolve()}}hasAny(){return this.instances.size>0}getAllInstances(){return this.instances}};var import_reflect_metadata2=require("reflect-metadata");var DiContainer=class extends RegistryAbstract{order=new Set;normalizeProvider;parentContainer;constructor(providers,parent,options={}){const normalizer=options.providerTokens?createProviderNormalizer({tokens:options.providerTokens}):createBasicNormalizer();super("DiContainer",parent,[],false);this.normalizeProvider=normalizer;this.parentContainer=parent;this.list=providers;const{tokens,defs,graph}=this.buildMap(providers);this.tokens=tokens;this.defs=defs;this.graph=graph;if(options.asyncTimeoutMs){this.asyncTimeoutMs=options.asyncTimeoutMs}this.buildGraph();this.topoSort();this.ready=this.initialize()}lookupDefInHierarchy(token){if(this.defs.has(token)){return{registry:this,rec:this.defs.get(token)}}return this.parentContainer?.lookupDefInHierarchy(token)}resolveDefaultFromHierarchy(token){const found=this.lookupDefInHierarchy(token);if(!found){throw new Error(`Cannot resolve token ${tokenName(token)}: not registered in hierarchy.`)}const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Dependency ${tokenName(token)} is scoped (${sc}) in ${registry.constructor.name}; cannot use as GLOBAL.`)}const inst=registry.instances.get(token);if(inst===void 0){throw new Error(`Dependency ${tokenName(token)} (GLOBAL) is not instantiated in ${registry.constructor.name}`)}return inst}buildMap(list){const tokens=new Set;const defs=new Map;const graph=new Map;const normalize=this.normalizeProvider??createBasicNormalizer();for(const raw of list){const rec=normalize(raw);const provide=rec.provide;tokens.add(provide);defs.set(provide,rec);graph.set(provide,new Set)}return{tokens,defs,graph}}buildGraph(){for(const token of this.tokens){const rec=this.defs.get(token);const deps=this.discoveryDeps(rec);for(const d of deps){const isLocal=this.tokens.has(d);const up=isLocal?void 0:this.lookupDefInHierarchy(d);if(!isLocal&&!up){throw new Error(`Provider ${tokenName(token)} depends on ${tokenName(d)}, which is not registered (local or parent).`)}const depRec=isLocal?this.defs.get(d):up.rec;const depScope=isLocal?this.getProviderScope(depRec):up.registry.getProviderScope(depRec);if(this.getProviderScope(rec)==="global"&&depScope!=="global"){throw new Error(`Invalid dependency: GLOBAL-scoped provider ${tokenName(token)} cannot depend on scoped provider ${tokenName(d)}.`)}if(isLocal){this.graph.get(token).add(d)}}}}topoSort(){const WHITE=0,GRAY=1,BLACK=2;const color=new Map;const order=new Set;const path=[];const dfs=n=>{color.set(n,GRAY);path.push(n);for(const dep of this.graph.get(n)??[]){const c=color.get(dep)??WHITE;if(c===GRAY){const idx=path.findIndex(p=>p===dep);const cycle=[...path.slice(idx),dep].map(t=>tokenName(t)).join(" -> ");throw new Error(`Dependency cycle detected: ${cycle}`)}if(c===WHITE)dfs(dep)}color.set(n,BLACK);order.add(n);path.pop()};for(const n of this.graph.keys()){if((color.get(n)??WHITE)===WHITE)dfs(n)}this.order=order}async initialize(opts){const force=!!opts?.force;const only=opts?.onlyTokens?new Set(opts.onlyTokens):null;for(const token of this.order){const rec=this.defs.get(token);if(this.getProviderScope(rec)!=="global")continue;if(only&&!only.has(token))continue;if(!force&&this.instances.has(token))continue;if(force&&this.instances.has(token)){this.instances.delete(token)}try{await this.instantiateOne(token,rec)}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing ${tokenName(token)}: ${msg}`)}}}async withTimeout(p,ms,label){if(ms<=0||!Number.isFinite(ms))return p;let timer;try{const tm=new Promise((_,rej)=>timer=setTimeout(()=>rej(new Error(`Timeout after ${ms}ms: ${label}`)),ms));return await Promise.race([p,tm])}finally{clearTimeout(timer)}}async resolveFactoryArg(t,store){if(store.has(t))return store.get(t);if(this.instances.has(t))return this.instances.get(t);const rec=this.defs.get(t);if(rec){const sc=this.getProviderScope(rec);if(sc==="global"){if(!this.instances.has(t)){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated`)}return this.instances.get(t)}else{await this.buildIntoStore(t,rec,store);return store.get(t)}}const up=this.lookupDefInHierarchy(t);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const inst=up.registry.instances.get(t);if(inst===void 0){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated in parent`)}return inst}else{await up.registry.buildIntoStore(t,up.rec,store);return store.get(t)}}if(isClass(t)){if(hasAsyncWith(t)){const out=t.with();return isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${t.name}.with(...)`):out}const instance=new t;const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${t.name}.init()`)}return instance}throw new Error(`Cannot resolve token ${tokenName(t)}: not registered in hierarchy and not constructable.`)}async instantiateOne(token,rec){switch(rec.kind){case"VALUE":{this.instances.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,this.instances))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;this.instances.set(token,val);return}case"CLASS":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.useClass;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}case"CLASS_TOKEN":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}}}async buildIntoStore(token,rec,store){if(store.has(token))return;try{switch(rec.kind){case"VALUE":{store.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,store))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;store.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveManagedForClass(d,store))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;store.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}store.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (scoped) ${tokenName(token)}: ${msg}`)}}async resolveManagedForClass(d,store){if(store.has(d))return store.get(d);if(this.instances.has(d))return this.instances.get(d);const depRec=this.defs.get(d);if(depRec){const sc=this.getProviderScope(depRec);if(sc==="global"){const v=this.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated`);return v}else{await this.buildIntoStore(d,depRec,store);return store.get(d)}}const up=this.lookupDefInHierarchy(d);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated in parent`);return v}else{await up.registry.buildIntoStore(d,up.rec,store);return store.get(d)}}throw new Error(`${tokenName(d)} is not instantiated`)}get(token){if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec&&this.getProviderScope(rec)!=="global"){throw new Error(`Provider ${tokenName(token)} is scoped (${this.getProviderScope(rec)}). Use getScoped(token, views).`)}if(this.parentContainer)return this.parentContainer.get(token);throw new Error(`Provider ${tokenName(token)} is not available in local or parent registries`)}resolve(cls){const found=this.lookupDefInHierarchy(cls);if(found){const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Provider ${tokenName(cls)} is scoped (${sc}). Use getScoped(token, views) or buildViews(...).`)}if(!registry.instances.has(cls)){throw new Error(`Provider ${tokenName(cls)} (GLOBAL scope) is not instantiated. Call await container.ready first.`)}return registry.instances.get(cls)}if(isClass(cls)){const instance=new cls;const init=instance?.init;if(typeof init==="function"){const result=init.call(instance);if(isPromise(result)){throw new Error(`Cannot use resolve() with async init() for ${cls.name??"unknown"}. Use get() after initialize() instead.`)}}return instance}throw new Error(`Cannot resolve ${tokenName(cls)}: not a registered GLOBAL provider and not a constructable class.`)}tryGet(token){try{return this.get(token)}catch{return void 0}}normalizeScope(scope){switch(scope){case"session":case"request":return"context";default:return scope}}getProviderScope(rec){const rawScope=rec.metadata?.scope??"global";return this.normalizeScope(rawScope)}discoveryDeps(rec){if(rec.kind==="FACTORY"){return rec.inject().filter(dep=>this.tokens.has(dep)||this.lookupDefInHierarchy(dep)!==void 0)}return providerDiscoveryDeps(rec,this.tokens,(k,phase)=>depsOfClass(k,phase))}invocationTokens(_token,rec){return providerInvocationTokens(rec,(k,phase)=>depsOfClass(k,phase))}getAllSingletons(){return this.instances}async buildViews(sessionKey,contextProviders){const global=this.getAllSingletons();const contextStore=new Map(contextProviders);for(const token of this.order){const rec=this.defs.get(token);if(!rec)continue;if(this.getProviderScope(rec)!=="context")continue;if(contextStore.has(token))continue;await this.buildIntoStoreWithViews(token,rec,contextStore,global)}return{global,context:contextStore}}async buildIntoStoreWithViews(token,rec,contextStore,globalStore){if(contextStore.has(token))return;try{switch(rec.kind){case"VALUE":{contextStore.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFromViews(d,contextStore,globalStore))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;contextStore.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveFromViews(d,contextStore,globalStore))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;contextStore.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}contextStore.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (context-scoped) ${tokenName(token)}: ${msg}`)}}async resolveFromViews(token,contextStore,globalStore){if(contextStore.has(token))return contextStore.get(token);if(globalStore.has(token))return globalStore.get(token);if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec){const scope=this.getProviderScope(rec);if(scope==="global"){throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated`)}else{await this.buildIntoStoreWithViews(token,rec,contextStore,globalStore);return contextStore.get(token)}}const up=this.lookupDefInHierarchy(token);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(token);if(v!==void 0)return v;throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated in parent`)}}throw new Error(`Cannot resolve dependency ${tokenName(token)} from views`)}getScoped(token,views){if(views.context.has(token))return views.context.get(token);if(views.global.has(token))return views.global.get(token);if(this.instances.has(token))return this.instances.get(token);throw new Error(`Provider ${tokenName(token)} not found in views. Ensure it was built via buildViews().`)}injectProvider(injected){const rec={...injected,kind:"INJECTED"};this.tokens.add(rec.provide);this.defs.set(rec.provide,rec);this.graph.set(rec.provide,new Set);this.instances.set(rec.provide,rec.value)}};function createBasicNormalizer(){return function normalizeProvider(item){if(isClass(item)){const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj){if(!obj.provide){throw new Error(`Provider with useValue must have a 'provide' field`)}return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){if(!obj.provide){throw new Error(`Provider with useFactory must have a 'provide' field`)}return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){if(!obj.provide){throw new Error(`Provider with useClass must have a 'provide' field`)}return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}throw new Error(`Cannot normalize provider: unrecognized format`)}}var IndexedRegistry=class extends RegistryAbstract{localRows=[];adopted=new Map;children=new Set;childSubscriptions=new Map;byQualifiedId=new Map;byName=new Map;byOwner=new Map;byOwnerAndName=new Map;version=0;emitter;createEmitter(){const handlers=new Set;return{emit:event=>handlers.forEach(h=>h(event)),on:handler=>{handlers.add(handler);return()=>handlers.delete(handler)}}}constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto);this.emitter=this.createEmitter()}adoptFromChild(child,childOwner){const childRows=child.listAllIndexed();const adoptedRows=childRows.map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,adoptedRows);this.children.add(child);const unsubscribe=child.subscribe({immediate:false},()=>{const latest=child.listAllIndexed().map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,latest);this.reindex();this.bump("reset")});this.childSubscriptions.set(child,unsubscribe);this.reindex();this.bump("reset")}removeChild(child){const unsub=this.childSubscriptions.get(child);if(unsub)unsub();this.childSubscriptions.delete(child);this.children.delete(child);this.adopted.delete(child);this.reindex();this.bump("reset")}relineage(row,prepend){const merged=[...prepend,...row.lineage];const lineage=this.dedupLineage(merged);const ownerKey=this.ownerKeyOf(lineage);const qualifiedName=this.qualifiedNameOf(row.baseName,lineage);const qualifiedId=`${ownerKey}:${row.token.toString()}`;return{...row,lineage,ownerKey,qualifiedName,qualifiedId}}dedupLineage(lineage){const result=[];for(const seg of lineage){const last=result[result.length-1];if(!last||last.type!==seg.type||last.id!==seg.id){result.push(seg)}}return result}ownerKeyOf(lineage){return lineage.map(s=>`${s.type}:${s.id}`).join("/")}qualifiedNameOf(baseName,lineage){const prefix=lineage.map(s=>s.name||s.id).join("/");return prefix?`${prefix}/${baseName}`:baseName}reindex(){const allRows=[...this.localRows];for(const rows of this.adopted.values()){allRows.push(...rows)}this.byQualifiedId.clear();this.byName.clear();this.byOwner.clear();this.byOwnerAndName.clear();for(const row of allRows){this.byQualifiedId.set(row.qualifiedId,row);const nameList=this.byName.get(row.baseName)??[];nameList.push(row);this.byName.set(row.baseName,nameList);const ownerList=this.byOwner.get(row.ownerKey)??[];ownerList.push(row);this.byOwner.set(row.ownerKey,ownerList);const ownerNameKey=`${row.ownerKey}:${row.baseName}`;this.byOwnerAndName.set(ownerNameKey,row)}this.buildIndexes(allRows)}subscribe(opts,callback){if(opts.immediate){callback({kind:"reset",changeScope:"global",version:this.version,snapshot:this.listAllIndexed()})}return this.emitter.on(e=>{if(opts.filter){const filtered=e.snapshot.filter(row=>opts.filter(row.instance));callback({...e,snapshot:filtered})}else{callback(e)}})}bump(kind){const version=++this.version;this.emitter.emit({kind,changeScope:"global",version,snapshot:this.listAllIndexed()})}findByName(name){const rows=this.byName.get(name);return rows?.[0]?.instance}findAllByName(name){const rows=this.byName.get(name);return rows?.map(r=>r.instance)??[]}findByQualifiedId(qualifiedId){return this.byQualifiedId.get(qualifiedId)?.instance}listByOwner(ownerKey){return this.byOwner.get(ownerKey)??[]}listAllIndexed(){const all=[...this.localRows];for(const rows of this.adopted.values()){all.push(...rows)}return all}listAllInstances(){return this.listAllIndexed().map(r=>r.instance)}hasAny(){return this.localRows.length>0||this.adopted.size>0}dispose(){for(const unsub of this.childSubscriptions.values()){unsub()}this.childSubscriptions.clear();this.children.clear();this.adopted.clear()}};var SimpleRegistry=class extends RegistryAbstract{constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto)}getAll(){return[...this.instances.values()]}count(){return this.instances.size}};var import_reflect_metadata3=require("reflect-metadata");function AsyncWith(tokensFactory){return target=>{Reflect.defineMetadata(META_ASYNC_WITH,true,target);Reflect.defineMetadata(META_ASYNC_WITH_TOKENS,tokensFactory,target)}}0&&(module.exports={AsyncWith,DESIGN_PARAMTYPES,DiContainer,DiTokens,IndexedRegistry,META_ASYNC_WITH,META_ASYNC_WITH_TOKENS,ProviderKind,ProviderScope,RegistryAbstract,SimpleRegistry,createProviderNormalizer,createTokenFactory,depsOfClass,depsOfFunc,getAsyncWithTokens,getMetadata,hasAsyncWith,isClass,isPromise,providerDiscoveryDeps,providerInvocationTokens,providerMetadataSchema,readWithParamTypes,setMetadata,tokenName});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/di",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "Generic dependency injection container and registry utilities for TypeScript applications",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=24.0.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@frontmcp/lazy-zod": "1.12.
|
|
51
|
+
"@frontmcp/lazy-zod": "1.12.1",
|
|
52
52
|
"reflect-metadata": "^0.2.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
package/registry/container.d.ts
CHANGED
|
@@ -124,6 +124,7 @@ export declare class DiContainer<ParentType extends DiContainer<any> | undefined
|
|
|
124
124
|
getProviderScope(rec: ProviderRecord): ProviderScope;
|
|
125
125
|
/**
|
|
126
126
|
* Get all discovered dependencies for a provider record.
|
|
127
|
+
* A factory's include the tokens it injects from a parent container, so their scope is checked too.
|
|
127
128
|
*/
|
|
128
129
|
discoveryDeps(rec: ProviderRecord): Token[];
|
|
129
130
|
/**
|