@follow-app/client-sdk
Version:
TypeScript client SDK for Follow RSS Server API
1,427 lines (1,387 loc) • 39.4 kB
JavaScript
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