/**
 * Logger interface used throughout Wooks. Passed via `EventContextOptions`
 * and accessed with `useLogger()`. Compatible with `@prostojs/logger` and
 * most standard loggers.
 */
interface Logger {
    info(msg: string, ...args: unknown[]): void;
    warn(msg: string, ...args: unknown[]): void;
    error(msg: string, ...args: unknown[]): void;
    debug(msg: string, ...args: unknown[]): void;
    /** Creates a child logger scoped to a topic. Matches `ProstoLogger.createTopic()`. */
    createTopic?: (name: string) => Logger;
}
/**
 * A typed, writable context slot. Created with `key<T>(name)`.
 * Use `ctx.set(k, value)` to store and `ctx.get(k)` to retrieve.
 */
interface Key<T> {
    readonly _id: number;
    readonly _name: string;
    /** @internal Type brand — not used at runtime. */
    readonly _T?: T;
}
/**
 * A lazily-computed, read-only context slot. Created with `cached<T>(fn)`.
 * The factory runs once per context on first `ctx.get()` call; the result is cached.
 */
interface Cached<T> {
    readonly _id: number;
    readonly _name: string;
    readonly _fn: (ctx: EventContext) => T;
    /** @internal Type brand — not used at runtime. */
    readonly _T?: T;
}
/** Union type for any context slot — either a writable `Key` or a computed `Cached`. */
type Accessor<T> = Key<T> | Cached<T>;
/**
 * Type-level marker used in `defineEventKind` schemas. Each `slot<T>()`
 * becomes a typed `Key<T>` on the resulting `EventKind`. No runtime behavior.
 */
interface SlotMarker<T> {
    /** @internal Type brand — not used at runtime. */
    readonly _T?: T;
}
/**
 * Declares the shape of an event type — its name and typed seed slots.
 * Created with `defineEventKind(name, schema)`.
 *
 * Use `kind.keys.<prop>` to access typed context keys for each slot.
 */
interface EventKind<S extends Record<string, SlotMarker<any>>> {
    /** Unique event kind name (e.g. `'http'`, `'cli'`). */
    readonly name: string;
    /** Typed context keys for each slot in the schema. */
    readonly keys: {
        [K in keyof S]: S[K] extends SlotMarker<infer V> ? Key<V> : never;
    };
    /** @internal Pre-computed entries for fast `seed()`. */
    readonly _entries: Array<[string, Key<unknown>]>;
}
/**
 * Extracts the seed values type for an `EventKind`. This is the object
 * shape passed to `ctx.seed(kind, seeds)` or `createEventContext(opts, kind, seeds, fn)`.
 */
type EventKindSeeds<K> = K extends EventKind<infer S> ? {
    [P in keyof S]: S[P] extends SlotMarker<infer V> ? V : never;
} : never;

/** Options for creating an {@link EventContext}. */
interface EventContextOptions {
    /** Logger instance available to all composables via `useLogger()`. */
    logger: Logger;
    /** Optional parent context. Enables transparent read-through and scoped writes. */
    parent?: EventContext;
}
/**
 * Per-event container for typed slots, propagated via `AsyncLocalStorage`.
 * Composables read and write data through `get`/`set` using typed `Key` or `Cached` accessors.
 *
 * Supports a parent chain: when a slot is not found locally, `get()` traverses
 * parent contexts. `set()` writes to the nearest context that already holds the slot,
 * or locally if the slot is new.
 *
 * Typically created by adapters (HTTP, CLI, etc.) — application code
 * interacts with it indirectly through composables.
 *
 * @example
 * ```ts
 * const ctx = new EventContext({ logger })
 * ctx.set(userIdKey, '123')
 * ctx.get(userIdKey) // '123'
 * ```
 */
