/**
 * Provider system for Beignet
 *
 * Providers are modular extensions that can add new ports or replace existing ones
 * during application initialization. They support configuration via Standard Schema
 * and optional lifecycle hooks.
 */
import type { StandardSchemaV1 } from "@standard-schema/spec";
type ProviderPorts = Record<string, unknown>;
declare const noProvidedPorts: unique symbol;
type NoProvidedPorts = {
    [noProvidedPorts]?: never;
};
/**
 * Extract the output type from a Standard Schema.
 * This is the validated/parsed type that results from schema validation.
 */
export type InferOutput<T extends StandardSchemaV1> = StandardSchemaV1.InferOutput<T>;
/**
 * Configuration definition for a service provider.
 * Specifies the schema for validating config and optional environment variable prefix.
 */
export interface ProviderConfigDef<CfgSchema extends StandardSchemaV1> {
    /**
     * Standard Schema for validating provider configuration.
     * Can be Zod, Valibot, ArkType, or any Standard Schema compatible library.
     */
    schema: CfgSchema;
    /**
     * Optional prefix to read env vars, e.g. "REDIS_".
     * When provided, the implementation will read process.env keys starting with this prefix
     * and pass them to the schema for validation.
     */
    envPrefix?: string;
    /**
     * Field-level config overrides, keyed by schema field name. Defined values
     * are merged over the env-derived (or server-supplied) input before
     * validation, so factory options win over environment variables and still
     * satisfy required fields when the env var is absent. `undefined` values
     * are ignored.
     */
    overrides?: Record<string, unknown>;
}
/**
 * Value or promise of that value.
 */
export type MaybePromise<T> = T | Promise<T>;
/**
 * Late-bound service context factory exposed to providers.
 *
 * Calling it before all providers have started throws, so providers should
 * only invoke it from runtime entrypoints such as job dispatch, listeners, or
 * scheduled work.
 *
 * App-local providers can type the factory by declaring `Context` and
 * `ServiceInput` through the curried `createProvider<Requires, Context,
 * ServiceInput>()` form. Untyped providers see `(input: void) =>
 * Promise<unknown>`.
 */
export type ProviderServiceContextFactory<Context = unknown, ServiceInput = void> = (input: ServiceInput) => Promise<Context>;
/**
 * Context passed to provider lifecycle hooks.
 */
export type ProviderLifecycleContext<Ports = ProviderPorts, Context = unknown, ServiceInput = void> = {
    /**
     * Final app ports after provider setup.
     */
    ports: Readonly<Ports>;
    /**
     * Build an app service context through the server context blueprint.
     */
    createServiceContext: ProviderServiceContextFactory<Context, ServiceInput>;
};
/**
 * Result returned from provider setup.
 */
export type ProviderSetupResult<ProvidedPorts extends ProviderPorts, Ports = ProviderPorts, Context = unknown, ServiceInput = void> = {
    /**
     * Ports contributed by this provider.
     * Keys overwrite earlier ports with the same name at runtime. Prefer unique
     * keys unless the replacement implements the same port contract.
     */
    ports?: ProvidedPorts;
    /**
     * Optional hook called after all providers have contributed their ports.
     *
     * Declared as a method so typed providers stay assignable to loosely typed
     * provider lists. Hooks that take `ctx` with an unannotated parameter keep
     * TypeScript from inferring `ProvidedPorts` from the returned `ports`.
     * Prefer closing over setup locals, or annotate `ctx` with
     * `ProviderLifecycleContext<...>`.
     */
    start?(ctx: ProviderLifecycleContext<Ports & ProvidedPorts, Context, ServiceInput>): MaybePromise<void>;
    /**
     * Optional hook called when the server is stopped.
     */
    stop?(ctx: ProviderLifecycleContext<Ports & ProvidedPorts, Context, ServiceInput>): MaybePromise<void>;
};
/**
 * Static provider metadata used by docs and app-local tooling.
 *
 * Metadata is descriptive. It does not change provider setup, ordering, or
 * runtime port merging behavior.
 *
 * Reusable provider packages should also declare package-owned
 * `beignet.provider` metadata in package.json so external tooling can inspect
 * provider facts without importing runtime code.
 */
export interface ServiceProviderMetadata {
    /**
     * Package that exports this provider, when it comes from a reusable package.
     */
    packageName?: string;
    /**
     * App port keys this provider contributes or replaces.
     */
    ports?: readonly string[];
    /**
     * App port keys this provider expects previous providers or base app ports to
     * have installed before setup runs.
     */
    requires?: readonly string[];
    /**
     * Environment variables this provider reads directly or via config loading.
     */
    env?: readonly string[];
    /**
     * Devtools watcher names this provider can emit through provider
     * instrumentation.
     */
    watchers?: readonly string[];
}
/**
 * A service provider that can extend or replace ports during app initialization.
 *
 * Providers support:
 * - Configuration via Standard Schema (any compatible library: Zod, Valibot, etc.)
 * - Returning ports with new capabilities (e.g., cache, mailer)
 * - Replacing existing ports by returning the same key
 * - Optional start/stop hooks
 *
 * @example
 * ```ts
 * const cacheProvider = createProvider({
 *   name: "cache-redis",
 *   config: {
 *     schema: z.object({ URL: z.string().url() }),
 *     envPrefix: "REDIS_",
 *   },
 *   async setup({ config }) {
 *     const client = new Redis(config.URL);
 *     return {
 *       ports: {
 *         cache: {
 *           get: (key) => client.get(key),
 *           set: (key, value) => client.set(key, value),
 *         },
 *       },
 *       stop: () => client.quit(),
 *     };
 *   },
 * });
 * ```
 */
