import { createAxiosClient } from "./utils/axios-client.js"; import { createEntitiesModule } from "./modules/entities.js"; import { createIntegrationsModule } from "./modules/integrations.js"; import { createAuthModule } from "./modules/auth.js"; import { createSsoModule } from "./modules/sso.js"; import { createConnectorsModule } from "./modules/connectors.js"; import { getAccessToken } from "./utils/auth-utils.js"; import { createFunctionsModule } from "./modules/functions.js"; import { createAgentsModule } from "./modules/agents.js"; import { createAppLogsModule } from "./modules/app-logs.js"; import { createUsersModule } from "./modules/users.js"; import { RoomsSocket } from "./utils/socket-utils.js"; import { createAnalyticsModule } from "./modules/analytics.js"; /** * Creates a Base44 client. * * This is the main entry point for the Base44 SDK. It creates a client that provides access to the SDK's modules, such as {@linkcode EntitiesModule | entities}, {@linkcode AuthModule | auth}, and {@linkcode FunctionsModule | functions}. * * How you get a client depends on your context: * - **Inside a Base44 app:** The client is automatically created and configured for you. Import it from `@/api/base44Client`. * - **External app using Base44 as a backend:** Call `createClient()` directly in your code to create and configure the client. * * The client supports three authentication modes: * - **Anonymous**: Access modules without authentication using `base44.moduleName`. Operations are scoped to public data and permissions. * - **User authentication**: Access modules with user-level permissions using `base44.moduleName`. Operations are scoped to the authenticated user's data and permissions. Use `base44.auth.loginViaEmailPassword()` or other auth methods to get a token. * - **Service role authentication**: Access modules with elevated permissions using `base44.asServiceRole.moduleName`. Operations can access any data available to the app's admin. Only available in Base44-hosted backend functions. Create a client with service role authentication using {@linkcode createClientFromRequest | createClientFromRequest()}. * * For example, when using the {@linkcode EntitiesModule | entities} module: * - **Anonymous**: Can only read public data. * - **User authentication**: Can access the current user's data. * - **Service role authentication**: Can access all data that admins can access. * * Most modules are available in all three modes, but with different permission levels. However, some modules are only available in specific authentication modes. * * @param config - Configuration object for the client. * @returns A configured Base44 client instance with access to all SDK modules. * * @example * ```typescript * // Create a client for your app * import { createClient } from '@base44/sdk'; * * const base44 = createClient({ * appId: 'my-app-id' * }); * * // Use the client to access your data * const products = await base44.entities.Products.list(); * ``` */ export function createClient(config) { const { serverUrl = "https://base44.app", appId, token, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config; // Normalize appBaseUrl to always be a string (empty if not provided or invalid) const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : ""; const socketConfig = { serverUrl, mountPath: "/ws-user-apps/socket.io/", transports: ["websocket"], appId, token, }; let socket = null; const getSocket = () => { if (!socket) { socket = RoomsSocket({ config: socketConfig, }); } return socket; }; const headers = { ...optionalHeaders, "X-App-Id": String(appId), }; const functionHeaders = functionsVersion ? { ...headers, "Base44-Functions-Version": functionsVersion, } : headers; const axiosClient = createAxiosClient({ baseURL: `${serverUrl}/api`, headers, token, onError: options === null || options === void 0 ? void 0 : options.onError, }); const functionsAxiosClient = createAxiosClient({ baseURL: `${serverUrl}/api`, headers: functionHeaders, token, interceptResponses: false, onError: options === null || options === void 0 ? void 0 : options.onError, }); const serviceRoleAxiosClient = createAxiosClient({ baseURL: `${serverUrl}/api`, headers, token: serviceToken, onError: options === null || options === void 0 ? void 0 : options.onError, }); const serviceRoleFunctionsAxiosClient = createAxiosClient({ baseURL: `${serverUrl}/api`, headers: functionHeaders, token: serviceToken, interceptResponses: false, }); const userAuthModule = createAuthModule(axiosClient, functionsAxiosClient, appId, { appBaseUrl: normalizedAppBaseUrl, serverUrl, }); const userModules = { entities: createEntitiesModule({ axios: axiosClient, appId, getSocket, }), integrations: createIntegrationsModule(axiosClient, appId), auth: userAuthModule, functions: createFunctionsModule(functionsAxiosClient, appId), agents: createAgentsModule({ axios: axiosClient, getSocket, appId, serverUrl, token, }), appLogs: createAppLogsModule(axiosClient, appId), users: createUsersModule(axiosClient, appId), analytics: createAnalyticsModule({ axiosClient, serverUrl, appId, userAuthModule, }), cleanup: () => { userModules.analytics.cleanup(); if (socket) { socket.disconnect(); } }, }; const serviceRoleModules = { entities: createEntitiesModule({ axios: serviceRoleAxiosClient, appId, getSocket, }), integrations: createIntegrationsModule(serviceRoleAxiosClient, appId), sso: createSsoModule(serviceRoleAxiosClient, appId, token), connectors: createConnectorsModule(serviceRoleAxiosClient, appId), functions: createFunctionsModule(serviceRoleFunctionsAxiosClient, appId), agents: createAgentsModule({ axios: serviceRoleAxiosClient, getSocket, appId, serverUrl, token, }), appLogs: createAppLogsModule(serviceRoleAxiosClient, appId), cleanup: () => { if (socket) { socket.disconnect(); } }, }; // Always try to get token from localStorage or URL parameters if (typeof window !== "undefined") { // Get token from URL or localStorage const accessToken = token || getAccessToken(); if (accessToken) { userModules.auth.setToken(accessToken); } } // If authentication is required, verify token and redirect to login if needed if (requiresAuth && typeof window !== "undefined") { // We perform this check asynchronously to not block client creation setTimeout(async () => { try { const isAuthenticated = await userModules.auth.isAuthenticated(); if (!isAuthenticated) { userModules.auth.redirectToLogin(window.location.href); } } catch (error) { console.error("Authentication check failed:", error); userModules.auth.redirectToLogin(window.location.href); } }, 0); } // Assemble and return the client const client = { ...userModules, /** * Sets a new authentication token for all subsequent requests. * * @param newToken - The new authentication token * * @example * ```typescript * // Update token after login * const { access_token } = await base44.auth.loginViaEmailPassword( * 'user@example.com', * 'password' * ); * base44.setToken(access_token); * ``` */ setToken(newToken) { userModules.auth.setToken(newToken); if (socket) { socket.updateConfig({ token: newToken, }); } socketConfig.token = newToken; }, /** * Gets the current client configuration. * * @internal */ getConfig() { return { serverUrl, appId, requiresAuth, }; }, /** * Provides access to service role modules. * * Service role authentication provides elevated permissions for backend operations. Unlike user authentication, which is scoped to a specific user's permissions, service role authentication has access to the data and operations available to the app's admin. * * @throws {Error} When accessed without providing a serviceToken during client creation. * * @example * ```typescript * const base44 = createClient({ * appId: 'my-app-id', * serviceToken: 'service-role-token' * }); * * // Also access a module with elevated permissions * const allUsers = await base44.asServiceRole.entities.User.list(); * ``` */ get asServiceRole() { if (!serviceToken) { throw new Error("Service token is required to use asServiceRole. Please provide a serviceToken when creating the client."); } return serviceRoleModules; }, }; return client; } /** * Creates a Base44 client from an HTTP request. * * This function is designed for use in Base44-hosted backend functions. For frontends and external backends, use {@linkcode createClient | createClient()} instead. * * When used in a Base44-hosted backend function, `createClientFromRequest()` automatically extracts authentication tokens from the request headers that Base44 injects when forwarding requests. The returned client includes service role access using `base44.asServiceRole`, which provides admin-level permissions. * * To learn more about the Base44 client, see {@linkcode createClient | createClient()}. * * @param request - The incoming HTTP request object containing Base44 authentication headers. * @returns A configured Base44 client instance with authentication from the incoming request. * * @example * ```typescript * // User authentication in backend function * import { createClientFromRequest } from 'npm:@base44/sdk'; * * Deno.serve(async (req) => { * try { * const base44 = createClientFromRequest(req); * const user = await base44.auth.me(); * * if (!user) { * return Response.json({ error: 'Unauthorized' }, { status: 401 }); * } * * // Access user's data * const userOrders = await base44.entities.Orders.filter({ userId: user.id }); * return Response.json({ orders: userOrders }); * } catch (error) { * return Response.json({ error: error.message }, { status: 500 }); * } * }); * ``` * * @example * ```typescript * // Service role authentication in backend function * import { createClientFromRequest } from 'npm:@base44/sdk'; * * Deno.serve(async (req) => { * try { * const base44 = createClientFromRequest(req); * * // Access admin data with service role permissions * const recentOrders = await base44.asServiceRole.entities.Orders.list('-created_at', 50); * * return Response.json({ orders: recentOrders }); * } catch (error) { * return Response.json({ error: error.message }, { status: 500 }); * } * }); * ``` * */ export function createClientFromRequest(request) { const authHeader = request.headers.get("Authorization"); const serviceRoleAuthHeader = request.headers.get("Base44-Service-Authorization"); const appId = request.headers.get("Base44-App-Id"); const serverUrlHeader = request.headers.get("Base44-Api-Url"); const functionsVersion = request.headers.get("Base44-Functions-Version"); const stateHeader = request.headers.get("Base44-State"); if (!appId) { throw new Error("Base44-App-Id header is required, but is was not found on the request"); } // Validate authorization header formats let serviceRoleToken; let userToken; if (serviceRoleAuthHeader !== null) { if (serviceRoleAuthHeader === "" || !serviceRoleAuthHeader.startsWith("Bearer ") || serviceRoleAuthHeader.split(" ").length !== 2) { throw new Error('Invalid authorization header format. Expected "Bearer "'); } serviceRoleToken = serviceRoleAuthHeader.split(" ")[1]; } if (authHeader !== null) { if (authHeader === "" || !authHeader.startsWith("Bearer ") || authHeader.split(" ").length !== 2) { throw new Error('Invalid authorization header format. Expected "Bearer "'); } userToken = authHeader.split(" ")[1]; } // Prepare additional headers to propagate const additionalHeaders = {}; if (stateHeader) { additionalHeaders["Base44-State"] = stateHeader; } return createClient({ serverUrl: serverUrlHeader || "https://base44.app", appId, token: userToken, serviceToken: serviceRoleToken, functionsVersion: functionsVersion !== null && functionsVersion !== void 0 ? functionsVersion : undefined, headers: additionalHeaders, }); }