/**
 * Generic types for capability definitions.
 *
 * A capability is defined with Zod schemas for methods, events, and settings.
 * TypeScript types are inferred via z.infer<> — zero duplication.
 *
 * Pattern:
 *   1. Define Zod schemas for data, methods, settings
 *   2. Export const capabilityDef = { ... } satisfies CapabilityDefinition
 *   3. Export type IProvider = InferProvider<typeof capabilityDef>
 *   4. Addon implements IProvider
 *   5. Registry auto-mounts tRPC router from definition.methods
 */
import { z } from 'zod';
import { DeviceType } from '../device/device-type.js';
import type { ConfigUISchemaWithValues } from '../interfaces/config-ui.js';
/**
 * tRPC procedure kind.
 * - 'query': read-only, no side effects
 * - 'mutation': write operation, side effects
 * - 'subscription': push-based real-time stream via WebSocket
 */
export type CapabilityMethodKind = 'query' | 'mutation' | 'subscription';
/** Auth level required to call a capability method. */
export type CapabilityMethodAuth = 'public' | 'protected' | 'admin' | 'superAdmin';
/** Schema pair for a single capability method: input + output + routing metadata. */
export interface CapabilityMethodSchema<TInput extends z.ZodType = z.ZodType, TOutput extends z.ZodType = z.ZodType, TKind extends CapabilityMethodKind = CapabilityMethodKind> {
    readonly input: TInput;
    readonly output: TOutput;
    /** tRPC procedure kind. Default: 'query'. */
    readonly kind: TKind;
    /** Auth level. Default: 'protected'. */
    readonly auth: CapabilityMethodAuth;
    /** Moleculer action timeout override (ms). Overrides broker global requestTimeout for this action only. */
    readonly timeoutMs?: number;
}
/** Schema for a capability event (emitted to EventBus) */
export interface CapabilityEventSchema<TData extends z.ZodType = z.ZodType> {
    readonly data: TData;
}
export type CapabilityStatusKind = 'push' | 'poll' | 'command-driven';
export interface CapabilityStatusSchema<TStatus extends z.ZodType = z.ZodType> {
    readonly schema: TStatus;
    /** Documentation hint on update cadence — used by the aggregator and UI. */
    readonly kind?: CapabilityStatusKind;
}
/** Supported field kinds in settings bindings. Deliberately limited — mirror FormBuilder primitives. */
export type CapSettingsFieldKind = 'boolean' | 'text' | 'number' | 'select';
/** Scalar (single value) binding — target a single scalar path inside `status`. */
export interface CapSettingsScalarBinding {
    readonly kind: 'scalar';
    /** Dot-path into `status` where the current value lives. */
    readonly statusPath: string;
    /** Cap method invoked when the field changes. Must exist in `methods`. */
    readonly method: string;
    /** Name of the method's input arg that receives the new value. */
    readonly valueArg: string;
    readonly field: {
        readonly label: string;
        readonly kind: CapSettingsFieldKind;
        readonly options?: readonly string[];
    };
    /** Optional section override (default: capability name). */
    readonly sectionId?: string;
    readonly sectionTitle?: string;
}
/** Array binding — target an array path in `status`, render one row per item. */
export interface CapSettingsArrayBinding {
    readonly kind: 'array';
    /** Dot-path into `status` pointing at an array. */
    readonly statusPath: string;
    /** Field on each array item used as stable id (e.g. 'id'). */
    readonly idField: string;
    /** Cap method invoked on any per-item field change. */
    readonly method: string;
    /** Method input arg that receives the item id (e.g. 'overlayId'). */
    readonly idArg: string;
    /** Method input arg that receives the patch object. */
    readonly patchArg: string;
    readonly sectionId: string;
    readonly sectionTitle: string;
    readonly itemFields: Readonly<Record<string, CapSettingsItemField>>;
}
export interface CapSettingsItemField {
    /** Static string or per-item resolver (runs server-side at contribution time). */
    readonly label: string | ((item: Record<string, unknown>) => string);
    readonly kind: CapSettingsFieldKind;
    /** Select options — static array or per-item resolver. */
    readonly options?: readonly string[] | ((item: Record<string, unknown>) => readonly string[]);
    /** Visibility predicate — hide the field for items returning false. */
    readonly when?: (item: Record<string, unknown>) => boolean;
}
export type CapSettingsBinding = CapSettingsScalarBinding | CapSettingsArrayBinding;
export interface CapabilitySettingsSpec {
    readonly bindings: readonly CapSettingsBinding[];
}
/**
 * Full definition of a capability — the single source of truth for the
 * functional contract (methods + events).
 *
 * Settings are NOT part of the capability definition — they belong to
 * the addon implementation. Each addon defines its own settings schema
 * via ICamstackAddon.getConfigSchema(), stored per-addon with optional
 * per-device overrides.
 */
