@forinda/kickjs-di 0.1.0 → 0.2.0

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/dist/index.d.mts CHANGED
@@ -73,7 +73,9 @@ type DiErrorCode =
73
73
  /** A REQUEST-scoped token, resolved outside a request. */
74
74
  'KICK003' |
75
75
  /** `@Value` key not set, and no default. */
76
- 'KICK004';
76
+ 'KICK004' |
77
+ /** A token bound normally after its `registerFallback` singleton was already resolved. */
78
+ 'KICK010';
77
79
  export declare class DiError extends Error {
78
80
  readonly code: DiErrorCode;
79
81
  /** The token name, or the env key for KICK004. */
@@ -251,6 +253,12 @@ interface KickJsRegistry {}
251
253
  export declare class Container {
252
254
  private static instance;
253
255
  private registrations;
256
+ /**
257
+ * Bindings from {@link registerFallback}, kept apart from `registrations`
258
+ * so a normal binding wins whenever it arrives and `has()` keeps answering
259
+ * "did the app bind its own?".
260
+ */
261
+ private fallbacks;
254
262
  private resolving;
255
263
  private changeListeners;
256
264
  private pendingChanges;
@@ -334,11 +342,36 @@ export declare class Container {
334
342
  registerFactory(token: any, factory: () => any, scope?: Scope): void;
335
343
  /** Register a pre-constructed singleton instance */
336
344
  registerInstance(token: any, instance: any): void;
337
- /** Check if a binding exists for the given token */
345
+ /**
346
+ * Register a default for `token`, used only while nothing else binds it.
347
+ *
348
+ * `resolve()` prefers a `register` / `registerFactory` / `registerInstance`
349
+ * binding whenever it was made — before or after this call — and falls back
350
+ * to this factory otherwise. `has()` stays `false` for a token that only has
351
+ * a fallback. A second fallback for the same token replaces the first, while
352
+ * the first has not been resolved.
353
+ *
354
+ * Binding the token normally once its SINGLETON fallback has been resolved
355
+ * throws KICK010: whoever resolved it holds the fallback instance, and would
356
+ * disagree with everyone resolving the new binding. Fallbacks are not kept
357
+ * across `Container.reset()` — register them from a hook that runs on every
358
+ * boot (an adapter's `beforeMount`, a module's `register`).
359
+ */
360
+ registerFallback<T>(token: InjectionToken<T> | Constructor<T> | string | symbol, factory: () => NoInfer<T>, scope?: Scope): void;
361
+ /** A fallback whose singleton is already out there can't be replaced. */
362
+ private assertFallbackUnused;
363
+ /** The binding `resolve(token)` uses: a normal registration, else the fallback. */
364
+ private binding;
365
+ /** Check if a binding exists for the given token. A {@link registerFallback} alone does not count. */
338
366
  has(token: any): boolean;
339
- /** Return a snapshot of all registered tokens with their scope and instantiation status */
367
+ /**
368
+ * Return a snapshot of all registered tokens with their scope and instantiation status.
369
+ * A token served by its {@link registerFallback} is listed with `fallback: true`;
370
+ * a fallback that a normal binding overrides is not listed.
371
+ */
340
372
  getRegistrations(): Array<{
341
373
  token: string;
374
+ fallback?: true;
342
375
  scope: string;
343
376
  kind: ClassKind;
344
377
  instantiated: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/interfaces.ts","../src/errors.ts","../src/token.ts","../src/container.ts","../src/decorators.ts","../src/disposables.ts","../src/metadata.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/interfaces.ts","../src/errors.ts","../src/token.ts","../src/container.ts","../src/decorators.ts","../src/disposables.ts","../src/metadata.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-di v0.1.0
2
+ * @forinda/kickjs-di v0.2.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import"reflect-metadata";const KEY=Symbol.for(`@forinda/kickjs-di:loaded-from`),store=globalThis,here=import.meta.url;store[KEY]===void 0?store[KEY]=here:store[KEY]!==here&&console.warn(` @forinda/kickjs-di is loaded twice:\n ${store[KEY]}\n ${here}\n Each copy has its own container, so services registered in one are missing from the other.\n Install a single version (it comes with @forinda/kickjs; don't add it separately).`);let Scope=/* @__PURE__ */ function(Scope){return Scope.SINGLETON=`singleton`,Scope.TRANSIENT=`transient`,Scope.REQUEST=`request`,Scope}({});const DI_METADATA={INJECTABLE:`kick:injectable`,SCOPE:`kick:scope`,CLASS_KIND:`kick:class-kind`,AUTOWIRED:`kick:autowired`,INJECT:`kick:inject`,POST_CONSTRUCT:`kick:post_construct`,PRE_DESTROY:`kick:pre_destroy`,BUILDER:`kick:builder`,VALUE:`kick:value`,PARAM_TYPES:`design:paramtypes`,PROPERTY_TYPE:`design:type`,RETURN_TYPE:`design:returntype`};var DiError=class extends Error{code;subject;constructor(code,subject,message){super(message),this.code=code,this.subject=subject,this.name=`DiError`}};const MESSAGES={KICK001:s=>`No provider for ${s}`,KICK002:s=>`Cannot resolve REQUEST-scoped "${s}": no request store is installed`,KICK003:s=>`Cannot resolve REQUEST-scoped "${s}" outside a request`,KICK004:s=>`@Value('${s}'): not set and no default provided`};function diError(code,subject){return new DiError(code,subject,`${code}: ${MESSAGES[code](subject)}`)}const defaultLogger={warn(){},error(err,message){console.error(message,err)}};function setClassMeta(key,value,target){Reflect.defineMetadata(key,value,target)}function setMethodMeta(key,value,target,method){Reflect.defineMetadata(key,value,target,method)}function getClassMeta(key,target,fallback){return Reflect.getMetadata(key,target)??fallback}function getMethodMeta(key,target,method,fallback){return Reflect.getMetadata(key,target,method)??fallback}function hasClassMeta(key,target){return Reflect.hasMetadata(key,target)}function getClassMetaOrUndefined(key,target){return Reflect.getMetadata(key,target)}function getMethodMetaOrUndefined(key,target,method){return Reflect.getMetadata(key,target,method)}function pushClassMeta(key,target,...items){let existing=Reflect.getOwnMetadata(key,target)??[];Reflect.defineMetadata(key,[...existing,...items],target)}function pushMethodMeta(key,target,method,...items){let existing=Reflect.getOwnMetadata(key,target,method)??[];Reflect.defineMetadata(key,[...existing,...items],target,method)}function setInMetaMap(key,target,mapKey,mapValue){let inherited=Reflect.getOwnMetadata(key,target),map=inherited?new Map(inherited):/* @__PURE__ */ new Map;map.set(mapKey,mapValue),Reflect.defineMetadata(key,map,target)}function getMetaMap(key,target){return Reflect.getMetadata(key,target)??/* @__PURE__ */ new Map}function setInMetaRecord(key,target,recKey,recValue){let inherited=Reflect.getOwnMetadata(key,target),rec=inherited?{...inherited}:{};rec[recKey]=recValue,Reflect.defineMetadata(key,rec,target)}function getMetaRecord(key,target){return Reflect.getMetadata(key,target)??{}}const INJECTION_TOKEN=Symbol.for(`@kickjs/InjectionToken`);function createToken(name){return Object.freeze({[INJECTION_TOKEN]:!0,name})}function isInjectionToken(value){return typeof value==`object`&&!!value&&value[INJECTION_TOKEN]===!0}function createReg(fields){return{kind:`unknown`,resolveCount:0,postConstructStatus:`skipped`,dependencies:[],...fields}}function tokenName(token){return typeof token==`symbol`?token.toString():isInjectionToken(token)?token.name:token?.name||String(token)}function getPersistentStore(){return globalThis.__kickjs_persistent||(globalThis.__kickjs_persistent={factories:[],instances:[],resolvedInstances:/* @__PURE__ */ new Map}),globalThis.__kickjs_persistent}var Container=class Container{static instance;registrations=/* @__PURE__ */ new Map;resolving=/* @__PURE__ */ new Set;changeListeners=/* @__PURE__ */ new Set;pendingChanges=[];notifyTimer=null;static DEBOUNCE_MS=50;static _onReady=null;static _onReset=null;static _onCreate=null;static _envResolver=null;static _error=diError;static _logger=defaultLogger;static _propertyInjectors=/* @__PURE__ */ new Map;isGlobal=!1;static _requestStoreProvider=null;static getInstance(){return Container.instance||(Container.instance=new Container,Container.instance.isGlobal=!0),Container._onReady&&=(Container._onReady(Container.instance),null),Container.instance}static reset(){Container.instance=new Container,Container.instance.isGlobal=!0,Container._onReset?.(Container.instance);let store=getPersistentStore();for(let{token,factory,scope}of store.factories)if(!Container.instance.has(token)){let name=tokenName(token),existing=store.resolvedInstances.get(name);Container.instance.registrations.set(token,createReg({target:Object,scope,factory,kind:`factory`,instance:existing,resolveCount:+!!existing,postConstructStatus:existing?`completed`:`skipped`}))}for(let{token,instance}of store.instances)Container.instance.has(token)||Container.instance.registrations.set(token,createReg({target:instance.constructor,scope:`singleton`,instance,kind:`instance`,postConstructStatus:`completed`,resolveCount:1}))}static create(){let container=new Container;return Container._onCreate?.(container),container}register(token,target,scope=`singleton`){let kind=getClassMeta(DI_METADATA.CLASS_KIND,target,`unknown`),reg=createReg({target,scope,kind,dependencies:this.extractDependencies(target)});this.registrations.set(token,reg),typeof token==`function`&&token.name&&this.registrations.set(`__hmr__${token.name}`,reg),this.emit(token,`registered`,kind)}registerFactory(token,factory,scope=`singleton`){let store=getPersistentStore(),name=tokenName(token),factoryUnchanged=(this.isGlobal?store.factories.find(r=>tokenName(r.token)===name):void 0)?.factory===factory,existingInstance=this.isGlobal&&factoryUnchanged?store.resolvedInstances.get(name):void 0;if(this.isGlobal&&!factoryUnchanged&&store.resolvedInstances.delete(name),this.registrations.set(token,createReg({target:Object,scope,factory,kind:`factory`,instance:existingInstance,resolveCount:+!!existingInstance,postConstructStatus:existingInstance?`completed`:`skipped`})),this.isGlobal){let idx=store.factories.findIndex(r=>tokenName(r.token)===name);idx>=0?store.factories[idx]={token,factory,scope}:store.factories.push({token,factory,scope})}this.emit(name,`registered`,`factory`)}registerInstance(token,instance){let store=getPersistentStore(),name=tokenName(token);if(this.registrations.set(token,createReg({target:instance.constructor,scope:`singleton`,instance,kind:`instance`,postConstructStatus:`completed`,resolveCount:1,firstResolvedAt:Date.now(),lastResolvedAt:Date.now()})),this.isGlobal){store.resolvedInstances.set(name,instance);let idx=store.instances.findIndex(r=>tokenName(r.token)===name);idx>=0?store.instances[idx]={token,instance}:store.instances.push({token,instance})}this.emit(name,`registered`,`instance`)}has(token){return this.registrations.has(token)}getRegistrations(){let entries=[];for(let[token,reg]of this.registrations)entries.push({token:tokenName(token),scope:reg.scope,kind:reg.kind,instantiated:reg.scope===`singleton`?reg.instance!==void 0:reg.resolveCount>0,resolveCount:reg.resolveCount,lastResolvedAt:reg.lastResolvedAt,firstResolvedAt:reg.firstResolvedAt,resolveDurationMs:reg.resolveDurationMs,postConstructStatus:reg.postConstructStatus,dependencies:reg.dependencies});return entries}resolve(token){let reg=this.registrations.get(token);if(!reg&&typeof token==`function`&&token.name&&(reg=this.registrations.get(`__hmr__${token.name}`)),!reg)throw Container._error(`KICK001`,tokenName(token));if(reg.scope===`singleton`&&reg.instance!==void 0)return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),reg.instance;if(reg.scope===`request`){if(!Container._requestStoreProvider)throw Container._error(`KICK002`,tokenName(token));let store=Container._requestStoreProvider();if(!store)throw Container._error(`KICK003`,tokenName(token));if(store.values.has(token))return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),store.values.get(token);if(store.instances.has(token))return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),store.instances.get(token);if(reg.factory){let start=performance.now(),instance=reg.factory();return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),store.instances.set(token,instance),this.emit(token,`resolved`,reg.kind),instance}let start=performance.now(),instance=this.createInstance(reg);return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),store.instances.set(token,instance),this.emit(token,`resolved`,reg.kind),instance}if(reg.factory){let start=performance.now(),instance=reg.factory();return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),reg.scope===`singleton`&&(reg.instance=instance,this.isGlobal&&getPersistentStore().resolvedInstances.set(tokenName(token),instance)),this.emit(token,`resolved`,reg.kind),instance}if(this.resolving.has(token)){let chain=[...this.resolving].map(tokenName);throw chain.push(tokenName(token)),Error(`Circular dependency detected: ${chain.join(` -> `)}`)}this.resolving.add(token);try{let start=performance.now(),instance=this.createInstance(reg);return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),reg.scope===`singleton`&&(reg.instance=instance),this.emit(token,`resolved`,reg.kind),instance}finally{this.resolving.delete(token)}}bootstrap(){}onChange(callback){return this.changeListeners.add(callback),()=>this.changeListeners.delete(callback)}invalidate(token,visited=/* @__PURE__ */ new Set){let name=tokenName(token),reg=this.registrations.get(token)??this.registrations.get(`__hmr__${name}`);if(reg&&!visited.has(reg)&&(visited.add(reg),!getPersistentStore().resolvedInstances.has(name))){reg.instance=void 0,reg.resolveCount=0,reg.postConstructStatus=`skipped`,this.emit(name,`invalidated`,reg.kind);for(let[depToken,depReg]of this.registrations)depReg.dependencies.includes(name)&&this.invalidate(depToken,visited)}}emit(rawToken,event,kind){if(this.changeListeners.size===0)return;let token=tokenName(rawToken),existing=this.pendingChanges.find(p=>p.token===token&&p.event===event);existing?existing.timestamp=Date.now():this.pendingChanges.push({token,event,kind,timestamp:Date.now()}),this.notifyTimer&&clearTimeout(this.notifyTimer),this.notifyTimer=setTimeout(()=>{let batch=this.pendingChanges.splice(0);if(batch.length!==0)for(let listener of this.changeListeners)try{listener(batch)}catch{}},Container.DEBOUNCE_MS),this.notifyTimer.unref?.()}flushChanges(){this.notifyTimer&&=(clearTimeout(this.notifyTimer),null);let batch=this.pendingChanges.splice(0);if(batch.length!==0)for(let listener of this.changeListeners)try{listener(batch)}catch{}}buildPlan(reg){let paramTypes=getClassMeta(DI_METADATA.PARAM_TYPES,reg.target,[]),injectTokens=getMetaRecord(DI_METADATA.INJECT,reg.target),maxInjectIndex=Object.keys(injectTokens).length>0?Math.max(...Object.keys(injectTokens).map(Number))+1:0,paramCount=Math.max(paramTypes.length,maxInjectIndex),ctorTokens=[];for(let index=0;index<paramCount;index++){let token=injectTokens[index]||paramTypes[index];if(!token)throw Error(`Cannot resolve constructor parameter at index ${index} of ${tokenName(reg.target)}. No @Inject token and no design:paramtypes metadata (tsx/ts-node don't emit it). Add @Inject(Token) to every constructor parameter.`);if(reg.scope===`singleton`){let depReg=this.registrations.get(token);if(depReg&&depReg.scope===`request`){let parent=tokenName(reg.target),dep=tokenName(token),fix=getClassMetaOrUndefined(DI_METADATA.CLASS_KIND,reg.target)===`controller`?`@Controller() is always SINGLETON, so inject "${dep}" with @Autowired() instead of the constructor.`:`Give "${parent}" TRANSIENT or REQUEST scope (e.g. @Service({ scope: Scope.REQUEST })), or inject "${dep}" with @Autowired() instead of the constructor.`;throw Error(`Cannot inject REQUEST-scoped "${dep}" into SINGLETON "${parent}". Singletons outlive requests. ${fix}`)}}ctorTokens.push(token)}let autowiredProps=getMetaMap(DI_METADATA.AUTOWIRED,reg.target.prototype),autowired=[];for(let[prop,token]of autowiredProps){let resolvedToken=token||getMethodMetaOrUndefined(DI_METADATA.PROPERTY_TYPE,reg.target.prototype,prop);resolvedToken&&autowired.push([prop,resolvedToken])}let values=[...getMetaMap(DI_METADATA.VALUE,reg.target.prototype).entries()],injected=[];for(let[key,prepare]of Container._propertyInjectors)for(let[prop,config]of getMetaMap(key,reg.target.prototype))injected.push([prop,prepare(config,prop)]);return{ctorTokens,autowired,values,injected,postConstruct:getClassMetaOrUndefined(DI_METADATA.POST_CONSTRUCT,reg.target.prototype)}}createInstance(reg){let plan=reg.plan??=this.buildPlan(reg),args=[];for(let token of plan.ctorTokens)args.push(this.resolve(token));let instance=new reg.target(...args);this.injectProperties(instance,plan);let postConstruct=plan.postConstruct;if(postConstruct&&typeof instance[postConstruct]==`function`)try{let result=instance[postConstruct]();result&&typeof result.then==`function`?(reg.postConstructStatus=`pending`,result.then(()=>{reg.postConstructStatus=`completed`}).catch(()=>{reg.postConstructStatus=`failed`}),Container._logger.warn(`@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() returned a Promise but the container is synchronous. The async operation will not be awaited.`)):reg.postConstructStatus=`completed`}catch(err){reg.postConstructStatus=`failed`,Container._logger.error(err,`@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() threw an error`)}return instance}extractDependencies(target){let paramTypes=getClassMeta(DI_METADATA.PARAM_TYPES,target,[]),injectTokens=getMetaRecord(DI_METADATA.INJECT,target),ctorDeps=paramTypes.map((type,index)=>tokenName(injectTokens[index]||type)),autowiredProps=getMetaMap(DI_METADATA.AUTOWIRED,target.prototype),propDeps=[];for(let[prop,token]of autowiredProps){let resolvedToken=token||getMethodMetaOrUndefined(DI_METADATA.PROPERTY_TYPE,target.prototype,prop);resolvedToken&&propDeps.push(tokenName(resolvedToken))}return[.../* @__PURE__ */ new Set([...ctorDeps,...propDeps])]}injectProperties(instance,plan){for(let[prop,resolvedToken]of plan.autowired)Object.defineProperty(instance,prop,{get:()=>{let value=this.resolve(resolvedToken);return this.registrations.get(resolvedToken)?.scope===`singleton`&&Object.defineProperty(instance,prop,{value,enumerable:!0,configurable:!0,writable:!1}),value},enumerable:!0,configurable:!0});for(let[prop,config]of plan.values)Object.defineProperty(instance,prop,{get(){if(Container._envResolver){let val=Container._envResolver(config.envKey);if(val!==void 0)return val}let val=process.env[config.envKey];if(val!==void 0)return val;if(config.defaultValue!==void 0)return config.defaultValue;throw Container._error(`KICK004`,config.envKey)},enumerable:!0,configurable:!0});for(let[prop,get]of plan.injected)Object.defineProperty(instance,prop,{get,enumerable:!0,configurable:!0})}};const pendingRegistrations=[],allRegistrations=/* @__PURE__ */ new Map;let containerRef=null;function flushPending(container){containerRef=container;for(let{target,scope}of pendingRegistrations)container.has(target)||container.register(target,target,scope);pendingRegistrations.length=0}Container._onReady=flushPending;function seed(container){for(let[,{target,scope}]of allRegistrations)container.has(target)||container.register(target,target,scope)}Container._onReset=container=>{containerRef=container,seed(container)},Container._onCreate=seed;const preDestroyWarned=/* @__PURE__ */ new WeakSet;function warnIfInertPreDestroy(target,scope){if(scope===`request`)return;let proto=target?.prototype;if(!proto||!Reflect.getMetadata?.(DI_METADATA.PRE_DESTROY,proto)||preDestroyWarned.has(target))return;preDestroyWarned.add(target);let name=target.name||String(target);console.warn(` kickjs: @PreDestroy on ${name} (${scope}) will never run — that hook fires only when a REQUEST scope closes.\n For application-lifetime resources, release them from an adapter's shutdown() hook instead.`)}function _registerInjectable(target,scope){setClassMeta(DI_METADATA.INJECTABLE,!0,target),setClassMeta(DI_METADATA.SCOPE,scope,target),warnIfInertPreDestroy(target,scope);let name=target.name||String(target);allRegistrations.set(name,{target,scope}),containerRef?containerRef.has(target)||containerRef.register(target,target,scope):pendingRegistrations.push({target,scope})}function Injectable(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`injectable`,target),_registerInjectable(target,options?.scope??`singleton`)}}function Service(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`service`,target),_registerInjectable(target,options?.scope??`singleton`)}}function Component(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`component`,target),_registerInjectable(target,options?.scope??`singleton`)}}function Repository(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`repository`,target),_registerInjectable(target,options?.scope??`singleton`)}}function PostConstruct(){return(target,propertyKey)=>{setClassMeta(DI_METADATA.POST_CONSTRUCT,propertyKey,target)}}function PreDestroy(){return(target,propertyKey)=>{setClassMeta(DI_METADATA.PRE_DESTROY,propertyKey,target)}}function applyInjection(token,target,propertyKey,parameterIndex){typeof parameterIndex==`number`?setInMetaRecord(DI_METADATA.INJECT,target,parameterIndex,token):propertyKey!=null&&setInMetaMap(DI_METADATA.AUTOWIRED,target,propertyKey,token)}function Autowired(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Inject(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Value(envKey,defaultValue){return(target,propertyKey)=>{setInMetaMap(DI_METADATA.VALUE,target,propertyKey,{envKey,defaultValue})}}function attachBuilder(target){setClassMeta(DI_METADATA.BUILDER,!0,target),target.builder=function(){let props={},proxy=new Proxy({},{get(_,key){return key===`build`?()=>Object.assign(new target,props):value=>(props[key]=value,proxy)}});return proxy}}function Builder(target){attachBuilder(target)}function withBuilder(ctor){return attachBuilder(ctor),ctor}const disposables=/* @__PURE__ */ new Set;function registerDisposable(fn){return disposables.add(fn),()=>disposables.delete(fn)}function drainDisposables(){let pending=[...disposables];return disposables.clear(),pending}async function runDisposables(list){await Promise.allSettled(list.map(async fn=>fn()))}async function disposeAll(){await runDisposables(drainDisposables())}export{Autowired,Builder,Component,Container,DI_METADATA,DiError,INJECTION_TOKEN,Inject,Injectable,PostConstruct,PreDestroy,Repository,Scope,Service,Value,_registerInjectable,createToken,defaultLogger,diError,disposeAll,drainDisposables,getClassMeta,getClassMetaOrUndefined,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,hasClassMeta,isInjectionToken,pushClassMeta,pushMethodMeta,registerDisposable,runDisposables,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,tokenName,withBuilder};
11
+ import"reflect-metadata";const KEY=Symbol.for(`@forinda/kickjs-di:loaded-from`),store=globalThis,here=import.meta.url;store[KEY]===void 0?store[KEY]=here:store[KEY]!==here&&console.warn(` @forinda/kickjs-di is loaded twice:\n ${store[KEY]}\n ${here}\n Each copy has its own container, so services registered in one are missing from the other.\n Install a single version (it comes with @forinda/kickjs; don't add it separately).`);let Scope=/* @__PURE__ */ function(Scope){return Scope.SINGLETON=`singleton`,Scope.TRANSIENT=`transient`,Scope.REQUEST=`request`,Scope}({});const DI_METADATA={INJECTABLE:`kick:injectable`,SCOPE:`kick:scope`,CLASS_KIND:`kick:class-kind`,AUTOWIRED:`kick:autowired`,INJECT:`kick:inject`,POST_CONSTRUCT:`kick:post_construct`,PRE_DESTROY:`kick:pre_destroy`,BUILDER:`kick:builder`,VALUE:`kick:value`,PARAM_TYPES:`design:paramtypes`,PROPERTY_TYPE:`design:type`,RETURN_TYPE:`design:returntype`};var DiError=class extends Error{code;subject;constructor(code,subject,message){super(message),this.code=code,this.subject=subject,this.name=`DiError`}};const MESSAGES={KICK001:s=>`No provider for ${s}`,KICK002:s=>`Cannot resolve REQUEST-scoped "${s}": no request store is installed`,KICK003:s=>`Cannot resolve REQUEST-scoped "${s}" outside a request`,KICK004:s=>`@Value('${s}'): not set and no default provided`,KICK010:s=>`Cannot bind "${s}": its fallback was already resolved, so code holding it would disagree with the new binding. Bind "${s}" before anything resolves it.`};function diError(code,subject){return new DiError(code,subject,`${code}: ${MESSAGES[code](subject)}`)}const defaultLogger={warn(){},error(err,message){console.error(message,err)}};function setClassMeta(key,value,target){Reflect.defineMetadata(key,value,target)}function setMethodMeta(key,value,target,method){Reflect.defineMetadata(key,value,target,method)}function getClassMeta(key,target,fallback){return Reflect.getMetadata(key,target)??fallback}function getMethodMeta(key,target,method,fallback){return Reflect.getMetadata(key,target,method)??fallback}function hasClassMeta(key,target){return Reflect.hasMetadata(key,target)}function getClassMetaOrUndefined(key,target){return Reflect.getMetadata(key,target)}function getMethodMetaOrUndefined(key,target,method){return Reflect.getMetadata(key,target,method)}function pushClassMeta(key,target,...items){let existing=Reflect.getOwnMetadata(key,target)??[];Reflect.defineMetadata(key,[...existing,...items],target)}function pushMethodMeta(key,target,method,...items){let existing=Reflect.getOwnMetadata(key,target,method)??[];Reflect.defineMetadata(key,[...existing,...items],target,method)}function setInMetaMap(key,target,mapKey,mapValue){let inherited=Reflect.getOwnMetadata(key,target),map=inherited?new Map(inherited):/* @__PURE__ */ new Map;map.set(mapKey,mapValue),Reflect.defineMetadata(key,map,target)}function getMetaMap(key,target){return Reflect.getMetadata(key,target)??/* @__PURE__ */ new Map}function setInMetaRecord(key,target,recKey,recValue){let inherited=Reflect.getOwnMetadata(key,target),rec=inherited?{...inherited}:{};rec[recKey]=recValue,Reflect.defineMetadata(key,rec,target)}function getMetaRecord(key,target){return Reflect.getMetadata(key,target)??{}}const INJECTION_TOKEN=Symbol.for(`@kickjs/InjectionToken`);function createToken(name){return Object.freeze({[INJECTION_TOKEN]:!0,name})}function isInjectionToken(value){return typeof value==`object`&&!!value&&value[INJECTION_TOKEN]===!0}function createReg(fields){return{kind:`unknown`,resolveCount:0,postConstructStatus:`skipped`,dependencies:[],...fields}}function tokenName(token){return typeof token==`symbol`?token.toString():isInjectionToken(token)?token.name:token?.name||String(token)}function getPersistentStore(){return globalThis.__kickjs_persistent||(globalThis.__kickjs_persistent={factories:[],instances:[],resolvedInstances:/* @__PURE__ */ new Map}),globalThis.__kickjs_persistent}var Container=class Container{static instance;registrations=/* @__PURE__ */ new Map;fallbacks=/* @__PURE__ */ new Map;resolving=/* @__PURE__ */ new Set;changeListeners=/* @__PURE__ */ new Set;pendingChanges=[];notifyTimer=null;static DEBOUNCE_MS=50;static _onReady=null;static _onReset=null;static _onCreate=null;static _envResolver=null;static _error=diError;static _logger=defaultLogger;static _propertyInjectors=/* @__PURE__ */ new Map;isGlobal=!1;static _requestStoreProvider=null;static getInstance(){return Container.instance||(Container.instance=new Container,Container.instance.isGlobal=!0),Container._onReady&&=(Container._onReady(Container.instance),null),Container.instance}static reset(){Container.instance=new Container,Container.instance.isGlobal=!0,Container._onReset?.(Container.instance);let store=getPersistentStore();for(let{token,factory,scope}of store.factories)if(!Container.instance.has(token)){let name=tokenName(token),existing=store.resolvedInstances.get(name);Container.instance.registrations.set(token,createReg({target:Object,scope,factory,kind:`factory`,instance:existing,resolveCount:+!!existing,postConstructStatus:existing?`completed`:`skipped`}))}for(let{token,instance}of store.instances)Container.instance.has(token)||Container.instance.registrations.set(token,createReg({target:instance.constructor,scope:`singleton`,instance,kind:`instance`,postConstructStatus:`completed`,resolveCount:1}))}static create(){let container=new Container;return Container._onCreate?.(container),container}register(token,target,scope=`singleton`){this.assertFallbackUnused(token);let kind=getClassMeta(DI_METADATA.CLASS_KIND,target,`unknown`),reg=createReg({target,scope,kind,dependencies:this.extractDependencies(target)});this.registrations.set(token,reg),typeof token==`function`&&token.name&&this.registrations.set(`__hmr__${token.name}`,reg),this.emit(token,`registered`,kind)}registerFactory(token,factory,scope=`singleton`){this.assertFallbackUnused(token);let store=getPersistentStore(),name=tokenName(token),factoryUnchanged=(this.isGlobal?store.factories.find(r=>tokenName(r.token)===name):void 0)?.factory===factory,existingInstance=this.isGlobal&&factoryUnchanged?store.resolvedInstances.get(name):void 0;if(this.isGlobal&&!factoryUnchanged&&store.resolvedInstances.delete(name),this.registrations.set(token,createReg({target:Object,scope,factory,kind:`factory`,instance:existingInstance,resolveCount:+!!existingInstance,postConstructStatus:existingInstance?`completed`:`skipped`})),this.isGlobal){let idx=store.factories.findIndex(r=>tokenName(r.token)===name);idx>=0?store.factories[idx]={token,factory,scope}:store.factories.push({token,factory,scope})}this.emit(name,`registered`,`factory`)}registerInstance(token,instance){this.assertFallbackUnused(token);let store=getPersistentStore(),name=tokenName(token);if(this.registrations.set(token,createReg({target:instance.constructor,scope:`singleton`,instance,kind:`instance`,postConstructStatus:`completed`,resolveCount:1,firstResolvedAt:Date.now(),lastResolvedAt:Date.now()})),this.isGlobal){store.resolvedInstances.set(name,instance);let idx=store.instances.findIndex(r=>tokenName(r.token)===name);idx>=0?store.instances[idx]={token,instance}:store.instances.push({token,instance})}this.emit(name,`registered`,`instance`)}registerFallback(token,factory,scope=`singleton`){this.assertFallbackUnused(token),this.fallbacks.set(token,createReg({target:Object,scope,factory,kind:`factory`})),this.registrations.has(token)||this.emit(token,`registered`,`factory`)}assertFallbackUnused(token){if(this.fallbacks.get(token)?.instance!==void 0)throw Container._error(`KICK010`,tokenName(token))}binding(token){return this.registrations.get(token)??this.fallbacks.get(token)}has(token){return this.registrations.has(token)}getRegistrations(){let entries=[],active=[...this.registrations];for(let[token,reg]of this.fallbacks)this.registrations.has(token)||active.push([token,reg]);for(let[token,reg]of active)entries.push({token:tokenName(token),...this.fallbacks.get(token)===reg&&{fallback:!0},scope:reg.scope,kind:reg.kind,instantiated:reg.scope===`singleton`?reg.instance!==void 0:reg.resolveCount>0,resolveCount:reg.resolveCount,lastResolvedAt:reg.lastResolvedAt,firstResolvedAt:reg.firstResolvedAt,resolveDurationMs:reg.resolveDurationMs,postConstructStatus:reg.postConstructStatus,dependencies:reg.dependencies});return entries}resolve(token){let reg=this.registrations.get(token);if(!reg&&typeof token==`function`&&token.name&&(reg=this.registrations.get(`__hmr__${token.name}`)),reg??=this.fallbacks.get(token),!reg)throw Container._error(`KICK001`,tokenName(token));if(reg.scope===`singleton`&&reg.instance!==void 0)return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),reg.instance;if(reg.scope===`request`){if(!Container._requestStoreProvider)throw Container._error(`KICK002`,tokenName(token));let store=Container._requestStoreProvider();if(!store)throw Container._error(`KICK003`,tokenName(token));if(store.values.has(token))return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),store.values.get(token);if(store.instances.has(token))return reg.resolveCount++,reg.lastResolvedAt=Date.now(),this.emit(token,`resolved`,reg.kind),store.instances.get(token);if(reg.factory){let start=performance.now(),instance=reg.factory();return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),store.instances.set(token,instance),this.emit(token,`resolved`,reg.kind),instance}let start=performance.now(),instance=this.createInstance(reg);return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),store.instances.set(token,instance),this.emit(token,`resolved`,reg.kind),instance}if(reg.factory){let start=performance.now(),instance=reg.factory();return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),reg.scope===`singleton`&&(reg.instance=instance,this.isGlobal&&getPersistentStore().resolvedInstances.set(tokenName(token),instance)),this.emit(token,`resolved`,reg.kind),instance}if(this.resolving.has(token)){let chain=[...this.resolving].map(tokenName);throw chain.push(tokenName(token)),Error(`Circular dependency detected: ${chain.join(` -> `)}`)}this.resolving.add(token);try{let start=performance.now(),instance=this.createInstance(reg);return reg.resolveDurationMs=performance.now()-start,reg.resolveCount++,reg.lastResolvedAt=Date.now(),reg.firstResolvedAt||(reg.firstResolvedAt=Date.now()),reg.scope===`singleton`&&(reg.instance=instance),this.emit(token,`resolved`,reg.kind),instance}finally{this.resolving.delete(token)}}bootstrap(){}onChange(callback){return this.changeListeners.add(callback),()=>this.changeListeners.delete(callback)}invalidate(token,visited=/* @__PURE__ */ new Set){let name=tokenName(token),reg=this.registrations.get(token)??this.registrations.get(`__hmr__${name}`)??this.fallbacks.get(token);if(reg&&!visited.has(reg)&&(visited.add(reg),!getPersistentStore().resolvedInstances.has(name))){reg.instance=void 0,reg.resolveCount=0,reg.postConstructStatus=`skipped`,this.emit(name,`invalidated`,reg.kind);for(let[depToken,depReg]of this.registrations)depReg.dependencies.includes(name)&&this.invalidate(depToken,visited)}}emit(rawToken,event,kind){if(this.changeListeners.size===0)return;let token=tokenName(rawToken),existing=this.pendingChanges.find(p=>p.token===token&&p.event===event);existing?existing.timestamp=Date.now():this.pendingChanges.push({token,event,kind,timestamp:Date.now()}),this.notifyTimer&&clearTimeout(this.notifyTimer),this.notifyTimer=setTimeout(()=>{let batch=this.pendingChanges.splice(0);if(batch.length!==0)for(let listener of this.changeListeners)try{listener(batch)}catch{}},Container.DEBOUNCE_MS),this.notifyTimer.unref?.()}flushChanges(){this.notifyTimer&&=(clearTimeout(this.notifyTimer),null);let batch=this.pendingChanges.splice(0);if(batch.length!==0)for(let listener of this.changeListeners)try{listener(batch)}catch{}}buildPlan(reg){let paramTypes=getClassMeta(DI_METADATA.PARAM_TYPES,reg.target,[]),injectTokens=getMetaRecord(DI_METADATA.INJECT,reg.target),maxInjectIndex=Object.keys(injectTokens).length>0?Math.max(...Object.keys(injectTokens).map(Number))+1:0,paramCount=Math.max(paramTypes.length,maxInjectIndex),ctorTokens=[];for(let index=0;index<paramCount;index++){let token=injectTokens[index]||paramTypes[index];if(!token)throw Error(`Cannot resolve constructor parameter at index ${index} of ${tokenName(reg.target)}. No @Inject token and no design:paramtypes metadata (tsx/ts-node don't emit it). Add @Inject(Token) to every constructor parameter.`);if(reg.scope===`singleton`){let depReg=this.binding(token);if(depReg&&depReg.scope===`request`){let parent=tokenName(reg.target),dep=tokenName(token),fix=getClassMetaOrUndefined(DI_METADATA.CLASS_KIND,reg.target)===`controller`?`@Controller() is always SINGLETON, so inject "${dep}" with @Autowired() instead of the constructor.`:`Give "${parent}" TRANSIENT or REQUEST scope (e.g. @Service({ scope: Scope.REQUEST })), or inject "${dep}" with @Autowired() instead of the constructor.`;throw Error(`Cannot inject REQUEST-scoped "${dep}" into SINGLETON "${parent}". Singletons outlive requests. ${fix}`)}}ctorTokens.push(token)}let autowiredProps=getMetaMap(DI_METADATA.AUTOWIRED,reg.target.prototype),autowired=[];for(let[prop,token]of autowiredProps){let resolvedToken=token||getMethodMetaOrUndefined(DI_METADATA.PROPERTY_TYPE,reg.target.prototype,prop);resolvedToken&&autowired.push([prop,resolvedToken])}let values=[...getMetaMap(DI_METADATA.VALUE,reg.target.prototype).entries()],injected=[];for(let[key,prepare]of Container._propertyInjectors)for(let[prop,config]of getMetaMap(key,reg.target.prototype))injected.push([prop,prepare(config,prop)]);return{ctorTokens,autowired,values,injected,postConstruct:getClassMetaOrUndefined(DI_METADATA.POST_CONSTRUCT,reg.target.prototype)}}createInstance(reg){let plan=reg.plan??=this.buildPlan(reg),args=[];for(let token of plan.ctorTokens)args.push(this.resolve(token));let instance=new reg.target(...args);this.injectProperties(instance,plan);let postConstruct=plan.postConstruct;if(postConstruct&&typeof instance[postConstruct]==`function`)try{let result=instance[postConstruct]();result&&typeof result.then==`function`?(reg.postConstructStatus=`pending`,result.then(()=>{reg.postConstructStatus=`completed`}).catch(()=>{reg.postConstructStatus=`failed`}),Container._logger.warn(`@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() returned a Promise but the container is synchronous. The async operation will not be awaited.`)):reg.postConstructStatus=`completed`}catch(err){reg.postConstructStatus=`failed`,Container._logger.error(err,`@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() threw an error`)}return instance}extractDependencies(target){let paramTypes=getClassMeta(DI_METADATA.PARAM_TYPES,target,[]),injectTokens=getMetaRecord(DI_METADATA.INJECT,target),ctorDeps=paramTypes.map((type,index)=>tokenName(injectTokens[index]||type)),autowiredProps=getMetaMap(DI_METADATA.AUTOWIRED,target.prototype),propDeps=[];for(let[prop,token]of autowiredProps){let resolvedToken=token||getMethodMetaOrUndefined(DI_METADATA.PROPERTY_TYPE,target.prototype,prop);resolvedToken&&propDeps.push(tokenName(resolvedToken))}return[.../* @__PURE__ */ new Set([...ctorDeps,...propDeps])]}injectProperties(instance,plan){for(let[prop,resolvedToken]of plan.autowired)Object.defineProperty(instance,prop,{get:()=>{let value=this.resolve(resolvedToken);return this.binding(resolvedToken)?.scope===`singleton`&&Object.defineProperty(instance,prop,{value,enumerable:!0,configurable:!0,writable:!1}),value},enumerable:!0,configurable:!0});for(let[prop,config]of plan.values)Object.defineProperty(instance,prop,{get(){if(Container._envResolver){let val=Container._envResolver(config.envKey);if(val!==void 0)return val}let val=process.env[config.envKey];if(val!==void 0)return val;if(config.defaultValue!==void 0)return config.defaultValue;throw Container._error(`KICK004`,config.envKey)},enumerable:!0,configurable:!0});for(let[prop,get]of plan.injected)Object.defineProperty(instance,prop,{get,enumerable:!0,configurable:!0})}};const pendingRegistrations=[],allRegistrations=/* @__PURE__ */ new Map;let containerRef=null;function flushPending(container){containerRef=container;for(let{target,scope}of pendingRegistrations)container.has(target)||container.register(target,target,scope);pendingRegistrations.length=0}Container._onReady=flushPending;function seed(container){for(let[,{target,scope}]of allRegistrations)container.has(target)||container.register(target,target,scope)}Container._onReset=container=>{containerRef=container,seed(container)},Container._onCreate=seed;const preDestroyWarned=/* @__PURE__ */ new WeakSet;function warnIfInertPreDestroy(target,scope){if(scope===`request`)return;let proto=target?.prototype;if(!proto||!Reflect.getMetadata?.(DI_METADATA.PRE_DESTROY,proto)||preDestroyWarned.has(target))return;preDestroyWarned.add(target);let name=target.name||String(target);console.warn(` kickjs: @PreDestroy on ${name} (${scope}) will never run — that hook fires only when a REQUEST scope closes.\n For application-lifetime resources, release them from an adapter's shutdown() hook instead.`)}function _registerInjectable(target,scope){setClassMeta(DI_METADATA.INJECTABLE,!0,target),setClassMeta(DI_METADATA.SCOPE,scope,target),warnIfInertPreDestroy(target,scope);let name=target.name||String(target);allRegistrations.set(name,{target,scope}),containerRef?containerRef.has(target)||containerRef.register(target,target,scope):pendingRegistrations.push({target,scope})}function Injectable(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`injectable`,target),_registerInjectable(target,options?.scope??`singleton`)}}function Service(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`service`,target),_registerInjectable(target,options?.scope??`singleton`)}}function Component(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`component`,target),_registerInjectable(target,options?.scope??`singleton`)}}function Repository(options){return target=>{setClassMeta(DI_METADATA.CLASS_KIND,`repository`,target),_registerInjectable(target,options?.scope??`singleton`)}}function PostConstruct(){return(target,propertyKey)=>{setClassMeta(DI_METADATA.POST_CONSTRUCT,propertyKey,target)}}function PreDestroy(){return(target,propertyKey)=>{setClassMeta(DI_METADATA.PRE_DESTROY,propertyKey,target)}}function applyInjection(token,target,propertyKey,parameterIndex){typeof parameterIndex==`number`?setInMetaRecord(DI_METADATA.INJECT,target,parameterIndex,token):propertyKey!=null&&setInMetaMap(DI_METADATA.AUTOWIRED,target,propertyKey,token)}function Autowired(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Inject(token){return((target,propertyKey,parameterIndex)=>{applyInjection(token,target,propertyKey,parameterIndex)})}function Value(envKey,defaultValue){return(target,propertyKey)=>{setInMetaMap(DI_METADATA.VALUE,target,propertyKey,{envKey,defaultValue})}}function attachBuilder(target){setClassMeta(DI_METADATA.BUILDER,!0,target),target.builder=function(){let props={},proxy=new Proxy({},{get(_,key){return key===`build`?()=>Object.assign(new target,props):value=>(props[key]=value,proxy)}});return proxy}}function Builder(target){attachBuilder(target)}function withBuilder(ctor){return attachBuilder(ctor),ctor}const disposables=/* @__PURE__ */ new Set;function registerDisposable(fn){return disposables.add(fn),()=>disposables.delete(fn)}function drainDisposables(){let pending=[...disposables];return disposables.clear(),pending}async function runDisposables(list){await Promise.allSettled(list.map(async fn=>fn()))}async function disposeAll(){await runDisposables(drainDisposables())}export{Autowired,Builder,Component,Container,DI_METADATA,DiError,INJECTION_TOKEN,Inject,Injectable,PostConstruct,PreDestroy,Repository,Scope,Service,Value,_registerInjectable,createToken,defaultLogger,diError,disposeAll,drainDisposables,getClassMeta,getClassMetaOrUndefined,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,hasClassMeta,isInjectionToken,pushClassMeta,pushMethodMeta,registerDisposable,runDisposables,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,tokenName,withBuilder};
12
12
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["METADATA","METADATA","\"SINGLETON\"","\"TRANSIENT\"","\"REQUEST\""],"sources":["../src/instance-guard.ts","../src/interfaces.ts","../src/errors.ts","../src/metadata.ts","../src/token.ts","../src/container.ts","../src/decorators.ts","../src/disposables.ts"],"sourcesContent":["/**\n * Two installed copies of this package each keep their own container and\n * their own decorator registry, so half the app's registrations would be\n * missing from whichever copy resolves them — with no error. Warn when a\n * second copy loads. The same file evaluated again (HMR, a test re-import)\n * is one copy, not two.\n */\nconst KEY = Symbol.for('@forinda/kickjs-di:loaded-from')\nconst store = globalThis as { [KEY]?: string }\nconst here = import.meta.url\n\nif (store[KEY] === undefined) {\n store[KEY] = here\n} else if (store[KEY] !== here) {\n console.warn(\n ` @forinda/kickjs-di is loaded twice:\\n ${store[KEY]}\\n ${here}\\n` +\n ` Each copy has its own container, so services registered in one are missing from the other.\\n` +\n ` Install a single version (it comes with @forinda/kickjs; don't add it separately).`,\n )\n}\n","import 'reflect-metadata'\n\n/** Lifecycle scope for DI registrations */\nexport enum Scope {\n SINGLETON = 'singleton',\n TRANSIENT = 'transient',\n /** One instance per HTTP request, cached in AsyncLocalStorage. Throws outside request context. */\n REQUEST = 'request',\n}\n\n/** Generic constructor type */\nexport type Constructor<T = any> = new (...args: any[]) => T\n\n/** Options for @Service, @Repository, @Component decorators */\nexport interface ServiceOptions {\n scope?: Scope\n}\n\n/** Fluent builder type */\nexport type BuilderOf<T> = {\n [K in keyof T as T[K] extends Function ? never : K]-?: (value: T[K]) => BuilderOf<T>\n} & { build(): T }\n\n/** Class with static builder() method */\nexport interface Buildable<T> {\n builder(): BuilderOf<T>\n}\n\n/** Decorator class kind for devtools introspection */\nexport type ClassKind =\n | 'service'\n | 'controller'\n | 'repository'\n | 'component'\n | 'injectable'\n | 'factory'\n | 'instance'\n | 'unknown'\n\n/** PostConstruct lifecycle status */\nexport type PostConstructStatus = 'pending' | 'completed' | 'failed' | 'skipped'\n\n/**\n * String metadata keys for the DI system + decorator subsystem. Every\n * key starts with the `kick:` prefix; the suffix is a short identifier\n * that may itself contain `:`, `-`, or `_` separators (historical —\n * keys grew alongside the framework). The §22 v4 convention is \"no\n * Symbols\", not \"use a strict naming scheme\" — Symbols don't survive\n * JSON serialisation, can't be addressed by string from worker\n * threads, and don't show up in DevTools snapshots.\n *\n * `reflect-metadata` accepts both Symbol and string keys identically,\n * so the consumer-side change is internal — references go through the\n * `METADATA` enum's named members (e.g. `METADATA.INJECT`), not the\n * underlying string.\n *\n * The `design:*` entries match what TypeScript's\n * `emitDecoratorMetadata` writes; those names are fixed by the\n * compiler + must stay as-is.\n */\nexport const DI_METADATA = {\n INJECTABLE: 'kick:injectable',\n SCOPE: 'kick:scope',\n CLASS_KIND: 'kick:class-kind',\n AUTOWIRED: 'kick:autowired',\n INJECT: 'kick:inject',\n POST_CONSTRUCT: 'kick:post_construct',\n PRE_DESTROY: 'kick:pre_destroy',\n BUILDER: 'kick:builder',\n VALUE: 'kick:value',\n // TypeScript emit metadata keys — fixed by the compiler.\n PARAM_TYPES: 'design:paramtypes',\n PROPERTY_TYPE: 'design:type',\n RETURN_TYPE: 'design:returntype',\n} as const\n","/**\n * The container's own errors, by stable code. A host can replace the factory\n * (`Container._error`) to turn them into richer errors — kickjs maps each\n * code to its catalogue entry, with the cause and the fix.\n */\nexport type DiErrorCode =\n /** Nothing is registered for the token. */\n | 'KICK001'\n /** A REQUEST-scoped token, but no request store is installed. */\n | 'KICK002'\n /** A REQUEST-scoped token, resolved outside a request. */\n | 'KICK003'\n /** `@Value` key not set, and no default. */\n | 'KICK004'\n\nexport class DiError extends Error {\n constructor(\n readonly code: DiErrorCode,\n /** The token name, or the env key for KICK004. */\n readonly subject: string,\n message: string,\n ) {\n super(message)\n this.name = 'DiError'\n }\n}\n\nconst MESSAGES: Record<DiErrorCode, (subject: string) => string> = {\n KICK001: (s) => `No provider for ${s}`,\n KICK002: (s) => `Cannot resolve REQUEST-scoped \"${s}\": no request store is installed`,\n KICK003: (s) => `Cannot resolve REQUEST-scoped \"${s}\" outside a request`,\n KICK004: (s) => `@Value('${s}'): not set and no default provided`,\n}\n\nexport function diError(code: DiErrorCode, subject: string): Error {\n return new DiError(code, subject, `${code}: ${MESSAGES[code](subject)}`)\n}\n\n/** Where the container reports non-fatal problems. A host can install its own. */\nexport interface DiLogger {\n warn(message: string): void\n error(err: unknown, message: string): void\n}\n\n/** Drops warnings; errors (a `@PostConstruct` that threw) go to `console.error`. */\nexport const defaultLogger: DiLogger = {\n warn() {},\n error(err, message) {\n console.error(message, err)\n },\n}\n","import 'reflect-metadata'\nimport type { InjectionToken } from './token'\n\n/**\n * Typed utilities for reflect-metadata operations.\n *\n * Replaces scattered Reflect.defineMetadata/getMetadata calls with a single\n * source of truth. If we later move to Stage 3 decorators or a WeakMap-based\n * store, only this file changes — all 24+ consumer files stay untouched.\n *\n * Keys may be symbols, strings, or `InjectionToken<T>` objects produced by\n * `createToken()`. Using `createToken` is the preferred pattern because the\n * token's phantom type flows through the helpers, enabling type-safe reads.\n */\n\ntype MetaKey = symbol | string | InjectionToken<unknown>\n\n// ── Setters ─────────────────────────────────────────────────────\n\n/** Set metadata on a class (or prototype for property decorators) */\nexport function setClassMeta<T>(key: MetaKey, value: T, target: object): void {\n Reflect.defineMetadata(key, value, target)\n}\n\n/** Set metadata on a specific method */\nexport function setMethodMeta<T>(key: MetaKey, value: T, target: object, method: string): void {\n Reflect.defineMetadata(key, value, target, method)\n}\n\n// ── Getters ─────────────────────────────────────────────────────\n\n/** Get metadata from a class, returning `fallback` if not set */\nexport function getClassMeta<T>(key: MetaKey, target: object, fallback: T): T {\n return (Reflect.getMetadata(key, target) as T) ?? fallback\n}\n\n/** Get metadata from a specific method, returning `fallback` if not set */\nexport function getMethodMeta<T>(key: MetaKey, target: object, method: string, fallback: T): T {\n return (Reflect.getMetadata(key, target, method) as T) ?? fallback\n}\n\n/** Check if metadata exists on a class */\nexport function hasClassMeta(key: MetaKey, target: object): boolean {\n return Reflect.hasMetadata(key, target)\n}\n\n/** Get metadata from a class with no default (returns undefined if absent) */\nexport function getClassMetaOrUndefined<T>(key: MetaKey, target: object): T | undefined {\n return Reflect.getMetadata(key, target) as T | undefined\n}\n\n/** Get metadata from a method with no default (returns undefined if absent) */\nexport function getMethodMetaOrUndefined<T>(\n key: MetaKey,\n target: object,\n method: string,\n): T | undefined {\n return Reflect.getMetadata(key, target, method) as T | undefined\n}\n\n// ── Accumulate: Arrays ──────────────────────────────────────────\n\n/** Append items to an array stored in class metadata */\nexport function pushClassMeta<T>(key: MetaKey, target: object, ...items: T[]): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target)\n}\n\n/** Append items to an array stored in method metadata */\nexport function pushMethodMeta<T>(\n key: MetaKey,\n target: object,\n method: string,\n ...items: T[]\n): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target, method) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target, method)\n}\n\n// ── Accumulate: Maps ────────────────────────────────────────────\n\n/** Set a key/value in a Map stored in class metadata */\nexport function setInMetaMap<K, V>(key: MetaKey, target: object, mapKey: K, mapValue: V): void {\n const inherited: Map<K, V> | undefined = Reflect.getOwnMetadata(key, target)\n const map = inherited ? new Map(inherited) : new Map<K, V>()\n map.set(mapKey, mapValue)\n Reflect.defineMetadata(key, map, target)\n}\n\n/** Get a Map from class metadata (returns empty Map if absent) */\nexport function getMetaMap<K, V>(key: MetaKey, target: object): Map<K, V> {\n return Reflect.getMetadata(key, target) ?? new Map()\n}\n\n// ── Accumulate: Records ─────────────────────────────────────────\n\n/** Set a key/value in a Record stored in class metadata */\nexport function setInMetaRecord<V>(\n key: MetaKey,\n target: object,\n recKey: string | number,\n recValue: V,\n): void {\n const inherited: Record<string | number, V> | undefined = Reflect.getOwnMetadata(key, target)\n const rec = inherited ? { ...inherited } : {}\n rec[recKey] = recValue\n Reflect.defineMetadata(key, rec, target)\n}\n\n/** Get a Record from class metadata (returns empty object if absent) */\nexport function getMetaRecord<V>(key: MetaKey, target: object): Record<string | number, V> {\n return Reflect.getMetadata(key, target) ?? {}\n}\n","/**\n * Collision-safe injection tokens with phantom type parameters.\n *\n * `createToken<T>(name)` is the recommended way to declare any DI token\n * that isn't a class. Each call returns a unique frozen object — even if\n * two files write `createToken<X>('Logger')`, they produce two distinct\n * references that the container treats as different tokens. Collisions\n * become impossible by construction, not by convention.\n *\n * The phantom type parameter `T` lets `container.resolve(token)` infer\n * the correct return type without any codegen.\n *\n * ## Hardening layers\n *\n * KickJS has four DI token kinds, listed from safest to riskiest:\n *\n * 1. **Class identity** — `container.resolve(UserService)`. Reference\n * equality, never collides. Type-safe via `Constructor<T>` overload.\n * 2. **`createToken<T>(name)`** — frozen object identity, never collides.\n * Type-safe via the `InjectionToken<T>` overload.\n * 3. **Symbol** — `Symbol('foo')` is unique per call but `Symbol.for('foo')`\n * is interned and CAN collide. Discouraged.\n * 4. **Raw string `@Inject('foo')`** — high collision risk, untyped.\n * Reserved for legacy code and runtime-computed token names.\n *\n * Prefer (1) and (2). Use (4) only when you have to.\n *\n * @example\n * ```ts\n * // src/tokens.ts\n * export const DATABASE_URL = createToken<string>('config.database.url')\n * export const FEATURE_FLAGS = createToken<FeatureFlags>('app.features')\n *\n * // bootstrap\n * container.registerInstance(DATABASE_URL, process.env.DATABASE_URL!)\n *\n * // anywhere else\n * const url = container.resolve(DATABASE_URL) // typed as `string`\n * ```\n *\n * @module @forinda/kickjs/core/token\n */\n\n/**\n * Symbol marker stamped on every InjectionToken so the container can\n * detect them at runtime regardless of which package created them.\n *\n * Uses `Symbol.for()` so cross-package isolation (different copies of\n * `@forinda/kickjs` loaded under different module identities) still\n * shares the same marker.\n */\nexport const INJECTION_TOKEN: unique symbol = Symbol.for('@kickjs/InjectionToken') as never\n\n/**\n * A type-safe DI token. Each `createToken()` call returns a unique frozen\n * object identified by reference, not by the `name` string. Two\n * `createToken<X>('foo')` calls in different files produce two distinct\n * tokens that the container treats independently.\n *\n * The phantom `_type` parameter is never assigned at runtime — it exists\n * solely to thread the type through `container.resolve(token)`.\n */\nexport interface InjectionToken<T> {\n readonly [INJECTION_TOKEN]: true\n /** Descriptive name used in error messages, logs, and devtools */\n readonly name: string\n /** Phantom — never assigned at runtime */\n readonly _type?: T\n}\n\n/**\n * Create a collision-safe injection token.\n *\n * The returned token is a frozen object identified by reference, not by\n * the `name` string. The `name` is used only for error messages, logs,\n * and debugging.\n */\nexport function createToken<T>(name: string): InjectionToken<T> {\n return Object.freeze({\n [INJECTION_TOKEN]: true as const,\n name,\n })\n}\n\n/** Type guard — returns `true` if `value` is an InjectionToken */\nexport function isInjectionToken(value: unknown): value is InjectionToken<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[INJECTION_TOKEN] === true\n )\n}\n","import 'reflect-metadata'\nimport {\n Constructor,\n Scope,\n DI_METADATA as METADATA,\n type ClassKind,\n type PostConstructStatus,\n} from './interfaces'\nimport { diError, defaultLogger, type DiErrorCode, type DiLogger } from './errors'\nimport {\n getClassMeta,\n getClassMetaOrUndefined,\n getMetaMap,\n getMetaRecord,\n getMethodMetaOrUndefined,\n} from './metadata'\nimport { isInjectionToken, type InjectionToken } from './token'\n\n/** Internal registration entry that tracks how a dependency should be resolved */\ninterface Registration {\n target: Constructor\n scope: Scope\n instance?: any\n factory?: () => any\n // Observability fields\n kind: ClassKind\n resolveCount: number\n lastResolvedAt?: number\n firstResolvedAt?: number\n resolveDurationMs?: number\n postConstructStatus: PostConstructStatus\n dependencies: string[]\n /** Lazily built instantiation plan — see {@link Container.buildPlan}. */\n plan?: InstantiationPlan\n}\n\n/**\n * Precomputed instantiation recipe for a class. Class metadata is immutable\n * after definition, so the Reflect reads (5 per create) and the derived\n * structures only need computing ONCE — previously they re-ran on every\n * REQUEST-scoped resolve (i.e. every request) and every TRANSIENT resolve.\n */\ninterface InstantiationPlan {\n /** Ordered, validated constructor parameter tokens. */\n ctorTokens: any[]\n /** `@Autowired` properties: [property, resolved token]. */\n autowired: Array<[string, any]>\n /** `@Value` properties: [property, env config]. */\n values: Array<[string, { envKey: string; defaultValue?: any }]>\n /** Host-injected properties (`_propertyInjectors`): [property, getter]. */\n injected: Array<[string, () => unknown]>\n /** `@PostConstruct` method name, if any. */\n postConstruct?: string | symbol\n}\n\n/** Create a Registration with observability defaults */\nfunction createReg(\n fields: Partial<Registration> & Pick<Registration, 'target' | 'scope'>,\n): Registration {\n return {\n kind: 'unknown',\n resolveCount: 0,\n postConstructStatus: 'skipped',\n dependencies: [],\n ...fields,\n }\n}\n\n/** Format a token for display in error messages */\nexport function tokenName(token: any): string {\n if (typeof token === 'symbol') return token.toString()\n if (isInjectionToken(token)) return token.name\n return token?.name || String(token)\n}\n\n// ── Persistent Store ────────────────────────────────────────────────────\n// globalThis-based storage that survives BOTH Container.reset() AND Vite's\n// ssrLoadModule() re-evaluation. When Vite re-evaluates a module, the\n// Container class gets a new identity (new statics). But globalThis persists.\n//\n// This is how DB connections, Redis clients, WsAdapter instances, and any\n// other stateful bindings survive HMR without being dropped.\n//\n// Pattern from: React Router (globalThis for state), Vinxi (globalThis.app)\n\n// ── Change Event System ─────────────────────────────────────────────────\n// Batched (50ms debounce) so `kick g module` creating 10+ files emits ONE\n// notification, not 10. Any adapter can subscribe: Swagger, DevTools, WS, etc.\n\n/** A single container change event */\nexport interface ContainerChangeEvent {\n token: string\n event: 'registered' | 'resolved' | 'invalidated'\n kind?: ClassKind\n timestamp: number\n}\n\n/** Listener that receives batched change events */\nexport type ContainerChangeListener = (changes: ContainerChangeEvent[]) => void\n\n/**\n * Type-safe registry of string DI tokens. Augmented by `kick typegen` —\n * the generated `.kickjs/types/registry.d.ts` adds entries like:\n *\n * ```ts\n * declare module '@forinda/kickjs' {\n * interface KickJsRegistry {\n * UserService: import('../../src/modules/users/user.service').UserService\n * }\n * }\n * ```\n *\n * Once typegen has run, `container.resolve('UserService')` returns the\n * correct type instead of `any`. Class-token resolution\n * (`container.resolve(UserService)`) is already type-safe via the\n * `Constructor<T>` overload and does not need typegen.\n */\nexport interface KickJsRegistry {}\n\ninterface PersistentStore {\n factories: Array<{ token: any; factory: () => any; scope: Scope }>\n instances: Array<{ token: any; instance: any }>\n /** Resolved instances keyed by string name — survives class identity changes */\n resolvedInstances: Map<string, any>\n}\n\nfunction getPersistentStore(): PersistentStore {\n if (!(globalThis as any).__kickjs_persistent) {\n ;(globalThis as any).__kickjs_persistent = {\n factories: [],\n instances: [],\n resolvedInstances: new Map(),\n } satisfies PersistentStore\n }\n return (globalThis as any).__kickjs_persistent\n}\n\n/**\n * Inversion-of-Control (IoC) container that manages dependency registration,\n * resolution, and lifecycle. Implements the Singleton pattern so all parts of\n * the application share a single container instance.\n *\n * Supports constructor injection, property injection (@Autowired),\n * factory registrations, and lifecycle hooks (@PostConstruct).\n *\n * Persistent state (DB connections, Redis, etc.) is stored on globalThis so\n * it survives both Container.reset() and Vite module re-evaluation.\n */\nexport class Container {\n private static instance: Container\n private registrations = new Map<any, Registration>()\n private resolving = new Set<any>()\n\n // ── Reactive change tracking ──────────────────────────────────────\n private changeListeners = new Set<ContainerChangeListener>()\n private pendingChanges: ContainerChangeEvent[] = []\n private notifyTimer: ReturnType<typeof setTimeout> | null = null\n private static readonly DEBOUNCE_MS = 50\n\n /** Callback set by the decorators module to flush pending registrations */\n static _onReady: ((container: Container) => void) | null = null\n /** Callback invoked on reset so decorators can update their container reference */\n static _onReset: ((container: Container) => void) | null = null\n\n /**\n * Seeds a NEWLY CREATED container with the decorator registrations, without\n * adopting it as the global one. `_onReset` does both; an isolated container\n * must only have the first half, or it would capture every subsequent\n * decorator registration in the process.\n */\n static _onCreate: ((container: Container) => void) | null = null\n /**\n * Environment resolver for @Value decorator. Set by the unified\n * `@forinda/kickjs` config layer to return Zod-validated, type-coerced\n * env values instead of raw process.env strings.\n */\n static _envResolver: ((key: string) => any) | null = null\n /**\n * Builds the container's errors from a stable code and the token name (or\n * env key). The default gives a plain `DiError`; kickjs installs one that\n * returns its catalogued `KickError`.\n */\n static _error: (code: DiErrorCode, subject: string) => Error = diError\n /** Where non-fatal problems go (an async `@PostConstruct`, one that threw). Errors reach `console.error` by default. */\n static _logger: DiLogger = defaultLogger\n /**\n * Extra lazy property injections, keyed by metadata key. For each property\n * a class stores under that key (a `Map` on the prototype, as `@Value`\n * does), `prepare(config, prop)` runs once when the class's plan is built\n * — so it can reject a bad config up front — and returns the getter each\n * instance's property reads through. kickjs adds `@Asset` this way.\n */\n static _propertyInjectors = new Map<string, (config: any, prop: string) => () => unknown>()\n /**\n * Request store provider for REQUEST-scoped DI. Set by the HTTP layer\n * inside `@forinda/kickjs` to return the current request's\n * AsyncLocalStorage store. Returns { instances: Map, values: Map } or\n * null if outside a request.\n */\n /**\n * Whether this container is the global singleton.\n *\n * The persistent store exists to carry the GLOBAL container's registrations\n * across HMR and module re-evaluation. An isolated container is per-test and\n * must neither write to it — a `registerInstance` override was replayed into\n * every later `Container.reset()` — nor read from it, which would pull global\n * state into a container created to be free of it.\n */\n private isGlobal = false\n\n static _requestStoreProvider:\n | (() => { instances: Map<any, any>; values: Map<any, any> } | null)\n | null = null\n\n static getInstance(): Container {\n if (!Container.instance) {\n Container.instance = new Container()\n Container.instance.isGlobal = true\n }\n // Flush any decorator registrations that queued before the container existed\n if (Container._onReady) {\n Container._onReady(Container.instance)\n Container._onReady = null\n }\n return Container.instance\n }\n\n /**\n * Resets the container by replacing the singleton with a fresh instance.\n * Persistent registrations (DB, Redis, etc.) are replayed from globalThis\n * so they survive both reset() and Vite module re-evaluation.\n */\n static reset(): void {\n Container.instance = new Container()\n Container.instance.isGlobal = true\n // Notify decorators so they update their container reference\n Container._onReset?.(Container.instance)\n // Replay persistent registrations from globalThis store\n const store = getPersistentStore()\n for (const { token, factory, scope } of store.factories) {\n if (!Container.instance.has(token)) {\n // Check if we have a previously resolved instance for this token\n const name = tokenName(token)\n const existing = store.resolvedInstances.get(name)\n Container.instance.registrations.set(\n token,\n createReg({\n target: Object as any,\n scope,\n factory,\n kind: 'factory',\n instance: existing, // Reuse resolved instance if available\n resolveCount: existing ? 1 : 0,\n postConstructStatus: existing ? 'completed' : 'skipped',\n }),\n )\n }\n }\n for (const { token, instance } of store.instances) {\n if (!Container.instance.has(token)) {\n Container.instance.registrations.set(\n token,\n createReg({\n target: instance.constructor,\n scope: Scope.SINGLETON,\n instance,\n kind: 'instance',\n postConstructStatus: 'completed',\n resolveCount: 1,\n }),\n )\n }\n }\n }\n\n /**\n * Create an isolated container instance (not the global singleton).\n * Useful for concurrent tests that must not share DI state.\n *\n * @example\n * ```ts\n * const container = Container.create()\n * container.register(UserService, UserService)\n * const svc = container.resolve(UserService)\n * ```\n */\n static create(): Container {\n const container = new Container()\n // Decorators register at class-definition time, into whichever container\n // was global then. Without this replay an isolated container is missing\n // every @Service / @Repository / @Controller in the process — and\n // `isolated: true` is what the generated docs recommend for test\n // isolation, so the failure reached adopters as `No provider for X`.\n Container._onCreate?.(container)\n return container\n }\n\n /** Register a class constructor under the given token */\n register(token: any, target: Constructor, scope: Scope = Scope.SINGLETON): void {\n const kind = getClassMeta<ClassKind>(METADATA.CLASS_KIND, target, 'unknown')\n const dependencies = this.extractDependencies(target)\n const reg = createReg({ target, scope, kind, dependencies })\n this.registrations.set(token, reg)\n // Store a name-based fallback so HMR class re-creation (new identity)\n // can still resolve by the original class name.\n if (typeof token === 'function' && token.name) {\n this.registrations.set(`__hmr__${token.name}`, reg)\n }\n this.emit(token, 'registered', kind)\n }\n\n /** Register a factory function under the given token */\n registerFactory(token: any, factory: () => any, scope: Scope = Scope.SINGLETON): void {\n const store = getPersistentStore()\n const name = tokenName(token)\n // Check if we already have a resolved instance from a previous HMR cycle.\n // Only reuse if the factory hasn't changed (same reference). When a new\n // factory is provided (e.g. swapping implementations), clear the stale\n // instance so the new factory runs on next resolve.\n // Reads are global-only too. An isolated container adopting a cached\n // instance from the global store would be contaminated on creation — the\n // same leak, in the other direction.\n const existingEntry = this.isGlobal\n ? store.factories.find((r) => tokenName(r.token) === name)\n : undefined\n const factoryUnchanged = existingEntry?.factory === factory\n const existingInstance =\n this.isGlobal && factoryUnchanged ? store.resolvedInstances.get(name) : undefined\n\n // Clear stale resolved instance when factory changes\n if (this.isGlobal && !factoryUnchanged) {\n store.resolvedInstances.delete(name)\n }\n\n this.registrations.set(\n token,\n createReg({\n target: Object as any,\n scope,\n factory,\n kind: 'factory',\n instance: existingInstance,\n resolveCount: existingInstance ? 1 : 0,\n postConstructStatus: existingInstance ? 'completed' : 'skipped',\n }),\n )\n // Track in globalThis for persistence across HMR + module re-evaluation —\n // global container only, or an isolated container's factories are replayed\n // into the next Container.reset().\n if (this.isGlobal) {\n const idx = store.factories.findIndex((r) => tokenName(r.token) === name)\n if (idx >= 0) {\n store.factories[idx] = { token, factory, scope }\n } else {\n store.factories.push({ token, factory, scope })\n }\n }\n this.emit(name, 'registered', 'factory')\n }\n\n /** Register a pre-constructed singleton instance */\n registerInstance(token: any, instance: any): void {\n const store = getPersistentStore()\n const name = tokenName(token)\n\n this.registrations.set(\n token,\n createReg({\n target: instance.constructor,\n scope: Scope.SINGLETON,\n instance,\n kind: 'instance',\n postConstructStatus: 'completed',\n resolveCount: 1,\n firstResolvedAt: Date.now(),\n lastResolvedAt: Date.now(),\n }),\n )\n // Track in globalThis for persistence across HMR + module re-evaluation —\n // global container only. An isolated one that wrote here had its overrides\n // replayed into the next Container.reset().\n if (this.isGlobal) {\n store.resolvedInstances.set(name, instance)\n const idx = store.instances.findIndex((r) => tokenName(r.token) === name)\n if (idx >= 0) {\n store.instances[idx] = { token, instance }\n } else {\n store.instances.push({ token, instance })\n }\n }\n this.emit(name, 'registered', 'instance')\n }\n\n /** Check if a binding exists for the given token */\n has(token: any): boolean {\n return this.registrations.has(token)\n }\n\n /** Return a snapshot of all registered tokens with their scope and instantiation status */\n getRegistrations(): Array<{\n token: string\n scope: string\n kind: ClassKind\n instantiated: boolean\n resolveCount: number\n lastResolvedAt?: number\n firstResolvedAt?: number\n resolveDurationMs?: number\n postConstructStatus: PostConstructStatus\n dependencies: string[]\n }> {\n const entries: Array<ReturnType<Container['getRegistrations']>[number]> = []\n for (const [token, reg] of this.registrations) {\n entries.push({\n token: tokenName(token),\n scope: reg.scope,\n kind: reg.kind,\n // Singletons check cached instance; transients check resolveCount\n instantiated:\n reg.scope === Scope.SINGLETON ? reg.instance !== undefined : reg.resolveCount > 0,\n resolveCount: reg.resolveCount,\n lastResolvedAt: reg.lastResolvedAt,\n firstResolvedAt: reg.firstResolvedAt,\n resolveDurationMs: reg.resolveDurationMs,\n postConstructStatus: reg.postConstructStatus,\n dependencies: reg.dependencies,\n })\n }\n return entries\n }\n\n /**\n * Resolve a dependency by its token.\n *\n * Four overloads, in order of preference:\n * - **`InjectionToken<T>`** (from `createToken<T>(name)`) → returns `T`.\n * Collision-safe by construction; no codegen required.\n * - **Class constructor** → returns an instance of that class. Always\n * type-safe; no codegen required.\n * - **String token in `KickJsRegistry`** (populated by `kick typegen`) →\n * returns the augmented type.\n * - **Anything else** → returns `any`. Use this only for legacy code or\n * string tokens that haven't been registered with typegen yet.\n */\n resolve<T>(token: InjectionToken<T>): T\n resolve<T>(token: Constructor<T>): T\n resolve<K extends keyof KickJsRegistry & string>(token: K): KickJsRegistry[K]\n resolve<T = any>(token: any): T\n resolve<T = any>(token: any): T {\n let reg = this.registrations.get(token)\n // HMR fallback: when Vite re-evaluates a module, decorated classes get new\n // identity. Try resolving by class name if the primary token lookup fails.\n if (!reg && typeof token === 'function' && token.name) {\n reg = this.registrations.get(`__hmr__${token.name}`)\n }\n if (!reg) {\n throw Container._error('KICK001', tokenName(token))\n }\n\n if (reg.scope === Scope.SINGLETON && reg.instance !== undefined) {\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n this.emit(token, 'resolved', reg.kind)\n return reg.instance\n }\n\n // REQUEST scope: one instance per HTTP request, cached in AsyncLocalStorage\n if (reg.scope === Scope.REQUEST) {\n if (!Container._requestStoreProvider) {\n throw Container._error('KICK002', tokenName(token))\n }\n const store = Container._requestStoreProvider()\n if (!store) {\n throw Container._error('KICK003', tokenName(token))\n }\n // Check for pre-registered request values (user, tenant, etc.)\n if (store.values.has(token)) {\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n this.emit(token, 'resolved', reg.kind)\n return store.values.get(token) as T\n }\n // Check per-request instance cache\n if (store.instances.has(token)) {\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n this.emit(token, 'resolved', reg.kind)\n return store.instances.get(token) as T\n }\n // Factory-registered binding: invoke the factory once per request\n // and cache the result alongside class-instantiated REQUEST tokens.\n // Without this branch, registerFactory(token, fn, Scope.REQUEST)\n // silently falls through to createInstance(reg) which `new`s the\n // factory's placeholder `target: Object` and returns an empty {}\n // — the factory function never runs.\n if (reg.factory) {\n const start = performance.now()\n const instance = reg.factory()\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n store.instances.set(token, instance)\n this.emit(token, 'resolved', reg.kind)\n return instance as T\n }\n // Create instance and cache for this request only\n const start = performance.now()\n const instance = this.createInstance(reg)\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n store.instances.set(token, instance)\n this.emit(token, 'resolved', reg.kind)\n return instance as T\n }\n\n if (reg.factory) {\n const start = performance.now()\n const instance = reg.factory()\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n if (reg.scope === Scope.SINGLETON) {\n reg.instance = instance\n // Persist resolved factory instances so they survive module\n // re-evaluation — global container only, for the same reason.\n if (this.isGlobal) {\n getPersistentStore().resolvedInstances.set(tokenName(token), instance)\n }\n }\n this.emit(token, 'resolved', reg.kind)\n return instance\n }\n\n if (this.resolving.has(token)) {\n const chain = [...this.resolving].map(tokenName)\n chain.push(tokenName(token))\n throw new Error(`Circular dependency detected: ${chain.join(' -> ')}`)\n }\n this.resolving.add(token)\n\n try {\n const start = performance.now()\n const instance = this.createInstance(reg)\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n if (reg.scope === Scope.SINGLETON) {\n reg.instance = instance\n }\n this.emit(token, 'resolved', reg.kind)\n return instance\n } finally {\n this.resolving.delete(token)\n }\n }\n\n /** Lifecycle hook called during Application.setup() after module registration */\n bootstrap(): void {\n // Reserved for future use — adapters and modules register via\n // container.register(), registerFactory(), and registerInstance().\n }\n\n // ── Reactive Change API ─────────────────────────────────────────────\n\n /**\n * Subscribe to container changes. Returns an unsubscribe function.\n *\n * Changes are batched (50ms debounce) so bulk operations like\n * `kick g module` creating 10+ files emit ONE batch, not 10 events.\n *\n * Any adapter can subscribe:\n * - SwaggerAdapter: re-build spec when controllers change\n * - DevToolsAdapter: push SSE events to dashboard\n * - WsAdapter: re-discover namespaces when controllers change\n * - OtelAdapter: emit tracing spans for DI resolution\n * - Custom user adapters\n *\n * @example\n * ```ts\n * const unsub = container.onChange((changes) => {\n * for (const c of changes) {\n * console.log(`${c.event}: ${c.token} (${c.kind})`)\n * }\n * })\n * // later:\n * unsub()\n * ```\n */\n onChange(callback: ContainerChangeListener): () => void {\n this.changeListeners.add(callback)\n return () => this.changeListeners.delete(callback)\n }\n\n /**\n * Invalidate a specific registration. Clears the cached instance so the\n * next resolve() creates a fresh one. Also invalidates dependents\n * (anything that injected this token) by walking the dependency graph.\n *\n * Called by the Vite HMR plugin when a module file changes.\n * Skips persistent registrations (DB connections, etc.).\n */\n invalidate(token: any, visited = new Set<Registration>()): void {\n const name = tokenName(token)\n // Try both the token itself and the HMR fallback name\n const reg = this.registrations.get(token) ?? this.registrations.get(`__hmr__${name}`)\n // `visited`: two classes that @Autowired each other depend on each other,\n // and the walk below went round that cycle until the stack overflowed.\n if (!reg || visited.has(reg)) return\n visited.add(reg)\n\n // Don't invalidate persistent registrations (DB, Redis, etc.)\n const store = getPersistentStore()\n if (store.resolvedInstances.has(name)) return\n\n // Clear cached instance — next resolve() will re-create\n reg.instance = undefined\n reg.resolveCount = 0\n reg.postConstructStatus = 'skipped'\n this.emit(name, 'invalidated', reg.kind)\n\n // Walk dependency graph — invalidate anything that injected this token\n for (const [depToken, depReg] of this.registrations) {\n if (depReg.dependencies.includes(name)) {\n this.invalidate(depToken, visited)\n }\n }\n }\n\n /**\n * Batch-emit a change event (debounced, flushed after 50ms of quiet).\n * Takes the RAW token — `tokenName()` is only computed after the\n * zero-listener guard, so the hot cached-singleton resolve path never pays\n * the name derivation when nobody is watching (the production default).\n */\n private emit(rawToken: any, event: ContainerChangeEvent['event'], kind?: ClassKind): void {\n // Zero-listener fast path. Nobody subscribes to container changes unless\n // DevTools (or a test) calls `onChange` — the production default. Without\n // this guard, every `resolve()` (including the hot cached-singleton path)\n // would scan `pendingChanges`, push an entry, and reschedule a `setTimeout`\n // only for the eventual flush to iterate an empty listener set and discard\n // the batch. Short-circuiting here keeps `resolve()` allocation- and\n // timer-free when no one is watching. When a listener attaches later,\n // subsequent emits work exactly as before — past resolves had no observer\n // to notify anyway.\n if (this.changeListeners.size === 0) return\n const token = tokenName(rawToken)\n\n // Dedupe within the pending batch: a high-throughput TRANSIENT\n // token that resolves 1000× per request would otherwise queue\n // 1000 identical 'resolved' events. The downstream listener only\n // cares that the token's state changed, so collapse to one entry\n // (the earliest timestamp wins; the latest update is reflected\n // in resolveCount/lastResolvedAt on the registration itself,\n // which the listener reads at notify time).\n const existing = this.pendingChanges.find((p) => p.token === token && p.event === event)\n if (existing) {\n existing.timestamp = Date.now()\n } else {\n this.pendingChanges.push({ token, event, kind, timestamp: Date.now() })\n }\n\n if (this.notifyTimer) clearTimeout(this.notifyTimer)\n this.notifyTimer = setTimeout(() => {\n const batch = this.pendingChanges.splice(0)\n if (batch.length === 0) return\n for (const listener of this.changeListeners) {\n try {\n listener(batch)\n } catch {\n // Don't let one broken listener break others\n }\n }\n }, Container.DEBOUNCE_MS)\n // Don't let a pending debounce window hold the event loop open — flush is\n // best-effort telemetry, not critical work.\n this.notifyTimer.unref?.()\n }\n\n /** Flush pending change events immediately (useful in tests) */\n flushChanges(): void {\n if (this.notifyTimer) {\n clearTimeout(this.notifyTimer)\n this.notifyTimer = null\n }\n const batch = this.pendingChanges.splice(0)\n if (batch.length === 0) return\n for (const listener of this.changeListeners) {\n try {\n listener(batch)\n } catch {\n // Don't let one broken listener break others\n }\n }\n }\n\n /**\n * Build the one-time instantiation plan for a registration. Runs on the\n * FIRST createInstance for the class — the metadata it reads is immutable\n * after class definition, so every later create (REQUEST scope: every\n * request) skips all Reflect reads and re-derivation.\n *\n * Scope validation (SINGLETON must not ctor-inject REQUEST) lives here\n * too: it's a static property of the (parent, dep) registration pair, so\n * checking it once at plan build is equivalent to the old per-create loop.\n */\n private buildPlan(reg: Registration): InstantiationPlan {\n const paramTypes = getClassMeta<Constructor[]>(METADATA.PARAM_TYPES, reg.target, [])\n const injectTokens = getMetaRecord<any>(METADATA.INJECT, reg.target)\n\n // When emitDecoratorMetadata is unavailable (tsx, ts-node without\n // SWC), paramTypes is empty even though @Inject set tokens on\n // specific constructor indices. Derive the constructor arity from\n // the @Inject metadata so constructor injection still works.\n const maxInjectIndex =\n Object.keys(injectTokens).length > 0\n ? Math.max(...Object.keys(injectTokens).map(Number)) + 1\n : 0\n const paramCount = Math.max(paramTypes.length, maxInjectIndex)\n\n const ctorTokens: any[] = []\n for (let index = 0; index < paramCount; index++) {\n const token = injectTokens[index] || paramTypes[index]\n if (!token) {\n throw new Error(\n `Cannot resolve constructor parameter at index ${index} of ${tokenName(reg.target)}. ` +\n `No @Inject token and no design:paramtypes metadata (tsx/ts-node don't emit it). ` +\n `Add @Inject(Token) to every constructor parameter.`,\n )\n }\n // Scope validation: SINGLETON cannot inject REQUEST-scoped dependencies\n if (reg.scope === Scope.SINGLETON) {\n const depReg = this.registrations.get(token)\n if (depReg && depReg.scope === Scope.REQUEST) {\n // The fix depends on the parent: @Controller() takes no options and\n // is always SINGLETON, so \"change the parent's scope\" is advice a\n // controller cannot follow. @Autowired works for every parent — its\n // getter re-resolves REQUEST deps per access (injectProperties).\n const parent = tokenName(reg.target)\n const dep = tokenName(token)\n const kind = getClassMetaOrUndefined<ClassKind>(METADATA.CLASS_KIND, reg.target)\n const fix =\n kind === 'controller'\n ? `@Controller() is always SINGLETON, so inject \"${dep}\" with @Autowired() instead of the constructor.`\n : `Give \"${parent}\" TRANSIENT or REQUEST scope (e.g. @Service({ scope: Scope.REQUEST })), ` +\n `or inject \"${dep}\" with @Autowired() instead of the constructor.`\n throw new Error(\n `Cannot inject REQUEST-scoped \"${dep}\" into SINGLETON \"${parent}\". ` +\n `Singletons outlive requests. ${fix}`,\n )\n }\n }\n ctorTokens.push(token)\n }\n\n // @Autowired properties — resolve each property's token once.\n const autowiredProps = getMetaMap<string, any>(METADATA.AUTOWIRED, reg.target.prototype)\n const autowired: InstantiationPlan['autowired'] = []\n for (const [prop, token] of autowiredProps) {\n const resolvedToken =\n token || getMethodMetaOrUndefined(METADATA.PROPERTY_TYPE, reg.target.prototype, prop)\n if (resolvedToken) autowired.push([prop, resolvedToken])\n }\n\n // @Value properties.\n const valueProps = getMetaMap<string, { envKey: string; defaultValue?: any }>(\n METADATA.VALUE,\n reg.target.prototype,\n )\n const values: InstantiationPlan['values'] = [...valueProps.entries()]\n\n // Properties added by host injectors (`_propertyInjectors`), e.g. `@Asset`.\n const injected: InstantiationPlan['injected'] = []\n for (const [key, prepare] of Container._propertyInjectors) {\n for (const [prop, config] of getMetaMap<string, unknown>(key, reg.target.prototype)) {\n injected.push([prop, prepare(config, prop)])\n }\n }\n\n const postConstruct = getClassMetaOrUndefined<string | symbol>(\n METADATA.POST_CONSTRUCT,\n reg.target.prototype,\n )\n\n return { ctorTokens, autowired, values, injected, postConstruct }\n }\n\n private createInstance(reg: Registration): any {\n const plan = (reg.plan ??= this.buildPlan(reg))\n\n const args: any[] = []\n for (const token of plan.ctorTokens) {\n args.push(this.resolve(token))\n }\n\n const instance = new reg.target(...args)\n\n // Property injection via @Autowired / @Value / @Asset\n this.injectProperties(instance, plan)\n\n // @PostConstruct lifecycle hook\n const postConstruct = plan.postConstruct\n if (postConstruct && typeof instance[postConstruct] === 'function') {\n try {\n const result = instance[postConstruct]()\n if (result && typeof result.then === 'function') {\n reg.postConstructStatus = 'pending'\n ;(result as Promise<void>)\n .then(() => {\n reg.postConstructStatus = 'completed'\n })\n .catch(() => {\n reg.postConstructStatus = 'failed'\n })\n Container._logger.warn(\n `@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() returned a Promise ` +\n `but the container is synchronous. The async operation will not be awaited.`,\n )\n } else {\n reg.postConstructStatus = 'completed'\n }\n } catch (err) {\n reg.postConstructStatus = 'failed'\n Container._logger.error(\n err,\n `@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() threw an error`,\n )\n }\n }\n\n return instance\n }\n\n /**\n * Extract every dependency token name a class declares — both\n * constructor params (`@Inject` + emitted `design:paramtypes`) and\n * `@Autowired` properties (read lazily at injection time, but\n * captured here so devtools surfaces a complete dependency graph).\n *\n * Without the property branch, classes that rely entirely on\n * `@Autowired` show `dependencies: []` in the introspection panel\n * even though they have real DI deps — see /_debug topology.\n */\n private extractDependencies(target: Constructor): string[] {\n const paramTypes = getClassMeta<Constructor[]>(METADATA.PARAM_TYPES, target, [])\n const injectTokens = getMetaRecord<any>(METADATA.INJECT, target)\n const ctorDeps = paramTypes.map((type, index) => {\n const token = injectTokens[index] || type\n return tokenName(token)\n })\n\n // @Autowired properties live on `target.prototype`. Token may be\n // explicit (`@Autowired('SOME_TOKEN')`) or fall back to the\n // emitted `design:type` metadata of the property.\n const autowiredProps = getMetaMap<string, any>(METADATA.AUTOWIRED, target.prototype)\n const propDeps: string[] = []\n for (const [prop, token] of autowiredProps) {\n const resolvedToken =\n token || getMethodMetaOrUndefined(METADATA.PROPERTY_TYPE, target.prototype, prop)\n if (resolvedToken) propDeps.push(tokenName(resolvedToken))\n }\n\n // De-dupe — a class can declare the same token in both surfaces.\n return [...new Set([...ctorDeps, ...propDeps])]\n }\n\n private injectProperties(instance: any, plan: InstantiationPlan): void {\n // @Autowired — lazy DI property injection. The getter re-resolves per\n // access by design (a SINGLETON may lazily read a REQUEST-scoped dep at\n // call time). For SINGLETON deps that laziness buys nothing after the\n // first read — the container returns the same cached instance forever —\n // so the getter memoizes itself into a plain data property then. REQUEST\n // and TRANSIENT deps keep the live getter (memoizing would pin the first\n // request's instance across requests).\n for (const [prop, resolvedToken] of plan.autowired) {\n Object.defineProperty(instance, prop, {\n get: () => {\n const value = this.resolve(resolvedToken)\n const depReg = this.registrations.get(resolvedToken)\n if (depReg?.scope === Scope.SINGLETON) {\n Object.defineProperty(instance, prop, {\n value,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n return value\n },\n enumerable: true,\n configurable: true,\n })\n }\n\n // @Value — lazy environment variable injection\n for (const [prop, config] of plan.values) {\n Object.defineProperty(instance, prop, {\n get() {\n // Use the registered env resolver if available (wired by the\n // config layer in @forinda/kickjs). Returns Zod-validated,\n // type-coerced values (e.g. PORT as number).\n if (Container._envResolver) {\n const val = Container._envResolver(config.envKey)\n if (val !== undefined) return val\n }\n\n // Fallback to raw process.env for apps that haven't called loadEnv()\n const val = process.env[config.envKey]\n if (val !== undefined) return val\n if (config.defaultValue !== undefined) return config.defaultValue\n throw Container._error('KICK004', config.envKey)\n },\n enumerable: true,\n configurable: true,\n })\n }\n\n // Host injectors — getters prepared at plan build.\n for (const [prop, get] of plan.injected) {\n Object.defineProperty(instance, prop, { get, enumerable: true, configurable: true })\n }\n }\n}\n","import 'reflect-metadata'\nimport { DI_METADATA as METADATA, Scope, type BuilderOf, type ServiceOptions } from './interfaces'\nimport { Container, type KickJsRegistry } from './container'\nimport { setClassMeta, setInMetaMap, setInMetaRecord } from './metadata'\n\n// ── Decorator Registration System ───────────────────────────────────────\n// Decorators execute at class-definition time (module load). The Container\n// may not exist yet when the first decorator fires. We queue registrations\n// and flush them on first Container.getInstance() call.\n//\n// `allRegistrations` is a persistent registry that survives Container.reset().\n// On reset (HMR), all previously decorated classes are re-registered on the\n// fresh container. This ensures @Service, @Controller, @Repository, etc.\n// all survive HMR without manual re-registration.\n\ntype PendingRegistration = { target: any; scope: Scope }\nconst pendingRegistrations: PendingRegistration[] = []\n// Keyed by class name to prevent memory leaks during HMR — new class with\n// the same name replaces the old entry instead of accumulating references.\nconst allRegistrations = new Map<string, { target: any; scope: Scope }>()\nlet containerRef: any = null\n\nfunction flushPending(container: any): void {\n containerRef = container\n for (const { target, scope } of pendingRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n pendingRegistrations.length = 0\n}\n\n// Wire up synchronously — Container._onReady is called on first getInstance()\nContainer._onReady = flushPending\n\n/** Replay every decorator registration onto a container. */\nfunction seed(container: any): void {\n for (const [, { target, scope }] of allRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n}\n\n// On Container.reset(), update containerRef and replay ALL decorator\n// registrations on the fresh container. This handles HMR where the container\n// is wiped but not all decorated modules are re-evaluated.\nContainer._onReset = (container: any) => {\n containerRef = container\n seed(container)\n}\n\n// On Container.create(), seed the same way but leave `containerRef` alone: an\n// isolated container should receive what has been registered so far, not\n// become the destination for everything registered next.\nContainer._onCreate = seed\n\n// ── Class Decorators ────────────────────────────────────────────────────\n\n/**\n * Classes already warned about.\n *\n * Keyed by the constructor, not its `name`: distinct classes can share a name —\n * two `DatabaseService` classes in separate modules is ordinary — and deduping\n * by name silently suppressed the second one's warning, which is precisely the\n * failure this warning exists to prevent.\n *\n * A WeakSet means an HMR reload, which builds a NEW class object, warns again.\n * That is correct rather than noisy: the reloaded code still has the problem,\n * and the notice stops as soon as it is fixed.\n */\nconst preDestroyWarned = new WeakSet<object>()\n\n/**\n * `@PreDestroy` only runs for REQUEST-scoped services — the request scope is\n * what closes and triggers it. On a SINGLETON it is silently inert, which\n * reads as a working teardown and is not: the reported case was a Postgres\n * pool never closed on shutdown, invisible in development and surfacing as\n * connection exhaustion under repeated restarts and HMR reloads.\n *\n * The asymmetry is the trap. `@PostConstruct` DOES run for singletons, so the\n * pair looks like init/teardown while one half quietly opts out on a scope the\n * author may never have considered.\n *\n * Warn rather than run it: making it fire on shutdown is a behaviour change\n * whose ordering, timeout and concurrency semantics have to match the adapter\n * shutdown path, and an adapter's `shutdown()` is already the seam for\n * application-lifetime resources.\n */\nfunction warnIfInertPreDestroy(target: any, scope: Scope): void {\n if (scope === Scope.REQUEST) return\n const proto = target?.prototype\n if (!proto) return\n const hook = Reflect.getMetadata?.(METADATA.PRE_DESTROY, proto)\n if (!hook) return\n if (preDestroyWarned.has(target)) return\n preDestroyWarned.add(target)\n const name = target.name || String(target)\n console.warn(\n ` kickjs: @PreDestroy on ${name} (${scope}) will never run — that hook fires ` +\n `only when a REQUEST scope closes.\\n` +\n ` For application-lifetime resources, release them from an adapter's ` +\n `shutdown() hook instead.`,\n )\n}\n\n/**\n * Register a decorated class with the container. @internal — exported for\n * class decorators built outside this module (`@Controller`).\n */\nexport function _registerInjectable(target: any, scope: Scope): void {\n setClassMeta(METADATA.INJECTABLE, true, target)\n setClassMeta(METADATA.SCOPE, scope, target)\n // Method decorators run before the class decorator, so PRE_DESTROY metadata\n // is already present by the time we get here.\n warnIfInertPreDestroy(target, scope)\n\n // Track in persistent registry — survives Container.reset() for HMR replay.\n // Keyed by name so HMR class re-creation replaces the old entry.\n const name = target.name || String(target)\n allRegistrations.set(name, { target, scope })\n\n if (containerRef) {\n // Container already initialized — register immediately\n if (!containerRef.has(target)) {\n containerRef.register(target, target, scope)\n }\n } else {\n // Container not ready yet — queue for later\n pendingRegistrations.push({ target, scope })\n }\n}\n\n/** Mark a class as injectable with lifecycle scope */\nexport function Injectable(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'injectable', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a service (semantic alias for Injectable) */\nexport function Service(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'service', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a generic managed component */\nexport function Component(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'component', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a repository */\nexport function Repository(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'repository', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n// ── Method Decorators ───────────────────────────────────────────────────\n\n/** Mark a method as a lifecycle hook called after instantiation */\nexport function PostConstruct(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.POST_CONSTRUCT, propertyKey, target)\n }\n}\n\n/**\n * Mark a method as a teardown hook — the counterpart to {@link PostConstruct}.\n *\n * For REQUEST-scoped services it runs when the request's scope closes\n * (response finished or aborted): release per-request resources there\n * (transactions, handles, subscriptions). May be async; errors are logged\n * and swallowed so one failing hook can't break request completion.\n *\n * It does NOT run on a SINGLETON — the default scope — because nothing closes\n * a singleton's scope. Unlike {@link PostConstruct}, which does run for\n * singletons, so the pair is not symmetric. Applying it to a singleton logs a\n * warning naming the class rather than failing silently.\n *\n * For application-lifetime resources (a connection pool, a timer, a socket\n * server) use an adapter's `shutdown()` hook, which the framework runs on a\n * real shutdown AND on every HMR reload, time-boxed and concurrent.\n */\nexport function PreDestroy(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.PRE_DESTROY, propertyKey, target)\n }\n}\n\n// ── Injection Decorators (property + constructor parameter) ─────────────\n\n/**\n * A decorator that's valid as either a property decorator OR a\n * constructor-parameter decorator. forinda/kick-js#235 §2 — `@Autowired`\n * and `@Inject` previously diverged at the type level (property-only\n * vs parameter-only) and adopters who picked the wrong name for the\n * position got a cryptic TS1240 error. Both names now accept either\n * position and route to the correct metadata bucket at runtime.\n */\nexport interface PropertyOrParameterDecorator {\n /** Property decorator form — `@Autowired(TOKEN) private foo!: Foo`. */\n (target: object, propertyKey: string | symbol): void\n /** Constructor-parameter form — `constructor(@Inject(TOKEN) foo: Foo) {}`. */\n (target: object, propertyKey: string | symbol | undefined, parameterIndex: number): void\n}\n\nfunction applyInjection(\n token: unknown,\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex: number | undefined,\n): void {\n // Legacy parameter decorators receive 3 args (target, propertyKey,\n // parameterIndex); property decorators receive 2 (target, propertyKey).\n // The 3rd-arg-number-vs-undefined check is the canonical position\n // detection in TypeScript's legacy decorator runtime.\n if (typeof parameterIndex === 'number') {\n setInMetaRecord(METADATA.INJECT, target, parameterIndex, token)\n return\n }\n if (propertyKey != null) {\n // `target` for a property decorator on a non-static field is the\n // class prototype; the AUTOWIRED bucket is keyed off the prototype\n // identity at resolution time, so pass it through unchanged.\n setInMetaMap(METADATA.AUTOWIRED, target, propertyKey as string, token)\n }\n}\n\n/**\n * Inject a dependency. Works in two positions:\n *\n * - **Property decorator** — `@Autowired(TOKEN) private repo!: UserRepo`.\n * Resolved lazily from the container the first time the property is\n * read.\n * - **Constructor parameter decorator** — `constructor(@Autowired(TOKEN) repo: UserRepo) {}`.\n * Resolved at instantiation; injected into the ctor call.\n *\n * `@Inject` is the same function under another name — they share\n * runtime + types so the two names are interchangeable. Pick whichever\n * reads better at the call site.\n *\n * Like `@Inject`, the typed string-literal overload narrows the token\n * to a key of the augmented `KickJsRegistry`. After `kick typegen`\n * populates the registry, `@Autowired('kick/prisma/Client')` auto-\n * completes the key and typo'd literals become TS2345 errors.\n */\nexport function Autowired<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Inject a dependency by token. Same shape as {@link Autowired} —\n * works as either a property decorator or a constructor-parameter\n * decorator. Pick whichever name reads better at the call site.\n *\n * ## Typed string-literal overload\n *\n * When called with a string literal that matches a key of the\n * augmented `KickJsRegistry`, TypeScript narrows the parameter type\n * to the registered type. Typo'd literals become compile errors\n * (the string isn't `keyof KickJsRegistry`). Class identities and\n * `InjectionToken<T>` keep working unchanged via the second overload.\n *\n * @example\n * ```ts\n * import { Service, Inject } from '@forinda/kickjs'\n * import type { PrismaClient } from '@prisma/client'\n *\n * `@Service()`\n * class UserRepo {\n * constructor(@Inject('kick/prisma/Client') private db: PrismaClient) {}\n * // OR property form, same decorator:\n * @Inject('kick/prisma/Client') private db2!: PrismaClient\n * }\n * ```\n *\n * After `kick typegen` runs, the literal autocompletes from the\n * registry and a typo (`@Inject('kick/prisma/Cleint')`) becomes a\n * TS2345 error.\n */\nexport function Inject<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Global ambient registry of typed environment variables, populated by\n * `kick typegen` from the project's env schema (typically\n * `src/env.ts`'s `export default defineEnv(...)`).\n *\n * Empty by default — declarations come from `.kickjs/types/env.ts`\n * generated alongside the rest of the typegen output. Once populated,\n * `@Value('PORT')` autocompletes the key, type-checks the default\n * value, and `process.env.PORT` is also typed via the parallel\n * `NodeJS.ProcessEnv` augmentation in the same generated file.\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface KickEnv {}\n}\n\n/** A string key known to the typed env registry (`never` until typegen runs) */\nexport type EnvKey = keyof KickEnv & string\n\n/**\n * Look up the type of a typed env entry.\n *\n * Decorators can't propagate the property type from the key, so users\n * still need to write the property's type — but `Env<K>` lets them\n * derive it from `KickEnv` instead of duplicating the schema:\n *\n * ```ts\n * @Service()\n * class MyService {\n * @Value('PORT')\n * private port!: Env<'PORT'> // → number, narrowed from KickEnv['PORT']\n * }\n * ```\n */\nexport type Env<K extends EnvKey = EnvKey> = KickEnv[K]\n\n/**\n * Constraint that allows any string when the typed env registry is\n * empty (no `kick typegen` has run yet, so existing code with raw\n * string keys keeps working) but locks the argument down to known\n * `EnvKey` literals once the registry is populated.\n *\n * Implementation: when `EnvKey` is `never`, the conditional resolves\n * to the user's `K`, accepting anything. Once typegen has populated\n * `KickEnv`, `EnvKey` becomes a real union and the conditional forces\n * `K` to be assignable to it.\n */\ntype ValueKey<K extends string> = [EnvKey] extends [never] ? K : K & EnvKey\n\n/**\n * Inject an environment variable value. Evaluated lazily so the env\n * is available at access time, not at decoration time.\n *\n * If no default is provided and the env var is missing, throws at access time\n * to catch misconfiguration early instead of returning undefined.\n *\n * Uses metadata + instance getter to work correctly with `useDefineForClassFields`.\n *\n * Type-safety:\n * - Without `kick typegen` run, any string key is accepted (back-compat).\n * - After typegen, `KickEnv` is populated and unknown keys become a tsc\n * error. The optional `defaultValue` is type-checked against the\n * schema's inferred type for that key.\n */\nexport function Value<K extends string>(\n envKey: ValueKey<K>,\n defaultValue?: K extends EnvKey ? KickEnv[K] : unknown,\n): PropertyDecorator {\n return (target, propertyKey) => {\n setInMetaMap(METADATA.VALUE, target, propertyKey as string, { envKey, defaultValue })\n }\n}\n// ── Builder Decorator ───────────────────────────────────────────────────\n\nfunction attachBuilder(target: any): void {\n setClassMeta(METADATA.BUILDER, true, target)\n target.builder = function () {\n const props: Record<string, any> = {}\n const proxy: any = new Proxy(\n {},\n {\n get(_, key) {\n if (key === 'build') {\n return () => Object.assign(new target(), props)\n }\n return (value: any) => {\n props[key as string] = value\n return proxy\n }\n },\n },\n )\n return proxy\n }\n}\n\n/**\n * Add a static `builder()` method for fluent construction.\n *\n * Legacy decorators cannot widen the class type, so by default the\n * caller sees the unmodified shape. Opt into typing by adding a single\n * `declare static` line:\n *\n * ```ts\n * @Builder\n * class UserDto {\n * name!: string\n * email!: string\n * declare static readonly builder: () => BuilderOf<UserDto>\n * }\n * ```\n *\n * `readonly` keeps SonarQube's `typescript:S1444` quiet — the runtime\n * assigns `target.builder` once at decoration time and never again.\n *\n * If you'd rather not write the `declare`, use `withBuilder()` instead —\n * same runtime, types inferred automatically.\n */\nexport function Builder(target: any): void {\n attachBuilder(target)\n}\n\n/**\n * Factory variant of `@Builder`. Wraps the class with the same runtime\n * and returns it intersected with `{ builder(): BuilderOf<T> }` so the\n * static is fully typed without any `declare` boilerplate.\n *\n * ```ts\n * class UserDtoBase {\n * name!: string\n * email!: string\n * }\n * export const UserDto = withBuilder(UserDtoBase)\n * export type UserDto = InstanceType<typeof UserDto>\n *\n * UserDto.builder().name('Alice').email('a@b.com').build() // fully typed\n * ```\n */\nexport function withBuilder<C extends new (...args: any[]) => any>(\n ctor: C,\n): C & { builder(): BuilderOf<InstanceType<C>> } {\n attachBuilder(ctor)\n return ctor as C & { builder(): BuilderOf<InstanceType<C>> }\n}\n","/**\n * Registry for framework-owned resources that hold timers or other handles\n * outside the adapter/plugin lifecycle — e.g. the in-memory session and\n * rate-limit stores' cleanup intervals. `Application.shutdown()` drains it so\n * startup and shutdown stay symmetric even for resources created lazily by\n * middleware factories.\n *\n * Intervals are `.unref()`'d so they never wedge process exit — this registry\n * exists for IN-PROCESS teardown (test suites, HMR, multi-app hosts) where\n * each un-disposed store would otherwise leak a live interval + backing Map\n * per app instance.\n */\n\nexport type Disposable = () => void | Promise<void>\n\nconst disposables = new Set<Disposable>()\n\n/** Register a cleanup callback. Returns an unregister function. */\nexport function registerDisposable(fn: Disposable): () => void {\n disposables.add(fn)\n return () => disposables.delete(fn)\n}\n\n/**\n * Remove and return all currently registered disposables WITHOUT running\n * them. Used by HMR rebuild to snapshot the outgoing app's resources before\n * `setup()` registers the incoming app's — so the stale set can be disposed\n * on success, or restored on failure, without touching the new one.\n */\nexport function drainDisposables(): Disposable[] {\n const pending = [...disposables]\n disposables.clear()\n return pending\n}\n\n/**\n * Run a list of disposables. Errors — including SYNCHRONOUS throws — are\n * swallowed per-entry: the `async` callback converts a sync throw into a\n * rejection that `allSettled` absorbs, so one bad disposable can't abort\n * the rest (a bare `Promise.resolve(fn())` would let the throw escape the\n * `.map()` before `allSettled` ever ran).\n */\nexport async function runDisposables(list: Disposable[]): Promise<void> {\n await Promise.allSettled(list.map(async (fn) => fn()))\n}\n\n/** Run and clear all registered disposables. Errors are swallowed per-entry. */\nexport async function disposeAll(): Promise<void> {\n await runDisposables(drainDisposables())\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["METADATA","METADATA","\"SINGLETON\"","\"TRANSIENT\"","\"REQUEST\""],"sources":["../src/instance-guard.ts","../src/interfaces.ts","../src/errors.ts","../src/metadata.ts","../src/token.ts","../src/container.ts","../src/decorators.ts","../src/disposables.ts"],"sourcesContent":["/**\n * Two installed copies of this package each keep their own container and\n * their own decorator registry, so half the app's registrations would be\n * missing from whichever copy resolves them — with no error. Warn when a\n * second copy loads. The same file evaluated again (HMR, a test re-import)\n * is one copy, not two.\n */\nconst KEY = Symbol.for('@forinda/kickjs-di:loaded-from')\nconst store = globalThis as { [KEY]?: string }\nconst here = import.meta.url\n\nif (store[KEY] === undefined) {\n store[KEY] = here\n} else if (store[KEY] !== here) {\n console.warn(\n ` @forinda/kickjs-di is loaded twice:\\n ${store[KEY]}\\n ${here}\\n` +\n ` Each copy has its own container, so services registered in one are missing from the other.\\n` +\n ` Install a single version (it comes with @forinda/kickjs; don't add it separately).`,\n )\n}\n","import 'reflect-metadata'\n\n/** Lifecycle scope for DI registrations */\nexport enum Scope {\n SINGLETON = 'singleton',\n TRANSIENT = 'transient',\n /** One instance per HTTP request, cached in AsyncLocalStorage. Throws outside request context. */\n REQUEST = 'request',\n}\n\n/** Generic constructor type */\nexport type Constructor<T = any> = new (...args: any[]) => T\n\n/** Options for @Service, @Repository, @Component decorators */\nexport interface ServiceOptions {\n scope?: Scope\n}\n\n/** Fluent builder type */\nexport type BuilderOf<T> = {\n [K in keyof T as T[K] extends Function ? never : K]-?: (value: T[K]) => BuilderOf<T>\n} & { build(): T }\n\n/** Class with static builder() method */\nexport interface Buildable<T> {\n builder(): BuilderOf<T>\n}\n\n/** Decorator class kind for devtools introspection */\nexport type ClassKind =\n | 'service'\n | 'controller'\n | 'repository'\n | 'component'\n | 'injectable'\n | 'factory'\n | 'instance'\n | 'unknown'\n\n/** PostConstruct lifecycle status */\nexport type PostConstructStatus = 'pending' | 'completed' | 'failed' | 'skipped'\n\n/**\n * String metadata keys for the DI system + decorator subsystem. Every\n * key starts with the `kick:` prefix; the suffix is a short identifier\n * that may itself contain `:`, `-`, or `_` separators (historical —\n * keys grew alongside the framework). The §22 v4 convention is \"no\n * Symbols\", not \"use a strict naming scheme\" — Symbols don't survive\n * JSON serialisation, can't be addressed by string from worker\n * threads, and don't show up in DevTools snapshots.\n *\n * `reflect-metadata` accepts both Symbol and string keys identically,\n * so the consumer-side change is internal — references go through the\n * `METADATA` enum's named members (e.g. `METADATA.INJECT`), not the\n * underlying string.\n *\n * The `design:*` entries match what TypeScript's\n * `emitDecoratorMetadata` writes; those names are fixed by the\n * compiler + must stay as-is.\n */\nexport const DI_METADATA = {\n INJECTABLE: 'kick:injectable',\n SCOPE: 'kick:scope',\n CLASS_KIND: 'kick:class-kind',\n AUTOWIRED: 'kick:autowired',\n INJECT: 'kick:inject',\n POST_CONSTRUCT: 'kick:post_construct',\n PRE_DESTROY: 'kick:pre_destroy',\n BUILDER: 'kick:builder',\n VALUE: 'kick:value',\n // TypeScript emit metadata keys — fixed by the compiler.\n PARAM_TYPES: 'design:paramtypes',\n PROPERTY_TYPE: 'design:type',\n RETURN_TYPE: 'design:returntype',\n} as const\n","/**\n * The container's own errors, by stable code. A host can replace the factory\n * (`Container._error`) to turn them into richer errors — kickjs maps each\n * code to its catalogue entry, with the cause and the fix.\n */\nexport type DiErrorCode =\n /** Nothing is registered for the token. */\n | 'KICK001'\n /** A REQUEST-scoped token, but no request store is installed. */\n | 'KICK002'\n /** A REQUEST-scoped token, resolved outside a request. */\n | 'KICK003'\n /** `@Value` key not set, and no default. */\n | 'KICK004'\n /** A token bound normally after its `registerFallback` singleton was already resolved. */\n | 'KICK010'\n\nexport class DiError extends Error {\n constructor(\n readonly code: DiErrorCode,\n /** The token name, or the env key for KICK004. */\n readonly subject: string,\n message: string,\n ) {\n super(message)\n this.name = 'DiError'\n }\n}\n\nconst MESSAGES: Record<DiErrorCode, (subject: string) => string> = {\n KICK001: (s) => `No provider for ${s}`,\n KICK002: (s) => `Cannot resolve REQUEST-scoped \"${s}\": no request store is installed`,\n KICK003: (s) => `Cannot resolve REQUEST-scoped \"${s}\" outside a request`,\n KICK004: (s) => `@Value('${s}'): not set and no default provided`,\n KICK010: (s) =>\n `Cannot bind \"${s}\": its fallback was already resolved, so code holding it would disagree with the new binding. Bind \"${s}\" before anything resolves it.`,\n}\n\nexport function diError(code: DiErrorCode, subject: string): Error {\n return new DiError(code, subject, `${code}: ${MESSAGES[code](subject)}`)\n}\n\n/** Where the container reports non-fatal problems. A host can install its own. */\nexport interface DiLogger {\n warn(message: string): void\n error(err: unknown, message: string): void\n}\n\n/** Drops warnings; errors (a `@PostConstruct` that threw) go to `console.error`. */\nexport const defaultLogger: DiLogger = {\n warn() {},\n error(err, message) {\n console.error(message, err)\n },\n}\n","import 'reflect-metadata'\nimport type { InjectionToken } from './token'\n\n/**\n * Typed utilities for reflect-metadata operations.\n *\n * Replaces scattered Reflect.defineMetadata/getMetadata calls with a single\n * source of truth. If we later move to Stage 3 decorators or a WeakMap-based\n * store, only this file changes — all 24+ consumer files stay untouched.\n *\n * Keys may be symbols, strings, or `InjectionToken<T>` objects produced by\n * `createToken()`. Using `createToken` is the preferred pattern because the\n * token's phantom type flows through the helpers, enabling type-safe reads.\n */\n\ntype MetaKey = symbol | string | InjectionToken<unknown>\n\n// ── Setters ─────────────────────────────────────────────────────\n\n/** Set metadata on a class (or prototype for property decorators) */\nexport function setClassMeta<T>(key: MetaKey, value: T, target: object): void {\n Reflect.defineMetadata(key, value, target)\n}\n\n/** Set metadata on a specific method */\nexport function setMethodMeta<T>(key: MetaKey, value: T, target: object, method: string): void {\n Reflect.defineMetadata(key, value, target, method)\n}\n\n// ── Getters ─────────────────────────────────────────────────────\n\n/** Get metadata from a class, returning `fallback` if not set */\nexport function getClassMeta<T>(key: MetaKey, target: object, fallback: T): T {\n return (Reflect.getMetadata(key, target) as T) ?? fallback\n}\n\n/** Get metadata from a specific method, returning `fallback` if not set */\nexport function getMethodMeta<T>(key: MetaKey, target: object, method: string, fallback: T): T {\n return (Reflect.getMetadata(key, target, method) as T) ?? fallback\n}\n\n/** Check if metadata exists on a class */\nexport function hasClassMeta(key: MetaKey, target: object): boolean {\n return Reflect.hasMetadata(key, target)\n}\n\n/** Get metadata from a class with no default (returns undefined if absent) */\nexport function getClassMetaOrUndefined<T>(key: MetaKey, target: object): T | undefined {\n return Reflect.getMetadata(key, target) as T | undefined\n}\n\n/** Get metadata from a method with no default (returns undefined if absent) */\nexport function getMethodMetaOrUndefined<T>(\n key: MetaKey,\n target: object,\n method: string,\n): T | undefined {\n return Reflect.getMetadata(key, target, method) as T | undefined\n}\n\n// ── Accumulate: Arrays ──────────────────────────────────────────\n\n/** Append items to an array stored in class metadata */\nexport function pushClassMeta<T>(key: MetaKey, target: object, ...items: T[]): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target)\n}\n\n/** Append items to an array stored in method metadata */\nexport function pushMethodMeta<T>(\n key: MetaKey,\n target: object,\n method: string,\n ...items: T[]\n): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target, method) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target, method)\n}\n\n// ── Accumulate: Maps ────────────────────────────────────────────\n\n/** Set a key/value in a Map stored in class metadata */\nexport function setInMetaMap<K, V>(key: MetaKey, target: object, mapKey: K, mapValue: V): void {\n const inherited: Map<K, V> | undefined = Reflect.getOwnMetadata(key, target)\n const map = inherited ? new Map(inherited) : new Map<K, V>()\n map.set(mapKey, mapValue)\n Reflect.defineMetadata(key, map, target)\n}\n\n/** Get a Map from class metadata (returns empty Map if absent) */\nexport function getMetaMap<K, V>(key: MetaKey, target: object): Map<K, V> {\n return Reflect.getMetadata(key, target) ?? new Map()\n}\n\n// ── Accumulate: Records ─────────────────────────────────────────\n\n/** Set a key/value in a Record stored in class metadata */\nexport function setInMetaRecord<V>(\n key: MetaKey,\n target: object,\n recKey: string | number,\n recValue: V,\n): void {\n const inherited: Record<string | number, V> | undefined = Reflect.getOwnMetadata(key, target)\n const rec = inherited ? { ...inherited } : {}\n rec[recKey] = recValue\n Reflect.defineMetadata(key, rec, target)\n}\n\n/** Get a Record from class metadata (returns empty object if absent) */\nexport function getMetaRecord<V>(key: MetaKey, target: object): Record<string | number, V> {\n return Reflect.getMetadata(key, target) ?? {}\n}\n","/**\n * Collision-safe injection tokens with phantom type parameters.\n *\n * `createToken<T>(name)` is the recommended way to declare any DI token\n * that isn't a class. Each call returns a unique frozen object — even if\n * two files write `createToken<X>('Logger')`, they produce two distinct\n * references that the container treats as different tokens. Collisions\n * become impossible by construction, not by convention.\n *\n * The phantom type parameter `T` lets `container.resolve(token)` infer\n * the correct return type without any codegen.\n *\n * ## Hardening layers\n *\n * KickJS has four DI token kinds, listed from safest to riskiest:\n *\n * 1. **Class identity** — `container.resolve(UserService)`. Reference\n * equality, never collides. Type-safe via `Constructor<T>` overload.\n * 2. **`createToken<T>(name)`** — frozen object identity, never collides.\n * Type-safe via the `InjectionToken<T>` overload.\n * 3. **Symbol** — `Symbol('foo')` is unique per call but `Symbol.for('foo')`\n * is interned and CAN collide. Discouraged.\n * 4. **Raw string `@Inject('foo')`** — high collision risk, untyped.\n * Reserved for legacy code and runtime-computed token names.\n *\n * Prefer (1) and (2). Use (4) only when you have to.\n *\n * @example\n * ```ts\n * // src/tokens.ts\n * export const DATABASE_URL = createToken<string>('config.database.url')\n * export const FEATURE_FLAGS = createToken<FeatureFlags>('app.features')\n *\n * // bootstrap\n * container.registerInstance(DATABASE_URL, process.env.DATABASE_URL!)\n *\n * // anywhere else\n * const url = container.resolve(DATABASE_URL) // typed as `string`\n * ```\n *\n * @module @forinda/kickjs/core/token\n */\n\n/**\n * Symbol marker stamped on every InjectionToken so the container can\n * detect them at runtime regardless of which package created them.\n *\n * Uses `Symbol.for()` so cross-package isolation (different copies of\n * `@forinda/kickjs` loaded under different module identities) still\n * shares the same marker.\n */\nexport const INJECTION_TOKEN: unique symbol = Symbol.for('@kickjs/InjectionToken') as never\n\n/**\n * A type-safe DI token. Each `createToken()` call returns a unique frozen\n * object identified by reference, not by the `name` string. Two\n * `createToken<X>('foo')` calls in different files produce two distinct\n * tokens that the container treats independently.\n *\n * The phantom `_type` parameter is never assigned at runtime — it exists\n * solely to thread the type through `container.resolve(token)`.\n */\nexport interface InjectionToken<T> {\n readonly [INJECTION_TOKEN]: true\n /** Descriptive name used in error messages, logs, and devtools */\n readonly name: string\n /** Phantom — never assigned at runtime */\n readonly _type?: T\n}\n\n/**\n * Create a collision-safe injection token.\n *\n * The returned token is a frozen object identified by reference, not by\n * the `name` string. The `name` is used only for error messages, logs,\n * and debugging.\n */\nexport function createToken<T>(name: string): InjectionToken<T> {\n return Object.freeze({\n [INJECTION_TOKEN]: true as const,\n name,\n })\n}\n\n/** Type guard — returns `true` if `value` is an InjectionToken */\nexport function isInjectionToken(value: unknown): value is InjectionToken<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[INJECTION_TOKEN] === true\n )\n}\n","import 'reflect-metadata'\nimport {\n Constructor,\n Scope,\n DI_METADATA as METADATA,\n type ClassKind,\n type PostConstructStatus,\n} from './interfaces'\nimport { diError, defaultLogger, type DiErrorCode, type DiLogger } from './errors'\nimport {\n getClassMeta,\n getClassMetaOrUndefined,\n getMetaMap,\n getMetaRecord,\n getMethodMetaOrUndefined,\n} from './metadata'\nimport { isInjectionToken, type InjectionToken } from './token'\n\n/** Internal registration entry that tracks how a dependency should be resolved */\ninterface Registration {\n target: Constructor\n scope: Scope\n instance?: any\n factory?: () => any\n // Observability fields\n kind: ClassKind\n resolveCount: number\n lastResolvedAt?: number\n firstResolvedAt?: number\n resolveDurationMs?: number\n postConstructStatus: PostConstructStatus\n dependencies: string[]\n /** Lazily built instantiation plan — see {@link Container.buildPlan}. */\n plan?: InstantiationPlan\n}\n\n/**\n * Precomputed instantiation recipe for a class. Class metadata is immutable\n * after definition, so the Reflect reads (5 per create) and the derived\n * structures only need computing ONCE — previously they re-ran on every\n * REQUEST-scoped resolve (i.e. every request) and every TRANSIENT resolve.\n */\ninterface InstantiationPlan {\n /** Ordered, validated constructor parameter tokens. */\n ctorTokens: any[]\n /** `@Autowired` properties: [property, resolved token]. */\n autowired: Array<[string, any]>\n /** `@Value` properties: [property, env config]. */\n values: Array<[string, { envKey: string; defaultValue?: any }]>\n /** Host-injected properties (`_propertyInjectors`): [property, getter]. */\n injected: Array<[string, () => unknown]>\n /** `@PostConstruct` method name, if any. */\n postConstruct?: string | symbol\n}\n\n/** Create a Registration with observability defaults */\nfunction createReg(\n fields: Partial<Registration> & Pick<Registration, 'target' | 'scope'>,\n): Registration {\n return {\n kind: 'unknown',\n resolveCount: 0,\n postConstructStatus: 'skipped',\n dependencies: [],\n ...fields,\n }\n}\n\n/** Format a token for display in error messages */\nexport function tokenName(token: any): string {\n if (typeof token === 'symbol') return token.toString()\n if (isInjectionToken(token)) return token.name\n return token?.name || String(token)\n}\n\n// ── Persistent Store ────────────────────────────────────────────────────\n// globalThis-based storage that survives BOTH Container.reset() AND Vite's\n// ssrLoadModule() re-evaluation. When Vite re-evaluates a module, the\n// Container class gets a new identity (new statics). But globalThis persists.\n//\n// This is how DB connections, Redis clients, WsAdapter instances, and any\n// other stateful bindings survive HMR without being dropped.\n//\n// Pattern from: React Router (globalThis for state), Vinxi (globalThis.app)\n\n// ── Change Event System ─────────────────────────────────────────────────\n// Batched (50ms debounce) so `kick g module` creating 10+ files emits ONE\n// notification, not 10. Any adapter can subscribe: Swagger, DevTools, WS, etc.\n\n/** A single container change event */\nexport interface ContainerChangeEvent {\n token: string\n event: 'registered' | 'resolved' | 'invalidated'\n kind?: ClassKind\n timestamp: number\n}\n\n/** Listener that receives batched change events */\nexport type ContainerChangeListener = (changes: ContainerChangeEvent[]) => void\n\n/**\n * Type-safe registry of string DI tokens. Augmented by `kick typegen` —\n * the generated `.kickjs/types/registry.d.ts` adds entries like:\n *\n * ```ts\n * declare module '@forinda/kickjs' {\n * interface KickJsRegistry {\n * UserService: import('../../src/modules/users/user.service').UserService\n * }\n * }\n * ```\n *\n * Once typegen has run, `container.resolve('UserService')` returns the\n * correct type instead of `any`. Class-token resolution\n * (`container.resolve(UserService)`) is already type-safe via the\n * `Constructor<T>` overload and does not need typegen.\n */\nexport interface KickJsRegistry {}\n\ninterface PersistentStore {\n factories: Array<{ token: any; factory: () => any; scope: Scope }>\n instances: Array<{ token: any; instance: any }>\n /** Resolved instances keyed by string name — survives class identity changes */\n resolvedInstances: Map<string, any>\n}\n\nfunction getPersistentStore(): PersistentStore {\n if (!(globalThis as any).__kickjs_persistent) {\n ;(globalThis as any).__kickjs_persistent = {\n factories: [],\n instances: [],\n resolvedInstances: new Map(),\n } satisfies PersistentStore\n }\n return (globalThis as any).__kickjs_persistent\n}\n\n/**\n * Inversion-of-Control (IoC) container that manages dependency registration,\n * resolution, and lifecycle. Implements the Singleton pattern so all parts of\n * the application share a single container instance.\n *\n * Supports constructor injection, property injection (@Autowired),\n * factory registrations, and lifecycle hooks (@PostConstruct).\n *\n * Persistent state (DB connections, Redis, etc.) is stored on globalThis so\n * it survives both Container.reset() and Vite module re-evaluation.\n */\nexport class Container {\n private static instance: Container\n private registrations = new Map<any, Registration>()\n /**\n * Bindings from {@link registerFallback}, kept apart from `registrations`\n * so a normal binding wins whenever it arrives and `has()` keeps answering\n * \"did the app bind its own?\".\n */\n private fallbacks = new Map<any, Registration>()\n private resolving = new Set<any>()\n\n // ── Reactive change tracking ──────────────────────────────────────\n private changeListeners = new Set<ContainerChangeListener>()\n private pendingChanges: ContainerChangeEvent[] = []\n private notifyTimer: ReturnType<typeof setTimeout> | null = null\n private static readonly DEBOUNCE_MS = 50\n\n /** Callback set by the decorators module to flush pending registrations */\n static _onReady: ((container: Container) => void) | null = null\n /** Callback invoked on reset so decorators can update their container reference */\n static _onReset: ((container: Container) => void) | null = null\n\n /**\n * Seeds a NEWLY CREATED container with the decorator registrations, without\n * adopting it as the global one. `_onReset` does both; an isolated container\n * must only have the first half, or it would capture every subsequent\n * decorator registration in the process.\n */\n static _onCreate: ((container: Container) => void) | null = null\n /**\n * Environment resolver for @Value decorator. Set by the unified\n * `@forinda/kickjs` config layer to return Zod-validated, type-coerced\n * env values instead of raw process.env strings.\n */\n static _envResolver: ((key: string) => any) | null = null\n /**\n * Builds the container's errors from a stable code and the token name (or\n * env key). The default gives a plain `DiError`; kickjs installs one that\n * returns its catalogued `KickError`.\n */\n static _error: (code: DiErrorCode, subject: string) => Error = diError\n /** Where non-fatal problems go (an async `@PostConstruct`, one that threw). Errors reach `console.error` by default. */\n static _logger: DiLogger = defaultLogger\n /**\n * Extra lazy property injections, keyed by metadata key. For each property\n * a class stores under that key (a `Map` on the prototype, as `@Value`\n * does), `prepare(config, prop)` runs once when the class's plan is built\n * — so it can reject a bad config up front — and returns the getter each\n * instance's property reads through. kickjs adds `@Asset` this way.\n */\n static _propertyInjectors = new Map<string, (config: any, prop: string) => () => unknown>()\n /**\n * Request store provider for REQUEST-scoped DI. Set by the HTTP layer\n * inside `@forinda/kickjs` to return the current request's\n * AsyncLocalStorage store. Returns { instances: Map, values: Map } or\n * null if outside a request.\n */\n /**\n * Whether this container is the global singleton.\n *\n * The persistent store exists to carry the GLOBAL container's registrations\n * across HMR and module re-evaluation. An isolated container is per-test and\n * must neither write to it — a `registerInstance` override was replayed into\n * every later `Container.reset()` — nor read from it, which would pull global\n * state into a container created to be free of it.\n */\n private isGlobal = false\n\n static _requestStoreProvider:\n | (() => { instances: Map<any, any>; values: Map<any, any> } | null)\n | null = null\n\n static getInstance(): Container {\n if (!Container.instance) {\n Container.instance = new Container()\n Container.instance.isGlobal = true\n }\n // Flush any decorator registrations that queued before the container existed\n if (Container._onReady) {\n Container._onReady(Container.instance)\n Container._onReady = null\n }\n return Container.instance\n }\n\n /**\n * Resets the container by replacing the singleton with a fresh instance.\n * Persistent registrations (DB, Redis, etc.) are replayed from globalThis\n * so they survive both reset() and Vite module re-evaluation.\n */\n static reset(): void {\n Container.instance = new Container()\n Container.instance.isGlobal = true\n // Notify decorators so they update their container reference\n Container._onReset?.(Container.instance)\n // Replay persistent registrations from globalThis store\n const store = getPersistentStore()\n for (const { token, factory, scope } of store.factories) {\n if (!Container.instance.has(token)) {\n // Check if we have a previously resolved instance for this token\n const name = tokenName(token)\n const existing = store.resolvedInstances.get(name)\n Container.instance.registrations.set(\n token,\n createReg({\n target: Object as any,\n scope,\n factory,\n kind: 'factory',\n instance: existing, // Reuse resolved instance if available\n resolveCount: existing ? 1 : 0,\n postConstructStatus: existing ? 'completed' : 'skipped',\n }),\n )\n }\n }\n for (const { token, instance } of store.instances) {\n if (!Container.instance.has(token)) {\n Container.instance.registrations.set(\n token,\n createReg({\n target: instance.constructor,\n scope: Scope.SINGLETON,\n instance,\n kind: 'instance',\n postConstructStatus: 'completed',\n resolveCount: 1,\n }),\n )\n }\n }\n }\n\n /**\n * Create an isolated container instance (not the global singleton).\n * Useful for concurrent tests that must not share DI state.\n *\n * @example\n * ```ts\n * const container = Container.create()\n * container.register(UserService, UserService)\n * const svc = container.resolve(UserService)\n * ```\n */\n static create(): Container {\n const container = new Container()\n // Decorators register at class-definition time, into whichever container\n // was global then. Without this replay an isolated container is missing\n // every @Service / @Repository / @Controller in the process — and\n // `isolated: true` is what the generated docs recommend for test\n // isolation, so the failure reached adopters as `No provider for X`.\n Container._onCreate?.(container)\n return container\n }\n\n /** Register a class constructor under the given token */\n register(token: any, target: Constructor, scope: Scope = Scope.SINGLETON): void {\n this.assertFallbackUnused(token)\n const kind = getClassMeta<ClassKind>(METADATA.CLASS_KIND, target, 'unknown')\n const dependencies = this.extractDependencies(target)\n const reg = createReg({ target, scope, kind, dependencies })\n this.registrations.set(token, reg)\n // Store a name-based fallback so HMR class re-creation (new identity)\n // can still resolve by the original class name.\n if (typeof token === 'function' && token.name) {\n this.registrations.set(`__hmr__${token.name}`, reg)\n }\n this.emit(token, 'registered', kind)\n }\n\n /** Register a factory function under the given token */\n registerFactory(token: any, factory: () => any, scope: Scope = Scope.SINGLETON): void {\n this.assertFallbackUnused(token)\n const store = getPersistentStore()\n const name = tokenName(token)\n // Check if we already have a resolved instance from a previous HMR cycle.\n // Only reuse if the factory hasn't changed (same reference). When a new\n // factory is provided (e.g. swapping implementations), clear the stale\n // instance so the new factory runs on next resolve.\n // Reads are global-only too. An isolated container adopting a cached\n // instance from the global store would be contaminated on creation — the\n // same leak, in the other direction.\n const existingEntry = this.isGlobal\n ? store.factories.find((r) => tokenName(r.token) === name)\n : undefined\n const factoryUnchanged = existingEntry?.factory === factory\n const existingInstance =\n this.isGlobal && factoryUnchanged ? store.resolvedInstances.get(name) : undefined\n\n // Clear stale resolved instance when factory changes\n if (this.isGlobal && !factoryUnchanged) {\n store.resolvedInstances.delete(name)\n }\n\n this.registrations.set(\n token,\n createReg({\n target: Object as any,\n scope,\n factory,\n kind: 'factory',\n instance: existingInstance,\n resolveCount: existingInstance ? 1 : 0,\n postConstructStatus: existingInstance ? 'completed' : 'skipped',\n }),\n )\n // Track in globalThis for persistence across HMR + module re-evaluation —\n // global container only, or an isolated container's factories are replayed\n // into the next Container.reset().\n if (this.isGlobal) {\n const idx = store.factories.findIndex((r) => tokenName(r.token) === name)\n if (idx >= 0) {\n store.factories[idx] = { token, factory, scope }\n } else {\n store.factories.push({ token, factory, scope })\n }\n }\n this.emit(name, 'registered', 'factory')\n }\n\n /** Register a pre-constructed singleton instance */\n registerInstance(token: any, instance: any): void {\n this.assertFallbackUnused(token)\n const store = getPersistentStore()\n const name = tokenName(token)\n\n this.registrations.set(\n token,\n createReg({\n target: instance.constructor,\n scope: Scope.SINGLETON,\n instance,\n kind: 'instance',\n postConstructStatus: 'completed',\n resolveCount: 1,\n firstResolvedAt: Date.now(),\n lastResolvedAt: Date.now(),\n }),\n )\n // Track in globalThis for persistence across HMR + module re-evaluation —\n // global container only. An isolated one that wrote here had its overrides\n // replayed into the next Container.reset().\n if (this.isGlobal) {\n store.resolvedInstances.set(name, instance)\n const idx = store.instances.findIndex((r) => tokenName(r.token) === name)\n if (idx >= 0) {\n store.instances[idx] = { token, instance }\n } else {\n store.instances.push({ token, instance })\n }\n }\n this.emit(name, 'registered', 'instance')\n }\n\n /**\n * Register a default for `token`, used only while nothing else binds it.\n *\n * `resolve()` prefers a `register` / `registerFactory` / `registerInstance`\n * binding whenever it was made — before or after this call — and falls back\n * to this factory otherwise. `has()` stays `false` for a token that only has\n * a fallback. A second fallback for the same token replaces the first, while\n * the first has not been resolved.\n *\n * Binding the token normally once its SINGLETON fallback has been resolved\n * throws KICK010: whoever resolved it holds the fallback instance, and would\n * disagree with everyone resolving the new binding. Fallbacks are not kept\n * across `Container.reset()` — register them from a hook that runs on every\n * boot (an adapter's `beforeMount`, a module's `register`).\n */\n registerFallback<T>(\n token: InjectionToken<T> | Constructor<T> | string | symbol,\n factory: () => NoInfer<T>,\n scope: Scope = Scope.SINGLETON,\n ): void {\n this.assertFallbackUnused(token)\n this.fallbacks.set(token, createReg({ target: Object as any, scope, factory, kind: 'factory' }))\n if (!this.registrations.has(token)) this.emit(token, 'registered', 'factory')\n }\n\n /** A fallback whose singleton is already out there can't be replaced. */\n private assertFallbackUnused(token: any): void {\n if (this.fallbacks.get(token)?.instance !== undefined) {\n throw Container._error('KICK010', tokenName(token))\n }\n }\n\n /** The binding `resolve(token)` uses: a normal registration, else the fallback. */\n private binding(token: any): Registration | undefined {\n return this.registrations.get(token) ?? this.fallbacks.get(token)\n }\n\n /** Check if a binding exists for the given token. A {@link registerFallback} alone does not count. */\n has(token: any): boolean {\n return this.registrations.has(token)\n }\n\n /**\n * Return a snapshot of all registered tokens with their scope and instantiation status.\n * A token served by its {@link registerFallback} is listed with `fallback: true`;\n * a fallback that a normal binding overrides is not listed.\n */\n getRegistrations(): Array<{\n token: string\n fallback?: true\n scope: string\n kind: ClassKind\n instantiated: boolean\n resolveCount: number\n lastResolvedAt?: number\n firstResolvedAt?: number\n resolveDurationMs?: number\n postConstructStatus: PostConstructStatus\n dependencies: string[]\n }> {\n const entries: Array<ReturnType<Container['getRegistrations']>[number]> = []\n const active = [...this.registrations]\n for (const [token, reg] of this.fallbacks) {\n if (!this.registrations.has(token)) active.push([token, reg])\n }\n for (const [token, reg] of active) {\n entries.push({\n token: tokenName(token),\n ...(this.fallbacks.get(token) === reg && { fallback: true as const }),\n scope: reg.scope,\n kind: reg.kind,\n // Singletons check cached instance; transients check resolveCount\n instantiated:\n reg.scope === Scope.SINGLETON ? reg.instance !== undefined : reg.resolveCount > 0,\n resolveCount: reg.resolveCount,\n lastResolvedAt: reg.lastResolvedAt,\n firstResolvedAt: reg.firstResolvedAt,\n resolveDurationMs: reg.resolveDurationMs,\n postConstructStatus: reg.postConstructStatus,\n dependencies: reg.dependencies,\n })\n }\n return entries\n }\n\n /**\n * Resolve a dependency by its token.\n *\n * Four overloads, in order of preference:\n * - **`InjectionToken<T>`** (from `createToken<T>(name)`) → returns `T`.\n * Collision-safe by construction; no codegen required.\n * - **Class constructor** → returns an instance of that class. Always\n * type-safe; no codegen required.\n * - **String token in `KickJsRegistry`** (populated by `kick typegen`) →\n * returns the augmented type.\n * - **Anything else** → returns `any`. Use this only for legacy code or\n * string tokens that haven't been registered with typegen yet.\n */\n resolve<T>(token: InjectionToken<T>): T\n resolve<T>(token: Constructor<T>): T\n resolve<K extends keyof KickJsRegistry & string>(token: K): KickJsRegistry[K]\n resolve<T = any>(token: any): T\n resolve<T = any>(token: any): T {\n let reg = this.registrations.get(token)\n // HMR fallback: when Vite re-evaluates a module, decorated classes get new\n // identity. Try resolving by class name if the primary token lookup fails.\n if (!reg && typeof token === 'function' && token.name) {\n reg = this.registrations.get(`__hmr__${token.name}`)\n }\n reg ??= this.fallbacks.get(token)\n if (!reg) {\n throw Container._error('KICK001', tokenName(token))\n }\n\n if (reg.scope === Scope.SINGLETON && reg.instance !== undefined) {\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n this.emit(token, 'resolved', reg.kind)\n return reg.instance\n }\n\n // REQUEST scope: one instance per HTTP request, cached in AsyncLocalStorage\n if (reg.scope === Scope.REQUEST) {\n if (!Container._requestStoreProvider) {\n throw Container._error('KICK002', tokenName(token))\n }\n const store = Container._requestStoreProvider()\n if (!store) {\n throw Container._error('KICK003', tokenName(token))\n }\n // Check for pre-registered request values (user, tenant, etc.)\n if (store.values.has(token)) {\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n this.emit(token, 'resolved', reg.kind)\n return store.values.get(token) as T\n }\n // Check per-request instance cache\n if (store.instances.has(token)) {\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n this.emit(token, 'resolved', reg.kind)\n return store.instances.get(token) as T\n }\n // Factory-registered binding: invoke the factory once per request\n // and cache the result alongside class-instantiated REQUEST tokens.\n // Without this branch, registerFactory(token, fn, Scope.REQUEST)\n // silently falls through to createInstance(reg) which `new`s the\n // factory's placeholder `target: Object` and returns an empty {}\n // — the factory function never runs.\n if (reg.factory) {\n const start = performance.now()\n const instance = reg.factory()\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n store.instances.set(token, instance)\n this.emit(token, 'resolved', reg.kind)\n return instance as T\n }\n // Create instance and cache for this request only\n const start = performance.now()\n const instance = this.createInstance(reg)\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n store.instances.set(token, instance)\n this.emit(token, 'resolved', reg.kind)\n return instance as T\n }\n\n if (reg.factory) {\n const start = performance.now()\n const instance = reg.factory()\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n if (reg.scope === Scope.SINGLETON) {\n reg.instance = instance\n // Persist resolved factory instances so they survive module\n // re-evaluation — global container only, for the same reason.\n if (this.isGlobal) {\n getPersistentStore().resolvedInstances.set(tokenName(token), instance)\n }\n }\n this.emit(token, 'resolved', reg.kind)\n return instance\n }\n\n if (this.resolving.has(token)) {\n const chain = [...this.resolving].map(tokenName)\n chain.push(tokenName(token))\n throw new Error(`Circular dependency detected: ${chain.join(' -> ')}`)\n }\n this.resolving.add(token)\n\n try {\n const start = performance.now()\n const instance = this.createInstance(reg)\n reg.resolveDurationMs = performance.now() - start\n reg.resolveCount++\n reg.lastResolvedAt = Date.now()\n if (!reg.firstResolvedAt) reg.firstResolvedAt = Date.now()\n if (reg.scope === Scope.SINGLETON) {\n reg.instance = instance\n }\n this.emit(token, 'resolved', reg.kind)\n return instance\n } finally {\n this.resolving.delete(token)\n }\n }\n\n /** Lifecycle hook called during Application.setup() after module registration */\n bootstrap(): void {\n // Reserved for future use — adapters and modules register via\n // container.register(), registerFactory(), and registerInstance().\n }\n\n // ── Reactive Change API ─────────────────────────────────────────────\n\n /**\n * Subscribe to container changes. Returns an unsubscribe function.\n *\n * Changes are batched (50ms debounce) so bulk operations like\n * `kick g module` creating 10+ files emit ONE batch, not 10 events.\n *\n * Any adapter can subscribe:\n * - SwaggerAdapter: re-build spec when controllers change\n * - DevToolsAdapter: push SSE events to dashboard\n * - WsAdapter: re-discover namespaces when controllers change\n * - OtelAdapter: emit tracing spans for DI resolution\n * - Custom user adapters\n *\n * @example\n * ```ts\n * const unsub = container.onChange((changes) => {\n * for (const c of changes) {\n * console.log(`${c.event}: ${c.token} (${c.kind})`)\n * }\n * })\n * // later:\n * unsub()\n * ```\n */\n onChange(callback: ContainerChangeListener): () => void {\n this.changeListeners.add(callback)\n return () => this.changeListeners.delete(callback)\n }\n\n /**\n * Invalidate a specific registration. Clears the cached instance so the\n * next resolve() creates a fresh one. Also invalidates dependents\n * (anything that injected this token) by walking the dependency graph.\n *\n * Called by the Vite HMR plugin when a module file changes.\n * Skips persistent registrations (DB connections, etc.).\n */\n invalidate(token: any, visited = new Set<Registration>()): void {\n const name = tokenName(token)\n // Try both the token itself and the HMR fallback name\n const reg =\n this.registrations.get(token) ??\n this.registrations.get(`__hmr__${name}`) ??\n this.fallbacks.get(token)\n // `visited`: two classes that @Autowired each other depend on each other,\n // and the walk below went round that cycle until the stack overflowed.\n if (!reg || visited.has(reg)) return\n visited.add(reg)\n\n // Don't invalidate persistent registrations (DB, Redis, etc.)\n const store = getPersistentStore()\n if (store.resolvedInstances.has(name)) return\n\n // Clear cached instance — next resolve() will re-create\n reg.instance = undefined\n reg.resolveCount = 0\n reg.postConstructStatus = 'skipped'\n this.emit(name, 'invalidated', reg.kind)\n\n // Walk dependency graph — invalidate anything that injected this token\n for (const [depToken, depReg] of this.registrations) {\n if (depReg.dependencies.includes(name)) {\n this.invalidate(depToken, visited)\n }\n }\n }\n\n /**\n * Batch-emit a change event (debounced, flushed after 50ms of quiet).\n * Takes the RAW token — `tokenName()` is only computed after the\n * zero-listener guard, so the hot cached-singleton resolve path never pays\n * the name derivation when nobody is watching (the production default).\n */\n private emit(rawToken: any, event: ContainerChangeEvent['event'], kind?: ClassKind): void {\n // Zero-listener fast path. Nobody subscribes to container changes unless\n // DevTools (or a test) calls `onChange` — the production default. Without\n // this guard, every `resolve()` (including the hot cached-singleton path)\n // would scan `pendingChanges`, push an entry, and reschedule a `setTimeout`\n // only for the eventual flush to iterate an empty listener set and discard\n // the batch. Short-circuiting here keeps `resolve()` allocation- and\n // timer-free when no one is watching. When a listener attaches later,\n // subsequent emits work exactly as before — past resolves had no observer\n // to notify anyway.\n if (this.changeListeners.size === 0) return\n const token = tokenName(rawToken)\n\n // Dedupe within the pending batch: a high-throughput TRANSIENT\n // token that resolves 1000× per request would otherwise queue\n // 1000 identical 'resolved' events. The downstream listener only\n // cares that the token's state changed, so collapse to one entry\n // (the earliest timestamp wins; the latest update is reflected\n // in resolveCount/lastResolvedAt on the registration itself,\n // which the listener reads at notify time).\n const existing = this.pendingChanges.find((p) => p.token === token && p.event === event)\n if (existing) {\n existing.timestamp = Date.now()\n } else {\n this.pendingChanges.push({ token, event, kind, timestamp: Date.now() })\n }\n\n if (this.notifyTimer) clearTimeout(this.notifyTimer)\n this.notifyTimer = setTimeout(() => {\n const batch = this.pendingChanges.splice(0)\n if (batch.length === 0) return\n for (const listener of this.changeListeners) {\n try {\n listener(batch)\n } catch {\n // Don't let one broken listener break others\n }\n }\n }, Container.DEBOUNCE_MS)\n // Don't let a pending debounce window hold the event loop open — flush is\n // best-effort telemetry, not critical work.\n this.notifyTimer.unref?.()\n }\n\n /** Flush pending change events immediately (useful in tests) */\n flushChanges(): void {\n if (this.notifyTimer) {\n clearTimeout(this.notifyTimer)\n this.notifyTimer = null\n }\n const batch = this.pendingChanges.splice(0)\n if (batch.length === 0) return\n for (const listener of this.changeListeners) {\n try {\n listener(batch)\n } catch {\n // Don't let one broken listener break others\n }\n }\n }\n\n /**\n * Build the one-time instantiation plan for a registration. Runs on the\n * FIRST createInstance for the class — the metadata it reads is immutable\n * after class definition, so every later create (REQUEST scope: every\n * request) skips all Reflect reads and re-derivation.\n *\n * Scope validation (SINGLETON must not ctor-inject REQUEST) lives here\n * too: it's a static property of the (parent, dep) registration pair, so\n * checking it once at plan build is equivalent to the old per-create loop.\n */\n private buildPlan(reg: Registration): InstantiationPlan {\n const paramTypes = getClassMeta<Constructor[]>(METADATA.PARAM_TYPES, reg.target, [])\n const injectTokens = getMetaRecord<any>(METADATA.INJECT, reg.target)\n\n // When emitDecoratorMetadata is unavailable (tsx, ts-node without\n // SWC), paramTypes is empty even though @Inject set tokens on\n // specific constructor indices. Derive the constructor arity from\n // the @Inject metadata so constructor injection still works.\n const maxInjectIndex =\n Object.keys(injectTokens).length > 0\n ? Math.max(...Object.keys(injectTokens).map(Number)) + 1\n : 0\n const paramCount = Math.max(paramTypes.length, maxInjectIndex)\n\n const ctorTokens: any[] = []\n for (let index = 0; index < paramCount; index++) {\n const token = injectTokens[index] || paramTypes[index]\n if (!token) {\n throw new Error(\n `Cannot resolve constructor parameter at index ${index} of ${tokenName(reg.target)}. ` +\n `No @Inject token and no design:paramtypes metadata (tsx/ts-node don't emit it). ` +\n `Add @Inject(Token) to every constructor parameter.`,\n )\n }\n // Scope validation: SINGLETON cannot inject REQUEST-scoped dependencies\n if (reg.scope === Scope.SINGLETON) {\n const depReg = this.binding(token)\n if (depReg && depReg.scope === Scope.REQUEST) {\n // The fix depends on the parent: @Controller() takes no options and\n // is always SINGLETON, so \"change the parent's scope\" is advice a\n // controller cannot follow. @Autowired works for every parent — its\n // getter re-resolves REQUEST deps per access (injectProperties).\n const parent = tokenName(reg.target)\n const dep = tokenName(token)\n const kind = getClassMetaOrUndefined<ClassKind>(METADATA.CLASS_KIND, reg.target)\n const fix =\n kind === 'controller'\n ? `@Controller() is always SINGLETON, so inject \"${dep}\" with @Autowired() instead of the constructor.`\n : `Give \"${parent}\" TRANSIENT or REQUEST scope (e.g. @Service({ scope: Scope.REQUEST })), ` +\n `or inject \"${dep}\" with @Autowired() instead of the constructor.`\n throw new Error(\n `Cannot inject REQUEST-scoped \"${dep}\" into SINGLETON \"${parent}\". ` +\n `Singletons outlive requests. ${fix}`,\n )\n }\n }\n ctorTokens.push(token)\n }\n\n // @Autowired properties — resolve each property's token once.\n const autowiredProps = getMetaMap<string, any>(METADATA.AUTOWIRED, reg.target.prototype)\n const autowired: InstantiationPlan['autowired'] = []\n for (const [prop, token] of autowiredProps) {\n const resolvedToken =\n token || getMethodMetaOrUndefined(METADATA.PROPERTY_TYPE, reg.target.prototype, prop)\n if (resolvedToken) autowired.push([prop, resolvedToken])\n }\n\n // @Value properties.\n const valueProps = getMetaMap<string, { envKey: string; defaultValue?: any }>(\n METADATA.VALUE,\n reg.target.prototype,\n )\n const values: InstantiationPlan['values'] = [...valueProps.entries()]\n\n // Properties added by host injectors (`_propertyInjectors`), e.g. `@Asset`.\n const injected: InstantiationPlan['injected'] = []\n for (const [key, prepare] of Container._propertyInjectors) {\n for (const [prop, config] of getMetaMap<string, unknown>(key, reg.target.prototype)) {\n injected.push([prop, prepare(config, prop)])\n }\n }\n\n const postConstruct = getClassMetaOrUndefined<string | symbol>(\n METADATA.POST_CONSTRUCT,\n reg.target.prototype,\n )\n\n return { ctorTokens, autowired, values, injected, postConstruct }\n }\n\n private createInstance(reg: Registration): any {\n const plan = (reg.plan ??= this.buildPlan(reg))\n\n const args: any[] = []\n for (const token of plan.ctorTokens) {\n args.push(this.resolve(token))\n }\n\n const instance = new reg.target(...args)\n\n // Property injection via @Autowired / @Value / @Asset\n this.injectProperties(instance, plan)\n\n // @PostConstruct lifecycle hook\n const postConstruct = plan.postConstruct\n if (postConstruct && typeof instance[postConstruct] === 'function') {\n try {\n const result = instance[postConstruct]()\n if (result && typeof result.then === 'function') {\n reg.postConstructStatus = 'pending'\n ;(result as Promise<void>)\n .then(() => {\n reg.postConstructStatus = 'completed'\n })\n .catch(() => {\n reg.postConstructStatus = 'failed'\n })\n Container._logger.warn(\n `@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() returned a Promise ` +\n `but the container is synchronous. The async operation will not be awaited.`,\n )\n } else {\n reg.postConstructStatus = 'completed'\n }\n } catch (err) {\n reg.postConstructStatus = 'failed'\n Container._logger.error(\n err,\n `@PostConstruct on ${tokenName(reg.target)}.${String(postConstruct)}() threw an error`,\n )\n }\n }\n\n return instance\n }\n\n /**\n * Extract every dependency token name a class declares — both\n * constructor params (`@Inject` + emitted `design:paramtypes`) and\n * `@Autowired` properties (read lazily at injection time, but\n * captured here so devtools surfaces a complete dependency graph).\n *\n * Without the property branch, classes that rely entirely on\n * `@Autowired` show `dependencies: []` in the introspection panel\n * even though they have real DI deps — see /_debug topology.\n */\n private extractDependencies(target: Constructor): string[] {\n const paramTypes = getClassMeta<Constructor[]>(METADATA.PARAM_TYPES, target, [])\n const injectTokens = getMetaRecord<any>(METADATA.INJECT, target)\n const ctorDeps = paramTypes.map((type, index) => {\n const token = injectTokens[index] || type\n return tokenName(token)\n })\n\n // @Autowired properties live on `target.prototype`. Token may be\n // explicit (`@Autowired('SOME_TOKEN')`) or fall back to the\n // emitted `design:type` metadata of the property.\n const autowiredProps = getMetaMap<string, any>(METADATA.AUTOWIRED, target.prototype)\n const propDeps: string[] = []\n for (const [prop, token] of autowiredProps) {\n const resolvedToken =\n token || getMethodMetaOrUndefined(METADATA.PROPERTY_TYPE, target.prototype, prop)\n if (resolvedToken) propDeps.push(tokenName(resolvedToken))\n }\n\n // De-dupe — a class can declare the same token in both surfaces.\n return [...new Set([...ctorDeps, ...propDeps])]\n }\n\n private injectProperties(instance: any, plan: InstantiationPlan): void {\n // @Autowired — lazy DI property injection. The getter re-resolves per\n // access by design (a SINGLETON may lazily read a REQUEST-scoped dep at\n // call time). For SINGLETON deps that laziness buys nothing after the\n // first read — the container returns the same cached instance forever —\n // so the getter memoizes itself into a plain data property then. REQUEST\n // and TRANSIENT deps keep the live getter (memoizing would pin the first\n // request's instance across requests).\n for (const [prop, resolvedToken] of plan.autowired) {\n Object.defineProperty(instance, prop, {\n get: () => {\n const value = this.resolve(resolvedToken)\n const depReg = this.binding(resolvedToken)\n if (depReg?.scope === Scope.SINGLETON) {\n Object.defineProperty(instance, prop, {\n value,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n return value\n },\n enumerable: true,\n configurable: true,\n })\n }\n\n // @Value — lazy environment variable injection\n for (const [prop, config] of plan.values) {\n Object.defineProperty(instance, prop, {\n get() {\n // Use the registered env resolver if available (wired by the\n // config layer in @forinda/kickjs). Returns Zod-validated,\n // type-coerced values (e.g. PORT as number).\n if (Container._envResolver) {\n const val = Container._envResolver(config.envKey)\n if (val !== undefined) return val\n }\n\n // Fallback to raw process.env for apps that haven't called loadEnv()\n const val = process.env[config.envKey]\n if (val !== undefined) return val\n if (config.defaultValue !== undefined) return config.defaultValue\n throw Container._error('KICK004', config.envKey)\n },\n enumerable: true,\n configurable: true,\n })\n }\n\n // Host injectors — getters prepared at plan build.\n for (const [prop, get] of plan.injected) {\n Object.defineProperty(instance, prop, { get, enumerable: true, configurable: true })\n }\n }\n}\n","import 'reflect-metadata'\nimport { DI_METADATA as METADATA, Scope, type BuilderOf, type ServiceOptions } from './interfaces'\nimport { Container, type KickJsRegistry } from './container'\nimport { setClassMeta, setInMetaMap, setInMetaRecord } from './metadata'\n\n// ── Decorator Registration System ───────────────────────────────────────\n// Decorators execute at class-definition time (module load). The Container\n// may not exist yet when the first decorator fires. We queue registrations\n// and flush them on first Container.getInstance() call.\n//\n// `allRegistrations` is a persistent registry that survives Container.reset().\n// On reset (HMR), all previously decorated classes are re-registered on the\n// fresh container. This ensures @Service, @Controller, @Repository, etc.\n// all survive HMR without manual re-registration.\n\ntype PendingRegistration = { target: any; scope: Scope }\nconst pendingRegistrations: PendingRegistration[] = []\n// Keyed by class name to prevent memory leaks during HMR — new class with\n// the same name replaces the old entry instead of accumulating references.\nconst allRegistrations = new Map<string, { target: any; scope: Scope }>()\nlet containerRef: any = null\n\nfunction flushPending(container: any): void {\n containerRef = container\n for (const { target, scope } of pendingRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n pendingRegistrations.length = 0\n}\n\n// Wire up synchronously — Container._onReady is called on first getInstance()\nContainer._onReady = flushPending\n\n/** Replay every decorator registration onto a container. */\nfunction seed(container: any): void {\n for (const [, { target, scope }] of allRegistrations) {\n if (!container.has(target)) {\n container.register(target, target, scope)\n }\n }\n}\n\n// On Container.reset(), update containerRef and replay ALL decorator\n// registrations on the fresh container. This handles HMR where the container\n// is wiped but not all decorated modules are re-evaluated.\nContainer._onReset = (container: any) => {\n containerRef = container\n seed(container)\n}\n\n// On Container.create(), seed the same way but leave `containerRef` alone: an\n// isolated container should receive what has been registered so far, not\n// become the destination for everything registered next.\nContainer._onCreate = seed\n\n// ── Class Decorators ────────────────────────────────────────────────────\n\n/**\n * Classes already warned about.\n *\n * Keyed by the constructor, not its `name`: distinct classes can share a name —\n * two `DatabaseService` classes in separate modules is ordinary — and deduping\n * by name silently suppressed the second one's warning, which is precisely the\n * failure this warning exists to prevent.\n *\n * A WeakSet means an HMR reload, which builds a NEW class object, warns again.\n * That is correct rather than noisy: the reloaded code still has the problem,\n * and the notice stops as soon as it is fixed.\n */\nconst preDestroyWarned = new WeakSet<object>()\n\n/**\n * `@PreDestroy` only runs for REQUEST-scoped services — the request scope is\n * what closes and triggers it. On a SINGLETON it is silently inert, which\n * reads as a working teardown and is not: the reported case was a Postgres\n * pool never closed on shutdown, invisible in development and surfacing as\n * connection exhaustion under repeated restarts and HMR reloads.\n *\n * The asymmetry is the trap. `@PostConstruct` DOES run for singletons, so the\n * pair looks like init/teardown while one half quietly opts out on a scope the\n * author may never have considered.\n *\n * Warn rather than run it: making it fire on shutdown is a behaviour change\n * whose ordering, timeout and concurrency semantics have to match the adapter\n * shutdown path, and an adapter's `shutdown()` is already the seam for\n * application-lifetime resources.\n */\nfunction warnIfInertPreDestroy(target: any, scope: Scope): void {\n if (scope === Scope.REQUEST) return\n const proto = target?.prototype\n if (!proto) return\n const hook = Reflect.getMetadata?.(METADATA.PRE_DESTROY, proto)\n if (!hook) return\n if (preDestroyWarned.has(target)) return\n preDestroyWarned.add(target)\n const name = target.name || String(target)\n console.warn(\n ` kickjs: @PreDestroy on ${name} (${scope}) will never run — that hook fires ` +\n `only when a REQUEST scope closes.\\n` +\n ` For application-lifetime resources, release them from an adapter's ` +\n `shutdown() hook instead.`,\n )\n}\n\n/**\n * Register a decorated class with the container. @internal — exported for\n * class decorators built outside this module (`@Controller`).\n */\nexport function _registerInjectable(target: any, scope: Scope): void {\n setClassMeta(METADATA.INJECTABLE, true, target)\n setClassMeta(METADATA.SCOPE, scope, target)\n // Method decorators run before the class decorator, so PRE_DESTROY metadata\n // is already present by the time we get here.\n warnIfInertPreDestroy(target, scope)\n\n // Track in persistent registry — survives Container.reset() for HMR replay.\n // Keyed by name so HMR class re-creation replaces the old entry.\n const name = target.name || String(target)\n allRegistrations.set(name, { target, scope })\n\n if (containerRef) {\n // Container already initialized — register immediately\n if (!containerRef.has(target)) {\n containerRef.register(target, target, scope)\n }\n } else {\n // Container not ready yet — queue for later\n pendingRegistrations.push({ target, scope })\n }\n}\n\n/** Mark a class as injectable with lifecycle scope */\nexport function Injectable(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'injectable', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a service (semantic alias for Injectable) */\nexport function Service(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'service', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a generic managed component */\nexport function Component(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'component', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n/** Mark a class as a repository */\nexport function Repository(options?: ServiceOptions): ClassDecorator {\n return (target: any) => {\n setClassMeta(METADATA.CLASS_KIND, 'repository', target)\n _registerInjectable(target, options?.scope ?? Scope.SINGLETON)\n }\n}\n\n// ── Method Decorators ───────────────────────────────────────────────────\n\n/** Mark a method as a lifecycle hook called after instantiation */\nexport function PostConstruct(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.POST_CONSTRUCT, propertyKey, target)\n }\n}\n\n/**\n * Mark a method as a teardown hook — the counterpart to {@link PostConstruct}.\n *\n * For REQUEST-scoped services it runs when the request's scope closes\n * (response finished or aborted): release per-request resources there\n * (transactions, handles, subscriptions). May be async; errors are logged\n * and swallowed so one failing hook can't break request completion.\n *\n * It does NOT run on a SINGLETON — the default scope — because nothing closes\n * a singleton's scope. Unlike {@link PostConstruct}, which does run for\n * singletons, so the pair is not symmetric. Applying it to a singleton logs a\n * warning naming the class rather than failing silently.\n *\n * For application-lifetime resources (a connection pool, a timer, a socket\n * server) use an adapter's `shutdown()` hook, which the framework runs on a\n * real shutdown AND on every HMR reload, time-boxed and concurrent.\n */\nexport function PreDestroy(): MethodDecorator {\n return (target, propertyKey) => {\n setClassMeta(METADATA.PRE_DESTROY, propertyKey, target)\n }\n}\n\n// ── Injection Decorators (property + constructor parameter) ─────────────\n\n/**\n * A decorator that's valid as either a property decorator OR a\n * constructor-parameter decorator. forinda/kick-js#235 §2 — `@Autowired`\n * and `@Inject` previously diverged at the type level (property-only\n * vs parameter-only) and adopters who picked the wrong name for the\n * position got a cryptic TS1240 error. Both names now accept either\n * position and route to the correct metadata bucket at runtime.\n */\nexport interface PropertyOrParameterDecorator {\n /** Property decorator form — `@Autowired(TOKEN) private foo!: Foo`. */\n (target: object, propertyKey: string | symbol): void\n /** Constructor-parameter form — `constructor(@Inject(TOKEN) foo: Foo) {}`. */\n (target: object, propertyKey: string | symbol | undefined, parameterIndex: number): void\n}\n\nfunction applyInjection(\n token: unknown,\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex: number | undefined,\n): void {\n // Legacy parameter decorators receive 3 args (target, propertyKey,\n // parameterIndex); property decorators receive 2 (target, propertyKey).\n // The 3rd-arg-number-vs-undefined check is the canonical position\n // detection in TypeScript's legacy decorator runtime.\n if (typeof parameterIndex === 'number') {\n setInMetaRecord(METADATA.INJECT, target, parameterIndex, token)\n return\n }\n if (propertyKey != null) {\n // `target` for a property decorator on a non-static field is the\n // class prototype; the AUTOWIRED bucket is keyed off the prototype\n // identity at resolution time, so pass it through unchanged.\n setInMetaMap(METADATA.AUTOWIRED, target, propertyKey as string, token)\n }\n}\n\n/**\n * Inject a dependency. Works in two positions:\n *\n * - **Property decorator** — `@Autowired(TOKEN) private repo!: UserRepo`.\n * Resolved lazily from the container the first time the property is\n * read.\n * - **Constructor parameter decorator** — `constructor(@Autowired(TOKEN) repo: UserRepo) {}`.\n * Resolved at instantiation; injected into the ctor call.\n *\n * `@Inject` is the same function under another name — they share\n * runtime + types so the two names are interchangeable. Pick whichever\n * reads better at the call site.\n *\n * Like `@Inject`, the typed string-literal overload narrows the token\n * to a key of the augmented `KickJsRegistry`. After `kick typegen`\n * populates the registry, `@Autowired('kick/prisma/Client')` auto-\n * completes the key and typo'd literals become TS2345 errors.\n */\nexport function Autowired<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator\nexport function Autowired(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Inject a dependency by token. Same shape as {@link Autowired} —\n * works as either a property decorator or a constructor-parameter\n * decorator. Pick whichever name reads better at the call site.\n *\n * ## Typed string-literal overload\n *\n * When called with a string literal that matches a key of the\n * augmented `KickJsRegistry`, TypeScript narrows the parameter type\n * to the registered type. Typo'd literals become compile errors\n * (the string isn't `keyof KickJsRegistry`). Class identities and\n * `InjectionToken<T>` keep working unchanged via the second overload.\n *\n * @example\n * ```ts\n * import { Service, Inject } from '@forinda/kickjs'\n * import type { PrismaClient } from '@prisma/client'\n *\n * `@Service()`\n * class UserRepo {\n * constructor(@Inject('kick/prisma/Client') private db: PrismaClient) {}\n * // OR property form, same decorator:\n * @Inject('kick/prisma/Client') private db2!: PrismaClient\n * }\n * ```\n *\n * After `kick typegen` runs, the literal autocompletes from the\n * registry and a typo (`@Inject('kick/prisma/Cleint')`) becomes a\n * TS2345 error.\n */\nexport function Inject<K extends keyof KickJsRegistry & string>(\n token: K,\n): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator\nexport function Inject(token?: unknown): PropertyOrParameterDecorator {\n return ((\n target: object,\n propertyKey: string | symbol | undefined,\n parameterIndex?: number,\n ): void => {\n applyInjection(token, target, propertyKey, parameterIndex)\n }) as PropertyOrParameterDecorator\n}\n\n/**\n * Global ambient registry of typed environment variables, populated by\n * `kick typegen` from the project's env schema (typically\n * `src/env.ts`'s `export default defineEnv(...)`).\n *\n * Empty by default — declarations come from `.kickjs/types/env.ts`\n * generated alongside the rest of the typegen output. Once populated,\n * `@Value('PORT')` autocompletes the key, type-checks the default\n * value, and `process.env.PORT` is also typed via the parallel\n * `NodeJS.ProcessEnv` augmentation in the same generated file.\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface KickEnv {}\n}\n\n/** A string key known to the typed env registry (`never` until typegen runs) */\nexport type EnvKey = keyof KickEnv & string\n\n/**\n * Look up the type of a typed env entry.\n *\n * Decorators can't propagate the property type from the key, so users\n * still need to write the property's type — but `Env<K>` lets them\n * derive it from `KickEnv` instead of duplicating the schema:\n *\n * ```ts\n * @Service()\n * class MyService {\n * @Value('PORT')\n * private port!: Env<'PORT'> // → number, narrowed from KickEnv['PORT']\n * }\n * ```\n */\nexport type Env<K extends EnvKey = EnvKey> = KickEnv[K]\n\n/**\n * Constraint that allows any string when the typed env registry is\n * empty (no `kick typegen` has run yet, so existing code with raw\n * string keys keeps working) but locks the argument down to known\n * `EnvKey` literals once the registry is populated.\n *\n * Implementation: when `EnvKey` is `never`, the conditional resolves\n * to the user's `K`, accepting anything. Once typegen has populated\n * `KickEnv`, `EnvKey` becomes a real union and the conditional forces\n * `K` to be assignable to it.\n */\ntype ValueKey<K extends string> = [EnvKey] extends [never] ? K : K & EnvKey\n\n/**\n * Inject an environment variable value. Evaluated lazily so the env\n * is available at access time, not at decoration time.\n *\n * If no default is provided and the env var is missing, throws at access time\n * to catch misconfiguration early instead of returning undefined.\n *\n * Uses metadata + instance getter to work correctly with `useDefineForClassFields`.\n *\n * Type-safety:\n * - Without `kick typegen` run, any string key is accepted (back-compat).\n * - After typegen, `KickEnv` is populated and unknown keys become a tsc\n * error. The optional `defaultValue` is type-checked against the\n * schema's inferred type for that key.\n */\nexport function Value<K extends string>(\n envKey: ValueKey<K>,\n defaultValue?: K extends EnvKey ? KickEnv[K] : unknown,\n): PropertyDecorator {\n return (target, propertyKey) => {\n setInMetaMap(METADATA.VALUE, target, propertyKey as string, { envKey, defaultValue })\n }\n}\n// ── Builder Decorator ───────────────────────────────────────────────────\n\nfunction attachBuilder(target: any): void {\n setClassMeta(METADATA.BUILDER, true, target)\n target.builder = function () {\n const props: Record<string, any> = {}\n const proxy: any = new Proxy(\n {},\n {\n get(_, key) {\n if (key === 'build') {\n return () => Object.assign(new target(), props)\n }\n return (value: any) => {\n props[key as string] = value\n return proxy\n }\n },\n },\n )\n return proxy\n }\n}\n\n/**\n * Add a static `builder()` method for fluent construction.\n *\n * Legacy decorators cannot widen the class type, so by default the\n * caller sees the unmodified shape. Opt into typing by adding a single\n * `declare static` line:\n *\n * ```ts\n * @Builder\n * class UserDto {\n * name!: string\n * email!: string\n * declare static readonly builder: () => BuilderOf<UserDto>\n * }\n * ```\n *\n * `readonly` keeps SonarQube's `typescript:S1444` quiet — the runtime\n * assigns `target.builder` once at decoration time and never again.\n *\n * If you'd rather not write the `declare`, use `withBuilder()` instead —\n * same runtime, types inferred automatically.\n */\nexport function Builder(target: any): void {\n attachBuilder(target)\n}\n\n/**\n * Factory variant of `@Builder`. Wraps the class with the same runtime\n * and returns it intersected with `{ builder(): BuilderOf<T> }` so the\n * static is fully typed without any `declare` boilerplate.\n *\n * ```ts\n * class UserDtoBase {\n * name!: string\n * email!: string\n * }\n * export const UserDto = withBuilder(UserDtoBase)\n * export type UserDto = InstanceType<typeof UserDto>\n *\n * UserDto.builder().name('Alice').email('a@b.com').build() // fully typed\n * ```\n */\nexport function withBuilder<C extends new (...args: any[]) => any>(\n ctor: C,\n): C & { builder(): BuilderOf<InstanceType<C>> } {\n attachBuilder(ctor)\n return ctor as C & { builder(): BuilderOf<InstanceType<C>> }\n}\n","/**\n * Registry for framework-owned resources that hold timers or other handles\n * outside the adapter/plugin lifecycle — e.g. the in-memory session and\n * rate-limit stores' cleanup intervals. `Application.shutdown()` drains it so\n * startup and shutdown stay symmetric even for resources created lazily by\n * middleware factories.\n *\n * Intervals are `.unref()`'d so they never wedge process exit — this registry\n * exists for IN-PROCESS teardown (test suites, HMR, multi-app hosts) where\n * each un-disposed store would otherwise leak a live interval + backing Map\n * per app instance.\n */\n\nexport type Disposable = () => void | Promise<void>\n\nconst disposables = new Set<Disposable>()\n\n/** Register a cleanup callback. Returns an unregister function. */\nexport function registerDisposable(fn: Disposable): () => void {\n disposables.add(fn)\n return () => disposables.delete(fn)\n}\n\n/**\n * Remove and return all currently registered disposables WITHOUT running\n * them. Used by HMR rebuild to snapshot the outgoing app's resources before\n * `setup()` registers the incoming app's — so the stale set can be disposed\n * on success, or restored on failure, without touching the new one.\n */\nexport function drainDisposables(): Disposable[] {\n const pending = [...disposables]\n disposables.clear()\n return pending\n}\n\n/**\n * Run a list of disposables. Errors — including SYNCHRONOUS throws — are\n * swallowed per-entry: the `async` callback converts a sync throw into a\n * rejection that `allSettled` absorbs, so one bad disposable can't abort\n * the rest (a bare `Promise.resolve(fn())` would let the throw escape the\n * `.map()` before `allSettled` ever ran).\n */\nexport async function runDisposables(list: Disposable[]): Promise<void> {\n await Promise.allSettled(list.map(async (fn) => fn()))\n}\n\n/** Run and clear all registered disposables. Errors are swallowed per-entry. */\nexport async function disposeAll(): Promise<void> {\n await runDisposables(drainDisposables())\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forinda/kickjs-di",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "The dependency injection container behind KickJS: typed tokens, decorators, REQUEST scope, HMR-safe registrations",
5
5
  "keywords": [
6
6
  "kickjs",