type Func$1 = (...args: Args) => Return; interface Ctor$1 { new(...args: Args): Instance; prototype: Instance; } /** * A stable string identifying an interface — the DI key. * * No branding, no literal types. Generated by the transformer from a TypeScript * type at compile time; referenced by hand when using the manual authoring surfaces. */ type Token = string; /** * Marks a constructor parameter to be injected as a *factory* producing the * registered type token, rather than a resolved instance. The factory's own * call signature is determined by the caller-supplied `params` list. * * `type` is the token of the produced type T (replaces the former `.factory` field). * `params` is the complete, authored-order list of caller-supplied parameter tokens; * when present it pins the factory shape so it no longer drifts with registration state. */ interface FactoryRef { readonly type: Token; readonly params?: readonly Token[]; } /** * Marks a parameter to be injected with the live resolution scope itself, * rather than a resolved token. Emitted for a factory parameter whose type is * `ResolveScope` — the engine fills the slot with the scope the factory is * resolved into, so the factory body can `scope.resolve(...)` / `createScope` * dynamically. Only meaningful on registration-level factory functions (a class * ctor never receives the scope); on a ctor it would simply never match. */ interface ScopeRef { readonly scope: true; } /** * A set of alternative dependency slots tried in declaration order (first * resolvable member wins). If no member is resolvable, resolution throws. * Each member is itself a `DepSlot` — nesting is allowed. */ interface Union { readonly union: readonly DepSlot[]; } /** * A SINGULAR (non-union) type that supplies its value directly — no container * lookup. Emitted for: * - a non-union literal param (`"dev"`, `42`, `true`, `1n`) → its value, and * - a whole-type `void` / `undefined` → `undefined`; a whole-type `null` → * `null` (a singleton type has exactly one inhabitant, so it is supplied * directly, NOT tokenized — Rule 2). * The engine injects `value` verbatim. A LITERAL/typed UNION (`"a" | "b"`, * `Foo | undefined`) is NOT a `LiteralRef`: a literal union stays a resolved * token, and a nullish union is stripped by the optional/overload path. Always * satisfiable — the value is self-supplying. * * NOTE: `value` may legitimately be `undefined` (the `void`/`undefined` case), * so a `LiteralRef` is identified by the PRESENCE of the `value` key, never by * `value !== undefined`. See `isLiteralRef`. */ interface LiteralRef { readonly value: string | number | boolean | bigint | undefined | null; } /** * Marks a parameter to be injected with the TOKEN STRING of one of the * registration's type arguments — the `typeof(T)` analog for open-generic * templates. `typeArg` is the 1-based hole number (`{ typeArg: 1 }` names the * argument bound to `$1`). At close time, substitution replaces the slot with * a `LiteralRef` carrying the substituted argument's token string; a raw * (unsubstituted) `TypeArgRef` reaching resolution is an error. */ interface TypeArgRef { readonly typeArg: number; } /** * One positional slot in a constructor / factory signature: * - a `Token` string — a container-resolved dependency, * - a `FactoryRef` — a factory-injected parameter (see `FactoryRef`), * - a `ScopeRef` — the live resolution scope (see `ScopeRef`), * - a `Union` — member-level alternatives tried in order, * - a `LiteralRef` — a singular literal supplying its value directly, or * - a `TypeArgRef` — the token string of a type argument (see `TypeArgRef`). */ type DepSlot = Token | FactoryRef | ScopeRef | Union | LiteralRef | TypeArgRef; /** * Per-constructor dependency metadata carried on a registration. * * `signatures` is an array of arrays: each element is one constructor signature * (for overload support). `signatures[i][j]` is the `DepSlot` — a token, a * `FactoryRef`, a `ScopeRef`, a `Union`, or a `LiteralRef` — for constructor * parameter `j` of overload `i`. */ interface DepRecord { readonly signatures: readonly (readonly DepSlot[])[]; } /** * The result of parsing a closed-generic token `base` into its base * and top-level args. A pure data shape (the parse routine that produces it is a * runtime helper that lives in `@fnioc/di`); kept here so the type surface a * consumer references stays in the types-only substrate. */ interface ParsedToken { readonly base: Token; readonly args: readonly Token[]; } /** * Compile-time phantom brand that pins a specific token for one constructor or * factory parameter, overriding the token the transformer would normally derive. * * The value type stays `T` — a plain `T` is assignable because the brand * property is optional. Zero runtime footprint. * * @example * ```ts * class Handler { * constructor( * cache: Inject, // pinned token * log: ILogger, // derived normally * ) {} * } * ``` */ declare const TOK: unique symbol; type Inject = T & { readonly [TOK]?: K; }; /** * Compile-time skolem standing in for the `N`th type argument of an open * template (1-based). Writing `add>>(SqlRepository<$<1>>)` binds * the hole; the transformer derives `$N` wherever a Hole-branded type appears. * * `C` is the constraint carrier: `Hole<1, Entity>` IS an `Entity` (the brand * property is optional, so the intersection stays assignable to `C`), which * lets a constrained implementation `class Repo` accept a * hole as its type argument. Zero runtime footprint. */ declare const HOLE: unique symbol; type Hole = C & { readonly [HOLE]?: N; }; /** * Unbounded sugar for the common unconstrained hole: `$<1>`, `$<2>`, … `$`. * `$` is exactly `Hole`; reach for `Hole` when the impl's type * parameter carries a constraint the skolem must satisfy. */ type $ = Hole; /** * Compile-time phantom brand marking a constructor parameter that receives the * TOKEN STRING of type argument `T` — the `typeof(T)` analog (hence the name). * The value type stays `Token` (a plain string is assignable; the brand * property is optional). * * `Typeof` is type-driven: the transformer infers the hole from `T`. The * manual counterpart `typeArg(n)` is positional — a plugin-less author names * the hole by number. * * When `T` is a Hole, the transformer emits an open `{ typeArg: N }` slot that * substitution closes per registration; when `T` is concrete, it emits the * derived token directly as a literal value slot. Zero runtime footprint. * * @example * ```ts * class SqlRepository { * constructor(readonly entityToken: Typeof) {} * } * ``` */ declare const ARG: unique symbol; type Typeof = Token & { readonly [ARG]?: T; }; /** * Capitalize the first character of a string literal type, leaving the rest * untouched (`"request"` → `"Request"`). Used to mint a per-scope method name * `add${ProperCase}` from a scope tag `K`. Because every scope tag is * guarded lowercase-first (`ValidScopes`), this map is INJECTIVE — two distinct * tags never collide on one minted name. */ type ProperCase = T extends `${infer H}${infer R}` ? `${Uppercase}${R}` : T; /** * EMPTY carrier interface the `@fnioc/transformer` augments with the AUTHORED * single-arg call signatures for a per-scope `add${ProperCase}` method * (`addRequest(C)` / `addRequest(fn)`). Like the other authoring forms, those * signatures are PURE TYPINGS contributed only when the transformer is in the * program — without it, a per-scope method exposes just the runtime two-arg * `(token, ctor) => void` shape. `S` is the full scope union, `K` the specific * scope this method tags with. */ interface ScopeAddAuthoring { } /** * The per-scope registration methods minted from the scope union `S`. For each * tag `K`, a method named `add${ProperCase}` whose runtime shape is * `(token, ctor) => void` (≡ `add(token, ctor).as(K)`), intersected with the * transformer-contributed `ScopeAddAuthoring` authored single-arg forms. * The scope is baked into the name, so there is no `.as()` continuation — the * methods return `void`. */ type ScopeAddMethods = { [K in S as `add${ProperCase}`]: ((token: Token, ctor: Ctor$1) => void) & ScopeAddAuthoring; }; /** * The scope-union guard. A `ServiceManifest` is only well-formed when every member * of `S` can mint a usable, non-colliding `add${ProperCase}` method. `S` * resolves to itself when valid, else to `never` — which makes * `new ServiceManifest()` a compile error at the construction site. * * Two rules, both checked NON-distributively (`[S] extends [...]`) so a union is * judged as a whole rather than member-by-member: * - lowercase-first: every member must satisfy `K extends Uncapitalize`. * This makes `ProperCase` injective (no two tags collapse onto one method * name) and keeps the transformer's uncapitalize-first scope recovery exact. * - no collision: a member may not be `""` | `"factory"` | `"value"`, which * would mint `add` / `addFactory` / `addValue` — the existing methods. */ type ValidScopes = [S] extends [Uncapitalize] ? [S & ("" | "factory" | "value")] extends [never] ? S : never : never; /** * The continuation returned by a class `ServiceManifest.add`. Carries the just-added * registration so `.as()` can attach its lifetime in place. An `.add()` with no * trailing `.as()` leaves the registration scopeless ⇒ transient. * * `Scopes` is threaded so `.as()` only accepts a declared scope name — * compile-time guard at the registration site. */ interface AddBuilder { /** * Attaches the lifetime — the RUNTIME (lowered) form. Must name a declared * scope. * * `.as("singleton")` is what the engine executes: the transformer rewrites the * authored type-arg form (`.as<"singleton">()`) to this value-arg form before * runtime, and a plugin-less caller writes it directly. The AUTHORED type-arg * form (`.as(): void`) is a PURE TYPING contributed by the * `@fnioc/transformer` augmentation — it is not part of di's published surface, * so it only type-checks when the transformer's types are in the program. */ as(scope: Scopes): void; } /** * A construction-site guard parameter that carries the `ValidScopes` verdict. * When `S` is a valid scope union, `ValidScopes` resolves to `S` (not * `never`), so the guard is an EMPTY rest tuple — `new ServiceManifest()` takes no * args. When `S` is invalid, `ValidScopes` collapses to `never`, and the * guard becomes a REQUIRED arg whose name spells out the error, so the no-arg * `new ServiceManifest()` fails to type-check at the construction site. * * This expresses the same intent as a self-referential `S extends ValidScopes` * constraint, which TypeScript rejects as circular (TS2313) and which silently * stops validating — the guard-param form is the working equivalent. */ type ScopeGuard = [ValidScopes] extends [never] ? [ error: "invalid ServiceManifest scope tag: every member must be lowercase-first and not \"\" / \"factory\" / \"value\"" ] : []; /** * The AUTHORING INTERFACE for the registration collection — the base surface a * lib author types a setup function against, and the interface `@fnioc/di`'s * `ServiceManifestClass` implements. It names the three runtime registration * methods (`add` / `addFactory` / `addValue`) plus `build`. * * `Provider` is the type `build()` returns. A core-only lib author never calls * `build()` (the application does), so it defaults to `unknown`; `@fnioc/di` * binds it to the concrete `ServiceProvider` when its class implements * this interface. Keeping it generic is what lets this interface live in the * types-only substrate without referencing di's runtime provider type. */ interface ServiceManifestBase { /** * Class registration — a string token bound to a concrete constructor. The * optional third `signatures` arg carries the positional dep signatures ON the * registration (a lib author authors them as plain `DepSlot` data literals). */ add(token: Token, ctor: Ctor$1, signatures?: readonly (readonly DepSlot[])[]): AddBuilder; /** * Factory registration — a string token bound to a factory function, its call * parameters injected by the optional third `signatures` arg. */ addFactory(token: Token, factory: Func$1, signatures?: readonly (readonly DepSlot[])[]): AddBuilder; /** Value registration — an already-built instance, no deps and no lifetime. */ addValue(token: Token, value: unknown): void; /** Seals the collection and returns the built provider. */ build(): Provider; } type Func = (...args: Args) => Return; interface Ctor { new(...args: Args): Instance; prototype: Instance; } /** * A registration-level factory function. Its parameters are filled by the * engine at resolve time, the same way a class constructor's are: a factory * WITH registration-carried signatures has each parameter resolved by its slot * (token → resolved instance, `ScopeRef` → the live provider, hole → * caller-supplied); a factory WITHOUT signatures is the plugin-less escape hatch * and is called with the live provider as its single argument (`(sp) => …`). * * May be async — it can return a `Promise`. The container never awaits; the * Promise flows through the sync resolution channel as a value (§"Async as * values"). A consumer that depends on it declares `Promise` and awaits. */ type Factory = Func; /** A class registration: a token bound to a concrete constructor. */ interface ClassRegistration { readonly kind: "class"; readonly ctor: Ctor; /** * The lifetime — the scope name that owns and caches the instance. * `undefined` means transient (never cached; a fresh instance per resolve). */ readonly scope: string | undefined; /** * Registration-carried dep signatures — the sole signature channel now that * the global metadata store is retired. Emitted inline by the transformer * (`add(token, ctor, [[...]])`) and hand-fed by the plugin-less caller. A * signature-less class with a nonzero-arg ctor throws `MissingMetadataError`; * a zero-arg ctor builds via `new Ctor()`. */ readonly signatures?: readonly (readonly DepSlot[])[]; } /** A factory-function registration — its params are injected like a ctor's. */ interface FactoryRegistration { readonly kind: "factory"; readonly factory: Factory; /** * The lifetime — the scope name that owns and caches the result. `undefined` * means transient (the factory runs on every resolve). Attached via `.as()`, * exactly like a class registration. */ readonly scope: string | undefined; /** * Registration-carried dep signatures for the factory's call parameters. * Emitted inline by the transformer (`addFactory(token, fn, [[...]])`); a * record-less factory (the plugin-less escape hatch) carries none and is * called with the live provider as its sole argument. */ readonly signatures?: readonly (readonly DepSlot[])[]; } /** A value registration — an already-built instance, no lifetime. */ interface ValueRegistration { readonly kind: "value"; readonly useValue: unknown; } /** Any registration the engine can resolve. */ type Registration = ClassRegistration | FactoryRegistration | ValueRegistration; /** * An OPEN registration — a class bound to an open template token whose type * arguments are all holes (`pkg:IRepo<$1>`). It never resolves directly; * resolving a closed token that misses the exact map matches against these * (base + arity + repeated-hole equality, last registered wins), substitutes * the closing's arg tokens through the carried signatures, and synthesizes an * ordinary `ClassRegistration` memoized per closed token. */ interface OpenRegistration { /** The full template token as registered (`pkg:IRepo<$1>`). */ readonly template: Token; /** The template's base (`pkg:IRepo`) — the open-table key. */ readonly base: Token; /** * The parsed top-level args of the template — each exactly a hole (`$N`). * Length is the arity; repeated holes (`["$1","$1"]`) constrain a match to * equal arg tokens. */ readonly pattern: readonly Token[]; readonly ctor: Ctor; /** The lifetime tag, applied per closing. `undefined` means transient. */ readonly scope: string | undefined; /** * The template dep signatures (holes and `TypeArgRef`s still open) — * substituted per closing. When absent, the closing has no template to * substitute (a zero-arg ctor closes to a bare `new Ctor()`). */ readonly signatures?: readonly (readonly DepSlot[])[]; } /** * The named lifetime tag for a registration. `"singleton"` and `"transient"` * are the built-in names; `U` is the user-declared scope-name union (defaults * to `"scoped"`). Transient is represented by the ABSENCE of a lifetime tag * (`undefined` on the registration), not by the string `"transient"`. */ type Lifetime = "singleton" | "transient" | U; /** * The minimal resolution surface — resolve tokens and get factories. Injected * into factory parameters typed `Resolver` (and for the plugin-less escape * hatch as the sole argument of a record-less factory). * * `resolve` has two published shapes (the tokenless authoring form * `resolve()` is a PURE TYPING contributed by the `@fnioc/transformer` * augmentation, not part of di's published surface): * - `resolve(token)` — explicit token, typed return. * - `resolve(token)` — explicit token, `unknown` return (dynamic). */ interface Resolver { resolve(token: Token): T; resolve(token: Token): unknown; /** * Resolves asynchronously — the only path that may satisfy `T` via a * `Promise` registration. Always returns a Promise; a lookup miss whose * honest `Promise` registration exists is awaited and delivers `T`. */ resolveAsync(token: Token): Promise; resolveAsync(token: Token): Promise; /** * Returns a FACTORY for `type` rather than an instance. When `params` is * absent or empty, returns a strict zero-arg `() => T` — every ctor slot must * resolve from the container. When `params` is present, it is the complete * authored-order list of caller-supplied parameter tokens; the returned factory * has shape `(...params) => T`. The authored `resolve<(a: A) => T>()` lowers * to `resolveFactory("pkg:T", ["pkg:A"])`. */ resolveFactory(type: Token, params?: readonly Token[]): unknown; } /** * The scope-creation surface. Injected into factory parameters typed * `ScopeFactory`, and implemented by `ServiceProvider`. */ interface ScopeFactory { createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider; } /** * @deprecated Use `Resolver` instead. Kept for backwards compatibility. * * The resolution surface a factory receives — either as an injected `ScopeRef` * parameter, or (plugin-less escape hatch) as the sole argument of a * record-less factory. */ interface ResolveScope extends Resolver { createScope(name: string): ServiceProvider; } /** * A scope frame — a node in the parent-linked chain. Holds this scope's name, * its instance cache, an ordered list for disposal, and an optional parent. * It does NOT hold registrations (those live sealed on the ServiceProvider). * * A `ServiceProvider` with "no frame" resolves everything transiently — a * tagged registration whose frame is not open resolves to a fresh instance, * exactly like an untagged (transient) one. Frames are opened with * `createScope(name)`, never auto-created. */ declare class Scope { /** This scope's name — must match the registration's lifetime tag. */ readonly name: string; /** The parent scope, or omitted for the topmost frame. */ readonly parent?: Scope | undefined; /** Instances this scope owns and caches, keyed by token. */ readonly cache: Map; /** Owned instances in construction order — disposed in reverse. */ readonly owned: unknown[]; constructor( /** This scope's name — must match the registration's lifetime tag. */ name: string, /** The parent scope, or omitted for the topmost frame. */ parent?: Scope | undefined); } /** * The public container surface. Implements `Resolver` (resolve + resolveFactory) * and `ScopeFactory` (createScope), plus native `Disposable`/`AsyncDisposable`. * * `S` is the user-declared scope-name union. The provider `ServiceManifest.build()` * returns is FRAMELESS — there is no root scope. With no frame open, every * resolution is transient; opening a scope with `createScope(name)` is what * lets a registration tagged with that name cache. "singleton" is not special — * it is just a tag you typically open once at the top via * `createScope("singleton")`. */ declare class ServiceProvider implements Resolver, ScopeFactory, Disposable, AsyncDisposable { #private; constructor(registrations: ReadonlyMap, openRegistrations: ReadonlyMap, closedMemo: Map, /** This provider's scope frame, if any. */ frame?: Scope); /** * The name of this provider's open scope frame. Throws if the provider is * frameless (no scope open — e.g. the provider straight from `build()`). */ get name(): S; /** * Creates a child `ServiceProvider` whose scope frame is a new `Scope` named * `name`, parented to this provider's frame (or a top-level frame if this * provider is unscoped). * * Default name `"scoped"` is accepted only when `"scoped"` ∈ S (the * conditional-rest-param type ensures this at the call site). */ createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider; /** * Resolves synchronously. Runs the spine in sync mode — async never enters * (the `Promise` fallback is gated off), so a miss is the honest * `UnregisteredTokenError`. A cached in-flight async construction throws * `AsyncResolutionRequiredError` (the guard here is defensive; sync mode * provably never RETURNS a Pending — a cached one throws inside the spine). * The public entry point starts a fresh cycle-detection stack. */ resolve(token: Token): T; resolve(token: Token): unknown; /** * Resolves asynchronously. Same spine, async mode: a lookup miss may be * satisfied by an honest `Promise` registration. Always returns a Promise; * the Pending carrier never escapes. (`async` keyword: resolution errors * surface as rejections, the natural channel for a Promise-returning API.) */ resolveAsync(token: Token): Promise; resolveAsync(token: Token): Promise; /** * Returns a FACTORY for `type` rather than an instance. When `params` is * absent or empty, returns a strict zero-arg `() => T` — every ctor slot must * resolve from the container (an unresolvable slot throws). When `params` is * present, it is the complete authored-order list of caller-supplied parameter * tokens; the returned factory has shape `(...params) => T`. The authored * `resolve<(a: A) => T>()` lowers to `resolveFactory("pkg:T", ["pkg:A"])`. */ resolveFactory(type: Token, params?: readonly Token[]): unknown; /** * Closes this provider synchronously, disposing the instances its scope frame * owns in REVERSE construction order. Only native `Disposable` instances are * disposed. NO cascade to child scopes. * * Throws `AsyncDisposalRequiredError` if any owned instance is a Promise * (thenable) — a pending Promise cannot be disposed synchronously; the caller * must use `disposeAsync()`. Idempotent: a second call is a no-op. */ dispose(): void; /** * Closes this provider asynchronously. Awaits each owned Promise-valued * instance first (so an async factory's result settles before teardown), then * disposes owned instances in REVERSE construction order — honoring both * `Symbol.asyncDispose` and `Symbol.dispose`. Idempotent. */ disposeAsync(): Promise; /** Native `using` support — delegates to `dispose()`. */ [Symbol.dispose](): void; /** Native `await using` support — delegates to `disposeAsync()`. */ [Symbol.asyncDispose](): Promise; } /** * The registration builder. * * `Scopes` is the union of declarable scope names — the tags `.as()` and * `.createScope()` accept (default `"singleton"`). There is no root: scopes are * uniform tags, and `"singleton"` is just a tag you happen to open once at the * top. `"transient"` is NOT a member — transient is the absence of a scope, not * a scope. A registration whose tagged scope is not open at resolution time * resolves transiently (fresh instance, no cache). * * @example * ```ts * const services = new ServiceManifest<"singleton" | "request">(); * services.add("pkg:ILogger", ConsoleLogger).as("singleton"); // lowered form * const provider = services.build(); // no frame pre-opened * const app = provider.createScope("singleton"); // open the singleton frame * const logger = app.resolve("pkg:ILogger"); * const req = app.createScope("request"); // nested child scope * ``` * * NOTE: this is the IMPLEMENTATION class. The public `ServiceManifest` value + type * (exported below) wrap it so the per-scope `add${ProperCase}` methods — * which a class declaration cannot express as mapped members — surface on the * type. The class stays exported so the `@fnioc/transformer` `declare module` * augmentation can merge its authored typings onto `interface ServiceManifestClass`. */ declare class ServiceManifestClass implements ServiceManifestBase> { #private; constructor(); /** * Class registration — a string token bound to a concrete constructor. The * runtime form: what the transformer emits for a class, and what a * plugin-less caller writes directly. Returns the `.as(scope?)` continuation. * * The optional third `signatures` param carries the dep signatures ON the * registration record — the sole signature channel now that the global * metadata store is retired. The transformer emits it inline for every * constructed class (`add(token, ctor, [[...]])`); a plugin-less caller * hand-feeds it directly. Keying signatures on the registration (not on the * ctor object) is what lets one JS class close differently per registration — * an open template and its closings never collide. * * An OPEN template token (`pkg:IRepo<$1>` — every type arg a hole) routes * into the open-registration table instead of the exact map; resolution * closes it per requested token. Mixing concrete args and holes in the * service token throws (v1 all-holes rule). */ add(token: Token, ctor: Ctor, signatures?: readonly (readonly DepSlot[])[]): AddBuilder; /** * Factory registration — a string token bound to a factory function. The * runtime form the transformer emits for an authored `add(fn)` / * `addFactory(fn)`, and what a plugin-less caller writes directly. * * Parameter injection follows the metadata rule (see `ServiceProvider`): a * factory WITH registration-carried signatures (the optional third arg, emitted * inline by the transformer) has each parameter injected by its slot; a * signature-less factory (the plugin-less escape hatch) is called with the live * provider — type it `(sp: Resolver) => T` and `sp.resolve(...)` its own deps. * Returns the `.as(scope?)` continuation so a factory caches at a named scope * exactly like a class. * * The implementation signature admits the single-arg authoring form * (`addFactory(fn)`) so the `@fnioc/transformer` overload merges onto it — * that form never runs post-transform, and the runtime guard below fails a * plugin-less call loud rather than registering junk (mirrors `add`). */ addFactory(token: Token, factory: Factory, signatures?: readonly (readonly DepSlot[])[]): AddBuilder; /** * Value registration — an already-built instance, no deps and no lifetime. * Separate from `add` because a value may itself be a function (a callable * service), which is structurally indistinguishable from a factory inside one * overload. The authoring form `addValue(v)` (which lowers to * `addValue("token", v)`) is a PURE TYPING contributed by the * `@fnioc/transformer` augmentation, not part of di's published surface. */ addValue(token: Token, value: unknown): void; /** * Builds the ServiceProvider with a SEALED copy of the registration map. * Sealing (deep-freezing the map and each per-token list) ensures that any * `.add()` call on the builder after `build()` cannot mutate what the * provider and its descendants see — the container's view is fixed at * construction time. * * NO frame is pre-opened: the returned provider is frameless. There is no * root scope — resolving a tagged registration with no matching frame open * yields a transient instance, and an untagged registration is transient as * always. Open a scope explicitly with `createScope(name)` when you want a * tagged registration to cache. */ build(): ServiceProvider; } /** * The static / constructor side of the public `ServiceManifest`. Extracted as an * interface so the value export can carry the `ValidScopes` guard on its type * parameter: `new ServiceManifest()` only type-checks when `S` is a valid scope * union (lowercase-first, no collision with `add`/`addFactory`/`addValue`). It * returns di's provider-bound `ServiceManifest`. */ interface ServiceManifestCtor { new (...guard: ScopeGuard): ServiceManifest; } /** * The public registration-builder TYPE for di consumers: the implementation * class intersected with the per-scope methods minted from `S`. A type alias * (not an interface) because an interface cannot extend a generic MAPPED type, * and `ScopeAddMethods` is one. * * The `ServiceManifestClass` arm (not core's `ServiceManifestBase`) is * deliberate: it carries di's concrete `build(): ServiceProvider` AND is the * interface the `@fnioc/transformer` augmentation merges its authored `add()` * forms onto, so the alias picks those up through this arm. `ScopeAddMethods` * comes from the pure-types `@fnioc/core`. (core's own `ServiceManifest` alias is * the provider-agnostic LIBRARY-AUTHOR view — no di class, no transformer forms.) */ type ServiceManifest = ServiceManifestClass & ScopeAddMethods; /** * The public registration-builder VALUE. It IS `ServiceManifestClass` at runtime (the * cast only re-types its construct signature to carry the `ValidScopes` guard * and the per-scope method surface). `new ServiceManifest<...>()` behaves identically; * the wrapper exists purely so the mapped per-scope methods type-check. */ declare const ServiceManifest: ServiceManifestCtor; /** Base class for every error the container raises. */ declare class DiError extends Error { constructor(message: string); } /** * A token was requested but no registration exists for it anywhere in the * resolving scope's chain (nor on the builder's base map). */ declare class UnregisteredTokenError extends DiError { readonly token: Token; constructor(token: Token); } /** * A constructor with parameters carries no dep signature on its registration — * the transformer never saw it and no signature was hand-fed. */ declare class MissingMetadataError extends DiError { readonly token: Token; readonly ctorName: string; constructor(token: Token, ctorName: string); } /** * A constructor has registration-carried signatures, but none of them is * directly satisfiable in the owning scope (every signature names at least one token * that is not registered, or contains a hole this phase cannot fill). */ declare class NoSatisfiableSignatureError extends DiError { readonly token: Token; readonly ctorName: string; readonly unsatisfiable: readonly Token[]; constructor(token: Token, ctorName: string, unsatisfiable: readonly Token[]); } /** * A token reappeared on the active resolution stack — the dependency graph has * a cycle. The message includes the full path that closed the loop. */ declare class CircularDependencyError extends DiError { readonly path: readonly Token[]; constructor(path: readonly Token[]); } /** * A constructor parameter is typed as a factory of some token (a `FactoryRef`), * but that token cannot be turned into a factory: either it is not registered, * or it is registered as a `useValue` / `useFactory` override rather than a * class. A factory injects a callable that constructs the target class on * demand, so the target must be a class registration. */ declare class FactoryTargetError extends DiError { readonly factoryToken: Token; readonly reason: "unregistered" | "not-a-class"; constructor(factoryToken: Token, reason: "unregistered" | "not-a-class"); } /** * A `Union` slot was encountered during resolution but none of its member slots * was resolvable. Resolution cannot proceed without at least one registered member. */ declare class NoSatisfiableUnionError extends DiError { readonly members: readonly DepSlot[]; constructor(members: readonly DepSlot[]); } /** * A token that still contains holes (`$N`) was resolved. An open template is * not a resolvable token — it names a FAMILY of tokens, one per closing. The * caller must close it first (substitute every hole with a concrete arg token). */ declare class OpenTokenResolutionError extends DiError { readonly token: Token; constructor(token: Token); } /** * An open template token was passed to a registration method that cannot * accept one: `addValue`/`addFactory` (open registrations are class-only), or * `add` with a template whose type arguments are not ALL holes (v1 forbids * mixing concrete args and holes in the service token). */ declare class OpenTokenRegistrationError extends DiError { readonly token: Token; readonly method: "add" | "addFactory" | "addValue"; constructor(token: Token, method: "add" | "addFactory" | "addValue"); } /** * Sync `dispose()` was called on a scope that owns a Promise-valued (thenable) * cached instance. A pending Promise cannot be disposed synchronously — the * caller must use `disposeAsync()`. */ declare class AsyncDisposalRequiredError extends DiError { constructor(); } /** * Sync `resolve()` met an async result: a cached in-flight async construction * (a concurrent `resolveAsync` is mid-build). The instance cannot be produced * synchronously — use `resolveAsync()`. */ declare class AsyncResolutionRequiredError extends DiError { readonly token: Token; constructor(token: Token); } /** * Constructs a `Union` slot — a set of alternative dependency slots tried in * declaration order. The first resolvable member wins; if none is resolvable, * resolution throws. * * @example * ```ts * services.add("pkg:IHandler", Handler, [[ * union("pkg:IRedis", "pkg:IMemoryCache"), * "pkg:ILogger", * ]]); * ``` */ declare function union(...slots: DepSlot[]): Union; /** * Constructs a `TypeArgRef` slot — a parameter that receives the TOKEN STRING * of the registration's `n`th type argument (1-based, matching `$n`). Used on * the manual authoring surface for hole-template signatures; substitution * closes it into a literal value slot per closing. * * @example * ```ts * services.add("app/IRepo<$1>", SqlRepository, [[typeArg(1), "app/IDb"]]); * ``` */ declare function typeArg(n: number): TypeArgRef; /** True when `slot` is a `FactoryRef` (carries a `.type` token). */ declare function isFactoryRef(slot: DepSlot): slot is FactoryRef; /** True when `slot` is a `ScopeRef` (the live-scope marker `{ scope: true }`). */ declare function isScopeRef(slot: DepSlot): slot is ScopeRef; /** True when `slot` is a `Union` (carries a `.union` array of member slots). */ declare function isUnionSlot(slot: DepSlot): slot is Union; /** * True when `slot` is a `LiteralRef` — an object slot carrying a `value` key. * The value supplies a singular literal directly (`"dev"`, `42`, `true`, `1n`) * OR the lone inhabitant of `void` / `undefined` / `null`. * * Identified by the PRESENCE of the `value` key (`"value" in slot`), never by * `value !== undefined` — `value` is legitimately `undefined` for the * `void`/`undefined` case. No other slot kind (FactoryRef `.type`, ScopeRef * `.scope`, Union `.union`) carries a `value` key, so this is unambiguous. */ declare function isLiteralRef(slot: DepSlot): slot is LiteralRef; /** * True when `slot` is a `TypeArgRef` — an object slot carrying a numeric * `typeArg` key (the 1-based hole number). Key-disjoint from every other slot * kind (FactoryRef `.type`, ScopeRef `.scope`, Union `.union`, LiteralRef * `.value`), so the check is unambiguous. */ declare function isTypeArgRef(slot: DepSlot): slot is TypeArgRef; /** * Renders the canonical closed-generic form `base`. With no args, * returns `base` unchanged. Args may themselves be closed-generic tokens * (nesting) or holes (`$N` — producing an open template). */ declare function closeToken(base: Token, ...args: Token[]): Token; /** * Parses a closed-generic token into its base and top-level args. * * Returns `undefined` for non-generic tokens (no top-level `<`) AND for * malformed input (empty base, unbalanced brackets, empty arg, trailing text * after the closing `>`, unterminated quote) — callers fall through to their * exact-match / unregistered-token handling either way. * * The scan is depth-tracked over `<` / `>` and quote-aware for double quotes * (backslash escapes honored), so literal-type args like `"a,b" | ""` split * correctly. */ declare function parseToken(token: Token): ParsedToken | undefined; /** * True when `token` contains a hole (`$N`) at any depth — i.e. it is an open * template rather than a resolvable token. Grammar-aware: a `$N` inside a * quoted literal arg is NOT a hole. */ declare function isOpenToken(token: Token): boolean; /** * Substitutes hole nodes in an open template with the supplied argument tokens * (1-based: `$1` → `args[0]`). Grammar-aware and recursive — a node that is * exactly `$N` is replaced by the arg token (which may itself be * closed-generic); this is NOT a naive string replace, so a `$N` inside a * quoted literal arg survives untouched. * * Throws `RangeError` when the template references a hole beyond the supplied * args — callers match arity before substituting. */ declare function substituteToken(template: Token, args: readonly Token[]): Token; /** * Substitutes the supplied argument tokens through every slot of every * signature, producing the closed signatures for one closing of an open * registration: * - a string token → `substituteToken`, * - a `FactoryRef` → `type` and each `params` token substituted, * - a `Union` → members substituted recursively, * - a `TypeArgRef` → a `LiteralRef` carrying `args[typeArg - 1]` (the * substituted argument's token string), * - a `LiteralRef` / `ScopeRef` → unchanged. */ declare function substituteSignatures(signatures: readonly (readonly DepSlot[])[], args: readonly Token[]): readonly (readonly DepSlot[])[]; export { AsyncDisposalRequiredError, AsyncResolutionRequiredError, CircularDependencyError, DiError, FactoryTargetError, MissingMetadataError, NoSatisfiableSignatureError, NoSatisfiableUnionError, OpenTokenRegistrationError, OpenTokenResolutionError, Scope, ServiceManifest, ServiceManifestClass, ServiceProvider, UnregisteredTokenError, closeToken, isFactoryRef, isLiteralRef, isOpenToken, isScopeRef, isTypeArgRef, isUnionSlot, parseToken, substituteSignatures, substituteToken, typeArg, union }; export type { $, AddBuilder, ClassRegistration, Ctor, DepRecord, DepSlot, Factory, FactoryRegistration, Hole, Inject, Lifetime, OpenRegistration, ParsedToken, ProperCase, Registration, ResolveScope, Resolver, ScopeAddAuthoring, ScopeAddMethods, ScopeFactory, ScopeGuard, ServiceManifestBase, ServiceManifestCtor, Token, TypeArgRef, Typeof, Union, ValidScopes, ValueRegistration };