export interface CapabilityDefinition<TName extends string = string, TMethods extends Record<string, CapabilityMethodSchema> = Record<string, CapabilityMethodSchema>, TEvents extends Record<string, CapabilityEventSchema> | undefined = Record<string, CapabilityEventSchema> | undefined> {
    /** Unique capability name (kebab-case) */
    readonly name: TName;
    /** system = global singleton/collection. device = per-device resolution. */
    readonly scope: 'system' | 'device';
    /**
     * Cardinality of active providers:
     *   - `singleton` — exactly one active provider cluster-wide
     *     (e.g. admin-ui, device-manager). User config can pick which addon
     *     is active when multiple are registered.
     *   - `collection` — every registered provider stays active
     *     (e.g. decoder, notification-output).
     *   - `per-node` — one active provider per node/agent. Every node that
     *     hosts an addon for this cap runs its own independent provider;
     *     consumers query `getProviderForNode(capName, nodeId)` instead of
     *     `getSingleton(capName)`. Used for node-local probes
     *     (platform-probe, metrics-provider, hwaccel-probe).
     */
    readonly mode: 'singleton' | 'collection' | 'per-node';
    /** Method definitions: name → { input, output } Zod schemas */
    readonly methods: TMethods;
    /** Event definitions: name → { data } Zod schema (optional) */
    readonly events?: TEvents;
    /**
     * Typed machine-readable status snapshot. When present, the provider
     * interface gains `getStatus({ deviceId })` auto-injected via
     * `InferProvider`. Consumers poll via this method or subscribe via
     * `device-manager.subscribeDeviceStatusAggregate`. Distinct from
     * `getDeviceSettingsContribution` — that serves the UI editable-form
     * schema; `status` is the live readable state.
     */
    readonly status?: CapabilityStatusSchema;
    /**
     * Per-device runtime-state slice owned by THIS cap. Persisted across
     * restarts under `device.state[capName]`. Every provider that
     * implements the cap inherits the same shape — drivers don't
     * redeclare it, they just write through `this.runtimeState
     * .setCapState('<capName>', value)`. Examples:
     *
     *   - `batteryCapability.runtimeState`: `{ percentage, charging,
     *     sleeping, lastUpdated }`. A Reolink Argus, a Frigate sensor
     *     proxy, an ONVIF battery cam — every provider stores the
     *     same shape under `state.battery`.
     *   - `ptzCapability.runtimeState`: `{ pan, tilt, zoom }`.
     *
     * Caps without runtime state (pure-RPC like `reboot`) leave this
     * undefined; setCapState refuses to write to those slices.
     */
    readonly runtimeState?: import('zod').ZodObject<import('zod').core.$ZodLooseShape>;
    /**
     * Declarative settings spec. When present, the device-manager's
     * settings aggregator auto-derives the ConfigUISchema and routes
     * patches back to the referenced imperative methods. The provider
     * implements ONLY the imperative methods — no manual FormBuilder
     * contribution code. Colocated with the cap for single-source-of-
     * truth ergonomics.
     */
    readonly settings?: CapabilitySettingsSpec;
    /** For `scope: 'device'` caps — allowed device types. Omit → applies to every type. */
    readonly deviceTypes?: readonly DeviceType[];
    /**
     * When `true`, every provider of this capability MUST implement the
     * device-settings contribution surface — three extra methods appearing
     * automatically on the provider interface via `InferProvider`:
     *
     *   - `getDeviceSettingsContribution({ deviceId })` → schema + values
     *     for the device-details page (cached, slow cadence). Returns null
     *     when the provider has nothing to contribute for the given device.
     *   - `getDeviceLiveContribution({ deviceId })` → readonly live data
     *     (status, counters). Returns null when not applicable. Polled ~2-3s.
     *   - `applyDeviceSettingsPatch({ deviceId, patch })` → single-entry
     *     mutation for field values returned by `getDeviceSettingsContribution`.
     *     The provider owns the storage.
     *
     * The naming is intentionally distinct from the three-level addon API
     * (`getGlobalSettings` / `getDeviceSettings` / `getAddonSettings`) to
     * avoid collisions when an addon implements both surfaces on the same
     * class. The words "Contribution" and "Patch" signal these are aggregator
     * hooks, not persistent settings accessors.
     *
     * The `device-manager` aggregator enumerates bindings for the target
     * device, picks providers whose cap has this flag, and concatenates
     * the per-provider contributions into the single aggregate consumed
     * by `deviceManager.getDeviceSettingsAggregate`.
     */
    readonly exposesDeviceSettings?: boolean;
    /**
     * Server-internal cap — registered for in-process consumption only.
     * NOT exposed via the SDK `System` proxy and NOT exposed via the
     * generated React hooks. Use this for collection caps whose
     * contributions are aggregated by another singleton cap (for example
     * `addon-pages-source` walked by the `addon-pages` aggregator) or for
     * declarative slots (`addon-routes`, `admin-ui`, `log-destination`,
     * `restreamer`, `streaming-engine`, `webrtc`) consumed by hub
     * builtins/addons via `capabilities.getCollection()` /
     * `getSingleton()`.
     *
     * Behaviour:
     *   - cap-router codegen still emits a `createCapRouter_<Cap>` so the
     *     router stays available for opt-in mounting (e.g. test harnesses);
     *     `trpc.router.ts` simply does not reference it.
     *   - system-proxy codegen skips it — `system.<cap>` is not minted.
     *   - system-hooks codegen skips it — no `use<Cap><Method>` exports.
     *   - The cap is still consumable internally via
     *     `ctx.capabilities.getCollection()` / `getSingleton()` from
     *     inside an addon.
     *
     * Default: `false` (cap is publicly mounted and exposed on the SDK
     * surface).
     */
    readonly internal?: boolean;
    /**
     * How the generated tRPC cap-router treats an input field named
     * `nodeId`:
     *
     *   - `'routing'` (default): extract `nodeId` from the input and
     *     route to that node via `createRemoteProxy`. Use when the cap
     *     has a provider registered on every target node.
     *
     *   - `'data'`: forward `nodeId` untouched to the local provider
     *     as part of the method's input. Use when the cap has a
     *     hub-centric provider that dispatches internally
     *     (`broker.call('<addonId>.settings.<method>', ..., {nodeID})`).
     *     `addon-settings` is the canonical example — one singleton on
     *     the hub resolves `addonId` → worker nodeId by consulting the
     *     cluster roster.
     */
    readonly nodeIdMode?: 'routing' | 'data';
}
/** Contract for the three methods added by `exposesDeviceSettings: true`. */
export interface DeviceSettingsContribution {
    readonly getDeviceSettingsContribution: (input: {
        readonly deviceId: number;
    }) => Promise<ConfigUISchemaWithValues | null>;
    readonly getDeviceLiveContribution: (input: {
        readonly deviceId: number;
    }) => Promise<ConfigUISchemaWithValues | null>;
    readonly applyDeviceSettingsPatch: (input: {
        readonly deviceId: number;
        readonly patch: Record<string, unknown>;
    }) => Promise<{
        readonly success: true;
    }>;
}
export declare const DEVICE_SETTINGS_CONTRIBUTION_METHODS: {
    readonly getDeviceSettingsContribution: {
        readonly input: z.ZodObject<{
            deviceId: z.ZodNumber;
        }, z.core.$strip>;
        readonly output: z.ZodNullable<z.ZodObject<{
            tabs: z.ZodOptional<z.ZodArray<z.ZodObject<{
                id: z.ZodString;
                label: z.ZodString;
                icon: z.ZodString;
                order: z.ZodOptional<z.ZodNumber>;
            }, z.core.$strip>>>;
            sections: z.ZodArray<z.ZodObject<{
                id: z.ZodString;
                title: z.ZodString;
                description: z.ZodOptional<z.ZodString>;
                style: z.ZodOptional<z.ZodEnum<{
                    card: "card";
                    accordion: "accordion";
                }>>;
                defaultCollapsed: z.ZodOptional<z.ZodBoolean>;
                columns: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
                tab: z.ZodOptional<z.ZodString>;
                location: z.ZodOptional<z.ZodEnum<{
                    settings: "settings";
                    "top-tab": "top-tab";
                }>>;
                order: z.ZodOptional<z.ZodNumber>;
                fields: z.ZodArray<z.ZodAny>;
            }, z.core.$strip>>;
        }, z.core.$strip>>;
        readonly kind: "query";
        readonly auth: "protected";
    };
    readonly getDeviceLiveContribution: {
        readonly input: z.ZodObject<{
            deviceId: z.ZodNumber;
        }, z.core.$strip>;
        readonly output: z.ZodNullable<z.ZodObject<{
            tabs: z.ZodOptional<z.ZodArray<z.ZodObject<{
                id: z.ZodString;
                label: z.ZodString;
                icon: z.ZodString;
                order: z.ZodOptional<z.ZodNumber>;
            }, z.core.$strip>>>;
            sections: z.ZodArray<z.ZodObject<{
                id: z.ZodString;
                title: z.ZodString;
                description: z.ZodOptional<z.ZodString>;
                style: z.ZodOptional<z.ZodEnum<{
                    card: "card";
                    accordion: "accordion";
                }>>;
                defaultCollapsed: z.ZodOptional<z.ZodBoolean>;
                columns: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
                tab: z.ZodOptional<z.ZodString>;
                location: z.ZodOptional<z.ZodEnum<{
                    settings: "settings";
                    "top-tab": "top-tab";
                }>>;
                order: z.ZodOptional<z.ZodNumber>;
                fields: z.ZodArray<z.ZodAny>;
            }, z.core.$strip>>;
        }, z.core.$strip>>;
        readonly kind: "query";
        readonly auth: "protected";
    };
    readonly applyDeviceSettingsPatch: {
        readonly input: z.ZodObject<{
            deviceId: z.ZodNumber;
            patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
        }, z.core.$strip>;
        readonly output: z.ZodObject<{
            success: z.ZodLiteral<true>;
        }, z.core.$strip>;
        readonly kind: "mutation";
        readonly auth: "admin";
    };
};
/**
 * Schema for the `getStatus` method auto-injected when a cap declares
 * `status`. The runtime shape of the output is the cap's own
 * `status.schema` — but at codegen time we need a concrete Zod to emit
 * a typed tRPC route, so we keep the output as `z.unknown().nullable()`
 * here and tighten it on the client side via the generated
 * `CapStatusTypeMap` (see `scripts/generate-cap-status-types.ts`).
 */
