import type { AtLeastOneOf } from "@homarr/common/types"; import type { IntegrationCategory, IntegrationKind } from "./integration"; export type WidgetModuleIconName = `Icon${string}`; export type WidgetModuleOperationKind = "query" | "mutation" | "subscription" | "route"; export interface WidgetModuleClientPolicy { refetchIntervalSeconds?: number | null; staleTimeSeconds?: number; persist?: boolean; } export interface WidgetModuleServerCachePolicy { namespace: string; ttlMs: number; staleIfErrorTtlMs?: number; scope: "integration" | "shared" | "private"; } export interface WidgetModuleOperation { name: string; kind?: WidgetModuleOperationKind; path: readonly [string, ...string[]]; client?: WidgetModuleClientPolicy; serverCache?: WidgetModuleServerCachePolicy; } type WidgetModuleIntegrationSelection = | { categories: Readonly>; kinds?: readonly IntegrationKind[]; } | { categories?: readonly IntegrationCategory[]; kinds: Readonly>; }; export type WidgetModuleIntegrationCapability = WidgetModuleIntegrationSelection & { excludeKinds?: readonly IntegrationKind[]; connectionOptional?: true; serverMaxIntegrations?: number; }; export interface WidgetModuleRouterRegistration { namespace: string; module: string; exportName: string; additionalWidgetKinds?: readonly string[]; mcp?: true | string; } export interface WidgetModuleDocumentation { slug: string; sourceDirectory: string; } /** * Pure authoring contract for a vertical widget module. * * This descriptor intentionally contains import paths instead of importing * client or server implementations. The sync tool turns them into explicit * static imports for Next.js and Turbopack without crossing runtime boundaries. */ export interface WidgetModuleDefinition { kind: string; icon: WidgetModuleIconName; clientEntry: string; documentation: WidgetModuleDocumentation; defaultSize?: Readonly<{ width: number; height: number }>; displayNameFromIntegration?: true; integration?: WidgetModuleIntegrationCapability; operations?: readonly WidgetModuleOperation[]; routers?: readonly WidgetModuleRouterRegistration[]; } export const defineWidgetModule = (definition: TDefinition) => definition; export type WidgetServerPermission = | "public" | "authenticated" | "integration-query" | "integration-interact" | "custom"; export interface WidgetServerInputSchema { parseAsync: (input: unknown) => Promise; } export interface WidgetServerContext { services: TServices; authorization: TAuthorization; signal: AbortSignal; } export interface WidgetServerOperationDefinition { kind: "query" | "mutation"; permission: WidgetServerPermission; input: WidgetServerInputSchema; cache?: WidgetModuleServerCachePolicy; resolve: (context: TContext, input: TInput) => Promise; } export interface WidgetServerSubscriptionDefinition { kind: "subscription"; permission: WidgetServerPermission; input: WidgetServerInputSchema; transport: "sse" | "websocket"; subscribe: (context: TContext, input: TInput) => AsyncIterable; } export interface WidgetServerRouteDefinition { kind: "route"; permission: WidgetServerPermission; handle: (context: TContext, request: TRequest) => Promise; } export const defineWidgetServerOperation = ( operation: WidgetServerOperationDefinition, ) => operation; export const defineWidgetServerSubscription = ( subscription: WidgetServerSubscriptionDefinition, ) => subscription; export const defineWidgetServerRoute = ( route: WidgetServerRouteDefinition, ) => route; export const defineWidgetServerModule = < const TModule extends { operations?: Readonly>; subscriptions?: Readonly>; routes?: Readonly>; }, >( module: TModule, ) => module;