2026-04-24 16:16:31 +08:00
|
|
|
|
export interface XuqmInitOptions {
|
2026-04-29 15:46:40 +08:00
|
|
|
|
appKey: string
|
2026-07-17 13:50:30 +08:00
|
|
|
|
platformUrl?: string // 不传则使用内置默认公有平台地址
|
2026-04-24 16:16:31 +08:00
|
|
|
|
debug?: boolean
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface XuqmConfig {
|
|
|
|
|
|
appKey: string
|
2026-04-29 15:46:40 +08:00
|
|
|
|
apiUrl: string
|
|
|
|
|
|
imWsUrl: string
|
2026-06-15 10:57:55 +08:00
|
|
|
|
fileServiceUrl: string
|
2026-04-24 16:16:31 +08:00
|
|
|
|
debug: boolean
|
2026-06-15 10:57:55 +08:00
|
|
|
|
// 服务开通状态(由平台远程配置决定)
|
|
|
|
|
|
imEnabled: boolean
|
|
|
|
|
|
pushEnabled: boolean
|
2026-06-16 17:39:18 +08:00
|
|
|
|
// 崩溃采集服务(rn-bugcollect 使用)
|
|
|
|
|
|
bugCollectApiUrl: string
|
|
|
|
|
|
bugCollectEnabled: boolean
|
2026-06-19 01:27:56 +08:00
|
|
|
|
// 请求签名密钥(从配置文件读取)
|
|
|
|
|
|
signingKey?: string
|
2026-04-24 16:16:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 01:44:20 +08:00
|
|
|
|
export interface XuqmUserInfo {
|
2026-06-15 10:57:55 +08:00
|
|
|
|
userId: string
|
2026-07-17 13:50:30 +08:00
|
|
|
|
userSig?: string // IM 登录凭证;未开通 IM 时可不传
|
2026-06-15 01:44:20 +08:00
|
|
|
|
name?: string
|
|
|
|
|
|
email?: string
|
|
|
|
|
|
phone?: string
|
2026-06-15 10:57:55 +08:00
|
|
|
|
avatar?: string
|
2026-06-15 01:44:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
export interface XuqmLoginOptions extends XuqmUserInfo {
|
|
|
|
|
|
/** 公共 HTTP 访问令牌;与 IM userSig 分开存储、分开使用。 */
|
|
|
|
|
|
accessToken?: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export type XuqmUserInfoHandler = (info: XuqmUserInfo | null) => void | Promise<void>
|
|
|
|
|
|
export type XuqmInitializationHandler = (config: Readonly<XuqmConfig>) => void | Promise<void>
|
|
|
|
|
|
|
2026-06-15 10:57:55 +08:00
|
|
|
|
// ─── Internal state ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
let _config: XuqmConfig | null = null
|
2026-06-15 01:44:20 +08:00
|
|
|
|
let _userInfo: XuqmUserInfo | null = null
|
2026-07-17 13:50:30 +08:00
|
|
|
|
const _userInfoHandlers = new Map<string, XuqmUserInfoHandler>()
|
|
|
|
|
|
const _initializationHandlers = new Map<string, XuqmInitializationHandler>()
|
2026-06-15 10:57:55 +08:00
|
|
|
|
|
|
|
|
|
|
// ─── Config ────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
export interface XuqmRemoteConfig {
|
|
|
|
|
|
imApiUrl?: string
|
|
|
|
|
|
apiUrl?: string
|
|
|
|
|
|
imWsUrl?: string
|
|
|
|
|
|
fileServiceUrl?: string
|
|
|
|
|
|
imEnabled?: boolean
|
|
|
|
|
|
pushEnabled?: boolean
|
2026-06-16 17:39:18 +08:00
|
|
|
|
bugCollectApiUrl?: string
|
|
|
|
|
|
bugCollectEnabled?: boolean
|
2026-06-15 10:57:55 +08:00
|
|
|
|
}
|
2026-06-15 01:44:20 +08:00
|
|
|
|
|
2026-04-25 16:41:19 +08:00
|
|
|
|
export function initConfigFromRemote(
|
|
|
|
|
|
options: XuqmInitOptions,
|
2026-06-15 10:57:55 +08:00
|
|
|
|
remote: XuqmRemoteConfig,
|
2026-06-19 01:27:56 +08:00
|
|
|
|
signingKey?: string,
|
2026-04-25 16:41:19 +08:00
|
|
|
|
): void {
|
2026-07-17 13:50:30 +08:00
|
|
|
|
const apiUrl = remote.apiUrl ?? remote.imApiUrl ?? ''
|
2026-04-24 16:16:31 +08:00
|
|
|
|
_config = {
|
2026-07-17 13:50:30 +08:00
|
|
|
|
appKey: options.appKey,
|
2026-06-15 10:57:55 +08:00
|
|
|
|
apiUrl,
|
2026-07-17 13:50:30 +08:00
|
|
|
|
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,
|
2026-06-19 01:27:56 +08:00
|
|
|
|
signingKey,
|
2026-04-24 16:16:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getConfig(): XuqmConfig {
|
2026-04-25 16:41:19 +08:00
|
|
|
|
if (!_config) throw new Error('[XuqmSDK] Not initialized — call XuqmSDK.initialize() first.')
|
2026-04-24 16:16:31 +08:00
|
|
|
|
return _config
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 10:57:55 +08:00
|
|
|
|
export function isInitialized(): boolean {
|
|
|
|
|
|
return _config !== null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
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)
|
2026-06-15 10:57:55 +08:00
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
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<void> {
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-28 16:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
// ─── UserId ────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-28 16:55:12 +08:00
|
|
|
|
export function getUserId(): string | null {
|
2026-07-17 13:50:30 +08:00
|
|
|
|
return _userInfo?.userId ?? null
|
2026-04-28 16:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 10:57:55 +08:00
|
|
|
|
// ─── UserInfo + subscribers ────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
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)
|
2026-04-24 16:16:31 +08:00
|
|
|
|
}
|
2026-06-15 01:44:20 +08:00
|
|
|
|
|
2026-07-17 13:50:30 +08:00
|
|
|
|
export async function _setUserInfo(info: XuqmUserInfo | null): Promise<void> {
|
2026-06-15 01:44:20 +08:00
|
|
|
|
_userInfo = info
|
2026-07-17 13:50:30 +08:00
|
|
|
|
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('; ')}`)
|
2026-06-15 01:44:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getUserInfo(): XuqmUserInfo | null {
|
|
|
|
|
|
return _userInfo
|
|
|
|
|
|
}
|