export declare const DEVICE_STATUS_METHOD: {
    readonly getStatus: {
        readonly input: z.ZodObject<{
            deviceId: z.ZodNumber;
        }, z.core.$strip>;
        readonly output: z.ZodNullable<z.ZodUnknown>;
        readonly kind: "query";
        readonly auth: "protected";
    };
};
/**
 * Expand a cap def's methods map with every auto-injected method:
 *   - `exposesDeviceSettings: true`      → 3 contribution methods
 *   - `status: {...}`                    → `getStatus`
 *
 * Callers walk `expandCapMethods(def)` instead of `def.methods` when
 * they need the effective runtime method surface (Moleculer actions,
 * proxy shape, tRPC router entries). The cap def stays the single
 * source of truth. Providers still declare their concrete `methods`
 * block; the expansion happens at every consumption point, so there's
 * no accidental divergence between the declared surface and what's
 * actually mounted.
 */
export declare function expandCapMethods(def: CapabilityDefinition): Record<string, CapabilityMethodSchema>;
/**
 * Infer the provider interface from a capability definition.
 *
 * All query/mutation methods return Promise<T> — this ensures consumers
 * can transparently call local or Moleculer-remote providers.
 * Providers may still implement methods as sync (return T); the Moleculer
 * action handler and tRPC router await the result either way.
 */
