/**
 * ------------------------------
 * Plugin authoring helpers
 * ------------------------------
 *
 * - `definePlugin`: identity wrapper that constrains a plugin's return to
 *   `PluginProvides` while preserving the narrow inferred shape, so callers
 *   can derive their `*PluginProvides` type via `ReturnType<typeof plugin>`
 *   instead of declaring it by hand.
 * - `createPluginMethod` / `createPaginatedPluginMethod`: per-method
 *   primitives that sit inside a `definePlugin` callback and build the
 *
 *     { [name]: wrappedFn, context: { meta: { [name]: meta } } }
 *
 *   fragment a plugin returns for a single method, wiring up
 *   `createFunction` / `createPaginatedFunction`, the telemetry callback,
 *   and the doubled `name` (function key + meta key) in one place.
 *
 * Two method helpers (rather than one with a `paginated: true` discriminant)
 * because the handler signature changes shape across pagination, and
 * discriminated unions on optional booleans produce noisy TS errors.
 */
import type { z } from "zod";
import type { PaginatedSdkResult } from "../types/functions";
import type { Plugin, PluginMeta, PluginProvides } from "../types/plugin";
/**
 * Identity helper that preserves the narrow inferred return type of a plugin.
 *
 * Wrapping a plugin function with `definePlugin(...)` lets callers derive the
 * plugin's contribution via `ReturnType<typeof plugin>` instead of declaring a
 * matching `*PluginProvides` interface by hand. The constraint
 * `TProvides extends PluginProvides` enforces the structural floor (must
 * return a valid plugin output); beyond that the inferred shape flows through.
 */
export declare function definePlugin<TSdk, TProvides extends PluginProvides>(fn: (sdk: TSdk & {
    context: {
        meta: Record<string, PluginMeta>;
    };
}) => TProvides): (sdk: TSdk & {
    context: {
        meta: Record<string, PluginMeta>;
    };
}) => TProvides;
/**
 * Method-level meta fields. Mirrors `PluginMeta` minus `inputSchema`, which is
 * passed at the top level alongside the handler and merged into the meta by
 * the helpers themselves.
 */
type MethodMeta = Omit<PluginMeta, "inputSchema">;
interface PluginMethodConfig<TSdk, TInput, TResult, TName extends string> extends MethodMeta {
    name: TName;
    /**
     * Schema for runtime input validation; drives the handler's `options`
     * type. For plugins that accept deprecated parameter aliases this is a
     * `z.union([CanonicalSchema, DeprecatedSchema])` — the registry
     * unwraps unions and exposes only the first variant (canonical) to
     * docs, CLI help, and MCP tool definitions.
     */
    inputSchema?: z.ZodSchema<TInput>;
    handler: (args: {
        sdk: TSdk;
        options: TInput;
    }) => Promise<TResult>;
}
type PluginMethodReturn<TName extends string, TInput, TResult> = {
    [K in TName]: (options?: TInput) => Promise<TResult>;
} & {
    context: {
        meta: {
            [K in TName]: PluginMeta;
        };
    };
};
/**
 * Build the method fragment for a non-paginated SDK method. Used inside a
 * `definePlugin(...)` callback:
 *
 *   export const getProfilePlugin = definePlugin(
 *     (sdk: ApiPluginProvides & EventEmissionProvides) =>
 *       createPluginMethod(sdk, {
 *         name: "getProfile",
 *         categories: ["account"],
 *         inputSchema: GetProfileSchema,
 *         handler: async ({ sdk }) => { ... },
 *       }),
 *   );
 */
export declare function createPluginMethod<const TName extends string, TSdk extends {
    context: unknown;
}, TInput, TResult>(sdk: TSdk, config: PluginMethodConfig<TSdk, TInput, TResult, TName>): PluginMethodReturn<TName, TInput, TResult>;
interface PaginatedPluginMethodConfig<TSdk, TInput, TResponse extends {
    data: unknown[];
}, TName extends string> extends MethodMeta {
    name: TName;
    /** Same semantics as `createPluginMethod`'s `inputSchema`. */
    inputSchema?: z.ZodSchema<TInput>;
    /**
     * Optional default page size when the caller doesn't pass one. Mirrors
     * `createPaginatedFunction`'s `defaultPageSize` arg.
     */
    defaultPageSize?: number;
    /**
     * Page handler. Returns whatever shape the upstream API gives back, as
     * long as it has a `data: TItem[]` field. JSON:API envelopes
     * (`{ data, links, meta }`) work directly: pair them with
     * `extractCursor` below to pull the next-page cursor out of the response.
     */
    handler: (args: {
        sdk: TSdk;
        options: TInput & {
            cursor?: string;
            pageSize?: number;
        };
    }) => Promise<TResponse>;
    /**
     * Pull the next-page cursor out of the handler's response. Most JSON:API
     * endpoints will use `(response) => extractCursor(response.links)` from
     * `function-utils`. Provide a custom extractor for non-standard shapes
     * (e.g. `(response) => response.meta?.pagination?.end_cursor`).
     *
     * If omitted, the runtime falls back to its built-in detection
     * (`response.nextCursor` or `response.links?.next` URL parsing) — i.e.
     * the same auto-detection that worked before this helper existed. New
     * plugins should set `extractCursor` explicitly so the pagination shape
     * is visible at the call site.
     */
    extractCursor?: (response: TResponse) => string | undefined;
}
type ItemOf<TResponse extends {
    data: unknown[];
}> = TResponse extends {
    data: readonly (infer TItem)[];
} ? TItem : never;
type PaginatedPluginMethodReturn<TName extends string, TInput, TItem> = {
    [K in TName]: (options?: TInput & {
        cursor?: string;
        pageSize?: number;
        maxItems?: number;
    }) => PaginatedSdkResult<TItem>;
} & {
    context: {
        meta: {
            [K in TName]: PluginMeta;
        };
    };
};
/**
 * Paginated variant of `createPluginMethod`. The handler receives
 * `{ cursor?, pageSize? }` in `options` and returns whatever shape the
 * upstream API gives back, as long as it has a `data: TItem[]` field.
 * Pair JSON:API responses with `extractCursor` to make the cursor
 * extraction visible at the call site:
 *
 *   createPaginatedPluginMethod(sdk, {
 *     name: "listApps",
 *     inputSchema: ListAppsSchema,
 *     handler: ({ sdk, options }) =>
 *       sdk.context.api.get<ListAppsResponse>("/api/v0/apps", {...}),
 *     extractCursor: (response) => extractCursor(response.links),
 *   });
 */
