diff --git a/scripts/mintlify-post-processing/appended-articles.json b/scripts/mintlify-post-processing/appended-articles.json index 100bcc4..969e10d 100644 --- a/scripts/mintlify-post-processing/appended-articles.json +++ b/scripts/mintlify-post-processing/appended-articles.json @@ -1,4 +1,7 @@ { "interfaces/EntitiesModule": "interfaces/EntityHandler", - "type-aliases/integrations": "interfaces/CoreIntegrations" + "type-aliases/integrations": [ + "interfaces/CoreIntegrations", + "interfaces/CustomIntegrationsModule" + ] } diff --git a/scripts/mintlify-post-processing/copy-to-local-docs.js b/scripts/mintlify-post-processing/copy-to-local-docs.js index 9fc2c10..ec29357 100644 --- a/scripts/mintlify-post-processing/copy-to-local-docs.js +++ b/scripts/mintlify-post-processing/copy-to-local-docs.js @@ -248,6 +248,12 @@ function main() { console.log(`Copying docs to ${sdkDocsTarget}...`); fs.cpSync(DOCS_SOURCE_PATH, sdkDocsTarget, { recursive: true }); + // Remove README.mdx - it's not used in the docs navigation + const readmePath = path.join(sdkDocsTarget, "README.mdx"); + if (fs.existsSync(readmePath)) { + fs.rmSync(readmePath, { force: true }); + } + // Scan the sdk-docs directory const sdkFiles = scanSdkDocs(sdkDocsTarget); console.debug(`SDK files: ${JSON.stringify(sdkFiles, null, 2)}`); diff --git a/scripts/mintlify-post-processing/file-processing/file-processing.js b/scripts/mintlify-post-processing/file-processing/file-processing.js index b14b347..67f7cbd 100755 --- a/scripts/mintlify-post-processing/file-processing/file-processing.js +++ b/scripts/mintlify-post-processing/file-processing/file-processing.js @@ -2,7 +2,7 @@ /** * Post-processing script for TypeDoc-generated MDX files - * + * * TypeDoc now emits .mdx files directly, so this script: * 1. Processes links to make them Mintlify-compatible * 2. Removes files for linked types that should be suppressed @@ -94,6 +94,15 @@ function processLinksInFile(filePath) { let content = fs.readFileSync(filePath, "utf-8"); let modified = false; + // Rename "Type Declaration" to "Type Declarations" (TypeDoc outputs singular) + if (content.includes("## Type Declaration\n")) { + content = content.replace( + "## Type Declaration\n", + "## Type Declarations\n" + ); + modified = true; + } + // Remove undesirable lines like "> **IntegrationsModule** = `object` & `object`" // This typically appears in type alias files using intersection types const typeDefinitionRegex = /^> \*\*\w+\*\* = `object` & `object`\s*$/m; @@ -113,21 +122,10 @@ function processLinksInFile(filePath) { \\[\`packageName\`: \`string\`\\]: [\`IntegrationPackage\`](IntegrationPackage) Access to additional integration packages. - -### Example - - - -\`\`\`typescript Access additional packages -// Access a custom integration package -base44.integrations.MyCustomPackage.MyFunction({ param: 'value' }); -\`\`\` - - `; - // Append it before the "Type Declaration" or "Core" section if possible, or just at the end before methods if any + // Append it before the "Type Declarations" or "Core" section if possible, or just at the end before methods if any // Finding a good insertion point - const typeDeclarationIndex = content.indexOf("## Type Declaration"); + const typeDeclarationIndex = content.indexOf("## Type Declarations"); if (typeDeclarationIndex !== -1) { content = content.slice(0, typeDeclarationIndex) + @@ -136,7 +134,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' }); content.slice(typeDeclarationIndex); modified = true; } else { - // If no Type Declaration, maybe append after the main description? + // If no Type Declarations, maybe append after the main description? // Look for the first horizontal rule or similar const firstHR = content.indexOf("***", 10); // skip first few chars if (firstHR !== -1) { @@ -149,7 +147,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' }); } } } - + // Remove .md and .mdx extensions from markdown links // This handles both relative and absolute paths // Regex breakdown: @@ -162,7 +160,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' }); let newContent = content.replace( linkRegex, (match, linkText, linkPath, ext) => { - modified = true; + modified = true; // Check if the link points to a renamed module const pathParts = linkPath.split("/"); @@ -179,7 +177,7 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' }); // Handle relative links that might be missing context (basic cleanup) // e.g. if linkPath is just "entities" but it should be relative - return `[${linkText}](${linkPath})`; + return `[${linkText}](${linkPath})`; } ); @@ -187,12 +185,12 @@ base44.integrations.MyCustomPackage.MyFunction({ param: 'value' }); // or if the above regex missed them (though it matches .mdx?) // The regex requires .md or .mdx extension. If links are already extensionless, this won't run. // But TypeDoc usually outputs links with extensions. - + if (modified) { fs.writeFileSync(filePath, newContent, "utf-8"); return true; } - + return false; } @@ -272,18 +270,18 @@ function scanDocsContent() { }; const sections = ["functions", "interfaces", "classes", "type-aliases"]; - + for (const section of sections) { const sectionDir = path.join(CONTENT_DIR, section); if (!fs.existsSync(sectionDir)) continue; - + const files = fs.readdirSync(sectionDir); const mdxFiles = files .filter((file) => file.endsWith(".mdx")) .map((file) => path.basename(file, ".mdx")) .sort() .map((fileName) => `content/${section}/${fileName}`); - + const key = section === "type-aliases" ? "typeAliases" : section; result[key] = mdxFiles; } @@ -298,7 +296,7 @@ function getGroupName(section, categoryMap) { if (categoryMap[section]) { return categoryMap[section]; } - + return section; } @@ -314,30 +312,30 @@ function generateDocsJson(docsContent) { // If file doesn't exist or can't be read, return empty object console.error(`Error: Category map file not found: ${CATEGORY_MAP_PATH}`); } - + const groups = []; - + if (docsContent.functions.length > 0 && categoryMap.functions) { groups.push({ group: getGroupName("functions", categoryMap), pages: docsContent.functions, }); } - + if (docsContent.interfaces.length > 0 && categoryMap.interfaces) { groups.push({ group: getGroupName("interfaces", categoryMap), pages: docsContent.interfaces, }); } - + if (docsContent.classes.length > 0 && categoryMap.classes) { groups.push({ group: getGroupName("classes", categoryMap), pages: docsContent.classes, }); } - + if (docsContent.typeAliases.length > 0 && categoryMap["type-aliases"]) { // Merge into existing group if name matches const groupName = getGroupName("type-aliases", categoryMap); @@ -348,13 +346,13 @@ function generateDocsJson(docsContent) { existingGroup.pages.push(...docsContent.typeAliases); existingGroup.pages.sort(); // Sort combined pages alphabetically } else { - groups.push({ + groups.push({ group: groupName, - pages: docsContent.typeAliases, - }); + pages: docsContent.typeAliases, + }); } } - + // Find or create SDK Reference tab let sdkTab = template.navigation.tabs.find( (tab) => tab.tab === "SDK Reference" @@ -363,9 +361,9 @@ function generateDocsJson(docsContent) { sdkTab = { tab: "SDK Reference", groups: [] }; template.navigation.tabs.push(sdkTab); } - + sdkTab.groups = groups; - + const docsJsonPath = path.join(DOCS_DIR, "docs.json"); fs.writeFileSync( docsJsonPath, @@ -405,10 +403,10 @@ function isTypeDocPath(relativePath) { */ function processAllFiles(dir, linkedTypeNames, exposedTypeNames) { const entries = fs.readdirSync(dir, { withFileTypes: true }); - + for (const entry of entries) { const entryPath = path.join(dir, entry.name); - + if (entry.isDirectory()) { processAllFiles(entryPath, linkedTypeNames, exposedTypeNames); } else if ( @@ -433,7 +431,7 @@ function processAllFiles(dir, linkedTypeNames, exposedTypeNames) { exposedTypeNames.has(originalName) || exposedTypeNames.has(fileName) || isRenamedModule; - + // Remove any type doc files that are not explicitly exposed if (isTypeDoc && !isExposedType) { fs.unlinkSync(entryPath); @@ -683,7 +681,7 @@ function applyAppendedArticles(appendedArticles) { console.warn( `Warning: Appended article not found: ${appendKey} (checked content/ and docs/ roots)` ); - continue; + continue; } } } @@ -692,7 +690,7 @@ function applyAppendedArticles(appendedArticles) { const { section, headings } = prepareAppendedSection(appendPath); combinedSections += `\n\n${section}`; if (PANELS_ENABLED && collectedHeadings) { - collectedHeadings.push(...headings); + collectedHeadings.push(...headings); } try { @@ -763,34 +761,197 @@ function applyHeadingDemotion(dir) { } } +/** + * Link type names in the Type Declarations section to their corresponding ## headings on the page + */ +function linkTypeDeclarationToSections(content) { + // Find all ## headings on the page (these are potential link targets) + const sectionHeadingRegex = /^## (\w+)\s*$/gm; + const sectionNames = new Set(); + let match; + while ((match = sectionHeadingRegex.exec(content)) !== null) { + sectionNames.add(match[1]); + } + + if (sectionNames.size === 0) { + return { content, modified: false }; + } + + // Find the Type Declarations section + const typeDeclarationStart = content.indexOf("## Type Declarations"); + if (typeDeclarationStart === -1) { + return { content, modified: false }; + } + + // Find the end of Type Declarations section (next ## heading or end of file) + const afterTypeDeclaration = content.slice( + typeDeclarationStart + "## Type Declarations".length + ); + const nextSectionMatch = afterTypeDeclaration.match(/\n## /); + const typeDeclarationEnd = nextSectionMatch + ? typeDeclarationStart + + "## Type Declarations".length + + nextSectionMatch.index + : content.length; + + const beforeSection = content.slice(0, typeDeclarationStart); + const typeDeclarationSection = content.slice( + typeDeclarationStart, + typeDeclarationEnd + ); + const afterSection = content.slice(typeDeclarationEnd); + + // In the Type Declarations section, find type names in backticks on blockquote lines + // Pattern: > **propertyName**: `TypeName` + let modified = false; + const updatedSection = typeDeclarationSection.replace( + /^(>\s*\*\*\w+\*\*:\s*)`(\w+)`/gm, + (match, prefix, typeName) => { + if (sectionNames.has(typeName)) { + modified = true; + const anchor = typeName.toLowerCase(); + return `${prefix}[\`${typeName}\`](#${anchor})`; + } + return match; + } + ); + + if (!modified) { + return { content, modified: false }; + } + + return { + content: beforeSection + updatedSection + afterSection, + modified: true, + }; +} + +/** + * Apply type declarations linking to all files + */ +function applyTypeDeclarationLinking(dir) { + if (!fs.existsSync(dir)) return; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const entryPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + applyTypeDeclarationLinking(entryPath); + } else if ( + entry.isFile() && + (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) + ) { + const content = fs.readFileSync(entryPath, "utf-8"); + const { content: updated, modified } = + linkTypeDeclarationToSections(content); + if (modified) { + fs.writeFileSync(entryPath, updated, "utf-8"); + console.log( + `Linked type declarations: ${path.relative(DOCS_DIR, entryPath)}` + ); + } + } + } +} + +/** + * Remove links to types that are not in the exposed types list. + * Converts [TypeName](path) or [`TypeName`](path) to just TypeName or `TypeName`. + */ +function removeNonExposedTypeLinks(content, exposedTypeNames) { + // Match markdown links where the link text is a type name (with or without backticks) + // Pattern: [TypeName](some/path/TypeName) or [`TypeName`](some/path/TypeName) + const linkRegex = /\[(`?)([A-Z][A-Za-z0-9]*)\1\]\(([^)]+)\)/g; + + let modified = false; + const updatedContent = content.replace( + linkRegex, + (match, backtick, typeName, linkPath) => { + // Check if this looks like a type doc link (path ends with the type name) + const pathEnd = linkPath + .split("/") + .pop() + .replace(/\.mdx?$/, ""); + + // If the link path ends with a type name that's NOT exposed, remove the link + if (pathEnd === typeName && !exposedTypeNames.has(typeName)) { + modified = true; + // Keep the type name with backticks if it had them, otherwise plain + return backtick ? `\`${typeName}\`` : typeName; + } + + return match; + } + ); + + return { content: updatedContent, modified }; +} + +/** + * Apply non-exposed type link removal to all files + */ +function applyNonExposedTypeLinkRemoval(dir, exposedTypeNames) { + if (!fs.existsSync(dir)) return; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const entryPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + applyNonExposedTypeLinkRemoval(entryPath, exposedTypeNames); + } else if ( + entry.isFile() && + (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) + ) { + const content = fs.readFileSync(entryPath, "utf-8"); + const { content: updated, modified } = removeNonExposedTypeLinks( + content, + exposedTypeNames + ); + if (modified) { + fs.writeFileSync(entryPath, updated, "utf-8"); + console.log( + `Removed non-exposed type links: ${path.relative( + DOCS_DIR, + entryPath + )}` + ); + } + } + } +} + /** * Main function */ function main() { console.log("Processing TypeDoc MDX files for Mintlify...\n"); - + if (!fs.existsSync(DOCS_DIR)) { console.error(`Error: Documentation directory not found: ${DOCS_DIR}`); console.error('Please run "npm run docs:generate" first.'); process.exit(1); } - + // Get list of linked types to suppress const linkedTypeNames = getLinkedTypeNames(); const exposedTypeNames = getTypesToExpose(); // First, perform module renames (EntitiesModule -> entities, etc.) performModuleRenames(DOCS_DIR); - + // Process all files (remove suppressed ones and fix links) - processAllFiles(DOCS_DIR, linkedTypeNames, exposedTypeNames); + processAllFiles(DOCS_DIR, linkedTypeNames, exposedTypeNames); // Append configured articles const appendedArticles = loadAppendedArticlesConfig(); applyAppendedArticles(appendedArticles); applyHeadingDemotion(DOCS_DIR); - + + // Link type names in Type Declarations sections to their corresponding headings + applyTypeDeclarationLinking(DOCS_DIR); + + // Remove links to types that aren't exposed (would 404) + applyNonExposedTypeLinkRemoval(DOCS_DIR, exposedTypeNames); + // Clean up the linked types file try { if (fs.existsSync(LINKED_TYPES_FILE)) { @@ -799,14 +960,14 @@ function main() { } catch (e) { // Ignore errors } - + // Scan content and generate docs.json const docsContent = scanDocsContent(); generateDocsJson(docsContent); - + // Copy styling.css copyStylingCss(); - + console.log(`\n✓ Post-processing complete!`); console.log(` Documentation directory: ${DOCS_DIR}`); } diff --git a/scripts/mintlify-post-processing/push-to-docs-repo.js b/scripts/mintlify-post-processing/push-to-docs-repo.js index 60f06ba..c1143c6 100644 --- a/scripts/mintlify-post-processing/push-to-docs-repo.js +++ b/scripts/mintlify-post-processing/push-to-docs-repo.js @@ -220,6 +220,11 @@ function main() { recursive: true, }); + // Remove README.mdx - it's not used in the docs navigation + fs.rmSync(path.join(tempRepoDir, "sdk-docs", "README.mdx"), { + force: true, + }); + // Scan the sdk-docs directory const sdkDocsDir = path.join(tempRepoDir, "sdk-docs"); const sdkFiles = scanSdkDocs(sdkDocsDir); diff --git a/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js b/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js index 5c3958d..3e6d9b1 100644 --- a/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js +++ b/scripts/mintlify-post-processing/typedoc-plugin/typedoc-mintlify-linked-types.js @@ -462,14 +462,29 @@ function extractPropertiesFromMarkdownFile(linkedTypeInfo, context) { */ function parsePropertiesFromTypeFile(content) { const properties = []; - const lines = content.split("\n"); + + // Strip YAML frontmatter if present + let contentWithoutFrontmatter = content; + if (content.startsWith("---")) { + const endIndex = content.indexOf("\n---", 3); + if (endIndex !== -1) { + contentWithoutFrontmatter = content.slice(endIndex + 4).trimStart(); + } + } + + // Strip leading horizontal rule (***) that TypeDoc adds after frontmatter + if (contentWithoutFrontmatter.startsWith("***")) { + contentWithoutFrontmatter = contentWithoutFrontmatter.slice(3).trimStart(); + } + + const lines = contentWithoutFrontmatter.split("\n"); // Collect intro description until Properties section const introLines = []; let descriptionCaptured = false; - // Extract Indexable section if present - const indexSignature = parseIndexableSection(content); + // Extract Indexable section if present (use content without frontmatter) + const indexSignature = parseIndexableSection(contentWithoutFrontmatter); // Find the Properties section let inPropertiesSection = false; diff --git a/scripts/mintlify-post-processing/types-to-expose.json b/scripts/mintlify-post-processing/types-to-expose.json index 94bd563..49e790d 100644 --- a/scripts/mintlify-post-processing/types-to-expose.json +++ b/scripts/mintlify-post-processing/types-to-expose.json @@ -3,6 +3,7 @@ "AppLogsModule", "AuthModule", "ConnectorsModule", + "CustomIntegrationsModule", "EntitiesModule", "EntityHandler", "FunctionsModule", diff --git a/src/modules/auth.types.ts b/src/modules/auth.types.ts index fe349a9..8795693 100644 --- a/src/modules/auth.types.ts +++ b/src/modules/auth.types.ts @@ -204,6 +204,7 @@ export interface AuthModule { * // Login with GitHub and redirect to dashboard * base44.auth.loginWithProvider('microsoft', '/dashboard'); * ``` + * @internal */ loginWithProvider(provider: string, fromUrl?: string): void; diff --git a/src/modules/connectors.types.ts b/src/modules/connectors.types.ts index a9a486b..3a5a774 100644 --- a/src/modules/connectors.types.ts +++ b/src/modules/connectors.types.ts @@ -13,9 +13,7 @@ export interface ConnectorAccessTokenResponse { /** * Connectors module for managing OAuth tokens for external services. * - * This module allows you to retrieve OAuth access tokens for external services - * that the app has connected to. Use these tokens to make API - * calls to external services. + * This module allows you to retrieve OAuth access tokens for external services that the app has connected to. Connectors are app-scoped. When an app builder connects an integration like Google Calendar or Slack, all users of the app share that same connection. * * Unlike the integrations module that provides pre-built functions, connectors give you * raw OAuth tokens so you can call external service APIs directly with full control over @@ -28,9 +26,9 @@ export interface ConnectorsModule { /** * Retrieves an OAuth access token for a specific external integration type. * - * Returns the OAuth token string for an external service that the app - * has connected to. You can then use this token to make authenticated API calls - * to that external service. + * Returns the OAuth token string for an external service that an app builder + * has connected to. This token represents the connected app builder's account + * and can be used to make authenticated API calls to that external service on behalf of the app. * * @param integrationType - The type of integration, such as `'googlecalendar'`, `'slack'`, or `'github'`. * @returns Promise resolving to the access token string. diff --git a/src/modules/custom-integrations.types.ts b/src/modules/custom-integrations.types.ts index 04c8fa5..959169f 100644 --- a/src/modules/custom-integrations.types.ts +++ b/src/modules/custom-integrations.types.ts @@ -9,7 +9,7 @@ export interface CustomIntegrationCallParams { payload?: Record; /** - * Path parameters to substitute in the URL (e.g., `{ owner: "user", repo: "repo" }`). + * Path parameters to substitute in the URL. For example, `{ owner: "user", repo: "repo" }`. */ pathParams?: Record; @@ -17,12 +17,6 @@ export interface CustomIntegrationCallParams { * Query string parameters to append to the URL. */ queryParams?: Record; - - /** - * Additional headers to send with this specific request. - * These are merged with the integration's configured headers. - */ - headers?: Record; } /** @@ -48,61 +42,17 @@ export interface CustomIntegrationCallResponse { } /** - * Module for calling custom workspace-level API integrations. - * - * Custom integrations allow workspace administrators to connect any external API - * by importing an OpenAPI specification. Apps in the workspace can then call - * these integrations using this module. - * - * Unlike the built-in integrations (like `Core`), custom integrations: - * - Are defined per-workspace by importing OpenAPI specs - * - Use a slug-based identifier instead of package names - * - Proxy requests through Base44's backend (credentials never exposed to frontend) - * - * @example - * ```typescript - * // Call a custom GitHub integration - * const response = await base44.integrations.custom.call( - * "github", // integration slug (defined by workspace admin) - * "get:/repos/{owner}/{repo}/issues", // endpoint in method:path format - * { - * pathParams: { owner: "myorg", repo: "myrepo" }, - * queryParams: { state: "open", per_page: 100 } - * } - * ); + * Module for calling custom pre-configured API integrations. * - * if (response.success) { - * console.log("Issues:", response.data); - * } else { - * console.error("API returned error:", response.status_code); - * } - * ``` - * - * @example - * ```typescript - * // Call with request body payload - * const response = await base44.integrations.custom.call( - * "github", - * "post:/repos/{owner}/{repo}/issues", - * { - * pathParams: { owner: "myorg", repo: "myrepo" }, - * payload: { - * title: "Bug report", - * body: "Something is broken", - * labels: ["bug"] - * } - * } - * ); - * ``` - * @internal + * Custom integrations allow workspace administrators to connect any external API by importing an OpenAPI specification. Apps in the workspace can then call these integrations using this module. */ export interface CustomIntegrationsModule { /** * Call a custom integration endpoint. * - * @param slug - The integration's unique identifier (slug), as defined by the workspace admin. - * @param operationId - The endpoint identifier in method:path format (e.g., "get:/repos/{owner}/{repo}/issues", "get:/users/{username}"). - * @param params - Optional parameters including payload, pathParams, queryParams, and headers. + * @param slug - The integration's unique identifier, as defined by the workspace admin. + * @param operationId - The endpoint in `method:path` format. For example, `"get:/contacts"`, or `"post:/users/{id}"`. The method is the HTTP verb in lowercase and the path matches the OpenAPI specification. + * @param params - Optional parameters including payload, pathParams, and queryParams. * @returns Promise resolving to the integration call response. * * @throws {Error} If slug is not provided. @@ -110,6 +60,36 @@ export interface CustomIntegrationsModule { * @throws {Base44Error} If the integration or operation is not found (404). * @throws {Base44Error} If the external API call fails (502). * @throws {Base44Error} If the request times out (504). + * + * @example + * ```typescript + * // Call a custom CRM integration + * const response = await base44.integrations.custom.call( + * "my-crm", + * "get:/contacts", + * { queryParams: { limit: 10 } } + * ); + * + * if (response.success) { + * console.log("Contacts:", response.data); + * } + * ``` + * + * @example + * ```typescript + * // Call with path params and request body + * const response = await base44.integrations.custom.call( + * "github", + * "post:/repos/{owner}/{repo}/issues", + * { + * pathParams: { owner: "myorg", repo: "myrepo" }, + * payload: { + * title: "Bug report", + * body: "Something is broken" + * } + * } + * ); + * ``` */ call( slug: string, diff --git a/src/modules/integrations.types.ts b/src/modules/integrations.types.ts index d109778..6696fa7 100644 --- a/src/modules/integrations.types.ts +++ b/src/modules/integrations.types.ts @@ -351,22 +351,28 @@ export interface CoreIntegrations { } /** - * Integrations module for calling integration endpoints. + * Integrations module for calling integration methods. * - * This module provides access to integration endpoints for interacting with external - * services. Integrations are organized into packages. Base44 provides built-in integrations - * in the `Core` package. + * This module provides access to integration methods for interacting with external services. Unlike the connectors module that gives you raw OAuth tokens, integrations provide pre-built functions that Base44 executes on your behalf. * - * Unlike the connectors module that gives you raw OAuth tokens, integrations provide - * pre-built functions that Base44 executes on your behalf. + * There are two types of integrations: * - * Integration endpoints are accessed dynamically using the pattern: - * `base44.integrations.PackageName.EndpointName(params)` + * - **Built-in integrations** (`Core`): Pre-built functions provided by Base44 for common tasks such as AI-powered text generation, image creation, file uploads, and email. Access core integration methods using: + * ``` + * base44.integrations.Core.FunctionName(params) + * ``` + * + * - **Custom integrations** (`custom`): Pre-configured external APIs. Custom integration calls are proxied through Base44's backend, so credentials are never exposed to the frontend. Access custom integration methods using: + * ``` + * base44.integrations.custom.call(slug, operationId, params) + * ``` + * + * To call a custom integration, it must be pre-configured by a workspace administrator who imports an OpenAPI specification. * * This module is available to use with a client in all authentication modes: * - * - **Anonymous or User authentication** (`base44.integrations`): Integration endpoints are invoked with the current user's permissions. Anonymous users invoke endpoints without authentication, while authenticated users invoke endpoints with their authentication context. - * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration endpoints are invoked with elevated admin-level permissions. The endpoints execute with admin authentication context. + * - **Anonymous or User authentication** (`base44.integrations`): Integration methods are invoked with the current user's permissions. Anonymous users invoke methods without authentication, while authenticated users invoke methods with their authentication context. + * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration methods are invoked with elevated admin-level permissions. The methods execute with admin authentication context. */ export type IntegrationsModule = { /** @@ -375,23 +381,7 @@ export type IntegrationsModule = { Core: CoreIntegrations; /** - * Custom integrations module for calling workspace-level API integrations. - * - * Allows calling external APIs that workspace admins have configured - * by importing OpenAPI specifications. - * - * @example - * ```typescript - * const response = await base44.integrations.custom.call( - * "github", // integration slug - * "get:/repos/{owner}/{repo}/issues", // endpoint in method:path format - * { - * pathParams: { owner: "myorg", repo: "myrepo" }, - * queryParams: { state: "open" } - * } - * ); - * ``` - * @internal + * Custom integrations module for calling pre-configured external APIs. */ custom: CustomIntegrationsModule; } & {