/** Extract the status output type — or `never` when the cap has no status block. */
export type InferStatus<T extends CapabilityDefinition> = T extends {
    status: CapabilityStatusSchema<infer S>;
} ? z.infer<S> : never;
/** Contract for the `getStatus` method auto-injected when `status: {...}` is set. */
export interface DeviceStatusContribution<TStatus> {
    readonly getStatus: (input: {
        readonly deviceId: number;
    }) => Promise<TStatus | null>;
}
export type InferProvider<T extends CapabilityDefinition> = {
    readonly [K in keyof T['methods']]: T['methods'][K] extends CapabilityMethodSchema<infer TIn, infer TOut> ? T['methods'][K]['kind'] extends 'subscription' ? (input: z.infer<TIn>, push: (value: z.infer<TOut>) => void) => (() => void) | void : (input: z.infer<TIn>) => Promise<z.infer<TOut>> : never;
} & (T['exposesDeviceSettings'] extends true ? DeviceSettingsContribution : Record<never, never>) & (T extends {
    status: CapabilityStatusSchema;
} ? DeviceStatusContribution<InferStatus<T>> : Record<never, never>);
/**
 * Interface for native (per-device) providers — strips the
 * `DeviceSettingsContribution` surface that `exposesDeviceSettings: true`
 * bolts onto the system-level `InferProvider`.
 *
 * Native providers are registered via `DeviceContext.registerNativeCap`
 * and only serve the cap's device-scoped runtime methods for that single
 * device. They do NOT own per-device settings — those flow through the
 * addon's system-level provider's `applyDeviceSettingsPatch`, not
 * through the camera driver's native hook. Keeping the two surfaces
 * separate stops every device class (RtspCamera, OnvifCamera, …) from
 * having to stub out three unused methods.
 */