export interface ServiceProvider<Ports, CfgSchema extends StandardSchemaV1 = StandardSchemaV1<void, void>, ProvidedPorts extends ProviderPorts = NoProvidedPorts, Context = unknown, ServiceInput = void> {
    /**
     * Unique name for this provider (used for logging/debugging)
     */
    name: string;
    /**
     * Optional static metadata for docs and diagnostics.
     */
    metadata?: ServiceProviderMetadata;
    /**
     * Optional configuration definition.
     * If provided, the config will be loaded and validated before calling setup.
     */
    config?: ProviderConfigDef<CfgSchema>;
    /**
     * Setup phase: create the ports this provider contributes.
     * Called during server initialization before provider `start` hooks and
     * before the server handles requests.
     *
     * @param ctx.ports - Ports contributed by previous providers
     * @param ctx.config - Validated config (if config was defined), or undefined
     * @param ctx.createServiceContext - Late-bound service context factory.
     * Throws until all providers have started, so call it lazily from runtime
     * entrypoints such as job dispatchers and event listeners.
     */
    setup(ctx: {
        ports: Readonly<Ports>;
        config: InferOutput<CfgSchema> | undefined;
        createServiceContext: ProviderServiceContextFactory<Context, ServiceInput>;
    }): MaybePromise<ProviderSetupResult<ProvidedPorts, Ports, Context, ServiceInput>>;
    /**
     * Type-only marker for ports this provider contributes.
     * Runtime provider objects do not need to set this property.
     */
    readonly __providedPorts?: ProvidedPorts;
}
/**
 * A provider configuration schema whose concrete validation library and input
 * shape are intentionally erased while its validated output may stay typed.
 *
 * Reusable provider packages use this in their named provider return types so
 * internal Zod schemas do not become part of the package's public API.
 */
export type AnyProviderConfigSchema<Output = any> = StandardSchemaV1<any, Output>;
/**
 * Loosely typed service provider.
 *
 * Required-port, config, app-context, and service-input generics are erased
 * here so any provider created with `createProvider(...)` — including the
 * typed curried form — stays assignable. Use this for code that works across
 * arbitrary providers, such as provider lists and test helpers.
 */
export type AnyServiceProvider = ServiceProvider<unknown, AnyProviderConfigSchema, ProviderPorts, any, any>;
/**
 * Extract the ports a provider contributes.
 */
export type ProvidedPortsOf<TProvider> = TProvider extends ServiceProvider<infer _Ports, infer _CfgSchema, infer ProvidedPorts, infer _Context, infer _ServiceInput> ? ProvidedPorts : NoProvidedPorts;
type UnionToIntersection<T> = (T extends unknown ? (value: T) => void : never) extends (value: infer I) => void ? I : never;
/**
 * Extract and merge the ports contributed by a provider list.
 *
 * Use this with `typeof providers` to type provider-contributed ports in app
 * code without hand-written casts:
 *
 * @example
 * ```ts
 * import type { InferProviderPorts } from "@beignet/core/providers";
 * import type { providers } from "@/server/providers";
 * import type { AppPorts } from "@/ports";
 *
 * export type AppRuntimePorts = AppPorts & InferProviderPorts<typeof providers>;
 * ```
 */
export type InferProviderPorts<TProviders> = TProviders extends readonly unknown[] ? [TProviders[number]] extends [never] ? NoProvidedPorts : UnionToIntersection<ProvidedPortsOf<TProviders[number]>> : NoProvidedPorts;
/**
 * Helper function to create a provider with proper type inference.
 *
 * This is a simple identity function that helps TypeScript infer the correct types
 * for the provider definition.
 *
 * App-local providers can use the curried zero-argument form to declare the
 * ports they require from earlier providers plus their app context and
 * service-context input. The required ports, `ctx.ports`, and
 * `ctx.createServiceContext` are then fully typed with no casts.
 *
 * @example
 * ```ts
 * export const myProvider = createProvider({
 *   name: "my-provider",
 *   config: {
 *     schema: z.object({ apiKey: z.string() }),
 *     envPrefix: "MY_SERVICE_",
 *   },
 *   async setup({ config }) {
 *     return { ports: { myService: createMyService(config) } };
 *   },
 * });
 *
 * // Typed app-local provider:
 * export const appDatabaseProvider = createProvider<
 *   { db: DbPort<typeof schema>; devtools?: DevtoolsPort },
 *   AppContext,
 *   AppServiceContextInput
 * >()({
 *   name: "app-database",
 *   async setup({ ports, createServiceContext }) {
 *     const repositories = createRepositories(ports.db.drizzle);
 *     return { ports: repositories };
 *   },
 * });
 * ```
 */
export declare function createProvider<Requires = unknown, Context = unknown, ServiceInput = void>(): <CfgSchema extends StandardSchemaV1 = StandardSchemaV1<void, void>, Provided extends ProviderPorts = NoProvidedPorts>(def: ServiceProvider<Requires, CfgSchema, Provided, Context, ServiceInput>) => ServiceProvider<Requires, CfgSchema, Provided, Context, ServiceInput>;
export declare function createProvider<Ports = unknown, CfgSchema extends StandardSchemaV1 = StandardSchemaV1<void, void>, ProvidedPorts extends ProviderPorts = NoProvidedPorts>(def: ServiceProvider<Ports, CfgSchema, ProvidedPorts>): ServiceProvider<Ports, CfgSchema, ProvidedPorts>;
export {};
//# sourceMappingURL=provider.d.ts.map