/** * Airtable Auth Manager - Unified auth management utility * * Encapsulates Airtable Auth best practices: * - Uses wish() with framework picker for account selection * - Detects missing scopes and navigates to auth piece * - Detects expired tokens and provides recovery UI * - Pre-composed UI components for consistent UX * * Usage: * ```typescript * const { auth, fullUI, isReady } = AirtableAuthManager({ * requiredScopes: ["data.records:read", "schema.bases:read"], * }); * * if (!isReady) return; * // Use auth.accessToken for API calls * * return { [UI]:
{fullUI}
}; * ``` */ import { action, navigateTo, pattern, Writable } from "commonfabric"; import { AuthManagerBase } from "../../../auth/create-auth-manager.tsx"; import type { AuthManagerDescriptor } from "../../../auth/auth-manager-descriptor.ts"; import AirtableAuth from "../airtable-auth.tsx"; // Re-export shared types for consumers export type { AuthInfo, AuthState, TokenExpiryWarning, } from "../../../auth/auth-types.ts"; export type { AuthManagerInput as AirtableAuthManagerInput, AuthManagerOutput as AirtableAuthManagerOutput, } from "../../../auth/create-auth-manager.tsx"; export type { AirtableAuth as AirtableAuthType } from "../airtable-auth.tsx"; /** Airtable scope keys */ export type ScopeKey = | "data.records:read" | "data.records:write" | "data.recordComments:read" | "data.recordComments:write" | "schema.bases:read" | "schema.bases:write" | "webhook:manage"; /** Human-readable scope descriptions */ const AIRTABLE_SCOPE_DESCRIPTIONS = { "data.records:read": "Read records", "data.records:write": "Write records", "data.recordComments:read": "Read record comments", "data.recordComments:write": "Write record comments", "schema.bases:read": "Read base schemas", "schema.bases:write": "Write base schemas", "webhook:manage": "Manage webhooks", } as const; export const SCOPE_DESCRIPTIONS: Record = AIRTABLE_SCOPE_DESCRIPTIONS; /** Unified scope registry for the auth manager base */ const SCOPES: AuthManagerDescriptor["scopes"] = Object.fromEntries( Object.entries(SCOPE_DESCRIPTIONS).map(([key, desc]) => [ key, { description: desc, scopeString: key }, ]), ); const AirtableAuthManagerDescriptor: AuthManagerDescriptor = { name: "airtable", displayName: "Airtable", brandColor: "#18BFFF", wishTag: "#airtableAuth", tokenField: "accessToken", scopes: SCOPES, hasAvatarSupport: false, }; export const AirtableAuthManager = pattern< import("../../../auth/create-auth-manager.tsx").AuthManagerInput, import("../../../auth/create-auth-manager.tsx").AuthManagerOutput >(({ requiredScopes, accountType, debugMode }) => { const createAuth = action(() => { const required = Array.isArray(requiredScopes) ? requiredScopes : []; const emptyAuth: Record = { tokenType: "", scope: [], expiresIn: 0, expiresAt: 0, refreshToken: "", user: { email: "", name: "", picture: "" }, accessToken: "", }; return navigateTo( AirtableAuth( { selectedScopes: { "data.records:read": new Writable( required.includes("data.records:read"), ), "data.records:write": new Writable( required.includes("data.records:write"), ), "data.recordComments:read": new Writable( required.includes("data.recordComments:read"), ), "data.recordComments:write": new Writable( required.includes("data.recordComments:write"), ), "schema.bases:read": new Writable( required.includes("schema.bases:read"), ), "schema.bases:write": new Writable( required.includes("schema.bases:write"), ), "webhook:manage": new Writable(required.includes("webhook:manage")), }, auth: emptyAuth, } as Parameters[0], ), ); }); return AuthManagerBase({ requiredScopes, accountType, debugMode, descriptor: AirtableAuthManagerDescriptor, createAuth, }); }); export default AirtableAuthManager;