export type InferNativeProvider<T extends CapabilityDefinition> = {
    readonly [K in keyof T['methods']]: T['methods'][K] extends CapabilityMethodSchema<infer TIn, infer TOut> ? T['methods'][K]['kind'] extends 'subscription' ? (input: z.infer<TIn>, push: (value: z.infer<TOut>) => void) => (() => void) | void : (input: z.infer<TIn>) => Promise<z.infer<TOut>> : never;
} & (T extends {
    status: CapabilityStatusSchema;
} ? Partial<DeviceStatusContribution<InferStatus<T>>> : Record<never, never>);
/**
 * Infer event data types from a capability definition.
 * Returns a map of event name → data type.
 */
export type InferEvents<T extends CapabilityDefinition> = T['events'] extends Record<string, CapabilityEventSchema> ? {
    readonly [K in keyof T['events']]: T['events'][K] extends CapabilityEventSchema<infer TData> ? z.infer<TData> : never;
} : never;
/**
 * Infer the device-proxy facade interface for a device-scoped capability.
 *
 * Strips `deviceId` from every method's input so consumers of the client-side
 * proxy (bound to a specific device) don't have to pass it. The proxy layer
 * injects `deviceId` before forwarding to the tRPC/Moleculer action.
 *
 * Query/Mutation methods always return Promise<T>. Subscription methods keep
 * the push-based shape, without `deviceId` in the input payload.
 */
export type InferDeviceProxyCap<T extends CapabilityDefinition> = {
    readonly [K in keyof T['methods']]: T['methods'][K] extends CapabilityMethodSchema<infer TIn, infer TOut> ? T['methods'][K]['kind'] extends 'subscription' ? (input: Omit<z.infer<TIn>, 'deviceId'>, push: (value: z.infer<TOut>) => void) => (() => void) | void : (input: Omit<z.infer<TIn>, 'deviceId'>) => Promise<z.infer<TOut>> : never;
} & (T['status'] extends {
    schema: infer S;
} ? {
    readonly getStatus: (input?: {
        deviceId?: number;
    }) => Promise<S extends z.ZodType ? z.infer<S> | null : unknown>;
} : Record<never, never>) & (T['exposesDeviceSettings'] extends true ? {
    readonly getDeviceSettingsContribution: (input?: {
        deviceId?: number;
    }) => Promise<unknown>;
    readonly getDeviceLiveContribution: (input?: {
        deviceId?: number;
    }) => Promise<unknown>;
    readonly applyDeviceSettingsPatch: (input: {
        deviceId?: number;
        patch: Record<string, unknown>;
    }) => Promise<{
        readonly success: true;
    }>;
} : Record<never, never>);
/** Extract the capability name as a string literal type. */
export type InferName<T extends CapabilityDefinition> = T['name'];
/**
 * Infer the runtime-state slice TS shape from a cap definition.
 *
 * Lives inside the types package on purpose: `z.infer<>` is resolved
 * against THIS package's Zod version (built with Zod 4), so consumers
 * that resolve a different Zod major (e.g. core resolving Zod 3 from
 * the workspace root) still see the correct concrete shape — they
 * never need to call `z.infer` themselves.
 *
 * Falls back to `unknown` for caps without `runtimeState:` so call sites
 * can't accidentally narrow to `never` and lose property-access type
 * checking.
 */
export type InferRuntimeState<T extends CapabilityDefinition> = T extends {
    readonly runtimeState: infer R;
} ? R extends z.ZodType<infer Out> ? Out : R extends {
    readonly _output: infer Out;
} ? Out : unknown : unknown;
/** Options for a capability method definition. */
export interface CapabilityMethodOptions<TKind extends CapabilityMethodKind = CapabilityMethodKind> {
    /** tRPC procedure kind. Default: 'query'. */
    readonly kind?: TKind;
    /** Auth level. Default: 'protected'. */
    readonly auth?: CapabilityMethodAuth;
    /** Moleculer action timeout override (ms). Overrides broker global requestTimeout for this action only. */
    readonly timeoutMs?: number;
}
/** Shorthand to define a method schema */
export declare function method<TIn extends z.ZodType, TOut extends z.ZodType, TKind extends CapabilityMethodKind = 'query'>(input: TIn, output: TOut, options?: CapabilityMethodOptions<TKind>): CapabilityMethodSchema<TIn, TOut, TKind>;
/** Shorthand to define an event schema */
export declare function event<TData extends z.ZodType>(data: TData): CapabilityEventSchema<TData>;
//# sourceMappingURL=capability-definition.d.ts.map