declare class EventContext {
    /** Logger instance for this event. */
    readonly logger: Logger;
    /** Parent context for read-through and scoped writes. */
    readonly parent?: EventContext;
    private slots;
    constructor(options: EventContextOptions);
    /**
     * Controls whether `get()`, `has()`, and `set()` traverse the parent chain
     * for a given slot. Override in subclasses to isolate specific slots —
     * returning `false` forces local computation/storage, preventing inheritance.
     *
     * @param _id - The numeric slot identifier (`accessor._id`)
     * @returns `true` to allow parent traversal (default), `false` to block it
     */
    protected _shouldTraverseParent(_id: number): boolean;
    /**
     * Reads a value from a typed slot.
     * - For `Key<T>`: returns the previously `set` value, checking parent chain if not found locally.
     * - For `Cached<T>`: returns a cached result from this context or any parent. If not found
     *   anywhere, runs the factory on first access, caches locally, and returns the result.
     *   Throws on circular dependencies. Errors are cached and re-thrown on subsequent access.
     */
    get<T>(accessor: Key<T> | Cached<T>): T;
    /**
     * Writes a value to a typed slot. If the slot already exists somewhere in the
     * parent chain, the value is written there. Otherwise, it is written locally.
     */
    set<T>(key: Key<T> | Cached<T>, value: T): void;
    /**
     * Returns `true` if the slot has been set or computed in this context or any parent.
     */
    has(accessor: Accessor<any>): boolean;
    /**
     * Reads a value from a typed slot in this context only, ignoring parents.
     * Same semantics as `get()` but without parent chain traversal.
     */
    getOwn<T>(accessor: Key<T> | Cached<T>): T;
    /**
     * Writes a value to a typed slot in this context only, ignoring parents.
     */
    setOwn<T>(key: Key<T> | Cached<T>, value: T): void;
    /**
     * Returns `true` if the slot has been set or computed in this context only.
     */
    hasOwn(accessor: Accessor<any>): boolean;
    /**
     * Seeds an event kind's slots into this context. Sets the event type key
     * and populates all slots declared in the kind's schema.
     *
     * @param kind - The event kind (from `defineEventKind`)
     * @param seeds - Values for each slot in the kind's schema
     * @param fn - Optional callback to run after seeding (returned value is forwarded)
     */
    seed<S extends Record<string, any>>(kind: EventKind<S>, seeds: EventKindSeeds<EventKind<S>>): void;
    seed<S extends Record<string, any>, R>(kind: EventKind<S>, seeds: EventKindSeeds<EventKind<S>>, fn: () => R): R;
    /** Walk the parent chain looking for a set slot. Returns `undefined` if not found. */
    private _findSlot;
    /** Set value in the first context in the chain that has this slot. Returns true if found. */
    private _setIfExists;
}

/**
 * Creates a typed, writable context slot. Use `ctx.set(k, value)` to store
 * and `ctx.get(k)` to retrieve. Throws if read before being set.
 *
 * @param name - Debug label (shown in error messages, not used for lookup)
 *
 * @example
 * ```ts
 * const userIdKey = key<string>('userId')
 * ctx.set(userIdKey, '123')
 * ctx.get(userIdKey) // '123'
 * ```
 */
declare function key<T>(name: string): Key<T>;
/**
 * Creates a lazily-computed, read-only context slot. The factory runs once
 * per `EventContext` on first `ctx.get(slot)` call; the result is cached
 * for the context lifetime. Errors are also cached and re-thrown.
 *
 * @param fn - Factory receiving the current `EventContext`, returning the value to cache
 *
 * @example
 * ```ts
 * const parsedUrl = cached((ctx) => new URL(ctx.get(rawUrlKey)))
 * // first call computes, subsequent calls return cached result
 * ctx.get(parsedUrl)
 * ```
 */
declare function cached<T>(fn: (ctx: EventContext) => T): Cached<T>;

/**
 * Creates a parameterized cached computation. Maintains a `Map<K, V>` per
 * event context — one cached result per unique key argument.
 *
 * @param fn - Factory receiving the lookup key and `EventContext`, returning the value to cache
 * @returns A function `(key: K, ctx?: EventContext) => V` that computes on first call per key
 *
 * @example
 * ```ts
 * const parseCookie = cachedBy((name: string, ctx) => {
 *   const raw = ctx.get(cookieHeaderKey)
 *   return parseSingleCookie(raw, name)
 * })
 *
 * parseCookie('session') // computed and cached for 'session'
 * parseCookie('theme')   // computed and cached for 'theme'
 * parseCookie('session') // returns cached result
 * ```
 */
declare function cachedBy<K, V>(fn: (key: K, ctx: EventContext) => V): (key: K, ctx?: EventContext) => V;

/**
 * Type-level marker used inside `defineEventKind` schemas. Each `slot<T>()`
 * becomes a typed `Key<T>` on the resulting `EventKind`. Has no runtime behavior.
 *
 * @example
 * ```ts
 * const httpKind = defineEventKind('http', {
 *   req: slot<IncomingMessage>(),
 *   response: slot<HttpResponse>(),
 * })
 * ```
 */