export declare function createPaginatedPluginMethod<const TName extends string, TSdk extends {
    context: unknown;
}, TInput, TResponse extends {
    data: unknown[];
}>(sdk: TSdk, config: PaginatedPluginMethodConfig<TSdk, TInput, TResponse, TName>): PaginatedPluginMethodReturn<TName, TInput, ItemOf<TResponse>>;
/**
 * Maps a tuple of plugins to a tuple of their TSdk requirement types.
 *
 *   SdkRequirementsOf<[Plugin<{ api }, _>, Plugin<{ options }, _>]>
 *     = [{ api }, { options }]
 */
type SdkRequirementsOf<T extends readonly Plugin<any, any>[]> = {
    [K in keyof T]: T[K] extends Plugin<infer Sdk, any> ? Sdk : never;
};
/**
 * Maps a tuple of plugins to a tuple of their TProvides output types.
 *
 *   ProvidesOf<[Plugin<_, { hello }>, Plugin<_, { goodbye }>]>
 *     = [{ hello }, { goodbye }]
 */
type ProvidesOf<T extends readonly Plugin<any, any>[]> = {
    [K in keyof T]: T[K] extends Plugin<any, infer Provides> ? Provides : never;
};
/**
 * Intersects every member of a tuple into a single combined type. The
 * result is an object that has every property of every member at once.
 *
 *   IntersectAll<[{ api }, { options }]> = { api } & { options }
 *   IntersectAll<[]>                     = {}
 *
 * Walks recursively: head & IntersectAll<tail>, base case is the empty
 * tuple. Why intersection (`&`) and not union (`|`): the composed plugin
 * must require ALL of the sub-plugins' needs at once — an SDK that has
 * both `api` AND `options` — not "either api or options."
 */
type IntersectAll<T extends readonly unknown[]> = T extends readonly [
    infer Head,
    ...infer Tail
] ? Head & IntersectAll<Tail> : {};
/**
 * The TSdk a composed plugin requires: every sub-plugin's TSdk requirement,
 * all at once. Composing a plugin that needs `{ api }` with one that needs
 * `{ options }` yields a composed plugin that needs `{ api } & { options }`.
 */
type ComposeSdk<T extends readonly Plugin<any, any>[]> = IntersectAll<SdkRequirementsOf<T>>;
/**
 * What a composed plugin provides: every sub-plugin's TProvides combined.
 * Composing a plugin that provides `{ hello }` with one that provides
 * `{ goodbye }` yields `{ hello } & { goodbye }`.
 */
type ComposeProvides<T extends readonly Plugin<any, any>[]> = IntersectAll<ProvidesOf<T>>;
/**
 * Bundle N plugins into a single plugin so a consumer can call
 * `addPlugin(combined)` once. Use this when an extension package wants to
 * expose granular sub-plugins for tree-shaking *and* a one-call convenience
 * entry point.
 *
 * Three commitments (per the boilerplate-reduction design):
 * 1. **Bag mode.** Sub-plugins must not depend on each other; order is
 *    irrelevant. If you need pipeline composition (later sub-plugins
 *    reading earlier sub-plugins' outputs), use a regular `definePlugin`
 *    callback that calls each `createPluginMethod` in turn.
 * 2. **Over-approximation of TSdk.** The composed plugin requires the
 *    intersection of every sub-plugin's requirements (all of them, all at
 *    once). Works fine for homogeneous-deps groups; widens slightly for
 *    heterogeneous ones, which keeps the variadic types tractable.
 * 3. **Collision detection.** Throws on duplicate root keys, duplicate
 *    `context.meta` keys, and duplicate non-meta `context.*` keys
 *    *inside* a single `composePlugins(...)` call. Cross-`addPlugin`
 *    collisions are handled separately by `addPlugin` itself, which
 *    warns-and-skips the colliding plugin (the first registration
 *    wins). Pure meta-only overrides (e.g. `cliOverridesPlugin`
 *    patching `meta.fetch.deprecated` without re-registering `fetch`)
 *    stay legitimate in both code paths.
 *
 * Why prefer this over an `addPlugin([...])` array overload: TypeScript's
 * overload resolution interacts badly with arrays carrying a mix of
 * `TProvides` shapes — chained inference downstream of `addPlugin([...])`
 * tends to widen and lose individual method types. A single composed
 * plugin flows through the existing single-plugin `addPlugin` cleanly.
 */
export declare function composePlugins<const Ts extends readonly Plugin<any, any>[]>(...plugins: Ts): Plugin<ComposeSdk<Ts>, ComposeProvides<Ts>>;
export {};
//# sourceMappingURL=plugin-utils.d.ts.map