@follow-app/client-sdk
Version:
TypeScript client SDK for Follow RSS Server API
1 lines • 101 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":[],"sources":["../src/types/errors.ts","../src/client/interceptors.ts","../src/client/base.ts","../src/shared/define-module.ts","../src/modules/actions/index.ts","../src/modules/admin/index.ts","../src/modules/ai/index.ts","../src/modules/ai-analytics/index.ts","../src/modules/ai-chat-sessions/index.ts","../src/modules/ai-task/index.ts","../src/modules/auth/index.ts","../src/modules/categories/index.ts","../src/modules/collections/index.ts","../src/modules/data/index.ts","../src/modules/discover/index.ts","../src/modules/entries/index.ts","../src/modules/feeds/index.ts","../src/modules/inboxes/index.ts","../src/modules/lists/index.ts","../src/modules/mcp/index.ts","../src/modules/messaging/index.ts","../src/modules/probes/index.ts","../src/modules/profiles/index.ts","../src/modules/reads/index.ts","../src/modules/referrals/index.ts","../src/modules/rsshub/index.ts","../src/modules/settings/index.ts","../src/modules/status/index.ts","../src/modules/subscriptions/index.ts","../src/modules/trending/index.ts","../src/modules/update/index.ts","../src/modules/upload/index.ts","../src/modules/wallets/constants.ts","../src/modules/wallets/index.ts","../src/modules/registry.ts","../src/shared/route-resolver.ts","../src/client/proxy.ts","../src/client/core.ts","../src/helper/index.ts"],"sourcesContent":["/**\n * Base error class for all Follow API errors\n */\nexport class FollowAPIError extends Error {\n constructor(\n message: string,\n public status: number,\n public code?: string,\n public data?: any,\n ) {\n super(message)\n this.name = \"FollowAPIError\"\n }\n}\n\nexport class NetworkError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"NetworkError\"\n }\n}\n/**\n * Authentication error for Follow API\n */\nexport class FollowAuthError extends FollowAPIError {\n constructor(message = \"Authentication required\", data?: any) {\n super(message, 401, \"AUTH_REQUIRED\", data)\n this.name = \"FollowAuthError\"\n }\n}\n\n/**\n * Validation error for Follow API requests\n */\nexport class FollowValidationError extends FollowAPIError {\n constructor(\n message: string,\n public validationErrors: any[],\n ) {\n super(message, 400, \"VALIDATION_ERROR\")\n this.name = \"FollowValidationError\"\n this.data = validationErrors\n }\n}\n\n/**\n * Network timeout error\n */\nexport class FollowTimeoutError extends FollowAPIError {\n constructor(message = \"Request timeout\") {\n super(message, 408, \"TIMEOUT_ERROR\")\n this.name = \"FollowTimeoutError\"\n }\n}\n","import type { RequestOptions } from \"../types\"\n\n/**\n * Base context object shared by all interceptors\n */\ninterface BaseInterceptorContext {\n url: string\n options: RequestOptions\n}\n\n/**\n * Context object passed to request interceptors\n */\nexport interface RequestInterceptorContext extends BaseInterceptorContext {}\n\n/**\n * Context object passed to response interceptors\n */\nexport interface ResponseInterceptorContext extends BaseInterceptorContext {\n response: Response\n}\n\n/**\n * Context object passed to error interceptors\n */\nexport interface ErrorInterceptorContext extends BaseInterceptorContext {\n response: Response | null\n error: Error\n}\n\n/**\n * Request interceptor function type\n */\nexport type RequestInterceptor = (\n ctx: RequestInterceptorContext,\n) =>\n | Promise<{ url: string, options: RequestOptions }> |\n { url: string, options: RequestOptions }\n\n/**\n * Response interceptor function type\n */\nexport type ResponseInterceptor = (\n ctx: ResponseInterceptorContext,\n) => Promise<Response> | Response\n\n/**\n * Error interceptor function type\n */\nexport type ErrorInterceptor = (\n ctx: ErrorInterceptorContext,\n) => Promise<Error | void> | Error | void\n\n/**\n * Interceptor manager for handling request/response middleware\n */\nexport class InterceptorManager {\n private requestInterceptors: RequestInterceptor[] = []\n private responseInterceptors: ResponseInterceptor[] = []\n private errorInterceptors: ErrorInterceptor[] = []\n\n /**\n * Generic method to add an interceptor to any array and return a cleanup function\n */\n private addInterceptor<T>(interceptor: T, interceptors: T[]): () => void {\n interceptors.push(interceptor)\n return () => {\n const index = interceptors.indexOf(interceptor)\n if (index !== -1) {\n interceptors.splice(index, 1)\n }\n }\n }\n\n /**\n * Add a request interceptor\n */\n addRequestInterceptor(interceptor: RequestInterceptor): () => void {\n return this.addInterceptor(interceptor.bind(null), this.requestInterceptors)\n }\n\n /**\n * Add a response interceptor\n */\n addResponseInterceptor(interceptor: ResponseInterceptor): () => void {\n return this.addInterceptor(interceptor.bind(null), this.responseInterceptors)\n }\n\n /**\n * Add an error interceptor\n */\n addErrorInterceptor(interceptor: ErrorInterceptor): () => void {\n return this.addInterceptor(interceptor.bind(null), this.errorInterceptors)\n }\n\n /**\n * Process request through all request interceptors\n */\n async processRequest(\n url: string,\n options: RequestOptions,\n ): Promise<{ url: string, options: RequestOptions }> {\n let currentUrl = url\n let currentOptions = options\n\n for (const interceptor of this.requestInterceptors) {\n const ctx: RequestInterceptorContext = {\n url: currentUrl,\n options: currentOptions,\n }\n const result = (await interceptor(ctx)) || ctx\n currentUrl = result.url\n currentOptions = result.options\n }\n\n return { url: currentUrl, options: currentOptions }\n }\n\n /**\n * Process response through all response interceptors\n */\n async processResponse(\n response: Response,\n url: string,\n options: RequestOptions,\n ): Promise<Response> {\n let currentResponse = response\n\n for (const interceptor of this.responseInterceptors) {\n const ctx: ResponseInterceptorContext = {\n url,\n options,\n response: currentResponse,\n }\n const returnedResponse = await interceptor(ctx)\n if (returnedResponse instanceof Response) {\n currentResponse = returnedResponse\n }\n // If interceptor returns undefined, it means the response should not be modified\n }\n\n return currentResponse\n }\n\n /**\n * Process error through all error interceptors\n */\n async processError(\n error: Error,\n response: Response | null,\n url: string,\n options: RequestOptions,\n ): Promise<Error | void> {\n let currentError: Error | void = error\n\n for (const interceptor of this.errorInterceptors) {\n const ctx: ErrorInterceptorContext = {\n url,\n options,\n response,\n error: currentError || error,\n }\n const result = await interceptor(ctx)\n if (result !== undefined) {\n currentError = result\n } else {\n // If interceptor returns undefined, it means the error should be handled/suppressed\n currentError = undefined\n break\n }\n }\n\n return currentError\n }\n\n /**\n * Clear all interceptors\n */\n clear(): void {\n this.requestInterceptors = []\n this.responseInterceptors = []\n this.errorInterceptors = []\n }\n}\n\n/**\n * Common interceptors for Follow API\n */\nexport const commonInterceptors = {\n /**\n * Add authentication token to requests\n */\n addAuthToken: (token: string): RequestInterceptor => {\n return (ctx) => {\n return {\n url: ctx.url,\n options: {\n ...ctx.options,\n headers: {\n ...ctx.options.headers,\n Authorization: `Bearer ${token}`,\n },\n },\n }\n }\n },\n\n /**\n * Log all requests\n */\n logRequests: (logger: {\n log: (message: string) => void\n }): RequestInterceptor => {\n return (ctx) => {\n logger.log(`Request: ${ctx.options.method || \"GET\"} ${ctx.url}`)\n return { url: ctx.url, options: ctx.options }\n }\n },\n\n /**\n * Log all responses\n */\n logResponses: (logger: {\n log: (message: string) => void\n }): ResponseInterceptor => {\n return (ctx) => {\n logger.log(\n `Response: ${ctx.response.status} ${ctx.options.method || \"GET\"} ${ctx.url}`,\n )\n return ctx.response\n }\n },\n\n /**\n * Retry failed requests\n */\n retryOnError: (maxRetries = 3, delay = 1000): ErrorInterceptor => {\n const retryCount = new WeakMap<Error, number>()\n\n return async (ctx) => {\n const currentRetries = retryCount.get(ctx.error) || 0\n\n if (currentRetries < maxRetries) {\n retryCount.set(ctx.error, currentRetries + 1)\n\n // Wait before retry\n await new Promise((resolve) =>\n setTimeout(resolve, delay * (currentRetries + 1)),\n )\n\n // Return undefined to indicate retry should happen\n return\n }\n\n // Max retries exceeded, return the error\n return ctx.error\n }\n },\n}\n","import type {\n ClientConfig,\n FollowAPIErrorResponse,\n FollowAPIResponse,\n RequestContentType,\n RequestOptions,\n} from \"../types\"\nimport {\n FollowAPIError,\n FollowAuthError,\n FollowTimeoutError,\n FollowValidationError,\n} from \"../types/errors\"\nimport { InterceptorManager } from \"./interceptors\"\n\n/**\n * Core HTTP client for Follow API using native fetch\n */\nexport class HttpClient {\n private config: Required<ClientConfig>\n private fetchInstance: typeof fetch\n private interceptors: InterceptorManager\n\n constructor(config: ClientConfig) {\n this.config = {\n timeout: 30000,\n headers: {},\n credentials: \"include\",\n fetch: globalThis.fetch,\n ...config,\n }\n this.fetchInstance = this.config.fetch\n this.interceptors = new InterceptorManager()\n }\n\n /**\n * Build URL with query parameters\n */\n private buildURL(\n path: string,\n query?: Record<string, string | number | boolean | string[]>,\n ): string {\n const url = new URL(path, this.config.baseURL)\n\n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (Array.isArray(value)) {\n value.forEach((v) => {\n url.searchParams.append(key, String(v))\n })\n } else {\n url.searchParams.append(key, String(value))\n }\n }\n })\n }\n\n return url.toString()\n }\n\n /**\n * Process request body based on content type\n */\n private processRequestBody(\n body: unknown,\n requestType?: RequestContentType | undefined,\n ): { processedBody: BodyInit | undefined, headers: Record<string, string> } {\n if (!body) {\n return { processedBody: undefined, headers: {} }\n }\n\n if (!requestType) {\n if (body instanceof FormData) {\n requestType = \"formData\"\n } else if (body instanceof ArrayBuffer) {\n requestType = \"arrayBuffer\"\n } else if (body instanceof Blob) {\n requestType = \"blob\"\n } else {\n requestType = \"json\"\n }\n }\n switch (requestType) {\n case \"json\": {\n return {\n processedBody: JSON.stringify(body),\n headers: { \"Content-Type\": \"application/json\" },\n }\n }\n\n case \"formData\": {\n if (body instanceof FormData) {\n return { processedBody: body, headers: {} }\n }\n if (typeof body === \"object\" && body !== null) {\n const formData = new FormData()\n Object.entries(body).forEach(([key, value]) => {\n if (value instanceof File || value instanceof Blob) {\n formData.append(key, value)\n } else if (value !== undefined && value !== null) {\n formData.append(key, String(value))\n }\n })\n return { processedBody: formData, headers: {} }\n }\n throw new Error(\"Invalid body type for formData request\")\n }\n\n case \"text\": {\n return {\n processedBody: typeof body === \"string\" ? body : String(body),\n headers: { \"Content-Type\": \"text/plain\" },\n }\n }\n\n case \"blob\": {\n if (body instanceof Blob) {\n return { processedBody: body, headers: {} }\n }\n throw new Error(\"Body must be a Blob for blob request type\")\n }\n\n case \"arrayBuffer\": {\n if (body instanceof ArrayBuffer) {\n return {\n processedBody: body,\n headers: { \"Content-Type\": \"application/octet-stream\" },\n }\n }\n throw new Error(\n \"Body must be an ArrayBuffer for arrayBuffer request type\",\n )\n }\n\n default: {\n throw new Error(`Unsupported request type: ${requestType}`)\n }\n }\n }\n\n /**\n * Handle response parsing and error handling\n */\n private async handleResponse<T>(\n response: Response,\n originalPath: string,\n finalUrl: string,\n requestOptions: RequestOptions,\n ): Promise<T> {\n const contentType = response.headers?.get(\"content-type\") || \"\"\n\n if (!response.ok) {\n let errorData: FollowAPIErrorResponse | null = null\n\n // Try to parse error response\n if (contentType.includes(\"application/json\")) {\n try {\n errorData = await response.json()\n } catch {\n // Fall back to status text if JSON parsing fails\n errorData = { code: response.status, message: response.statusText }\n }\n } else {\n errorData = { code: response.status, message: response.statusText }\n }\n\n // Extract pathname from final URL for error context\n const finalPathname = new URL(finalUrl).pathname\n\n // Create detailed error context\n const requestContext = {\n originalPath,\n finalPathname,\n method: requestOptions.method || \"GET\",\n query: requestOptions.query,\n body: requestOptions.body,\n headers: requestOptions.headers,\n }\n\n const contextStr = `${requestContext.method} ${finalPathname} (original: ${originalPath})`\n const argsStr = JSON.stringify(\n {\n query: requestContext.query,\n body: requestContext.body,\n headers: requestContext.headers,\n },\n null,\n 2,\n )\n\n // Handle specific error types\n if (response.status === 401) {\n throw new FollowAuthError(\n `${errorData?.message || \"Authentication required\"}\\nRequest: ${contextStr}\\nArgs: ${argsStr}`,\n errorData,\n )\n }\n\n if (response.status === 400 && errorData?.data) {\n throw new FollowValidationError(\n `${errorData.message || \"Validation error\"}\\nRequest: ${contextStr}\\nArgs: ${argsStr}`,\n Array.isArray(errorData.data) ? errorData.data : [errorData.data],\n )\n }\n\n throw new FollowAPIError(\n `${errorData?.message || response.statusText}\\nRequest: ${contextStr}\\nArgs: ${argsStr}`,\n response.status,\n errorData?.code?.toString(),\n errorData?.data,\n )\n }\n\n // Handle successful responses based on content type\n return requestOptions.asRaw ? response as unknown as T : this.parseResponseByContentType<T>(response, contentType)\n }\n\n /**\n * Parse response based on content type\n */\n private async parseResponseByContentType<T>(\n response: Response,\n contentType: string,\n ): Promise<T> {\n // Handle event stream\n if (contentType.includes(\"text/event-stream\")) {\n return response as unknown as T\n }\n\n // Handle JSON responses\n if (contentType.includes(\"application/json\")) {\n const jsonResponse = await response.json()\n\n // Handle Follow API response format\n if (typeof jsonResponse === \"object\" && \"code\" in jsonResponse) {\n const apiResponse = jsonResponse as FollowAPIResponse<T>\n\n return apiResponse as unknown as T\n }\n\n return jsonResponse\n }\n\n // Handle blob responses\n if (\n contentType.includes(\"application/octet-stream\") ||\n contentType.includes(\"image/\") ||\n contentType.includes(\"video/\") ||\n contentType.includes(\"audio/\")\n ) {\n const blobResponse = await response.blob()\n return blobResponse as unknown as T\n }\n\n // Handle text responses\n if (contentType.includes(\"text/\")) {\n const textResponse = await response.text()\n return textResponse as T\n }\n\n // Default to arrayBuffer for unknown binary types\n if (contentType.includes(\"application/\")) {\n const arrayBufferResponse = await response.arrayBuffer()\n return arrayBufferResponse as unknown as T\n }\n\n // Fallback to text\n const textResponse = await response.text()\n return textResponse as T\n }\n\n /**\n * Make an HTTP request\n */\n async request<T>(path: string, options: RequestOptions = {}): Promise<T> {\n let currentUrl = this.buildURL(path, options.query)\n let currentOptions = options\n let response: Response | null = null\n\n try {\n // Process request interceptors\n const interceptedRequest = await this.interceptors.processRequest(\n currentUrl,\n currentOptions,\n )\n currentUrl = interceptedRequest.url\n currentOptions = interceptedRequest.options\n\n const timeout = currentOptions.timeout || this.config.timeout\n\n // Create abort controller for timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // Combine signals if user provided one\n let { signal } = controller\n if (currentOptions.signal) {\n const combinedController = new AbortController()\n const cleanup = () => {\n clearTimeout(timeoutId)\n combinedController.abort()\n }\n\n currentOptions.signal.addEventListener(\"abort\", cleanup)\n controller.signal.addEventListener(\"abort\", cleanup)\n\n signal = combinedController.signal\n }\n\n // Process request body based on content type\n const { processedBody, headers: bodyHeaders } = this.processRequestBody(\n currentOptions.body,\n currentOptions.requestType,\n )\n\n response = await this.fetchInstance(currentUrl, {\n method: currentOptions.method || \"GET\",\n headers: {\n ...this.config.headers,\n ...bodyHeaders,\n ...currentOptions.headers,\n },\n credentials: this.config.credentials,\n body: processedBody,\n signal,\n })\n\n clearTimeout(timeoutId)\n\n // Process response interceptors\n const interceptedResponse = await this.interceptors.processResponse(\n response,\n currentUrl,\n currentOptions,\n )\n\n return this.handleResponse<T>(\n interceptedResponse,\n path,\n currentUrl,\n currentOptions,\n )\n } catch (error) {\n const processedError = await this.interceptors.processError(\n error instanceof Error ? error : new Error(\"Unknown error\"),\n response,\n currentUrl,\n currentOptions,\n )\n\n if (processedError) {\n throw processedError\n }\n\n // Handle specific error types\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw new FollowTimeoutError(\"Request timeout\")\n }\n\n // Re-throw our custom errors\n if (error instanceof FollowAPIError) {\n throw error\n }\n\n throw error\n }\n }\n\n /**\n * Convenience methods for different HTTP verbs\n */\n async get<T>(\n path: string,\n options?: Omit<RequestOptions, \"method\">,\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: \"GET\" })\n }\n\n async post<T>(\n path: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: \"POST\", body })\n }\n\n async put<T>(\n path: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: \"PUT\", body })\n }\n\n async patch<T>(\n path: string,\n body?: unknown,\n options?: Omit<RequestOptions, \"method\" | \"body\">,\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: \"PATCH\", body })\n }\n\n /**\n * Convenience method for form data uploads\n */\n async postForm<T>(\n path: string,\n formData: FormData | Record<string, unknown>,\n options?: Omit<RequestOptions, \"method\" | \"body\" | \"requestType\">,\n ): Promise<T> {\n return this.request<T>(path, {\n ...options,\n method: \"POST\",\n body: formData,\n requestType: \"formData\",\n })\n }\n\n /**\n * Convenience method for event stream responses\n */\n async getStream(\n path: string,\n options?: Omit<RequestOptions, \"method\">,\n ): Promise<Response> {\n return this.request<Response>(path, { ...options, method: \"GET\" })\n }\n\n async delete<T>(\n path: string,\n options?: Omit<RequestOptions, \"method\">,\n ): Promise<T> {\n return this.request<T>(path, { ...options, method: \"DELETE\" })\n }\n\n /**\n * Update client configuration\n */\n setConfig(config: Partial<ClientConfig>): void {\n this.config = { ...this.config, ...config }\n if (config.fetch) {\n this.fetchInstance = config.fetch\n }\n }\n\n /**\n * Set additional headers\n */\n setHeaders(headers: Record<string, string>): void {\n this.config.headers = { ...this.config.headers, ...headers }\n }\n\n /**\n * Set custom fetch instance\n */\n setFetch(fetchInstance: typeof fetch): void {\n this.fetchInstance = fetchInstance\n this.config.fetch = fetchInstance\n }\n\n /**\n * Get current configuration (readonly)\n */\n getConfig(): Readonly<Required<ClientConfig>> {\n return { ...this.config }\n }\n\n /**\n * Get interceptor manager for advanced usage\n */\n getInterceptors(): InterceptorManager {\n return this.interceptors\n }\n}\n","/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport type {\n HTTPMethod,\n RequestContentType,\n} from \"../types\"\n\n/**\n * Route definition with type annotations\n */\nexport interface RouteDefinition<TInput = any, TResponse = any> {\n method: HTTPMethod\n path: string\n params?: readonly string[]\n /** Fields that should be sent as query parameters */\n query?: readonly string[]\n /** Fields that should be sent as request body */\n body?: readonly string[]\n input?: TInput\n response?: TResponse\n requestType?: RequestContentType\n asRaw?: boolean\n}\n\n/**\n * Nested routes structure - supports any depth of nesting\n */\nexport type NestedRoutes = {\n [key: string]: RouteDefinition | NestedRoutes\n}\n\n/**\n * Module definition interface\n */\nexport interface ModuleDefinition<TRoutes extends NestedRoutes> {\n name: string\n prefix?: string\n routes: TRoutes\n api: ModuleAPI<TRoutes>\n}\n\n/**\n * Fetch options for route requests\n */\ninterface FetchOptions {\n headers?: Record<string, string>\n timeout?: number\n signal?: AbortSignal\n}\n\n/**\n * Check if arguments are required\n */\ntype IsRequired<TInput> = [TInput] extends [never] ?\n false :\n {} extends TInput ?\n false :\n true\n\n/**\n * Route function with proper argument requirements\n */\nexport type RouteFunction<TInput, TResponse> =\n IsRequired<TInput> extends true ?\n (args: TInput, options?: FetchOptions) => Promise<TResponse> :\n (args?: TInput, options?: FetchOptions) => Promise<TResponse>\n\n/**\n * Generate API types from route structure\n */\nexport type ModuleAPI<TRoutes> = {\n [K in keyof TRoutes]: TRoutes[K] extends RouteDefinition<\n infer TInput,\n infer TResponse\n > ?\n RouteFunction<TInput, TResponse> :\n TRoutes[K] extends NestedRoutes ?\n ModuleAPI<TRoutes[K]> :\n never;\n}\n\n/**\n * Legacy route args interface for backward compatibility\n */\nexport interface LegacyRouteArgs {\n params?: Record<string, string>\n query?: Record<string, string | number | boolean>\n body?: unknown\n headers?: Record<string, string>\n timeout?: number\n signal?: AbortSignal\n}\n\n/**\n * Helper function to define a single route with proper type inference\n */\nexport function defineRoute<TInput = never, TResponse = never>(\n method: HTTPMethod,\n path: string,\n options: {\n /** Fields that should be sent as query parameters */\n query?: readonly string[]\n /** Fields that should be sent as request body */\n body?: readonly string[]\n input?: TInput\n response?: TResponse\n requestType?: RequestContentType\n asRaw?: boolean\n } = {},\n): RouteDefinition<TInput, TResponse> {\n // Extract parameters from path string\n const params = extractParamsFromPath(path)\n\n return {\n method,\n path,\n params: params.length > 0 ? params : undefined,\n query: options.query,\n body: options.body,\n input: options.input,\n response: options.response,\n requestType: options.requestType,\n asRaw: options.asRaw,\n }\n}\n\n/**\n * Extract parameter names from a path string\n * e.g. \"/users/{userId}/posts\" -> [\"userId\"]\n */\nfunction extractParamsFromPath(path: string): string[] {\n const paramRegex = /\\{([^}]+)\\}/g\n const params: string[] = []\n let match\n\n while ((match = paramRegex.exec(path)) !== null) {\n params.push(match[1])\n }\n\n return params\n}\n\n/**\n * Helper type to infer params from path string\n */\nexport type InferParams<T extends string> =\n T extends `${string}{${infer Param}}${infer Rest}` ?\n { [K in Param]: string } & InferParams<Rest> :\n {}\n\n/**\n * Define a module with routes and metadata\n */\nexport function defineModule<TRoutes extends NestedRoutes>(definition: {\n name: string\n prefix?: string\n routes: TRoutes\n}): ModuleDefinition<TRoutes> {\n return {\n ...definition,\n api: {} as ModuleAPI<TRoutes>, // Will be created by proxy\n }\n}\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n ActionsGetResponse,\n ActionsPutRequest,\n ActionsPutResponse,\n} from \"./types\"\n\n/**\n * Actions module definition - User automation rules\n */\nexport const actionsModule = defineModule({\n name: \"actions\",\n prefix: \"/actions\",\n routes: {\n // Get user actions/automation rules\n get: defineRoute<never, ActionsGetResponse>(\"GET\", \"/\"),\n\n // Update user actions/automation rules\n put: defineRoute<ActionsPutRequest, ActionsPutResponse>(\"PUT\", \"/\"),\n },\n})\n\n// Export the API type\nexport type ActionsAPI = typeof actionsModule.api\nexport * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n AffectedUsersInput,\n AffectedUsersResponse,\n CleanRequest,\n CleanResponse,\n FeatureFlagListResponse,\n FeatureFlagResponse,\n FeatureFlagUpdateInput,\n FeatureStatsInput,\n FeatureStatsResponse,\n MessageResponse,\n MintRequest,\n MintResponse,\n RemoveOverrideInput,\n UserOverrideInput,\n} from \"./types\"\n\n/**\n * Admin module definition with nested routes\n */\nexport const adminModule = defineModule({\n name: \"admin\",\n prefix: \"/admin\",\n routes: {\n // Feature flags management (nested)\n featureFlags: {\n list: defineRoute<never, FeatureFlagListResponse>(\n \"GET\",\n \"/feature-flags\",\n ),\n\n update: defineRoute<FeatureFlagUpdateInput, FeatureFlagResponse>(\n \"PUT\",\n \"/feature-flags/{name}\",\n ),\n\n override: defineRoute<UserOverrideInput, MessageResponse>(\n \"POST\",\n \"/feature-flags/{name}/overrides\",\n ),\n\n removeOverride: defineRoute<RemoveOverrideInput, MessageResponse>(\n \"DELETE\",\n \"/feature-flags/{name}/overrides/{userId}\",\n ),\n\n stats: defineRoute<FeatureStatsInput, FeatureStatsResponse>(\n \"GET\",\n \"/feature-flags/{name}/stats\",\n ),\n\n affectedUsers: defineRoute<AffectedUsersInput, AffectedUsersResponse>(\n \"GET\",\n \"/feature-flags/{name}/affected-users\",\n ),\n },\n\n // Clean operations (nested)\n clean: {\n execute: defineRoute<CleanRequest, CleanResponse>(\"POST\", \"/clean\"),\n },\n\n // Mint operations (nested)\n mint: {\n execute: defineRoute<MintRequest, MintResponse>(\"POST\", \"/mintdscsafr\"),\n },\n },\n})\n\n// Export the API type\nexport type AdminAPI = typeof adminModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n AIMemoryCreateRequest,\n AIMemoryListQuery,\n AIMemoryListResponse,\n AIMemoryRecord,\n AIMemoryUpdateRequest,\n ChatRequest,\n ChatResponse,\n ConfigResponse,\n DailyRequest,\n DailyResponse,\n SummaryRequest,\n SummaryResponse,\n TextToSpeechRequest,\n TextToSpeechResponse,\n TitleRequest,\n TitleResponse,\n TranslationBatchRequest,\n TranslationBatchResponse,\n TranslationRequest,\n TranslationResponse,\n} from \"./types\"\n\n/**\n * AI module definition with nested AI-powered features\n */\nexport const aiModule = defineModule({\n name: \"ai\",\n prefix: \"/ai\",\n routes: {\n // AI chat interface - streaming response\n chat: defineRoute<ChatRequest, ChatResponse>(\"POST\", \"/chat\"),\n\n // Content summarization\n summary: defineRoute<SummaryRequest, SummaryResponse>(\"GET\", \"/summary\"),\n\n // Content translation\n translation: defineRoute<TranslationRequest, TranslationResponse>(\n \"GET\",\n \"/translation\",\n ),\n translationBatch: defineRoute<\n TranslationBatchRequest,\n TranslationBatchResponse\n >(\"POST\", \"/translation/batch\", {\n asRaw: true,\n }),\n\n // Text-to-speech streaming audio\n tts: defineRoute<TextToSpeechRequest, TextToSpeechResponse>(\n \"POST\",\n \"/tts\",\n {\n asRaw: true,\n },\n ),\n\n // Title generation for chat/content\n summaryTitle: defineRoute<TitleRequest, TitleResponse>(\n \"POST\",\n \"/summary-title\",\n ),\n\n // Daily summaries\n daily: defineRoute<DailyRequest, DailyResponse>(\"GET\", \"/daily\"),\n\n // AI chat configuration\n config: defineRoute<never, ConfigResponse>(\"GET\", \"/chat/config\"),\n\n memory: {\n list: defineRoute<AIMemoryListQuery, AIMemoryListResponse>(\"GET\", \"/memory\"),\n create: defineRoute<AIMemoryCreateRequest, AIMemoryRecord>(\"POST\", \"/memory\"),\n update: defineRoute<AIMemoryUpdateRequest, AIMemoryRecord>(\"PATCH\", \"/memory/{memoryId}\"),\n delete: defineRoute<{ memoryId: string }, { success: boolean }>(\n \"DELETE\",\n \"/memory/{memoryId}\",\n ),\n },\n },\n})\n\n// Export the API type\nexport type AIAPI = typeof aiModule.api\n\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type { AnalyticsRequest, AnalyticsResponse } from \"./types\"\n\n/**\n * AI Analytics module definition for usage analytics and insights\n */\nexport const aiAnalyticsModule = defineModule({\n name: \"ai-analytics\",\n prefix: \"/ai/analytics\",\n routes: {\n // Get AI usage analytics\n get: defineRoute<AnalyticsRequest, AnalyticsResponse>(\"GET\", \"/\"),\n },\n})\n\n// Export the API type\nexport type AIAnalyticsAPI = typeof aiAnalyticsModule.api\n\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n DeleteSessionRequest,\n GetMessagesQuery,\n GetMessagesResponse,\n GetSessionRequest,\n GetUnreadQuery,\n GetUnreadResponse,\n ListSessionsQuery,\n ListSessionsResponse,\n MarkSeenRequest,\n SessionResponse,\n UpdateSessionRequest,\n} from \"./types\"\n\n/**\n * AI Chat Sessions module - comprehensive chat session management\n * Base path: /ai/chat-sessions\n */\nexport const aiChatSessionsModule = defineModule({\n name: \"aiChatSessions\",\n prefix: \"/ai/chat-sessions\",\n routes: {\n // List chat sessions\n list: defineRoute<ListSessionsQuery, ListSessionsResponse>(\"GET\", \"/\"),\n\n // Get specific session\n get: defineRoute<GetSessionRequest, SessionResponse>(\"GET\", \"/{chatId}\"),\n\n // Create new session\n // create: defineRoute<CreateSessionRequest, SessionResponse>(\"POST\", \"/\"),\n\n // Update session\n update: defineRoute<UpdateSessionRequest, SessionResponse>(\"PATCH\", \"/{chatId}\"),\n\n // Delete session\n delete: defineRoute<DeleteSessionRequest, { success: boolean }>(\n \"DELETE\",\n \"/{chatId}\",\n ),\n\n // Nested routes\n messages: {\n get: defineRoute<GetMessagesQuery, GetMessagesResponse>(\"GET\", \"/{chatId}/messages\"),\n },\n\n markSeen: defineRoute<MarkSeenRequest, SessionResponse>(\"POST\", \"/{chatId}/mark-seen\"),\n\n unread: defineRoute<GetUnreadQuery, GetUnreadResponse>(\"GET\", \"/unread\"),\n },\n})\n\n// Export the API type\nexport type AIChatSessionsAPI = typeof aiChatSessionsModule.api\n\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n CreateTaskRequest,\n TaskCreateResponse,\n TaskDeleteResponse,\n TaskGetResponse,\n TaskListResponse,\n TaskTestRunResponse,\n TaskUpdateResponse,\n UpdateTaskRequest,\n} from \"./types\"\n\n/**\n * AI Task module - standalone module for managing AI scheduled tasks\n * Base path: /ai/task\n */\nexport const aiTaskModule = defineModule({\n name: \"aiTask\",\n prefix: \"/ai/task\",\n routes: {\n // List all tasks\n list: defineRoute<never, TaskListResponse>(\"GET\", \"/\"),\n\n // Get a task by ID\n get: defineRoute<{ id: string }, TaskGetResponse>(\"GET\", \"/{id}\"),\n\n // Create a new task\n create: defineRoute<CreateTaskRequest, TaskCreateResponse>(\n \"POST\",\n \"/\",\n ),\n\n // Update an existing task\n update: defineRoute<UpdateTaskRequest, TaskUpdateResponse>(\n \"PUT\",\n \"/{id}\",\n ),\n\n // Delete a task by ID\n delete: defineRoute<{ id: string }, TaskDeleteResponse>(\n \"DELETE\",\n \"/{id}\",\n ),\n\n // Test run (execute immediately)\n testRun: defineRoute<{ id: string }, TaskTestRunResponse>(\n \"POST\",\n \"/{id}/test-run\",\n ),\n },\n})\n\n// Export the API type\nexport type AITaskAPI = typeof aiTaskModule.api\n\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n AuthSessionResponse,\n} from \"./types\"\n\n/**\n * Authentication module definition with Better Auth integration\n *\n * This module provides client SDK interfaces for all Better Auth endpoints.\n * The endpoints follow Better Auth's standard path conventions under /better-auth prefix.\n */\nexport const authModule = defineModule({\n name: \"auth\",\n prefix: \"/better-auth\",\n routes: {\n // Session management\n getSession: defineRoute<void, AuthSessionResponse>(\"GET\", \"/get-session\"),\n },\n})\n\n// Export the API type\nexport type AuthAPI = typeof authModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n CategoriesGetQuery,\n CategoriesGetResponse,\n CategoryDeleteRequest,\n CategoryDeleteResponse,\n CategoryPatchRequest,\n CategoryPatchResponse,\n} from \"./types\"\n\n/**\n * Categories module definition - Subscription categorization\n */\nexport const categoriesModule = defineModule({\n name: \"categories\",\n prefix: \"/categories\",\n routes: {\n // Get user categories\n get: defineRoute<CategoriesGetQuery, CategoriesGetResponse>(\"GET\", \"/\"),\n\n // Update category for subscription\n update: defineRoute<CategoryPatchRequest, CategoryPatchResponse>(\n \"PATCH\",\n \"/\",\n ),\n\n // Delete category\n delete: defineRoute<CategoryDeleteRequest, CategoryDeleteResponse>(\n \"DELETE\",\n \"/\",\n ),\n },\n})\n\n// Export the API type\nexport type CategoriesAPI = typeof categoriesModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n CollectionCheckQuery,\n CollectionCheckResponse,\n CollectionCreateRequest,\n CollectionCreateResponse,\n CollectionDeleteRequest,\n CollectionDeleteResponse,\n} from \"./types\"\n\n/**\n * Collections module definition - Content collections management\n */\nexport const collectionsModule = defineModule({\n name: \"collections\",\n prefix: \"/collections\",\n routes: {\n // Check if entry is in collection\n get: defineRoute<CollectionCheckQuery, CollectionCheckResponse>(\"GET\", \"/\"),\n\n // Add entry to collection\n post: defineRoute<CollectionCreateRequest, CollectionCreateResponse>(\n \"POST\",\n \"/\",\n ),\n\n // Remove entry from collection\n delete: defineRoute<CollectionDeleteRequest, CollectionDeleteResponse>(\n \"DELETE\",\n \"/\",\n ),\n },\n})\n\n// Export the API type\nexport type CollectionsAPI = typeof collectionsModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type { GoogleAnalyticsRequest, GoogleAnalyticsResponse } from \"./types\"\n\n/**\n * Data module for Google Analytics data collection\n */\nexport const dataModule = defineModule({\n name: \"data\",\n prefix: \"/data\",\n routes: {\n // Send Google Analytics data\n sendAnalytics: defineRoute<GoogleAnalyticsRequest, GoogleAnalyticsResponse>(\"POST\", \"/g\"),\n },\n})\n\n// Export the API type\nexport type DataAPI = typeof dataModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n DiscoverRequest,\n DiscoverResponse,\n RSSHubAnalyticsQuery,\n RSSHubAnalyticsResponse,\n RSSHubQuery,\n RSSHubResponse,\n RSSHubRouteQuery,\n RSSHubRouteResponse,\n} from \"./types\"\n\n/**\n * Discover module definition - Feed and list discovery\n */\nexport const discoverModule = defineModule({\n name: \"discover\",\n prefix: \"/discover\",\n routes: {\n // Main discovery endpoint\n discover: defineRoute<DiscoverRequest, DiscoverResponse>(\"POST\", \"/\"),\n\n // RSSHub integration endpoints\n rsshub: defineRoute<RSSHubQuery, RSSHubResponse>(\"GET\", \"/rsshub\"),\n\n rsshubRoute: defineRoute<RSSHubRouteQuery, RSSHubRouteResponse>(\n \"GET\",\n \"/rsshub/route\",\n ),\n\n rsshubAnalytics: defineRoute<RSSHubAnalyticsQuery, RSSHubAnalyticsResponse>(\n \"GET\",\n \"/rsshub-analytics\",\n ),\n },\n})\n\n// Export the API type\nexport type DiscoverAPI = typeof discoverModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import type { StructuredSuccessResponse } from \"@/types\"\n\nimport { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n CheckNewEntriesQuery,\n CheckNewEntriesResponse,\n EntryGetByIdResponse,\n EntryGetQuery,\n EntryListRequest,\n EntryListResponse,\n EntryPreviewRequest,\n EntryPreviewResponse,\n EntryReadabilityRequest,\n EntryReadabilityResponse,\n EntryStreamRequest,\n EntryTagsQueryRequest,\n EntryTagsQueryResponse,\n EntryTranscriptionRequest,\n EntryTranscriptionResponse,\n InboxEntryGetQuery,\n InboxEntryGetResponse,\n InboxListEntryRequestInput,\n InboxListEntryResponse,\n InboxRemoveInput,\n ReadHistoriesInput,\n ReadHistoriesResponse,\n} from \"./types\"\n\n/**\n * Entries module definition with nested routes\n */\nexport const entriesModule = defineModule({\n name: \"entries\",\n prefix: \"/entries\",\n routes: {\n // Basic entry operations\n get: defineRoute<EntryGetQuery, EntryGetByIdResponse>(\"GET\", \"/\"),\n list: defineRoute<EntryListRequest, EntryListResponse>(\"POST\", \"/\"),\n\n preview: defineRoute<EntryPreviewRequest, EntryPreviewResponse>(\n \"POST\",\n \"/preview\",\n ),\n readability: defineRoute<EntryReadabilityRequest, EntryReadabilityResponse>(\n \"GET\",\n \"/readability\",\n ),\n transcription: defineRoute<\n EntryTranscriptionRequest,\n EntryTranscriptionResponse\n >(\"GET\", \"/transcription\"),\n\n stream: defineRoute<EntryStreamRequest, Response>(\"POST\", \"/stream\", {\n asRaw: true,\n }),\n\n // Check for new entries\n checkNew: defineRoute<CheckNewEntriesQuery, CheckNewEntriesResponse>(\n \"GET\",\n \"/check-new\",\n ),\n\n tagsQuery: defineRoute<EntryTagsQueryRequest, EntryTagsQueryResponse>(\n \"POST\",\n \"/tags/query\",\n ),\n\n // Read histories\n readHistories: defineRoute<ReadHistoriesInput, ReadHistoriesResponse>(\n \"GET\",\n \"/read-histories/{id}\",\n ),\n\n // Inbox operations (nested)\n inbox: {\n get: defineRoute<InboxEntryGetQuery, InboxEntryGetResponse>(\n \"GET\",\n \"/inbox\",\n ),\n\n list: defineRoute<InboxListEntryRequestInput, InboxListEntryResponse>(\n \"POST\",\n \"/inbox\",\n ),\n\n delete: defineRoute<InboxRemoveInput, StructuredSuccessResponse>(\n \"DELETE\",\n \"/inbox\",\n ),\n },\n },\n})\n\n// Export the API type\nexport type EntriesAPI = typeof entriesModule.api\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n FeedAnalyticsRequest,\n FeedAnalyticsResponse,\n FeedClaimChallengeRequest,\n FeedClaimChallengeResponse,\n FeedClaimListResponse,\n FeedClaimMessageQuery,\n FeedClaimMessageResponse,\n FeedGetQuery,\n FeedGetResponse,\n FeedRefreshQuery,\n FeedRefreshResponse,\n FeedResetQuery,\n FeedResetResponse,\n} from \"./types\"\n\n/**\n * Feeds module definition with nested routes\n */\nexport const feedsModule = defineModule({\n name: \"feeds\",\n prefix: \"/feeds\",\n routes: {\n // Basic feed operations\n get: defineRoute<FeedGetQuery, FeedGetResponse>(\"GET\", \"/\"),\n\n refresh: defineRoute<FeedRefreshQuery, FeedRefreshResponse>(\n \"GET\",\n \"/refresh\",\n ),\n\n reset: defineRoute<FeedResetQuery, FeedResetResponse>(\"GET\", \"/reset\"),\n\n analytics: defineRoute<FeedAnalyticsRequest, FeedAnalyticsResponse>(\n \"POST\",\n \"/analytics\",\n ),\n\n // Feed claiming operations (nested)\n claim: {\n challenge: defineRoute<\n FeedClaimChallengeRequest,\n FeedClaimChallengeResponse\n >(\"POST\", \"/claim/challenge\"),\n\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n list: defineRoute<{}, FeedClaimListResponse>(\n \"GET\",\n \"/claim/list\",\n ),\n\n message: defineRoute<FeedClaimMessageQuery, FeedClaimMessageResponse>(\n \"GET\",\n \"/claim/message\",\n ),\n },\n },\n})\n\n// Export the API type\nexport type FeedsAPI = typeof feedsModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n InboxCreateRequest,\n InboxCreateResponse,\n InboxDeleteRequest,\n InboxDeleteResponse,\n InboxEmailRequest,\n InboxEmailResponse,\n InboxGetQuery,\n InboxGetResponse,\n InboxListResponse,\n InboxUpdateRequest,\n InboxUpdateResponse,\n InboxWebhookRequest,\n InboxWebhookResponse,\n} from \"./types\"\n\n/**\n * Inboxes module definition - Email inboxes management\n */\nexport const inboxesModule = defineModule({\n name: \"inboxes\",\n prefix: \"/inboxes\",\n routes: {\n // Get specific inbox by handle\n get: defineRoute<InboxGetQuery, InboxGetResponse>(\"GET\", \"/\"),\n\n // List user's inboxes\n list: defineRoute<never, InboxListResponse>(\"GET\", \"/list\"),\n\n // Create new inbox\n post: defineRoute<InboxCreateRequest, InboxCreateResponse>(\"POST\", \"/\"),\n\n // Update inbox\n put: defineRoute<InboxUpdateRequest, InboxUpdateResponse>(\"PUT\", \"/\"),\n\n // Delete inbox\n delete: defineRoute<InboxDeleteRequest, InboxDeleteResponse>(\"DELETE\", \"/\"),\n\n // Email operations\n email: defineRoute<InboxEmailRequest, InboxEmailResponse>(\"POST\", \"/email\"),\n\n // Webhook operations\n webhook: defineRoute<InboxWebhookRequest, InboxWebhookResponse>(\n \"POST\",\n \"/webhook\",\n ),\n },\n})\n\n// Export the API type\nexport type InboxesAPI = typeof inboxesModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n AddFeedsRequest,\n AddFeedsResponse,\n CreateListRequest,\n CreateListResponse,\n DeleteListRequest,\n DeleteListResponse,\n GetListQuery,\n GetListResponse,\n ListsListQuery,\n ListUserListsResponse,\n RemoveFeedRequest,\n RemoveFeedResponse,\n UpdateListRequest,\n UpdateListResponse,\n} from \"./types\"\n\n/**\n * Lists module definition with core routes\n */\nexport const listsModule = defineModule({\n name: \"lists\",\n prefix: \"/lists\",\n routes: {\n // Basic list operations\n get: defineRoute<GetListQuery, GetListResponse>(\"GET\", \"/\"),\n list: defineRoute<ListsListQuery, ListUserListsResponse>(\"GET\", \"/list\"),\n create: defineRoute<CreateListRequest, CreateListResponse>(\"POST\", \"/\"),\n update: defineRoute<UpdateListRequest, UpdateListResponse>(\"PATCH\", \"/\"),\n delete: defineRoute<DeleteListRequest, DeleteListResponse>(\"DELETE\", \"/\"),\n\n // Feed management\n addFeeds: defineRoute<AddFeedsRequest, AddFeedsResponse>(\"POST\", \"/feeds\"),\n removeFeed: defineRoute<RemoveFeedRequest, RemoveFeedResponse>(\n \"DELETE\",\n \"/feeds\",\n ),\n },\n})\n\n// Export the API type\nexport type ListsAPI = typeof listsModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n ConnectionParams,\n CreateConnectionRequest,\n CreateConnectionResponse,\n DeleteConnectionResponse,\n GetConnectionsResponse,\n GetToolsResponse,\n RefreshToolsRequest,\n RefreshToolsResponse,\n UpdateConnectionRequest,\n UpdateConnectionResponse,\n} from \"./types\"\n\n/**\n * MCP (Model Context Protocol) module definition\n * Handles MCP server connections and tool management\n */\nexport const mcpModule = defineModule({\n name: \"mcp\",\n prefix: \"/mcp\",\n routes: {\n // Connection management\n createConnection: defineRoute<CreateConnectionRequest, CreateConnectionResponse>(\n \"POST\",\n \"/connections\",\n ),\n\n updateConnection: defineRoute<UpdateConnectionRequest, UpdateConnectionResponse>(\n \"PUT\",\n \"/connections/{connectionId}\",\n ),\n\n getConnections: defineRoute<never, GetConnectionsResponse>(\n \"GET\",\n \"/connections\",\n ),\n\n deleteConnection: defineRoute<ConnectionParams, DeleteConnectionResponse>(\n \"DELETE\",\n \"/connections/{connectionId}\",\n ),\n\n // Tool management\n getTools: defineRoute<ConnectionParams, GetToolsResponse>(\n \"GET\",\n \"/connections/{connectionId}/tools\",\n ),\n\n refreshTools: defineRoute<RefreshToolsRequest, RefreshToolsResponse>(\n \"POST\",\n \"/tools/refresh\",\n ),\n\n },\n})\n\n// Export the API type\nexport type McpAPI = typeof mcpModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\n","import { defineModule, defineRoute } from \"../../shared/define-module\"\nimport type {\n CreateMessagingTokenRequest,\n CreateMessagingTokenResponse,\n DeleteMessagingTokenRequest,\n DeleteMessagingTokenResponse,\n GetMessagingTokensResponse,\n TestMessagingQuery,\n TestMessagingResponse,\n} from \"./types\"\n\n/**\n * Messaging module for push notification management\n */\nexport const messagingModule = defineModule({\n name: \"messaging\",\n prefix: \"/messaging\",\n routes: {\n // List messaging tokens for user\n getTokens: defineRoute<never, GetMessagingTokensResponse>(\"GET\", \"/\"),\n\n // Create or update messaging token\n createToken: defineRoute<\n CreateMessagingTokenRequest,\n CreateMessagingTokenResponse\n >(\"POST\", \"/\"),\n\n // Remove messaging token\n deleteToken: defineRoute<\n DeleteMessagingTokenRequest,\n DeleteMessagingTokenResponse\n >(\"DELETE\", \"/\"),\n\n // Test messaging notification\n testNotification: defineRoute<TestMessagingQuery, TestMessagingResponse>(\n \"GET\",\n \"/test\",\n ),\n },\n})\n\n// Export the API type\nexport type MessagingAPI = typeof messagingModule.api\n\n// Re-export types for external consumption\nexport type * from \"./types\"\