declare function slot<T>(): SlotMarker<T>;
/**
 * Declares a named event kind with typed seed slots. The returned object
 * contains `keys` — typed accessors for reading seed values from context —
 * and is passed to `ctx.seed(kind, seeds)` or `createEventContext()`.
 *
 * @param name - Unique event kind name (e.g. `'http'`, `'cli'`, `'workflow'`)
 * @param schema - Object mapping slot names to `slot<T>()` markers
 * @returns An `EventKind` with typed `keys` for context access
 *
 * @example
 * ```ts
 * const httpKind = defineEventKind('http', {
 *   req: slot<IncomingMessage>(),
 *   response: slot<HttpResponse>(),
 * })
 *
 * // Access typed seed values:
 * const req = ctx.get(httpKind.keys.req) // IncomingMessage
 * ```
 */
declare function defineEventKind<S extends Record<string, SlotMarker<any>>>(name: string, schema: S): EventKind<S>;

/**
 * A composable function created by {@link defineWook}. Callable as `(ctx?) => T`,
 * with an exposed `_slot` for advanced use cases such as slot isolation in child contexts.
 */
interface WookComposable<T> {
    (ctx?: EventContext): T;
    /** The underlying `Cached` slot. Useful for building isolation lists in child contexts. */
    readonly _slot: Cached<T>;
}
/**
 * Creates a composable with per-event caching. The factory runs once per
 * `EventContext`; subsequent calls within the same event return the cached result.
 *
 * This is the recommended way to build composables in Wooks. All built-in
 * composables (`useRequest`, `useResponse`, `useCookies`, etc.) are created with `defineWook`.
 *
 * @param factory - Receives the `EventContext` and returns the composable's public API
 * @returns A composable function with an exposed `_slot` for isolation
 *
 * @example
 * ```ts
 * export const useCurrentUser = defineWook((ctx) => {
 *   const { basicCredentials } = useAuthorization(ctx)
 *   const username = basicCredentials()?.username
 *   return {
 *     username,
 *     profile: async () => username ? await db.findUser(username) : null,
 *   }
 * })
 *
 * // In a handler — factory runs once, cached for the request:
 * const { username, profile } = useCurrentUser()
 * ```
 */
declare function defineWook<T>(factory: (ctx: EventContext) => T): WookComposable<T>;

/**
 * No-op base class for observability integration. Subclass and override
 * `with()` / `hook()` to add tracing, metrics, or logging around event
 * lifecycle points.
 *
 * The default implementation simply calls the callback with no overhead.
 * Replace via `replaceContextInjector()` to enable instrumentation.
 */
declare class ContextInjector<N> {
    /**
     * Wraps a callback with optional named attributes for observability.
     * Default implementation just calls `cb()` — override for tracing.
     */
    with<T>(name: N, attributes: Record<string, string | number | boolean>, cb: () => T): T;
    with<T>(name: N, cb: () => T): T;
    /**
     * Hook called by adapters at specific lifecycle points (e.g., after route lookup).
     * Default implementation is a no-op — override for observability.
     */
    hook(_method: string, _name: 'Handler:not_found' | 'Handler:routed', _route?: string): void;
}
/**
 * Returns the current `ContextInjector` instance, or `null` if none has been installed.
 * Used internally by adapters to wrap lifecycle events.
 */
declare function getContextInjector<N = TContextInjectorHooks>(): ContextInjector<N> | null;
/**
 * Replaces the global `ContextInjector` with a custom implementation.
 * Use this to integrate OpenTelemetry or other observability tools.
 *
 * @param newCi - Custom `ContextInjector` subclass instance
 *
 * @example
 * ```ts
 * class OtelInjector extends ContextInjector<string> {
 *   with<T>(name: string, attrs: Record<string, any>, cb: () => T): T {
 *     return tracer.startActiveSpan(name, (span) => {
 *       span.setAttributes(attrs)
 *       try { return cb() } finally { span.end() }
 *     })
 *   }
 * }
 * replaceContextInjector(new OtelInjector())
 * ```
 */
declare function replaceContextInjector(newCi: ContextInjector<string>): void;
/**
 * Resets the global `ContextInjector` back to `null` (no-op default).
 * Useful for tests or when disabling instrumentation.
 */
declare function resetContextInjector(): void;
/** Built-in hook names used by the framework. */
type TContextInjectorHooks = 'Event:start';

/** Context key for route parameters. Set by adapters after route matching. */
declare const routeParamsKey: Key<Record<string, string | string[]>>;
/** Context key for the event type name (e.g. `'http'`, `'cli'`). Set by `ctx.seed()`. */
declare const eventTypeKey: Key<string>;

