UNPKG

@follow-app/client-sdk

Version:

TypeScript client SDK for Follow RSS Server API

1,427 lines (1,387 loc) 39.4 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); let _folo_services_constants = require("@folo-services/constants"); let _folo_services_exceptions = require("@folo-services/exceptions"); //#region src/types/errors.ts /** * Base error class for all Follow API errors */ var FollowAPIError = class extends Error { constructor(message, status, code, data) { super(message); this.status = status; this.code = code; this.data = data; this.name = "FollowAPIError"; } }; var NetworkError = class extends Error { constructor(message) { super(message); this.name = "NetworkError"; } }; /** * Authentication error for Follow API */ var FollowAuthError = class extends FollowAPIError { constructor(message = "Authentication required", data) { super(message, 401, "AUTH_REQUIRED", data); this.name = "FollowAuthError"; } }; /** * Validation error for Follow API requests */ var FollowValidationError = class extends FollowAPIError { constructor(message, validationErrors) { super(message, 400, "VALIDATION_ERROR"); this.validationErrors = validationErrors; this.name = "FollowValidationError"; this.data = validationErrors; } }; /** * Network timeout error */ var FollowTimeoutError = class extends FollowAPIError { constructor(message = "Request timeout") { super(message, 408, "TIMEOUT_ERROR"); this.name = "FollowTimeoutError"; } }; //#endregion //#region src/client/interceptors.ts /** * Interceptor manager for handling request/response middleware */ var InterceptorManager = class { requestInterceptors = []; responseInterceptors = []; errorInterceptors = []; /** * Generic method to add an interceptor to any array and return a cleanup function */ addInterceptor(interceptor, interceptors) { interceptors.push(interceptor); return () => { const index = interceptors.indexOf(interceptor); if (index !== -1) interceptors.splice(index, 1); }; } /** * Add a request interceptor */ addRequestInterceptor(interceptor) { return this.addInterceptor(interceptor.bind(null), this.requestInterceptors); } /** * Add a response interceptor */ addResponseInterceptor(interceptor) { return this.addInterceptor(interceptor.bind(null), this.responseInterceptors); } /** * Add an error interceptor */ addErrorInterceptor(interceptor) { return this.addInterceptor(interceptor.bind(null), this.errorInterceptors); } /** * Process request through all request interceptors */ async processRequest(url, options) { let currentUrl = url; let currentOptions = options; for (const interceptor of this.requestInterceptors) { const ctx = { url: currentUrl, options: currentOptions }; const result = await interceptor(ctx) || ctx; currentUrl = result.url; currentOptions = result.options; } return { url: currentUrl, options: currentOptions }; } /** * Process response through all response interceptors */ async processResponse(response, url, options) { let currentResponse = response; for (const interceptor of this.responseInterceptors) { const returnedResponse = await interceptor({ url, options, response: currentResponse }); if (returnedResponse instanceof Response) currentResponse = returnedResponse; } return currentResponse; } /** * Process error through all error interceptors */ async processError(error, response, url, options) { let currentError = error; for (const interceptor of this.errorInterceptors) { const result = await interceptor({ url, options, response, error: currentError || error }); if (result !== void 0) currentError = result; else { currentError = void 0; break; } } return currentError; } /** * Clear all interceptors */ clear() { this.requestInterceptors = []; this.responseInterceptors = []; this.errorInterceptors = []; } }; /** * Common interceptors for Follow API */ const commonInterceptors = { addAuthToken: (token) => { return (ctx) => { return { url: ctx.url, options: { ...ctx.options, headers: { ...ctx.options.headers, Authorization: `Bearer ${token}` } } }; }; }, logRequests: (logger) => { return (ctx) => { logger.log(`Request: ${ctx.options.method || "GET"} ${ctx.url}`); return { url: ctx.url, options: ctx.options }; }; }, logResponses: (logger) => { return (ctx) => { logger.log(`Response: ${ctx.response.status} ${ctx.options.method || "GET"} ${ctx.url}`); return ctx.response; }; }, retryOnError: (maxRetries = 3, delay = 1e3) => { const retryCount = /* @__PURE__ */ new WeakMap(); return async (ctx) => { const currentRetries = retryCount.get(ctx.error) || 0; if (currentRetries < maxRetries) { retryCount.set(ctx.error, currentRetries + 1); await new Promise((resolve) => setTimeout(resolve, delay * (currentRetries + 1))); return; } return ctx.error; }; } }; //#endregion //#region src/client/base.ts /** * Core HTTP client for Follow API using native fetch */ var HttpClient = class { config; fetchInstance; interceptors; constructor(config) { this.config = { timeout: 3e4, headers: {}, credentials: "include", fetch: globalThis.fetch, ...config }; this.fetchInstance = this.config.fetch; this.interceptors = new InterceptorManager(); } /** * Build URL with query parameters */ buildURL(path, query) { const url = new URL(path, this.config.baseURL); if (query) Object.entries(query).forEach(([key, value]) => { if (value !== void 0 && value !== null) if (Array.isArray(value)) value.forEach((v) => { url.searchParams.append(key, String(v)); }); else url.searchParams.append(key, String(value)); }); return url.toString(); } /** * Process request body based on content type */ processRequestBody(body, requestType) { if (!body) return { processedBody: void 0, headers: {} }; if (!requestType) if (body instanceof FormData) requestType = "formData"; else if (body instanceof ArrayBuffer) requestType = "arrayBuffer"; else if (body instanceof Blob) requestType = "blob"; else requestType = "json"; switch (requestType) { case "json": return { processedBody: JSON.stringify(body), headers: { "Content-Type": "application/json" } }; case "formData": if (body instanceof FormData) return { processedBody: body, headers: {} }; if (typeof body === "object" && body !== null) { const formData = new FormData(); Object.entries(body).forEach(([key, value]) => { if (value instanceof File || value instanceof Blob) formData.append(key, value); else if (value !== void 0 && value !== null) formData.append(key, String(value)); }); return { processedBody: formData, headers: {} }; } throw new Error("Invalid body type for formData request"); case "text": return { processedBody: typeof body === "string" ? body : String(body), headers: { "Content-Type": "text/plain" } }; case "blob": if (body instanceof Blob) return { processedBody: body, headers: {} }; throw new Error("Body must be a Blob for blob request type"); case "arrayBuffer": if (body instanceof ArrayBuffer) return { processedBody: body, headers: { "Content-Type": "application/octet-stream" } }; throw new Error("Body must be an ArrayBuffer for arrayBuffer request type"); default: throw new Error(`Unsupported request type: ${requestType}`); } } /** * Handle response parsing and error handling */ async handleResponse(response, originalPath, finalUrl, requestOptions) { const contentType = response.headers?.get("content-type") || ""; if (!response.ok) { let errorData = null; if (contentType.includes("application/json")) try { errorData = await response.json(); } catch { errorData = { code: response.status, message: response.statusText }; } else errorData = { code: response.status, message: response.statusText }; const finalPathname = new URL(finalUrl).pathname; const requestContext = { originalPath, finalPathname, method: requestOptions.method || "GET", query: requestOptions.query, body: requestOptions.body, headers: requestOptions.headers }; const contextStr = `${requestContext.method} ${finalPathname} (original: ${originalPath})`; const argsStr = JSON.stringify({ query: requestContext.query, body: requestContext.body, headers: requestContext.headers }, null, 2); if (response.status === 401) throw new FollowAuthError(`${errorData?.message || "Authentication required"}\nRequest: ${contextStr}\nArgs: ${argsStr}`, errorData); if (response.status === 400 && errorData?.data) throw new FollowValidationError(`${errorData.message || "Validation error"}\nRequest: ${contextStr}\nArgs: ${argsStr}`, Array.isArray(errorData.data) ? errorData.data : [errorData.data]); throw new FollowAPIError(`${errorData?.message || response.statusText}\nRequest: ${contextStr}\nArgs: ${argsStr}`, response.status, errorData?.code?.toString(), errorData?.data); } return requestOptions.asRaw ? response : this.parseResponseByContentType(response, contentType); } /** * Parse response based on content type */ async parseResponseByContentType(response, contentType) { if (contentType.includes("text/event-stream")) return response; if (contentType.includes("application/json")) { const jsonResponse = await response.json(); if (typeof jsonResponse === "object" && "code" in jsonResponse) return jsonResponse; return jsonResponse; } if (contentType.includes("application/octet-stream") || contentType.includes("image/") || contentType.includes("video/") || contentType.includes("audio/")) return await response.blob(); if (contentType.includes("text/")) return await response.text(); if (contentType.includes("application/")) return await response.arrayBuffer(); return await response.text(); } /** * Make an HTTP request */ async request(path, options = {}) { let currentUrl = this.buildURL(path, options.query); let currentOptions = options; let response = null; try { const interceptedRequest = await this.interceptors.processRequest(currentUrl, currentOptions); currentUrl = interceptedRequest.url; currentOptions = interceptedRequest.options; const timeout = currentOptions.timeout || this.config.timeout; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); let { signal } = controller; if (currentOptions.signal) { const combinedController = new AbortController(); const cleanup = () => { clearTimeout(timeoutId); combinedController.abort(); }; currentOptions.signal.addEventListener("abort", cleanup); controller.signal.addEventListener("abort", cleanup); signal = combinedController.signal; } const { processedBody, headers: bodyHeaders } = this.processRequestBody(currentOptions.body, currentOptions.requestType); response = await this.fetchInstance(currentUrl, { method: currentOptions.method || "GET", headers: { ...this.config.headers, ...bodyHeaders, ...currentOptions.headers }, credentials: this.config.credentials, body: processedBody, signal }); clearTimeout(timeoutId); const interceptedResponse = await this.interceptors.processResponse(response, currentUrl, currentOptions); return this.handleResponse(interceptedResponse, path, currentUrl, currentOptions); } catch (error) { const processedError = await this.interceptors.processError(error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown error"), response, currentUrl, currentOptions); if (processedError) throw processedError; if (error instanceof DOMException && error.name === "AbortError") throw new FollowTimeoutError("Request timeout"); if (error instanceof FollowAPIError) throw error; throw error; } } /** * Convenience methods for different HTTP verbs */ async get(path, options) { return this.request(path, { ...options, method: "GET" }); } async post(path, body, options) { return this.request(path, { ...options, method: "POST", body }); } async put(path, body, options) { return this.request(path, { ...options, method: "PUT", body }); } async patch(path, body, options) { return this.request(path, { ...options, method: "PATCH", body }); } /** * Convenience method for form data uploads */ async postForm(path, formData, options) { return this.request(path, { ...options, method: "POST", body: formData, requestType: "formData" }); } /** * Convenience method for event stream responses */ async getStream(path, options) { return this.request(path, { ...options, method: "GET" }); } async delete(path, options) { return this.request(path, { ...options, method: "DELETE" }); } /** * Update client configuration */ setConfig(config) { this.config = { ...this.config, ...config }; if (config.fetch) this.fetchInstance = config.fetch; } /** * Set additional headers */ setHeaders(headers) { this.config.headers = { ...this.config.headers, ...headers }; } /** * Set custom fetch instance */ setFetch(fetchInstance) { this.fetchInstance = fetchInstance; this.config.fetch = fetchInstance; } /** * Get current configuration (readonly) */ getConfig() { return { ...this.config }; } /** * Get interceptor manager for advanced usage */ getInterceptors() { return this.interceptors; } }; //#endregion //#region src/shared/define-module.ts /** * Helper function to define a single route with proper type inference */ function defineRoute(method, path, options = {}) { const params = extractParamsFromPath(path); return { method, path, params: params.length > 0 ? params : void 0, query: options.query, body: options.body, input: options.input, response: options.response, requestType: options.requestType, asRaw: options.asRaw }; } /** * Extract parameter names from a path string * e.g. "/users/{userId}/posts" -> ["userId"] */ function extractParamsFromPath(path) { const paramRegex = /\{([^}]+)\}/g; const params = []; let match; while ((match = paramRegex.exec(path)) !== null) params.push(match[1]); return params; } /** * Define a module with routes and metadata */ function defineModule(definition) { return { ...definition, api: {} }; } //#endregion //#region src/modules/actions/index.ts /** * Actions module definition - User automation rules */ const actionsModule = defineModule({ name: "actions", prefix: "/actions", routes: { get: defineRoute("GET", "/"), put: defineRoute("PUT", "/") } }); //#endregion //#region src/modules/admin/index.ts /** * Admin module definition with nested routes */ const adminModule = defineModule({ name: "admin", prefix: "/admin", routes: { featureFlags: { list: defineRoute("GET", "/feature-flags"), update: defineRoute("PUT", "/feature-flags/{name}"), override: defineRoute("POST", "/feature-flags/{name}/overrides"), removeOverride: defineRoute("DELETE", "/feature-flags/{name}/overrides/{userId}"), stats: defineRoute("GET", "/feature-flags/{name}/stats"), affectedUsers: defineRoute("GET", "/feature-flags/{name}/affected-users") }, clean: { execute: defineRoute("POST", "/clean") }, mint: { execute: defineRoute("POST", "/mintdscsafr") } } }); //#endregion //#region src/modules/ai/index.ts /** * AI module definition with nested AI-powered features */ const aiModule = defineModule({ name: "ai", prefix: "/ai", routes: { chat: defineRoute("POST", "/chat"), summary: defineRoute("GET", "/summary"), translation: defineRoute("GET", "/translation"), translationBatch: defineRoute("POST", "/translation/batch", { asRaw: true }), tts: defineRoute("POST", "/tts", { asRaw: true }), summaryTitle: defineRoute("POST", "/summary-title"), daily: defineRoute("GET", "/daily"), config: defineRoute("GET", "/chat/config"), memory: { list: defineRoute("GET", "/memory"), create: defineRoute("POST", "/memory"), update: defineRoute("PATCH", "/memory/{memoryId}"), delete: defineRoute("DELETE", "/memory/{memoryId}") } } }); //#endregion //#region src/modules/ai-analytics/index.ts /** * AI Analytics module definition for usage analytics and insights */ const aiAnalyticsModule = defineModule({ name: "ai-analytics", prefix: "/ai/analytics", routes: { get: defineRoute("GET", "/") } }); //#endregion //#region src/modules/ai-chat-sessions/index.ts /** * AI Chat Sessions module - comprehensive chat session management * Base path: /ai/chat-sessions */ const aiChatSessionsModule = defineModule({ name: "aiChatSessions", prefix: "/ai/chat-sessions", routes: { list: defineRoute("GET", "/"), get: defineRoute("GET", "/{chatId}"), update: defineRoute("PATCH", "/{chatId}"), delete: defineRoute("DELETE", "/{chatId}"), messages: { get: defineRoute("GET", "/{chatId}/messages") }, markSeen: defineRoute("POST", "/{chatId}/mark-seen"), unread: defineRoute("GET", "/unread") } }); //#endregion //#region src/modules/ai-task/index.ts /** * AI Task module - standalone module for managing AI scheduled tasks * Base path: /ai/task */ const aiTaskModule = defineModule({ name: "aiTask", prefix: "/ai/task", routes: { list: defineRoute("GET", "/"), get: defineRoute("GET", "/{id}"), create: defineRoute("POST", "/"), update: defineRoute("PUT", "/{id}"), delete: defineRoute("DELETE", "/{id}"), testRun: defineRoute("POST", "/{id}/test-run") } }); //#endregion //#region src/modules/auth/index.ts /** * Authentication module definition with Better Auth integration * * This module provides client SDK interfaces for all Better Auth endpoints. * The endpoints follow Better Auth's standard path conventions under /better-auth prefix. */ const authModule = defineModule({ name: "auth", prefix: "/better-auth", routes: { getSession: defineRoute("GET", "/get-session") } }); //#endregion //#region src/modules/categories/index.ts /** * Categories module definition - Subscription categorization */ const categoriesModule = defineModule({ name: "categories", prefix: "/categories", routes: { get: defineRoute("GET", "/"), update: defineRoute("PATCH", "/"), delete: defineRoute("DELETE", "/") } }); //#endregion //#region src/modules/collections/index.ts /** * Collections module definition - Content collections management */ const collectionsModule = defineModule({ name: "collections", prefix: "/collections", routes: { get: defineRoute("GET", "/"), post: defineRoute("POST", "/"), delete: defineRoute("DELETE", "/") } }); //#endregion //#region src/modules/data/index.ts /** * Data module for Google Analytics data collection */ const dataModule = defineModule({ name: "data", prefix: "/data", routes: { sendAnalytics: defineRoute("POST", "/g") } }); //#endregion //#region src/modules/discover/index.ts /** * Discover module definition - Feed and list discovery */ const discoverModule = defineModule({ name: "discover", prefix: "/discover", routes: { discover: defineRoute("POST", "/"), rsshub: defineRoute("GET", "/rsshub"), rsshubRoute: defineRoute("GET", "/rsshub/route"), rsshubAnalytics: defineRoute("GET", "/rsshub-analytics") } }); //#endregion //#region src/modules/entries/index.ts /** * Entries module definition with nested routes */ const entriesModule = defineModule({ name: "entries", prefix: "/entries", routes: { get: defineRoute("GET", "/"), list: defineRoute("POST", "/"), preview: defineRoute("POST", "/preview"), readability: defineRoute("GET", "/readability"), transcription: defineRoute("GET", "/transcription"), stream: defineRoute("POST", "/stream", { asRaw: true }), checkNew: defineRoute("GET", "/check-new"), tagsQuery: defineRoute("POST", "/tags/query"), readHistories: defineRoute("GET", "/read-histories/{id}"), inbox: { get: defineRoute("GET", "/inbox"), list: defineRoute("POST", "/inbox"), delete: defineRoute("DELETE", "/inbox") } } }); //#endregion //#region src/modules/feeds/index.ts /** * Feeds module definition with nested routes */ const feedsModule = defineModule({ name: "feeds", prefix: "/feeds", routes: { get: defineRoute("GET", "/"), refresh: defineRoute("GET", "/refresh"), reset: defineRoute("GET", "/reset"), analytics: defineRoute("POST", "/analytics"), claim: { challenge: defineRoute("POST", "/claim/challenge"), list: defineRoute("GET", "/claim/list"), message: defineRoute("GET", "/claim/message") } } }); //#endregion //#region src/modules/inboxes/index.ts /** * Inboxes module definition - Email inboxes management */ const inboxesModule = defineModule({ name: "inboxes", prefix: "/inboxes", routes: { get: defineRoute("GET", "/"), list: defineRoute("GET", "/list"), post: defineRoute("POST", "/"), put: defineRoute("PUT", "/"), delete: defineRoute("DELETE", "/"), email: defineRoute("POST", "/email"), webhook: defineRoute("POST", "/webhook") } }); //#endregion //#region src/modules/lists/index.ts /** * Lists module definition with core routes */ const listsModule = defineModule({ name: "lists", prefix: "/lists", routes: { get: defineRoute("GET", "/"), list: defineRoute("GET", "/list"), create: defineRoute("POST", "/"), update: defineRoute("PATCH", "/"), delete: defineRoute("DELETE", "/"), addFeeds: defineRoute("POST", "/feeds"), removeFeed: defineRoute("DELETE", "/feeds") } }); //#endregion //#region src/modules/mcp/index.ts /** * MCP (Model Context Protocol) module definition * Handles MCP server connections and tool management */ const mcpModule = defineModule({ name: "mcp", prefix: "/mcp", routes: { createConnection: defineRoute("POST", "/connections"), updateConnection: defineRoute("PUT", "/connections/{connectionId}"), getConnections: defineRoute("GET", "/connections"), deleteConnection: defineRoute("DELETE", "/connections/{connectionId}"), getTools: defineRoute("GET", "/connections/{connectionId}/tools"), refreshTools: defineRoute("POST", "/tools/refresh") } }); //#endregion //#region src/modules/messaging/index.ts /** * Messaging module for push notification management */ const messagingModule = defineModule({ name: "messaging", prefix: "/messaging", routes: { getTokens: defineRoute("GET", "/"), createToken: defineRoute("POST", "/"), deleteToken: defineRoute("DELETE", "/"), testNotification: defineRoute("GET", "/test") } }); //#endregion //#region src/modules/probes/index.ts /** * Probes module for health checks and system monitoring */ const probesModule = defineModule({ name: "probes", prefix: "/probes", routes: { checkPostgreSQL: defineRoute("GET", "/postgresql"), checkRedis: defineRoute("GET", "/redis"), checkBullMQ: defineRoute("GET", "/bullmq"), getRSSHubAnalytics: defineRoute("GET", "/rsshub") } }); //#endregion //#region src/modules/profiles/index.ts /** * Profiles module for user profile management */ const profilesModule = defineModule({ name: "profiles", prefix: "/profiles", routes: { getProfile: defineRoute("GET", "/"), getBatch: defineRoute("POST", "/batch") } }); //#endregion //#region src/modules/reads/index.ts /** * Reads module definition with nested routes */ const readsModule = defineModule({ name: "reads", prefix: "/reads", routes: { get: defineRoute("GET", "/"), markAsRead: defineRoute("POST", "/"), markAsUnread: defineRoute("DELETE", "/"), markAllAsRead: defineRoute("POST", "/all"), getTotalCount: defineRoute("GET", "/total-count") } }); //#endregion //#region src/modules/referrals/index.ts /** * Referrals module for referral system management */ const referralsModule = defineModule({ name: "referrals", prefix: "/referrals", routes: { getReferrals: defineRoute("GET", "/"), getDays: defineRoute("GET", "/days"), verifyReceipt: defineRoute("POST", "/verify-receipt") } }); //#endregion //#region src/modules/rsshub/index.ts /** * RSSHub module definition for RSSHub instance management */ const rsshubModule = defineModule({ name: "rsshub", prefix: "/rsshub", routes: { create: defineRoute("POST", "/"), list: defineRoute("GET", "/list"), delete: defineRoute("DELETE", "/"), use: defineRoute("POST", "/use"), get: defineRoute("GET", "/"), status: defineRoute("GET", "/status") } }); //#endregion //#region src/modules/settings/index.ts /** * Settings module definition with nested routes */ const settingsModule = defineModule({ name: "settings", prefix: "/settings", routes: { get: defineRoute("GET", "/"), update: defineRoute("PATCH", "/{tab}") } }); //#endregion //#region src/modules/status/index.ts /** * Status module for system status and configuration */ const statusModule = defineModule({ name: "status", prefix: "/status", routes: { getConfigs: defineRoute("GET", "/configs") } }); //#endregion //#region src/modules/subscriptions/index.ts /** * Subscriptions module definition with nested routes */ const subscriptionsModule = defineModule({ name: "subscriptions", prefix: "/subscriptions", routes: { get: defineRoute("GET", "/"), create: defineRoute("POST", "/"), update: defineRoute("PATCH", "/"), delete: defineRoute("DELETE", "/"), batchUpdate: defineRoute("PATCH", "/batch"), import: defineRoute("POST", "/import", { requestType: "formData" }), export: defineRoute("GET", "/export"), parseOpml: defineRoute("POST", "/parse-opml", { requestType: "arrayBuffer" }) } }); //#endregion //#region src/modules/trending/index.ts /** * Trending module for discovering trending feeds */ const trendingModule = defineModule({ name: "trending", prefix: "/trending", routes: { getFeeds: defineRoute("GET", "/feeds") } }); //#endregion //#region src/modules/update/index.ts const updatesModule = defineModule({ name: "updates", prefix: "/updates", routes: { getLatestRelease: defineRoute("GET", "/releases/latest", { query: ["refresh"] }), getManifest: defineRoute("GET", "/{manifestName}", { query: ["refresh"] }), getDistributionStatus: defineRoute("GET", "/distribution/{distribution}", { query: ["refresh"] }) } }); //#endregion //#region src/modules/upload/index.ts /** * Upload module for file uploads */ const uploadModule = defineModule({ name: "upload", prefix: "/upload", routes: { uploadAvatar: defineRoute("POST", "/avatar", { requestType: "formData" }), uploadChatAttachment: defineRoute("POST", "/chat-attachment", { requestType: "formData" }) } }); //#endregion //#region src/modules/wallets/constants.ts const TransactionTypes = [ "mint", "purchase", "tip", "withdraw", "airdrop" ]; //#endregion //#region src/modules/wallets/index.ts /** * Wallets module definition - Web3 wallet operations and transactions */ const walletsModule = defineModule({ name: "wallets", prefix: "/wallets", routes: { get: defineRoute("GET", "/"), post: defineRoute("POST", "/"), transactions: { get: defineRoute("GET", "/transactions"), withdraw: defineRoute("POST", "/transactions/withdraw") } } }); //#endregion //#region src/modules/registry.ts /** * Central module registry * All modules now use the new unified system */ const moduleRegistry = { actions: actionsModule, admin: adminModule, auth: authModule, ai: aiModule, aiAnalytics: aiAnalyticsModule, aiChatSessions: aiChatSessionsModule, aiTask: aiTaskModule, categories: categoriesModule, collections: collectionsModule, data: dataModule, discover: discoverModule, entries: entriesModule, feeds: feedsModule, inboxes: inboxesModule, lists: listsModule, mcp: mcpModule, messaging: messagingModule, profiles: profilesModule, probes: probesModule, reads: readsModule, referrals: referralsModule, rsshub: rsshubModule, settings: settingsModule, status: statusModule, subscriptions: subscriptionsModule, trending: trendingModule, upload: uploadModule, updates: updatesModule, wallets: walletsModule }; //#endregion //#region src/shared/route-resolver.ts /** * Route resolution utilities */ var RouteResolver = class { /** * Resolve path by combining prefix and route path */ static resolvePath(prefix = "", path) { const cleanPrefix = prefix.replace(/\/+$/, ""); const cleanPath = path.replace(/^\/+/, ""); if (!cleanPath) return cleanPrefix || "/"; return cleanPrefix ? `${cleanPrefix}/${cleanPath}` : `/${cleanPath}`; } /** * Flatten nested routes into a flat structure for proxy consumption */ static flattenRoutes(routes, prefix = "", parentKey = "") { const flattened = {}; for (const [key, value] of Object.entries(routes)) { const routeKey = parentKey ? `${parentKey}.${key}` : key; if (this.isRouteDefinition(value)) flattened[routeKey] = { ...value, path: this.resolvePath(prefix, value.path) }; else { const nestedFlattened = this.flattenRoutes(value, prefix, routeKey); Object.assign(flattened, nestedFlattened); } } return flattened; } /** * Type guard to check if a value is a RouteDefinition */ static isRouteDefinition(value) { return value && typeof value === "object" && "method" in value && "path" in value; } }; //#endregion //#region src/client/proxy.ts /** * Dynamic proxy handler for API routes */ var APIProxyHandler = class APIProxyHandler { routes; client; constructor(client, routes) { this.client = client; this.routes = routes; } /** * Proxy get trap that creates route functions dynamically */ get(target, propKey) { const routeKey = String(propKey); if (routeKey === "constructor" || routeKey === "prototype") return target[propKey]; if (routeKey.includes(".")) return this.handleNestedRoute(routeKey); const route = this.routes.get(routeKey); if (!route) { if (this.hasNestedRoute(routeKey)) return this.createNestedProxy(routeKey); throw new Error(`Route '${routeKey}' not found`); } return this.createRouteFunction(route); } /** * Check if a route key has nested routes */ hasNestedRoute(prefix) { for (const routeKey of this.routes.keys()) if (routeKey.startsWith(`${prefix}.`)) return true; return false; } /** * Create a nested proxy for route chains */ createNestedProxy(prefix) { const nestedRoutes = /* @__PURE__ */ new Map(); for (const [routeKey, route] of this.routes.entries()) if (routeKey.startsWith(`${prefix}.`)) { const nestedKey = routeKey.slice(Math.max(0, prefix.length + 1)); nestedRoutes.set(nestedKey, route); } return new Proxy({}, new APIProxyHandler(this.client, nestedRoutes)); } /** * Handle nested route calls */ handleNestedRoute(routeKey) { const route = this.routes.get(routeKey); if (!route) throw new Error(`Nested route '${routeKey}' not found`); return this.createRouteFunction(route); } /** * Create a route function from a route definition */ createRouteFunction(route) { return async (inputArgs = {}, args = {}) => { const { headers, timeout, signal } = args; const params = {}; const query = {}; let body; const isFormData = typeof FormData !== "undefined" && inputArgs instanceof FormData; const isArrayBuffer = typeof ArrayBuffer !== "undefined" && inputArgs instanceof ArrayBuffer; const isBlob = typeof Blob !== "undefined" && inputArgs instanceof Blob; if (isFormData || isArrayBuffer || isBlob) body = inputArgs; else { const normalizedArgs = inputArgs && typeof inputArgs === "object" ? inputArgs : {}; if (route.params) route.params.forEach((param) => { if (normalizedArgs[param] !== void 0) { params[param] = String(normalizedArgs[param]); delete normalizedArgs[param]; } }); if (route.query) route.query.forEach((field) => { if (normalizedArgs[field] !== void 0) { query[field] = normalizedArgs[field]; delete normalizedArgs[field]; } }); if (route.body) { const bodyData = {}; route.body.forEach((field) => { if (normalizedArgs[field] !== void 0) { bodyData[field] = normalizedArgs[field]; delete normalizedArgs[field]; } }); if (Object.keys(bodyData).length > 0) body = bodyData; } const remainingFields = Object.keys(normalizedArgs).filter((key) => normalizedArgs[key] !== void 0); if (remainingFields.length > 0) { const remainingData = Object.fromEntries(remainingFields.map((key) => [key, normalizedArgs[key]])); if (route.method === "GET") Object.assign(query, remainingData); else if (body && typeof body === "object") Object.assign(body, remainingData); else body = remainingData; } } let url = route.path; if (route.params) route.params.forEach((param) => { const value = params[param]; if (value !== void 0) url = url.replace(`{${param}}`, encodeURIComponent(value)); }); const requestOptions = { method: route.method, headers, timeout, signal, requestType: route.requestType, asRaw: route.asRaw }; if (Object.keys(query).length > 0) requestOptions.query = query; if (body !== void 0) requestOptions.body = body; return this.client.request(url, requestOptions); }; } }; /** * Create a typed API proxy from a module definition */ function createAPIProxy(client, module) { const flatRoutes = RouteResolver.flattenRoutes(module.routes, module.prefix); const routeMap = new Map(Object.entries(flatRoutes)); return new Proxy({}, new APIProxyHandler(client, routeMap)); } //#endregion //#region src/client/core.ts var FollowClient = class FollowClient { httpClient; api = {}; constructor(config = {}) { this.httpClient = new HttpClient({ baseURL: config.baseURL || "https://api.folo.is", timeout: config.timeout || 3e4, headers: config.headers || {}, credentials: config.credentials || "include", fetch: config.fetch || globalThis.fetch.bind(globalThis) }); this.initializeRoutes(); if (config.enableDefaultInterceptors) this.setupDefaultInterceptors(); if (config.authToken) this.setAuthToken(config.authToken); } /** * Initialize API route groups with proper typing */ initializeRoutes() { for (const [moduleName, moduleDefinition] of Object.entries(moduleRegistry)) this.api[moduleName] = createAPIProxy(this.httpClient, moduleDefinition); } /** * Setup default interceptors */ setupDefaultInterceptors() { const interceptors = this.httpClient.getInterceptors(); interceptors.addRequestInterceptor(commonInterceptors.logRequests({ log: console.info })); interceptors.addResponseInterceptor(commonInterceptors.logResponses({ log: console.info })); } /** * Set authentication token for API requests */ setAuthToken(token) { this.httpClient.setHeaders({ Authorization: `Bearer ${token}` }); } /** * Remove authentication token */ removeAuthToken() { const { Authorization, ...newHeaders } = this.httpClient.getConfig().headers; this.httpClient.setHeaders(newHeaders); } /** * Set custom headers for API requests */ setHeaders(headers) { this.httpClient.setHeaders(headers); } /** * Set custom fetch instance */ setFetch(fetchInstance) { this.httpClient.setFetch(fetchInstance); } /** * Update client configuration */ updateConfig(config) { this.httpClient.setConfig(config); } /** * Get current configuration (readonly) */ getConfig() { return this.httpClient.getConfig(); } /** * Make a custom HTTP request using the underlying HTTP client * This allows direct access to the HTTP client for custom requests */ async request(path, options) { return this.httpClient.request(path, options); } /** * Batch multiple requests */ async batch(requests) { return Promise.all(requests); } /** * Create a new instance with different configuration */ clone(config) { return new FollowClient({ ...this.getConfig(), ...config }); } /** * Add request interceptor */ addRequestInterceptor(interceptor) { return this.httpClient.getInterceptors().addRequestInterceptor(interceptor); } /** * Add response interceptor */ addResponseInterceptor(interceptor) { return this.httpClient.getInterceptors().addResponseInterceptor(interceptor); } /** * Add error interceptor */ addErrorInterceptor(interceptor) { return this.httpClient.getInterceptors().addErrorInterceptor(interceptor); } }; //#endregion //#region src/helper/index.ts /** * Type guard to check if response is successful */ function isSuccessResponse(response) { return response.code === 0; } /** * Type guard to check if response is an error */ function isErrorResponse(response) { return response.code !== 0; } /** * Extract data from successful response, throw error if unsuccessful */ function extractResponseData(response) { if (isSuccessResponse(response)) return response.data; throw new Error(response.message || `API error with code: ${response.code}`); } //#endregion Object.defineProperty(exports, 'ACHIEVEMENTS_TYPES', { enumerable: true, get: function () { return _folo_services_constants.ACHIEVEMENTS_TYPES; } }); Object.defineProperty(exports, 'ExceptionCodeMap', { enumerable: true, get: function () { return _folo_services_exceptions.ExceptionCodeMap; } }); Object.defineProperty(exports, 'FEATURE_NAMES', { enumerable: true, get: function () { return _folo_services_constants.FEATURE_NAMES; } }); Object.defineProperty(exports, 'FeedViewType', { enumerable: true, get: function () { return _folo_services_constants.FeedViewType; } }); exports.FollowAPIError = FollowAPIError; exports.FollowAuthError = FollowAuthError; exports.FollowClient = FollowClient; exports.FollowTimeoutError = FollowTimeoutError; exports.FollowValidationError = FollowValidationError; exports.NetworkError = NetworkError; Object.defineProperty(exports, 'SETTINGS_TABS', { enumerable: true, get: function () { return _folo_services_constants.SETTINGS_TABS; } }); exports.TransactionTypes = TransactionTypes; exports.actionsModule = actionsModule; exports.adminModule = adminModule; exports.aiAnalyticsModule = aiAnalyticsModule; exports.aiChatSessionsModule = aiChatSessionsModule; exports.aiModule = aiModule; exports.aiTaskModule = aiTaskModule; exports.authModule = authModule; exports.categoriesModule = categoriesModule; exports.collectionsModule = collectionsModule; exports.dataModule = dataModule; exports.discoverModule = discoverModule; exports.entriesModule = entriesModule; exports.extractResponseData = extractResponseData; exports.feedsModule = feedsModule; exports.inboxesModule = inboxesModule; exports.isErrorResponse = isErrorResponse; exports.isSuccessResponse = isSuccessResponse; exports.listsModule = listsModule; exports.mcpModule = mcpModule; exports.messagingModule = messagingModule; exports.moduleRegistry = moduleRegistry; exports.probesModule = probesModule; exports.profilesModule = profilesModule; exports.readsModule = readsModule; exports.referralsModule = referralsModule; exports.rsshubModule = rsshubModule; exports.settingsModule = settingsModule; exports.statusModule = statusModule; exports.subscriptionsModule = subscriptionsModule; exports.trendingModule = trendingModule; exports.updatesModule = updatesModule; exports.uploadModule = uploadModule; exports.walletsModule = walletsModule; //# sourceMappingURL=index.cjs.map