export interface XuqmInitOptions { appKey: string platformUrl?: string // 不传则使用内置默认公有平台地址 debug?: boolean } export interface XuqmConfig { appKey: string apiUrl: string imWsUrl: string fileServiceUrl: string debug: boolean // 服务开通状态(由平台远程配置决定) imEnabled: boolean pushEnabled: boolean // 崩溃采集服务(rn-bugcollect 使用) bugCollectApiUrl: string bugCollectEnabled: boolean // 请求签名密钥(从配置文件读取) signingKey?: string } export interface XuqmUserInfo { userId: string userSig?: string // IM 登录凭证;未开通 IM 时可不传 name?: string email?: string phone?: string avatar?: string } export interface XuqmLoginOptions extends XuqmUserInfo { /** 公共 HTTP 访问令牌;与 IM userSig 分开存储、分开使用。 */ accessToken?: string } export type XuqmUserInfoHandler = (info: XuqmUserInfo | null) => void | Promise export type XuqmInitializationHandler = (config: Readonly) => void | Promise // ─── Internal state ──────────────────────────────────────────────────────────── let _config: XuqmConfig | null = null let _userInfo: XuqmUserInfo | null = null const _userInfoHandlers = new Map() const _initializationHandlers = new Map() // ─── Config ──────────────────────────────────────────────────────────────────── export interface XuqmRemoteConfig { imApiUrl?: string apiUrl?: string imWsUrl?: string fileServiceUrl?: string imEnabled?: boolean pushEnabled?: boolean bugCollectApiUrl?: string bugCollectEnabled?: boolean } export function initConfigFromRemote( options: XuqmInitOptions, remote: XuqmRemoteConfig, signingKey?: string, ): void { const apiUrl = remote.apiUrl ?? remote.imApiUrl ?? '' _config = { appKey: options.appKey, apiUrl, imWsUrl: remote.imWsUrl ?? '', fileServiceUrl: remote.fileServiceUrl ?? apiUrl, debug: options.debug ?? false, imEnabled: remote.imEnabled ?? !!remote.imWsUrl, pushEnabled: remote.pushEnabled ?? true, bugCollectApiUrl: remote.bugCollectApiUrl ?? '', bugCollectEnabled: remote.bugCollectEnabled ?? false, signingKey, } } export function getConfig(): XuqmConfig { if (!_config) throw new Error('[XuqmSDK] Not initialized — call XuqmSDK.initialize() first.') return _config } /** * Optional SDK context for common capabilities that also work standalone. * Extension packages should use getConfig() so a missing initialization remains explicit. */ export function getOptionalConfig(): XuqmConfig | null { return _config } export function isInitialized(): boolean { return _config !== null } export function _registerInitializationHandler( name: string, handler: XuqmInitializationHandler, ): () => void { if (_initializationHandlers.has(name)) { throw new Error(`[XuqmSDK] Initialization handler "${name}" is already registered.`) } _initializationHandlers.set(name, handler) const current = _config if (current) { Promise.resolve(handler(current)).catch(error => { console.error(`[XuqmSDK] Late initialization handler failed: ${name}`, error) }) } return () => _initializationHandlers.delete(name) } export async function _notifyInitialized(): Promise { const config = getConfig() const results = await Promise.allSettled( Array.from(_initializationHandlers.entries(), ([name, handler]) => Promise.resolve(handler(config)).catch(error => { const message = error instanceof Error ? error.message : String(error) throw new Error(`${name}: ${message}`) }), ), ) for (const result of results) { if (result.status === 'rejected') { console.error('[XuqmSDK] Extension initialization failed:', result.reason) } } } // ─── UserId ──────────────────────────────────────────────────────────────────── export function getUserId(): string | null { return _userInfo?.userId ?? null } // ─── UserInfo + subscribers ──────────────────────────────────────────────────── export function _registerUserInfoHandler(name: string, handler: XuqmUserInfoHandler): () => void { if (_userInfoHandlers.has(name)) { throw new Error(`[XuqmSDK] User session handler "${name}" is already registered.`) } _userInfoHandlers.set(name, handler) return () => _userInfoHandlers.delete(name) } export async function _setUserInfo(info: XuqmUserInfo | null): Promise { _userInfo = info const results = await Promise.allSettled( Array.from(_userInfoHandlers.entries(), async ([name, handler]) => { try { await handler(info) } catch (error) { const message = error instanceof Error ? error.message : String(error) throw new Error(`${name}: ${message}`) } }), ) const failures = results .filter((result): result is PromiseRejectedResult => result.status === 'rejected') .map(result => String(result.reason)) if (failures.length > 0) { throw new Error(`[XuqmSDK] Session propagation failed: ${failures.join('; ')}`) } } export function getUserInfo(): XuqmUserInfo | null { return _userInfo }