/**
 * Returns the route parameters for the current event. Works with HTTP
 * routes, CLI commands, workflow steps — any adapter that sets `routeParamsKey`.
 *
 * @param ctx - Optional explicit context (defaults to `current()`)
 * @returns Object with `params` (the full params record) and `get(name)` for typed access
 *
 * @example
 * ```ts
 * app.get('/users/:id', () => {
 *   const { params, get } = useRouteParams<{ id: string }>()
 *   console.log(get('id')) // typed as string
 * })
 * ```
 */
declare function useRouteParams<T extends Record<string, string | string[]> = Record<string, string | string[]>>(ctx?: EventContext): {
    params: T;
    get: <K extends keyof T>(name: K) => T[K];
};
/**
 * Provides a unique, per-event identifier. The ID is a random UUID, generated
 * lazily on first `getId()` call and cached for the event lifetime.
 *
 * @param ctx - Optional explicit context (defaults to `current()`)
 *
 * @example
 * ```ts
 * const { getId } = useEventId()
 * logger.info(`Request ${getId()}`)
 * ```
 */
declare function useEventId(ctx?: EventContext): {
    getId: () => string;
};

/**
 * Runs a callback with the given `EventContext` as the active context.
 * All composables and `current()` calls inside `fn` will resolve to `ctx`.
 *
 * @param ctx - The event context to make active
 * @param fn - Callback to execute within the context scope
 * @returns The return value of `fn`
 *
 * @example
 * ```ts
 * const ctx = new EventContext({ logger })
 * run(ctx, () => {
 *   // current() returns ctx here
 *   const logger = useLogger()
 * })
 * ```
 */
declare function run<R>(ctx: EventContext, fn: () => R): R;
/**
 * Returns the active `EventContext` for the current async scope.
 * Throws if called outside an event context (e.g., at module level).
 *
 * All composables use this internally. Prefer composables over direct `current()` access.
 *
 * @throws Error if no active event context exists
 */
declare function current(): EventContext;
/**
 * Returns the active `EventContext`, or `undefined` if none is active.
 * Use this when context availability is uncertain (e.g., in code that may
 * run both inside and outside an event handler).
 */
declare function tryGetCurrent(): EventContext | undefined;
/**
 * Returns the logger for the current event context.
 *
 * When called with a `topic` string, creates a child logger via
 * `logger.createTopic()` (if supported). Falls back to the base
 * logger when `createTopic` is not available.
 *
 * @example
 * ```ts
 * const logger = useLogger()
 * logger.info('Processing request')
 *
 * const scoped = useLogger('auth')
 * scoped.warn('Token expired')
 * ```
 */
declare function useLogger(): Logger;
declare function useLogger(topic: string): Logger;
declare function useLogger(ctx: EventContext): Logger;
declare function useLogger(topic: string, ctx: EventContext): Logger;
/**
 * Creates a new `EventContext`, makes it the active context via
 * `AsyncLocalStorage`, and runs `fn` inside it.
 *
 * @param options - Context options (must include `logger`)
 * @param fn - Callback to execute within the new context
 * @returns The return value of `fn`
 *
 * The kindless overload is a convenience for tests that need a context scope
 * without declaring an event kind. Production code should always provide a kind.
 *
 * @example
 * ```ts
 * createEventContext({ logger }, () => {
 *   // composables work here
 * })
 * ```
 */
declare function createEventContext<R>(options: EventContextOptions, fn: () => R): R;
/**
 * Creates a new `EventContext` with an event kind, seeds the kind's slots,
 * and runs `fn` inside the context.
 *
 * @param options - Context options (must include `logger`)
 * @param kind - Event kind (from `defineEventKind`)
 * @param seeds - Seed values for the event kind's slots
 * @param fn - Callback to execute within the new context
 * @returns The return value of `fn`
 *
 * @example
 * ```ts
 * const httpKind = defineEventKind('http', { req: slot<IncomingMessage>() })
 *
 * createEventContext({ logger }, httpKind, { req: incomingMessage }, () => {
 *   const req = current().get(httpKind.keys.req)
 * })
 * ```
 */
declare function createEventContext<S extends Record<string, any>, R>(options: EventContextOptions, kind: EventKind<S>, seeds: EventKindSeeds<EventKind<S>>, fn: () => R): R;

export { ContextInjector, EventContext, cached, cachedBy, createEventContext, current, defineEventKind, defineWook, eventTypeKey, getContextInjector, key, replaceContextInjector, resetContextInjector, routeParamsKey, run, slot, tryGetCurrent, useEventId, useLogger, useRouteParams };
export type { Accessor, Cached, EventContextOptions, EventKind, EventKindSeeds, Key, Logger, SlotMarker, TContextInjectorHooks, WookComposable };
