171 行
5.6 KiB
TypeScript
171 行
5.6 KiB
TypeScript
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<void>
|
||
export type XuqmInitializationHandler = (config: Readonly<XuqmConfig>) => void | Promise<void>
|
||
|
||
// ─── Internal state ────────────────────────────────────────────────────────────
|
||
|
||
let _config: XuqmConfig | null = null
|
||
let _userInfo: XuqmUserInfo | null = null
|
||
const _userInfoHandlers = new Map<string, XuqmUserInfoHandler>()
|
||
const _initializationHandlers = new Map<string, XuqmInitializationHandler>()
|
||
|
||
// ─── 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<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)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─── 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<void> {
|
||
_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
|
||
}
|