@vielzeug/conduit 2.0.1 → 2.1.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/conduit.cjs +1 -1
- package/dist/conduit.cjs.map +1 -1
- package/dist/conduit.iife.js +1 -1
- package/dist/conduit.iife.js.map +1 -1
- package/dist/conduit.js +1 -1
- package/dist/conduit.js.map +1 -1
- package/dist/container.cjs.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -4
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +7 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +22 -16
package/dist/conduit.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e.description??`anonymous`}var t=class
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e.description??`anonymous`}var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{cycle;constructor(t){super(`Circular dependency detected: ${t.map(e).join(` -> `)}`),this.cycle=t}},r=class extends t{containerName;token;constructor(t,n){super(`No provider registered for token: ${e(t)} (in container '${n}')`),this.containerName=n,this.token=t}},i=class extends t{token;constructor(t){super(`Token "${e(t)}" is already registered.`),this.token=t}},a=class extends t{requiredScope;token;constructor(t,n){super(`Token "${e(t)}" requires scope "${n.description??`anonymous`}".`),this.requiredScope=n,this.token=t}},o=class extends t{containerName;constructor(e){super(`Cannot use disposed container '${e}'.`),this.containerName=e}},s=class extends t{errors;constructor(e){super(`Container disposal failed with ${e.length} cleanup error${e.length===1?``:`s`}.`),this.errors=e}},c=class e{#e=new Map;#t=new Set;#n=[];#r=new AbortController;#i=new Set;#a=`active`;#o;#s=[];#c;#l=new Map;#u;name;constructor(e,t={}){this.#c=e,this.#u=t.scope,e&&e.#t.add(this),this.name=t.name??(e?`${e.name}:${t.scope?.description??`child`}`:`root`)}get disposalSignal(){return this.#r.signal}get disposed(){return this.#a===`disposed`}value(e,t,n={}){this.#f(),this.#p(e);let r={dispose:n.dispose,kind:`value`,value:t};return this.#l.set(e,r),n.dispose&&this.#s.push({dispose:n.dispose,value:t}),this}factory(e,t,n,r={}){this.#f(),this.#p(e);let i={create:n,dependencies:Object.freeze([...t]),dispose:r.dispose,kind:`factory`,lifetime:r.lifetime??`singleton`};return this.#l.set(e,i),this}has(e){return this.#f(),this.#m(e)!==void 0}async resolve(e){return this.#f(),this.#_(e,[])}validate(){this.#f();for(let{owner:e,registration:t,token:n}of this.#h())e.#g(n,t,new Set,[]);return this}createScope(t,n){return this.#f(),new e(this,{name:n?.name,scope:t})}dispose(){return this.#a===`disposed`?Promise.resolve():this.#o?this.#o:(this.#a=`disposing`,this.#r.abort(),this.#o=this.#d(),this.#o)}async#d(){let e=await Promise.allSettled([...this.#t].map(e=>e.dispose()));await Promise.allSettled([...this.#i]);let t=[...this.#n,...e.flatMap(e=>e.status===`rejected`?e.reason instanceof s?e.reason.errors:[e.reason]:[])];for(let e of[...this.#s].reverse())if(e.dispose)try{await e.dispose(e.value)}catch(e){t.push(e)}if(this.#e.clear(),this.#t.clear(),this.#n=[],this.#s=[],this.#l.clear(),this.#c&&this.#c.#t.delete(this),this.#a=`disposed`,t.length)throw new s(t)}async[Symbol.asyncDispose](){await this.dispose()}#f(){if(this.#a!==`active`)throw new o(this.name)}#p(e){if(this.#l.has(e))throw new i(e)}#m(e){let t=this.#l.get(e);if(t)return{owner:this,registration:t};if(this.#c)return this.#c.#m(e)}#h(){let e=this.#c?this.#c.#h():[],t=[...this.#l].flatMap(([e,t])=>t.kind===`factory`?[{owner:this,registration:t,token:e}]:[]);return[...e,...t]}#g(e,t,i,a){if(i.has(e))throw new n([...a,e]);i.add(e);for(let n of t.dependencies){let t=this.#m(n);if(!t)throw new r(n,this.name);t.registration.kind===`factory`&&t.owner.#g(n,t.registration,i,[...a,e])}i.delete(e)}async#_(e,t){if(t.includes(e))throw new n([...t,e]);let i=this.#m(e);if(!i)throw new r(e,this.name);if(i.registration.kind===`value`)return i.registration.value;let a=this.#v(i.owner,i.registration,e);if(i.registration.lifetime===`transient`)return this.#b(i.registration,a,[...t,e]);let o=a.#e.get(i.registration);if(o?.value!==void 0)return o.value;if(o?.promise)return o.promise;let s={},c=a.#b(i.registration,a,[...t,e]).then(e=>(s.value=e,e));return s.promise=c,a.#e.set(i.registration,s),c}#v(e,t,n){if(t.lifetime===`singleton`)return e;if(t.lifetime===`transient`)return this;let r=this.#y(t.lifetime);if(!r)throw new a(n,t.lifetime);return r}#y(e){return this.#u===e?this:this.#c?this.#c.#y(e):void 0}async#b(e,t,n){let r=Promise.all(e.dependencies.map(e=>t.#_(e,n))).then(t=>e.create(...t));t.#i.add(r);try{let n=await r;if(t.#a!==`active`){if(e.dispose)try{await e.dispose(n)}catch(e){t.#n.push(e)}throw new o(t.name)}return e.dispose&&t.#s.push({dispose:e.dispose,value:n}),n}finally{t.#i.delete(r)}}};function l(e){return new c(void 0,e)}function u(e){return Symbol(e)}function d(e){return Symbol(e)}exports.ConduitCircularDependencyError=n,exports.ConduitDisposeError=s,exports.ConduitDisposedError=o,exports.ConduitDuplicateRegistrationError=i,exports.ConduitError=t,exports.ConduitProviderNotFoundError=r,exports.ConduitScopedResolutionError=a,exports.createContainer=l,exports.scope=d,exports.token=u;
|
|
2
2
|
//# sourceMappingURL=conduit.cjs.map
|
package/dist/conduit.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conduit.cjs","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/errors.ts","../src/container.ts","../src/types.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n static is(error: unknown): error is ConduitError {\n return error instanceof ConduitError;\n }\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n","import type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\nimport {\n ConduitCircularDependencyError,\n ConduitDisposeError,\n ConduitDisposedError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n","declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n [Symbol.asyncDispose](): Promise<void>;\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n dispose(): Promise<void>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conduit.cjs","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/errors.ts","../src/container.ts","../src/types.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n","import {\n ConduitCircularDependencyError,\n ConduitDisposedError,\n ConduitDisposeError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\nimport type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n","declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n readonly name: string;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n [Symbol.asyncDispose](): Promise<void>;\n}\n"],"mappings":"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"}
|
package/dist/conduit.iife.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Conduit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){return e.description??`anonymous`}var n=class
|
|
1
|
+
var Conduit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){return e.description??`anonymous`}var n=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},r=class extends n{cycle;constructor(e){super(`Circular dependency detected: ${e.map(t).join(` -> `)}`),this.cycle=e}},i=class extends n{containerName;token;constructor(e,n){super(`No provider registered for token: ${t(e)} (in container '${n}')`),this.containerName=n,this.token=e}},a=class extends n{token;constructor(e){super(`Token "${t(e)}" is already registered.`),this.token=e}},o=class extends n{requiredScope;token;constructor(e,n){super(`Token "${t(e)}" requires scope "${n.description??`anonymous`}".`),this.requiredScope=n,this.token=e}},s=class extends n{containerName;constructor(e){super(`Cannot use disposed container '${e}'.`),this.containerName=e}},c=class extends n{errors;constructor(e){super(`Container disposal failed with ${e.length} cleanup error${e.length===1?``:`s`}.`),this.errors=e}},l=class e{#e=new Map;#t=new Set;#n=[];#r=new AbortController;#i=new Set;#a=`active`;#o;#s=[];#c;#l=new Map;#u;name;constructor(e,t={}){this.#c=e,this.#u=t.scope,e&&e.#t.add(this),this.name=t.name??(e?`${e.name}:${t.scope?.description??`child`}`:`root`)}get disposalSignal(){return this.#r.signal}get disposed(){return this.#a===`disposed`}value(e,t,n={}){this.#f(),this.#p(e);let r={dispose:n.dispose,kind:`value`,value:t};return this.#l.set(e,r),n.dispose&&this.#s.push({dispose:n.dispose,value:t}),this}factory(e,t,n,r={}){this.#f(),this.#p(e);let i={create:n,dependencies:Object.freeze([...t]),dispose:r.dispose,kind:`factory`,lifetime:r.lifetime??`singleton`};return this.#l.set(e,i),this}has(e){return this.#f(),this.#m(e)!==void 0}async resolve(e){return this.#f(),this.#_(e,[])}validate(){this.#f();for(let{owner:e,registration:t,token:n}of this.#h())e.#g(n,t,new Set,[]);return this}createScope(t,n){return this.#f(),new e(this,{name:n?.name,scope:t})}dispose(){return this.#a===`disposed`?Promise.resolve():this.#o?this.#o:(this.#a=`disposing`,this.#r.abort(),this.#o=this.#d(),this.#o)}async#d(){let e=await Promise.allSettled([...this.#t].map(e=>e.dispose()));await Promise.allSettled([...this.#i]);let t=[...this.#n,...e.flatMap(e=>e.status===`rejected`?e.reason instanceof c?e.reason.errors:[e.reason]:[])];for(let e of[...this.#s].reverse())if(e.dispose)try{await e.dispose(e.value)}catch(e){t.push(e)}if(this.#e.clear(),this.#t.clear(),this.#n=[],this.#s=[],this.#l.clear(),this.#c&&this.#c.#t.delete(this),this.#a=`disposed`,t.length)throw new c(t)}async[Symbol.asyncDispose](){await this.dispose()}#f(){if(this.#a!==`active`)throw new s(this.name)}#p(e){if(this.#l.has(e))throw new a(e)}#m(e){let t=this.#l.get(e);if(t)return{owner:this,registration:t};if(this.#c)return this.#c.#m(e)}#h(){let e=this.#c?this.#c.#h():[],t=[...this.#l].flatMap(([e,t])=>t.kind===`factory`?[{owner:this,registration:t,token:e}]:[]);return[...e,...t]}#g(e,t,n,a){if(n.has(e))throw new r([...a,e]);n.add(e);for(let r of t.dependencies){let t=this.#m(r);if(!t)throw new i(r,this.name);t.registration.kind===`factory`&&t.owner.#g(r,t.registration,n,[...a,e])}n.delete(e)}async#_(e,t){if(t.includes(e))throw new r([...t,e]);let n=this.#m(e);if(!n)throw new i(e,this.name);if(n.registration.kind===`value`)return n.registration.value;let a=this.#v(n.owner,n.registration,e);if(n.registration.lifetime===`transient`)return this.#b(n.registration,a,[...t,e]);let o=a.#e.get(n.registration);if(o?.value!==void 0)return o.value;if(o?.promise)return o.promise;let s={},c=a.#b(n.registration,a,[...t,e]).then(e=>(s.value=e,e));return s.promise=c,a.#e.set(n.registration,s),c}#v(e,t,n){if(t.lifetime===`singleton`)return e;if(t.lifetime===`transient`)return this;let r=this.#y(t.lifetime);if(!r)throw new o(n,t.lifetime);return r}#y(e){return this.#u===e?this:this.#c?this.#c.#y(e):void 0}async#b(e,t,n){let r=Promise.all(e.dependencies.map(e=>t.#_(e,n))).then(t=>e.create(...t));t.#i.add(r);try{let n=await r;if(t.#a!==`active`){if(e.dispose)try{await e.dispose(n)}catch(e){t.#n.push(e)}throw new s(t.name)}return e.dispose&&t.#s.push({dispose:e.dispose,value:n}),n}finally{t.#i.delete(r)}}};function u(e){return new l(void 0,e)}function d(e){return Symbol(e)}function f(e){return Symbol(e)}return e.ConduitCircularDependencyError=r,e.ConduitDisposeError=c,e.ConduitDisposedError=s,e.ConduitDuplicateRegistrationError=a,e.ConduitError=n,e.ConduitProviderNotFoundError=i,e.ConduitScopedResolutionError=o,e.createContainer=u,e.scope=f,e.token=d,e})({});
|
|
2
2
|
//# sourceMappingURL=conduit.iife.js.map
|
package/dist/conduit.iife.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conduit.iife.js","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/errors.ts","../src/container.ts","../src/types.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n static is(error: unknown): error is ConduitError {\n return error instanceof ConduitError;\n }\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n","import type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\nimport {\n ConduitCircularDependencyError,\n ConduitDisposeError,\n ConduitDisposedError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n","declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n [Symbol.asyncDispose](): Promise<void>;\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n dispose(): Promise<void>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conduit.iife.js","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/errors.ts","../src/container.ts","../src/types.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n","import {\n ConduitCircularDependencyError,\n ConduitDisposedError,\n ConduitDisposeError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\nimport type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n","declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n readonly name: string;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n [Symbol.asyncDispose](): Promise<void>;\n}\n"],"mappings":"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"}
|
package/dist/conduit.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){return e.description??`anonymous`}var t=class
|
|
1
|
+
function e(e){return e.description??`anonymous`}var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{cycle;constructor(t){super(`Circular dependency detected: ${t.map(e).join(` -> `)}`),this.cycle=t}},r=class extends t{containerName;token;constructor(t,n){super(`No provider registered for token: ${e(t)} (in container '${n}')`),this.containerName=n,this.token=t}},i=class extends t{token;constructor(t){super(`Token "${e(t)}" is already registered.`),this.token=t}},a=class extends t{requiredScope;token;constructor(t,n){super(`Token "${e(t)}" requires scope "${n.description??`anonymous`}".`),this.requiredScope=n,this.token=t}},o=class extends t{containerName;constructor(e){super(`Cannot use disposed container '${e}'.`),this.containerName=e}},s=class extends t{errors;constructor(e){super(`Container disposal failed with ${e.length} cleanup error${e.length===1?``:`s`}.`),this.errors=e}},c=class e{#e=new Map;#t=new Set;#n=[];#r=new AbortController;#i=new Set;#a=`active`;#o;#s=[];#c;#l=new Map;#u;name;constructor(e,t={}){this.#c=e,this.#u=t.scope,e&&e.#t.add(this),this.name=t.name??(e?`${e.name}:${t.scope?.description??`child`}`:`root`)}get disposalSignal(){return this.#r.signal}get disposed(){return this.#a===`disposed`}value(e,t,n={}){this.#f(),this.#p(e);let r={dispose:n.dispose,kind:`value`,value:t};return this.#l.set(e,r),n.dispose&&this.#s.push({dispose:n.dispose,value:t}),this}factory(e,t,n,r={}){this.#f(),this.#p(e);let i={create:n,dependencies:Object.freeze([...t]),dispose:r.dispose,kind:`factory`,lifetime:r.lifetime??`singleton`};return this.#l.set(e,i),this}has(e){return this.#f(),this.#m(e)!==void 0}async resolve(e){return this.#f(),this.#_(e,[])}validate(){this.#f();for(let{owner:e,registration:t,token:n}of this.#h())e.#g(n,t,new Set,[]);return this}createScope(t,n){return this.#f(),new e(this,{name:n?.name,scope:t})}dispose(){return this.#a===`disposed`?Promise.resolve():this.#o?this.#o:(this.#a=`disposing`,this.#r.abort(),this.#o=this.#d(),this.#o)}async#d(){let e=await Promise.allSettled([...this.#t].map(e=>e.dispose()));await Promise.allSettled([...this.#i]);let t=[...this.#n,...e.flatMap(e=>e.status===`rejected`?e.reason instanceof s?e.reason.errors:[e.reason]:[])];for(let e of[...this.#s].reverse())if(e.dispose)try{await e.dispose(e.value)}catch(e){t.push(e)}if(this.#e.clear(),this.#t.clear(),this.#n=[],this.#s=[],this.#l.clear(),this.#c&&this.#c.#t.delete(this),this.#a=`disposed`,t.length)throw new s(t)}async[Symbol.asyncDispose](){await this.dispose()}#f(){if(this.#a!==`active`)throw new o(this.name)}#p(e){if(this.#l.has(e))throw new i(e)}#m(e){let t=this.#l.get(e);if(t)return{owner:this,registration:t};if(this.#c)return this.#c.#m(e)}#h(){let e=this.#c?this.#c.#h():[],t=[...this.#l].flatMap(([e,t])=>t.kind===`factory`?[{owner:this,registration:t,token:e}]:[]);return[...e,...t]}#g(e,t,i,a){if(i.has(e))throw new n([...a,e]);i.add(e);for(let n of t.dependencies){let t=this.#m(n);if(!t)throw new r(n,this.name);t.registration.kind===`factory`&&t.owner.#g(n,t.registration,i,[...a,e])}i.delete(e)}async#_(e,t){if(t.includes(e))throw new n([...t,e]);let i=this.#m(e);if(!i)throw new r(e,this.name);if(i.registration.kind===`value`)return i.registration.value;let a=this.#v(i.owner,i.registration,e);if(i.registration.lifetime===`transient`)return this.#b(i.registration,a,[...t,e]);let o=a.#e.get(i.registration);if(o?.value!==void 0)return o.value;if(o?.promise)return o.promise;let s={},c=a.#b(i.registration,a,[...t,e]).then(e=>(s.value=e,e));return s.promise=c,a.#e.set(i.registration,s),c}#v(e,t,n){if(t.lifetime===`singleton`)return e;if(t.lifetime===`transient`)return this;let r=this.#y(t.lifetime);if(!r)throw new a(n,t.lifetime);return r}#y(e){return this.#u===e?this:this.#c?this.#c.#y(e):void 0}async#b(e,t,n){let r=Promise.all(e.dependencies.map(e=>t.#_(e,n))).then(t=>e.create(...t));t.#i.add(r);try{let n=await r;if(t.#a!==`active`){if(e.dispose)try{await e.dispose(n)}catch(e){t.#n.push(e)}throw new o(t.name)}return e.dispose&&t.#s.push({dispose:e.dispose,value:n}),n}finally{t.#i.delete(r)}}};function l(e){return new c(void 0,e)}function u(e){return Symbol(e)}function d(e){return Symbol(e)}export{n as ConduitCircularDependencyError,s as ConduitDisposeError,o as ConduitDisposedError,i as ConduitDuplicateRegistrationError,t as ConduitError,r as ConduitProviderNotFoundError,a as ConduitScopedResolutionError,l as createContainer,d as scope,u as token};
|
|
2
2
|
//# sourceMappingURL=conduit.js.map
|
package/dist/conduit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conduit.js","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/errors.ts","../src/container.ts","../src/types.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n static is(error: unknown): error is ConduitError {\n return error instanceof ConduitError;\n }\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n","import type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\nimport {\n ConduitCircularDependencyError,\n ConduitDisposeError,\n ConduitDisposedError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n","declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n [Symbol.asyncDispose](): Promise<void>;\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly name: string;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n dispose(): Promise<void>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conduit.js","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/errors.ts","../src/container.ts","../src/types.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n","import {\n ConduitCircularDependencyError,\n ConduitDisposedError,\n ConduitDisposeError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\nimport type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n","declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n readonly name: string;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n [Symbol.asyncDispose](): Promise<void>;\n}\n"],"mappings":"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"}
|
package/dist/container.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.cjs","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/container.ts"],"sourcesContent":["import type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\nimport {\n ConduitCircularDependencyError,\n ConduitDisposeError,\n ConduitDisposedError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"container.cjs","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/container.ts"],"sourcesContent":["import {\n ConduitCircularDependencyError,\n ConduitDisposedError,\n ConduitDisposeError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\nimport type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n"],"mappings":"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"}
|
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAA0E,MAAM,YAAY,CAAC;AAmTpH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAEtE"}
|
package/dist/container.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.js","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/container.ts"],"sourcesContent":["import type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\nimport {\n ConduitCircularDependencyError,\n ConduitDisposeError,\n ConduitDisposedError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"container.js","names":["#parent","#scope","#children","#disposalController","#lifecycle","#assertActive","#assertUnregistered","#registry","#owned","#lookup","#resolve","#factories","#validatePath","#disposePromise","#finishDisposal","#inFlight","#cleanupFailures","#cache","#ownerFor","#create","#scopeOwner"],"sources":["../src/container.ts"],"sourcesContent":["import {\n ConduitCircularDependencyError,\n ConduitDisposedError,\n ConduitDisposeError,\n ConduitDuplicateRegistrationError,\n ConduitProviderNotFoundError,\n ConduitScopedResolutionError,\n} from './errors.js';\nimport type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types.js';\n\ntype Disposer = (value: never) => Promise<void> | void;\n\ntype ValueRegistration<T> = Readonly<{\n dispose?: Disposer;\n kind: 'value';\n value: T;\n}>;\n\ntype FactoryRegistration<T = unknown> = Readonly<{\n create: (...values: unknown[]) => Promise<T> | T;\n dependencies: readonly Token<unknown>[];\n dispose?: Disposer;\n kind: 'factory';\n lifetime: Lifetime;\n}>;\n\ntype Registration<T = unknown> = FactoryRegistration<T> | ValueRegistration<T>;\ntype OwnedResource = Readonly<{ dispose?: Disposer; value: unknown }>;\ntype CacheEntry = { promise?: Promise<unknown>; value?: unknown };\ntype Lifecycle = 'active' | 'disposed' | 'disposing';\n\nclass ContainerImpl implements Container {\n #cache = new Map<FactoryRegistration<any>, CacheEntry>();\n #children = new Set<ContainerImpl>();\n #cleanupFailures: unknown[] = [];\n #disposalController = new AbortController();\n #inFlight = new Set<Promise<unknown>>();\n #lifecycle: Lifecycle = 'active';\n #disposePromise?: Promise<void>;\n #owned: OwnedResource[] = [];\n #parent?: ContainerImpl;\n #registry = new Map<Token<any>, Registration<any>>();\n #scope?: ScopeToken;\n readonly name: string;\n\n constructor(parent?: ContainerImpl, options: { name?: string; scope?: ScopeToken } = {}) {\n this.#parent = parent;\n this.#scope = options.scope;\n\n if (parent) parent.#children.add(this);\n\n this.name = options.name ?? (parent ? `${parent.name}:${options.scope?.description ?? 'child'}` : 'root');\n }\n\n get disposalSignal(): AbortSignal {\n return this.#disposalController.signal;\n }\n\n get disposed(): boolean {\n return this.#lifecycle === 'disposed';\n }\n\n value<T>(token: Token<T>, value: T, options: ValueOptions<T> = {}): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: ValueRegistration<T> = {\n dispose: options.dispose as Disposer | undefined,\n kind: 'value',\n value,\n };\n\n this.#registry.set(token, registration);\n\n if (options.dispose) this.#owned.push({ dispose: options.dispose, value });\n\n return this;\n }\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options: FactoryOptions<T> = {},\n ): this {\n this.#assertActive();\n this.#assertUnregistered(token);\n\n const registration: FactoryRegistration<T> = {\n create: create as (...values: unknown[]) => Promise<T> | T,\n dependencies: Object.freeze([...dependencies]),\n dispose: options.dispose as Disposer | undefined,\n kind: 'factory',\n lifetime: options.lifetime ?? 'singleton',\n };\n\n this.#registry.set(token, registration);\n\n return this;\n }\n\n has<T>(token: Token<T>): boolean {\n this.#assertActive();\n\n return this.#lookup(token) !== undefined;\n }\n\n async resolve<T>(token: Token<T>): Promise<T> {\n this.#assertActive();\n\n return this.#resolve(token, []);\n }\n\n validate(): this {\n this.#assertActive();\n\n for (const { owner, registration, token } of this.#factories()) {\n owner.#validatePath(token, registration, new Set(), []);\n }\n\n return this;\n }\n\n createScope(scope?: ScopeToken, options?: { name?: string }): Container {\n this.#assertActive();\n\n return new ContainerImpl(this, { name: options?.name, scope });\n }\n\n dispose(): Promise<void> {\n if (this.#lifecycle === 'disposed') return Promise.resolve();\n\n if (this.#disposePromise) return this.#disposePromise;\n\n this.#lifecycle = 'disposing';\n this.#disposalController.abort();\n this.#disposePromise = this.#finishDisposal();\n\n return this.#disposePromise;\n }\n\n async #finishDisposal(): Promise<void> {\n const childOutcomes = await Promise.allSettled([...this.#children].map((child) => child.dispose()));\n\n await Promise.allSettled([...this.#inFlight]);\n\n const failures = [\n ...this.#cleanupFailures,\n ...childOutcomes.flatMap((outcome) => {\n if (outcome.status !== 'rejected') return [];\n\n return outcome.reason instanceof ConduitDisposeError ? outcome.reason.errors : [outcome.reason];\n }),\n ];\n\n for (const resource of [...this.#owned].reverse()) {\n if (!resource.dispose) continue;\n\n try {\n await resource.dispose(resource.value as never);\n } catch (error) {\n failures.push(error);\n }\n }\n\n this.#cache.clear();\n this.#children.clear();\n this.#cleanupFailures = [];\n this.#owned = [];\n this.#registry.clear();\n\n if (this.#parent) this.#parent.#children.delete(this);\n\n this.#lifecycle = 'disposed';\n\n if (failures.length) throw new ConduitDisposeError(failures);\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n\n #assertActive(): void {\n if (this.#lifecycle !== 'active') throw new ConduitDisposedError(this.name);\n }\n\n #assertUnregistered(token: Token<unknown>): void {\n if (this.#registry.has(token)) throw new ConduitDuplicateRegistrationError(token);\n }\n\n #lookup<T>(token: Token<T>): { owner: ContainerImpl; registration: Registration<T> } | undefined {\n const local = this.#registry.get(token) as Registration<T> | undefined;\n\n if (local) return { owner: this, registration: local };\n\n if (!this.#parent) return undefined;\n\n return this.#parent.#lookup(token);\n }\n\n #factories(): { owner: ContainerImpl; registration: FactoryRegistration; token: Token<unknown> }[] {\n const parentFactories = this.#parent ? this.#parent.#factories() : [];\n const localFactories = [...this.#registry].flatMap(([token, registration]) =>\n registration.kind === 'factory' ? [{ owner: this, registration, token }] : [],\n );\n\n return [...parentFactories, ...localFactories];\n }\n\n #validatePath(\n token: Token<unknown>,\n registration: FactoryRegistration,\n visiting: Set<Token<unknown>>,\n path: Token<unknown>[],\n ): void {\n if (visiting.has(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n visiting.add(token);\n\n for (const dependency of registration.dependencies) {\n const found = this.#lookup(dependency);\n\n if (!found) throw new ConduitProviderNotFoundError(dependency, this.name);\n\n if (found.registration.kind === 'factory') {\n found.owner.#validatePath(dependency, found.registration, visiting, [...path, token]);\n }\n }\n\n visiting.delete(token);\n }\n\n async #resolve<T>(token: Token<T>, path: Token<unknown>[]): Promise<T> {\n if (path.includes(token)) throw new ConduitCircularDependencyError([...path, token]);\n\n const found = this.#lookup(token);\n\n if (!found) throw new ConduitProviderNotFoundError(token, this.name);\n\n if (found.registration.kind === 'value') return found.registration.value as T;\n\n const owner = this.#ownerFor(found.owner, found.registration, token);\n\n if (found.registration.lifetime === 'transient') {\n return this.#create(found.registration, owner, [...path, token]) as Promise<T>;\n }\n\n const existing = owner.#cache.get(found.registration);\n\n if (existing?.value !== undefined) return existing.value as T;\n\n if (existing?.promise) return existing.promise as Promise<T>;\n\n const entry: CacheEntry = {};\n const promise = owner.#create(found.registration, owner, [...path, token]).then((value) => {\n entry.value = value;\n\n return value;\n });\n\n entry.promise = promise;\n owner.#cache.set(found.registration, entry);\n\n return promise as Promise<T>;\n }\n\n #ownerFor(owner: ContainerImpl, registration: FactoryRegistration, token: Token<unknown>): ContainerImpl {\n if (registration.lifetime === 'singleton') return owner;\n\n if (registration.lifetime === 'transient') return this;\n\n const scope = this.#scopeOwner(registration.lifetime);\n\n if (!scope) throw new ConduitScopedResolutionError(token, registration.lifetime);\n\n return scope;\n }\n\n #scopeOwner(scope: ScopeToken): ContainerImpl | undefined {\n if (this.#scope === scope) return this;\n\n return this.#parent ? this.#parent.#scopeOwner(scope) : undefined;\n }\n\n async #create(registration: FactoryRegistration, owner: ContainerImpl, path: Token<unknown>[]): Promise<unknown> {\n const promise = Promise.all(registration.dependencies.map((dependency) => owner.#resolve(dependency, path))).then(\n (dependencies) => registration.create(...dependencies),\n );\n\n owner.#inFlight.add(promise);\n\n try {\n const value = await promise;\n\n if (owner.#lifecycle !== 'active') {\n if (registration.dispose) {\n try {\n await registration.dispose(value as never);\n } catch (error) {\n owner.#cleanupFailures.push(error);\n }\n }\n\n throw new ConduitDisposedError(owner.name);\n }\n\n if (registration.dispose) owner.#owned.push({ dispose: registration.dispose, value });\n\n return value;\n } finally {\n owner.#inFlight.delete(promise);\n }\n }\n}\n\nexport function createContainer(options?: { name?: string }): Container {\n return new ContainerImpl(undefined, options);\n}\n"],"mappings":"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"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){return e.description??`anonymous`}var t=class
|
|
1
|
+
function e(e){return e.description??`anonymous`}var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{cycle;constructor(t){super(`Circular dependency detected: ${t.map(e).join(` -> `)}`),this.cycle=t}},r=class extends t{containerName;token;constructor(t,n){super(`No provider registered for token: ${e(t)} (in container '${n}')`),this.containerName=n,this.token=t}},i=class extends t{token;constructor(t){super(`Token "${e(t)}" is already registered.`),this.token=t}},a=class extends t{requiredScope;token;constructor(t,n){super(`Token "${e(t)}" requires scope "${n.description??`anonymous`}".`),this.requiredScope=n,this.token=t}},o=class extends t{containerName;constructor(e){super(`Cannot use disposed container '${e}'.`),this.containerName=e}},s=class extends t{errors;constructor(e){super(`Container disposal failed with ${e.length} cleanup error${e.length===1?``:`s`}.`),this.errors=e}};exports.ConduitCircularDependencyError=n,exports.ConduitDisposeError=s,exports.ConduitDisposedError=o,exports.ConduitDuplicateRegistrationError=i,exports.ConduitError=t,exports.ConduitProviderNotFoundError=r,exports.ConduitScopedResolutionError=a,exports.tokenName=e;
|
|
2
2
|
//# sourceMappingURL=errors.cjs.map
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n"],"mappings":"AAEA,SAAgB,EAAU,EAA+B,CACvD,OAAO,EAAM,aAAe,WAC9B,CAEA,IAAa,EAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,EAAwB,CACnD,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAAoD,CAAa,CAC/D,MAEA,YAAY,EAAyB,CACnC,MAAM,iCAAiC,EAAM,IAAI,CAAS,CAAC,CAAC,KAAK,MAAM,GAAG,EAC1E,KAAK,MAAQ,CACf,CACF,EAEa,EAAb,cAAkD,CAAa,CAC7D,cACA,MAEA,YAAY,EAAuB,EAAuB,CACxD,MAAM,qCAAqC,EAAU,CAAK,EAAE,kBAAkB,EAAc,GAAG,EAC/F,KAAK,cAAgB,EACrB,KAAK,MAAQ,CACf,CACF,EAEa,EAAb,cAAuD,CAAa,CAClE,MAEA,YAAY,EAAuB,CACjC,MAAM,UAAU,EAAU,CAAK,EAAE,yBAAyB,EAC1D,KAAK,MAAQ,CACf,CACF,EAEa,EAAb,cAAkD,CAAa,CAC7D,cACA,MAEA,YAAY,EAAuB,EAA2B,CAC5D,MAAM,UAAU,EAAU,CAAK,EAAE,oBAAoB,EAAc,aAAe,YAAY,GAAG,EACjG,KAAK,cAAgB,EACrB,KAAK,MAAQ,CACf,CACF,EAEa,EAAb,cAA0C,CAAa,CACrD,cAEA,YAAY,EAAuB,CACjC,MAAM,kCAAkC,EAAc,GAAG,EACzD,KAAK,cAAgB,CACvB,CACF,EAEa,EAAb,cAAyC,CAAa,CACpD,OAEA,YAAY,EAAmB,CAC7B,MAAM,kCAAkC,EAAO,OAAO,gBAAgB,EAAO,SAAW,EAAI,GAAK,IAAI,EAAE,EACvG,KAAK,OAAS,CAChB,CACF"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ScopeToken, Token } from './types.js';
|
|
2
2
|
export declare function tokenName(token: Token<unknown>): string;
|
|
3
3
|
export declare class ConduitError extends Error {
|
|
4
|
-
static is(error: unknown): error is ConduitError;
|
|
5
4
|
constructor(message: string, options?: ErrorOptions);
|
|
6
5
|
}
|
|
7
6
|
export declare class ConduitCircularDependencyError extends ConduitError {
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEpD,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,MAAM,CAEvD;AAED,qBAAa,YAAa,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEpD,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,MAAM,CAEvD;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAKpD;AAED,qBAAa,8BAA+B,SAAQ,YAAY;IAC9D,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;gBAErB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE;CAIpC;AAED,qBAAa,4BAA6B,SAAQ,YAAY;IAC5D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAEnB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,MAAM;CAKzD;AAED,qBAAa,iCAAkC,SAAQ,YAAY;IACjE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAEnB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;CAIlC;AAED,qBAAa,4BAA6B,SAAQ,YAAY;IAC5D,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAEnB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,UAAU;CAK7D;AAED,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,aAAa,EAAE,MAAM;CAIlC;AAED,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBAEf,MAAM,EAAE,OAAO,EAAE;CAI9B"}
|
package/dist/errors.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
function e(e) {
|
|
3
3
|
return e.description ?? "anonymous";
|
|
4
4
|
}
|
|
5
|
-
var t = class
|
|
6
|
-
static is(t) {
|
|
7
|
-
return t instanceof e;
|
|
8
|
-
}
|
|
5
|
+
var t = class extends Error {
|
|
9
6
|
constructor(e, t) {
|
|
10
7
|
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
11
8
|
}
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { ScopeToken, Token } from './types.js';\n\nexport function tokenName(token: Token<unknown>): string {\n return token.description ?? 'anonymous';\n}\n\nexport class ConduitError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class ConduitCircularDependencyError extends ConduitError {\n readonly cycle: Token<unknown>[];\n\n constructor(cycle: Token<unknown>[]) {\n super(`Circular dependency detected: ${cycle.map(tokenName).join(' -> ')}`);\n this.cycle = cycle;\n }\n}\n\nexport class ConduitProviderNotFoundError extends ConduitError {\n readonly containerName: string;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, containerName: string) {\n super(`No provider registered for token: ${tokenName(token)} (in container '${containerName}')`);\n this.containerName = containerName;\n this.token = token;\n }\n}\n\nexport class ConduitDuplicateRegistrationError extends ConduitError {\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>) {\n super(`Token \"${tokenName(token)}\" is already registered.`);\n this.token = token;\n }\n}\n\nexport class ConduitScopedResolutionError extends ConduitError {\n readonly requiredScope: ScopeToken;\n readonly token: Token<unknown>;\n\n constructor(token: Token<unknown>, requiredScope: ScopeToken) {\n super(`Token \"${tokenName(token)}\" requires scope \"${requiredScope.description ?? 'anonymous'}\".`);\n this.requiredScope = requiredScope;\n this.token = token;\n }\n}\n\nexport class ConduitDisposedError extends ConduitError {\n readonly containerName: string;\n\n constructor(containerName: string) {\n super(`Cannot use disposed container '${containerName}'.`);\n this.containerName = containerName;\n }\n}\n\nexport class ConduitDisposeError extends ConduitError {\n readonly errors: unknown[];\n\n constructor(errors: unknown[]) {\n super(`Container disposal failed with ${errors.length} cleanup error${errors.length === 1 ? '' : 's'}.`);\n this.errors = errors;\n }\n}\n"],"mappings":";AAEA,SAAgB,EAAU,GAA+B;CACvD,OAAO,EAAM,eAAe;AAC9B;AAEA,IAAa,IAAb,cAAkC,MAAM;CACtC,YAAY,GAAiB,GAAwB;EAGnD,AAFA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAEa,IAAb,cAAoD,EAAa;CAC/D;CAEA,YAAY,GAAyB;EAEnC,AADA,MAAM,iCAAiC,EAAM,IAAI,CAAS,CAAC,CAAC,KAAK,MAAM,GAAG,GAC1E,KAAK,QAAQ;CACf;AACF,GAEa,IAAb,cAAkD,EAAa;CAC7D;CACA;CAEA,YAAY,GAAuB,GAAuB;EAGxD,AAFA,MAAM,qCAAqC,EAAU,CAAK,EAAE,kBAAkB,EAAc,GAAG,GAC/F,KAAK,gBAAgB,GACrB,KAAK,QAAQ;CACf;AACF,GAEa,IAAb,cAAuD,EAAa;CAClE;CAEA,YAAY,GAAuB;EAEjC,AADA,MAAM,UAAU,EAAU,CAAK,EAAE,yBAAyB,GAC1D,KAAK,QAAQ;CACf;AACF,GAEa,IAAb,cAAkD,EAAa;CAC7D;CACA;CAEA,YAAY,GAAuB,GAA2B;EAG5D,AAFA,MAAM,UAAU,EAAU,CAAK,EAAE,oBAAoB,EAAc,eAAe,YAAY,GAAG,GACjG,KAAK,gBAAgB,GACrB,KAAK,QAAQ;CACf;AACF,GAEa,IAAb,cAA0C,EAAa;CACrD;CAEA,YAAY,GAAuB;EAEjC,AADA,MAAM,kCAAkC,EAAc,GAAG,GACzD,KAAK,gBAAgB;CACvB;AACF,GAEa,IAAb,cAAyC,EAAa;CACpD;CAEA,YAAY,GAAmB;EAE7B,AADA,MAAM,kCAAkC,EAAO,OAAO,gBAAgB,EAAO,WAAW,IAAI,KAAK,IAAI,EAAE,GACvG,KAAK,SAAS;CAChB;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createContainer } from './container';
|
|
2
|
-
export { ConduitCircularDependencyError,
|
|
2
|
+
export { ConduitCircularDependencyError, ConduitDisposedError, ConduitDisposeError, ConduitDuplicateRegistrationError, ConduitError, ConduitProviderNotFoundError, ConduitScopedResolutionError, } from './errors';
|
|
3
3
|
export type { Container, FactoryOptions, InferTokens, Lifetime, ScopeToken, Token, ValueOptions } from './types';
|
|
4
4
|
export { scope, token } from './types';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,8BAA8B,EAC9B,mBAAmB,EACnB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,8BAA8B,EAC9B,oBAAoB,EACpB,mBAAmB,EACnB,iCAAiC,EACjC,YAAY,EACZ,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n
|
|
1
|
+
{"version":3,"file":"types.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n readonly name: string;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n [Symbol.asyncDispose](): Promise<void>;\n}\n"],"mappings":"AAMA,SAAgB,EAAS,EAA+B,CACtD,OAAO,OAAO,CAAW,CAC3B,CAEA,SAAgB,EAAM,EAA0B,CAC9C,OAAO,OAAO,CAAI,CACpB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -20,19 +20,19 @@ export type InferTokens<T extends readonly Token<unknown>[]> = {
|
|
|
20
20
|
[K in keyof T]: T[K] extends Token<infer Value> ? Value : never;
|
|
21
21
|
};
|
|
22
22
|
export interface Container {
|
|
23
|
-
|
|
23
|
+
createScope(scope?: ScopeToken, options?: {
|
|
24
|
+
name?: string;
|
|
25
|
+
}): Container;
|
|
24
26
|
readonly disposalSignal: AbortSignal;
|
|
27
|
+
dispose(): Promise<void>;
|
|
25
28
|
readonly disposed: boolean;
|
|
26
|
-
readonly name: string;
|
|
27
|
-
value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;
|
|
28
29
|
factory<T, Dependencies extends readonly Token<unknown>[]>(token: Token<T>, dependencies: Dependencies, create: (...values: InferTokens<Dependencies>) => Promise<T> | T, options?: FactoryOptions<T>): this;
|
|
29
30
|
has<T>(token: Token<T>): boolean;
|
|
31
|
+
readonly name: string;
|
|
30
32
|
resolve<T>(token: Token<T>): Promise<T>;
|
|
31
33
|
validate(): this;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}): Container;
|
|
35
|
-
dispose(): Promise<void>;
|
|
34
|
+
value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;
|
|
35
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
36
36
|
}
|
|
37
37
|
export {};
|
|
38
38
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AACxC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC,MAAM,MAAM,KAAK,CAAC,CAAC,GAAG,OAAO,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AACvE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAElE,wBAAgB,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAEtD;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAE9C;AAED,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,QAAQ,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC9C,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,QAAQ,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI;KAC5D,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK;CAChE,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AACxC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC,MAAM,MAAM,KAAK,CAAC,CAAC,GAAG,OAAO,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AACvE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAElE,wBAAgB,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAEtD;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAE9C;AAED,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,QAAQ,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC9C,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,QAAQ,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI;KAC5D,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK;CAChE,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACxE,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B,OAAO,CAAC,CAAC,EAAE,YAAY,SAAS,SAAS,KAAK,CAAC,OAAO,CAAC,EAAE,EACvD,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,WAAW,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAChE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,IAAI,CAAC;IAER,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,QAAQ,IAAI,IAAI,CAAC;IAEjB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACrE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["declare const tokenBrand: unique symbol;\ndeclare const scopeBrand: unique symbol;\n\nexport type Token<T = unknown> = symbol & { readonly [tokenBrand]: T };\nexport type ScopeToken = symbol & { readonly [scopeBrand]: true };\n\nexport function token<T>(description: string): Token<T> {\n return Symbol(description) as Token<T>;\n}\n\nexport function scope(name: string): ScopeToken {\n return Symbol(name) as ScopeToken;\n}\n\nexport type Lifetime = 'singleton' | 'transient' | ScopeToken;\n\nexport type ValueOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n}>;\n\nexport type FactoryOptions<T> = Readonly<{\n dispose?: (value: T) => Promise<void> | void;\n lifetime?: Lifetime;\n}>;\n\nexport type InferTokens<T extends readonly Token<unknown>[]> = {\n [K in keyof T]: T[K] extends Token<infer Value> ? Value : never;\n};\n\nexport interface Container {\n createScope(scope?: ScopeToken, options?: { name?: string }): Container;\n readonly disposalSignal: AbortSignal;\n dispose(): Promise<void>;\n readonly disposed: boolean;\n\n factory<T, Dependencies extends readonly Token<unknown>[]>(\n token: Token<T>,\n dependencies: Dependencies,\n create: (...values: InferTokens<Dependencies>) => Promise<T> | T,\n options?: FactoryOptions<T>,\n ): this;\n\n has<T>(token: Token<T>): boolean;\n readonly name: string;\n resolve<T>(token: Token<T>): Promise<T>;\n validate(): this;\n\n value<T>(token: Token<T>, value: T, options?: ValueOptions<T>): this;\n [Symbol.asyncDispose](): Promise<void>;\n}\n"],"mappings":";AAMA,SAAgB,EAAS,GAA+B;CACtD,OAAO,OAAO,CAAW;AAC3B;AAEA,SAAgB,EAAM,GAA0B;CAC9C,OAAO,OAAO,CAAI;AACpB"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/conduit",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Lightweight dependency injection container — singletons, transient instances, factories, and named scopes",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/helmuthdu/vielzeug",
|
|
8
|
+
"directory": "packages/conduit"
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22"
|
|
13
|
+
},
|
|
6
14
|
"files": [
|
|
7
15
|
"dist"
|
|
8
16
|
],
|
|
@@ -11,32 +19,30 @@
|
|
|
11
19
|
"types": "dist/index.d.ts",
|
|
12
20
|
"exports": {
|
|
13
21
|
".": {
|
|
14
|
-
"types": "./dist/index.d.ts",
|
|
15
22
|
"import": "./dist/index.js",
|
|
16
|
-
"require": "./dist/index.cjs"
|
|
23
|
+
"require": "./dist/index.cjs",
|
|
24
|
+
"types": "./dist/index.d.ts"
|
|
17
25
|
}
|
|
18
26
|
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org/"
|
|
31
|
+
},
|
|
19
32
|
"scripts": {
|
|
20
33
|
"build": "vite build && pnpm run build:bundle && pnpm run build:types",
|
|
34
|
+
"build:bundle": "vite build --config vite.bundle.config.ts",
|
|
21
35
|
"build:types": "tsc -p tsconfig.declarations.json",
|
|
22
|
-
"fix": "
|
|
23
|
-
"lint": "
|
|
36
|
+
"fix": "biome check --write src",
|
|
37
|
+
"lint": "biome ci src",
|
|
24
38
|
"prepublishOnly": "pnpm run build",
|
|
25
39
|
"preview": "vite preview",
|
|
26
|
-
"test": "vitest"
|
|
27
|
-
"build:bundle": "vite build --config vite.bundle.config.ts"
|
|
28
|
-
},
|
|
29
|
-
"publishConfig": {
|
|
30
|
-
"access": "public",
|
|
31
|
-
"registry": "https://registry.npmjs.org/"
|
|
32
|
-
},
|
|
33
|
-
"engines": {
|
|
34
|
-
"node": ">=22"
|
|
40
|
+
"test": "vitest"
|
|
35
41
|
},
|
|
36
42
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^26.
|
|
43
|
+
"@types/node": "^26.2.0",
|
|
38
44
|
"typescript": "^6.0.3",
|
|
39
|
-
"vite": "^8.2.
|
|
45
|
+
"vite": "^8.2.1",
|
|
40
46
|
"vitest": "^4.1.10"
|
|
41
47
|
}
|
|
42
48
|
}
|