import { initConfigFromRemote, isInitialized, type XuqmInitOptions, type XuqmUserInfo, setUserId as setCommonUserId, getUserId as getCommonUserId, setUserInfo as setCommonUserInfo, getUserInfo as getCommonUserInfo } from './config' import { DEFAULT_IM_WS_URL, DEFAULT_TENANT_PLATFORM_URL } from './constants' import { configureHttp } from './http' import { decryptConfigFile } from './configCrypto' let _initPromise: Promise | null = null let _initResolve: (() => void) | null = null function ensureInitPromise(): Promise { if (!_initPromise) { _initPromise = new Promise((resolve) => { _initResolve = resolve }) } return _initPromise } function markInitialized(): void { if (_initResolve) { _initResolve() _initResolve = null } } export const XuqmSDK = { /** * @param options.appKey - Your application key (from the tenant platform) * @param options.debug - Enable verbose logging */ async initialize(options: XuqmInitOptions): Promise { if (isInitialized()) return const configUrl = `${DEFAULT_TENANT_PLATFORM_URL}/api/sdk/config?appKey=${options.appKey}` try { const res = await fetch(configUrl) const json = await res.json() const remote = json.data ?? json initConfigFromRemote(options, { imWsUrl: remote.imWsUrl, fileServiceUrl: remote.fileServiceUrl, apiUrl: remote.imApiUrl ?? DEFAULT_TENANT_PLATFORM_URL, }) configureHttp({ baseUrl: remote.imApiUrl ?? DEFAULT_TENANT_PLATFORM_URL, debug: options.debug, }) } catch (e) { // Fallback: construct URLs from the built-in platform endpoint initConfigFromRemote(options, { imWsUrl: DEFAULT_IM_WS_URL, fileServiceUrl: DEFAULT_TENANT_PLATFORM_URL, apiUrl: DEFAULT_TENANT_PLATFORM_URL, }) configureHttp({ baseUrl: DEFAULT_TENANT_PLATFORM_URL, debug: options.debug, }) if (options.debug) console.warn('[XuqmSDK] Config fetch failed, using fallback URLs', e) } markInitialized() }, /** * @param options.appKey - Your application key (from the tenant platform) * @param options.debug - Enable verbose logging */ init(options: XuqmInitOptions): void { if (isInitialized()) return initConfigFromRemote(options, { imWsUrl: DEFAULT_IM_WS_URL, fileServiceUrl: DEFAULT_TENANT_PLATFORM_URL, apiUrl: DEFAULT_TENANT_PLATFORM_URL, }) configureHttp({ baseUrl: DEFAULT_TENANT_PLATFORM_URL, debug: options.debug, }) markInitialized() }, /** * Initialize from a decrypted license file object. */ initializeFromLicense(file: { appKey: string; baseUrl?: string; serverUrl?: string }, options?: { debug?: boolean }): void { if (isInitialized()) return const serverUrl = file.serverUrl || file.baseUrl || DEFAULT_TENANT_PLATFORM_URL initConfigFromRemote({ appKey: file.appKey, debug: options?.debug }, { imWsUrl: DEFAULT_IM_WS_URL, fileServiceUrl: serverUrl, apiUrl: serverUrl, }) configureHttp({ baseUrl: serverUrl, debug: options?.debug }) markInitialized() }, /** * 从加密配置文件初始化 SDK。 * * 宿主只需传入 .xuqmconfig 文件的加密内容,SDK 自动解密并初始化。 * 支持 XUQM-CONFIG-V1 和 XUQM-LICENSE-V1 两种格式。 * * @param encryptedContent 加密配置文件的完整内容 * @param options.debug 是否开启调试日志 * * @example * // common bundle 入口 * import config from './assets/app.xuqmconfig' * await XuqmSDK.initWithConfigFile(config) */ async initWithConfigFile(encryptedContent: string, options?: { debug?: boolean }): Promise { if (isInitialized()) return const file = await decryptConfigFile(encryptedContent) this.initializeFromLicense(file, options) }, /** * Wait for initialization to complete. */ async awaitInitialization(): Promise { if (isInitialized()) return await ensureInitPromise() }, setUserId(userId: string | null): void { setCommonUserId(userId) }, getUserId(): string | null { return getCommonUserId() }, /** * Set user info for gray release targeting and license verification. * Call this after user login. */ setUserInfo(info: XuqmUserInfo | null): void { setCommonUserInfo(info) }, getUserInfo(): XuqmUserInfo | null { return getCommonUserInfo() }, } export async function awaitInitialization(): Promise { if (isInitialized()) return await ensureInitPromise() }