@strav/kernel
Advanced tools
+1
-1
| { | ||
| "name": "@strav/kernel", | ||
| "version": "1.0.0-alpha.31", | ||
| "version": "1.0.0-alpha.33", | ||
| "description": "Strav kernel — IoC container, service providers, lifecycle, config, events, helpers", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -26,3 +26,3 @@ /** | ||
| import { Application } from '../core/application.ts' | ||
| import type { ServiceProvider } from '../core/service_provider.ts' | ||
| import type { ServiceProviderEntry } from '../core/service_provider.ts' | ||
| import { asStravError } from '../exceptions/as_strav_error.ts' | ||
@@ -43,3 +43,3 @@ import { ConfigError } from '../exceptions/config_error.ts' | ||
| /** Providers to register before boot. Ignored when `app` is already booted. */ | ||
| providers?: readonly ServiceProvider[] | ||
| providers?: readonly ServiceProviderEntry[] | ||
| /** Commands to register. */ | ||
@@ -46,0 +46,0 @@ commands?: readonly CommandClass[] |
+38
-11
@@ -16,3 +16,3 @@ /** | ||
| import { Container } from './container.ts' | ||
| import type { ServiceProvider } from './service_provider.ts' | ||
| import { ServiceProvider, type ServiceProviderEntry } from './service_provider.ts' | ||
| import type { Constructor } from './types.ts' | ||
@@ -35,2 +35,23 @@ | ||
| async function resolveProviderEntry(entry: ServiceProviderEntry): Promise<ServiceProvider> { | ||
| if (entry instanceof ServiceProvider) return entry | ||
| if (typeof entry === 'function') { | ||
| // Constructor: a class whose prototype derives from ServiceProvider. | ||
| if (entry.prototype instanceof ServiceProvider) { | ||
| return new (entry as new () => ServiceProvider)() | ||
| } | ||
| // Closure: returns (or resolves to) a ServiceProvider instance. | ||
| const result = await (entry as () => ServiceProvider | Promise<ServiceProvider>)() | ||
| if (!(result instanceof ServiceProvider)) { | ||
| throw new Error( | ||
| 'Application: provider closure must return a ServiceProvider instance.', | ||
| ) | ||
| } | ||
| return result | ||
| } | ||
| throw new Error( | ||
| 'Application: provider entry must be a ServiceProvider, a constructor, or a closure.', | ||
| ) | ||
| } | ||
| export class Application extends Container { | ||
@@ -57,3 +78,3 @@ /** | ||
| private _providers: ServiceProvider[] = [] | ||
| private _providerEntries: ServiceProviderEntry[] = [] | ||
| private _bootedProviders: ServiceProvider[] = [] | ||
@@ -78,10 +99,12 @@ private _booted = false | ||
| /** Add one provider. Must be called before `start()`. */ | ||
| use(provider: ServiceProvider): this { | ||
| /** | ||
| * Add one provider. Must be called before `start()`. Accepts an instance, | ||
| * a zero-arg constructor, or a closure that builds (and optionally awaits) | ||
| * the instance. | ||
| */ | ||
| use(entry: ServiceProviderEntry): this { | ||
| if (this._booted) { | ||
| throw new Error( | ||
| `Application: cannot add provider "${provider.name}" after the application has started.`, | ||
| ) | ||
| throw new Error('Application: cannot add provider after the application has started.') | ||
| } | ||
| this._providers.push(provider) | ||
| this._providerEntries.push(entry) | ||
| return this | ||
@@ -91,7 +114,7 @@ } | ||
| /** Add several providers at once. Must be called before `start()`. */ | ||
| useProviders(providers: ServiceProvider[]): this { | ||
| useProviders(entries: ServiceProviderEntry[]): this { | ||
| if (this._booted) { | ||
| throw new Error('Application: cannot add providers after the application has started.') | ||
| } | ||
| this._providers.push(...providers) | ||
| this._providerEntries.push(...entries) | ||
| return this | ||
@@ -129,3 +152,7 @@ } | ||
| const sorted = this.topologicalSort(this._providers) | ||
| const resolved: ServiceProvider[] = [] | ||
| for (const entry of this._providerEntries) { | ||
| resolved.push(await resolveProviderEntry(entry)) | ||
| } | ||
| const sorted = this.topologicalSort(resolved) | ||
@@ -132,0 +159,0 @@ // Phase 1 — synchronous register pass. |
@@ -6,3 +6,3 @@ // Core kernel exports: Application, Container, ServiceProvider, inject, types. | ||
| export { getParamTypes, INJECTABLE, inject, isInjectable } from './inject.ts' | ||
| export { ServiceProvider } from './service_provider.ts' | ||
| export { ServiceProvider, type ServiceProviderEntry } from './service_provider.ts' | ||
| export type { | ||
@@ -9,0 +9,0 @@ Binding, |
@@ -33,2 +33,17 @@ /** | ||
| /** | ||
| * Anything `Application.use(...)` / `useProviders([...])` accepts: | ||
| * - a `ServiceProvider` instance (`new HttpProvider()`) | ||
| * - a zero-arg constructor (`HttpProvider`) | ||
| * - a closure that builds (and optionally awaits) the instance | ||
| * (`() => ConfigProvider.fromDirectory('config')`) | ||
| * | ||
| * Constructors and closures are resolved during `start()`, before the | ||
| * topological sort. | ||
| */ | ||
| export type ServiceProviderEntry = | ||
| | ServiceProvider | ||
| | (new () => ServiceProvider) | ||
| | (() => ServiceProvider | Promise<ServiceProvider>) | ||
| export abstract class ServiceProvider { | ||
@@ -35,0 +50,0 @@ /** Unique name used for dependency resolution between providers. */ |
143587
1.19%3509
1.15%