@cloudbase/app
Version:
cloudbase javascript sdk core
383 lines (331 loc) • 11.8 kB
text/typescript
import { adapters, constants, utils, helpers } from '@cloudbase/utilities'
import { SDKAdapterInterface, CloudbaseAdapter, IRequestConfig } from '@cloudbase/adapter-interface'
import {
ICloudbaseConfig,
ICloudbaseUpgradedConfig,
ICloudbase,
ICloudbaseExtension,
KV,
ICloudbasePlatformInfo,
EndPointKey,
ICloudbaseApis,
} from '@cloudbase/types'
import { ICloudbaseAuth } from '@cloudbase/types/auth'
import { registerComponent, registerHook } from './libs/component'
import { getWxDefaultAdapter, Platform } from './libs/adapter'
import { ICloudbaseComponent, ICloudbaseHook } from '@cloudbase/types/component'
import { ICloudbaseCache } from '@cloudbase/types/cache'
import { initCache, getCacheByEnvId, getLocalCache } from './libs/cache'
import { ICloudbaseRequest } from '@cloudbase/types/request'
import { initRequest, getRequestByEnvId } from './libs/request'
import {
getSdkName,
setSdkVersion,
setRegionLevelEndpoint,
setSdkName,
setGatewayEndPointWithEnv,
type ISetEndPointWithKey,
setEndPointInfo,
getEndPointInfo,
getSdkVersion,
DEFAULT_PROTOCOL,
} from './constants/common'
import { i18nProxy, LANGS } from './libs/lang'
export { getBaseEndPoint } from './constants/common'
export { LANGS } from './libs/lang'
const { useAdapters, useDefaultAdapter } = adapters
const { ERRORS, COMMUNITY_SITE_URL } = constants
const { printWarn } = utils
const { catchErrorsDecorator } = helpers
/**
* @constant 默认配置
*/
const DEFAULT_INIT_CONFIG: Partial<ICloudbaseConfig> = {
timeout: 15000,
persistence: 'local', // 持久化存储类型
}
// timeout上限10分钟
const MAX_TIMEOUT = 1000 * 60 * 10
// timeout下限100ms
const MIN_TIMEOUT = 100
const extensionMap: KV<ICloudbaseExtension> = {}
class Cloudbase implements ICloudbase {
public authInstance: ICloudbaseAuth
public oauthInstance: any
public requestClient: any
public oauthClient: any
public version: string
public auth: ICloudbase['auth'] & ICloudbaseAuth
public apis: ICloudbaseApis
private cloudbaseConfig: ICloudbaseConfig
constructor(config?: ICloudbaseConfig) {
this.cloudbaseConfig = config ? config : this.cloudbaseConfig
this.authInstance = null
this.oauthInstance = null
this.version = getSdkVersion()
}
get config() {
return this.cloudbaseConfig
}
get platform(): ICloudbasePlatformInfo {
return Platform
}
get cache(): ICloudbaseCache {
return getCacheByEnvId(this.cloudbaseConfig.env)
}
get localCache(): ICloudbaseCache {
return getLocalCache(this.cloudbaseConfig.env)
}
get request(): ICloudbaseRequest {
return getRequestByEnvId(this.cloudbaseConfig.env)
}
({
mode: 'sync',
title: 'Cloudbase 初始化失败',
messages: [
'请确认以下各项:',
' 1 - 调用 cloudbase.init() 的语法或参数是否正确',
' 2 - 如果是非浏览器环境,是否配置了安全应用来源(https://docs.cloudbase.net/api-reference/webv3/adapter#%E7%AC%AC-2-%E6%AD%A5%E9%85%8D%E7%BD%AE%E5%AE%89%E5%85%A8%E5%BA%94%E7%94%A8%E6%9D%A5%E6%BA%90)',
`如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
],
})
public init(config: ICloudbaseConfig & { lang?: LANGS } = { env: '' }): Cloudbase {
config.endPointMode = config.endPointMode || 'GATEWAY'
// 初始化时若未兼容平台,则使用默认adapter
if (!Platform.adapter) {
this.useDefaultAdapter()
}
// 在判断config.env之前,先处理node环境的配置
config = this.dealNodeAdapterConfig(config)
if (!config.env) {
throw new Error(JSON.stringify({
code: ERRORS.INVALID_PARAMS,
msg: 'env must not be specified',
}),)
}
this.isInitialized(config)
const reqConfig: IRequestConfig & { auth: ICloudbaseConfig['auth'] } = {
timeout: config.timeout || 15000,
timeoutMsg: `[${getSdkName()}][REQUEST TIMEOUT] request had been abort since didn't finished within${
config.timeout / 1000
}s`,
auth: config.auth,
}
this.requestClient = new Platform.adapter.reqClass(reqConfig)
this.cloudbaseConfig = {
...DEFAULT_INIT_CONFIG,
...config,
i18n: i18nProxy(Platform, config),
}
delete (this.cloudbaseConfig as any).lang
// 修正timeout取值
this.cloudbaseConfig.timeout = this.formatTimeout(this.cloudbaseConfig.timeout)
// 初始化cache和request
const { env, persistence, debug, timeout, oauthClient, i18n } = this.cloudbaseConfig
initCache({ env, persistence, debug, platformInfo: this.platform })
setRegionLevelEndpoint(env, config.region || '')
setGatewayEndPointWithEnv(env, DEFAULT_PROTOCOL, config.intl)
const app = new Cloudbase(this.cloudbaseConfig)
initRequest({
env,
region: config.region || '',
timeout,
oauthClient,
_fromApp: app,
i18n,
endPointMode: config.endPointMode,
auth: config.auth,
})
app.requestClient = this.requestClient
;(this as any)?.fire?.('cloudbase_init', app)
this.try2InitAuth(app)
// node环境可以从adapter获取nodeTool的方法
if (Platform.adapter?.nodeTool) {
Platform.adapter?.nodeTool(app, this.cloudbaseConfig)
}
return app
}
public updateConfig(config: ICloudbaseUpgradedConfig) {
const { persistence, debug } = config
this.cloudbaseConfig.persistence = persistence
this.cloudbaseConfig.debug = debug
// persistence改动影响cache
initCache({ env: this.cloudbaseConfig.env, persistence, debug, platformInfo: this.platform })
}
public updateLang(lang: LANGS) {
if (!lang || lang === this.cloudbaseConfig.i18n?.lang) return
this.cloudbaseConfig.i18n.lang = lang
}
public registerExtension(ext: ICloudbaseExtension) {
extensionMap[ext.name] = ext
}
({
title: '调用扩展能力失败',
messages: [
'请确认以下各项:',
' 1 - 调用 invokeExtension() 的语法或参数是否正确',
' 2 - 被调用的扩展能力是否已经安装并通过 registerExtension() 注册',
`如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
],
})
public async invokeExtension(name: string, opts: any) {
const ext = extensionMap[name]
if (!ext) {
throw new Error(JSON.stringify({
code: ERRORS.INVALID_PARAMS,
msg: `extension:${name} must be registered before invoke`,
}),)
}
return await ext.invoke(opts, this)
}
public useAdapters(adapters: CloudbaseAdapter | CloudbaseAdapter[], options?: any) {
const { adapter, runtime } = useAdapters(adapters, options) || {}
adapter && (Platform.adapter = adapter as SDKAdapterInterface)
runtime && (Platform.runtime = runtime as string)
}
public registerHook(hook: ICloudbaseHook) {
registerHook(Cloudbase, hook)
}
public registerComponent(component: ICloudbaseComponent) {
registerComponent(Cloudbase, component)
}
public registerVersion(version: string) {
setSdkVersion(version)
this.version = version
}
public registerSdkName(name: string) {
setSdkName(name)
}
/**
* 检查当前 Cloudbase 实例是否已完成初始化
*
* @example
* ```javascript
* import cloudbase from '@cloudbase/js-sdk'
*
* // 初始化前
* console.log(cloudbase.isInitialized()) // false
*
* const app = cloudbase.init({ env: 'your-env-id' })
*
* // 初始化后
* console.log(app.isInitialized()) // true
* ```
*
* @returns {boolean} 是否已初始化
*/
public isInitialized(config = this.cloudbaseConfig): boolean {
// 检测常见占位符 env 值,在开发阶段给出友好提示
const PLACEHOLDER_PATTERNS = [
'your-env-id',
'your-envid',
'your_env_id',
'xxx-yyy',
'xxxx-yyy',
'envId',
'env-id',
'your-environment-id',
'REPLACE_ME',
]
if (PLACEHOLDER_PATTERNS.some(p => config.env.toLowerCase() === p.toLowerCase())) {
printWarn(
ERRORS.INVALID_PARAMS,
`[CloudBase] 检测到环境 ID "${config.env}" 可能是占位符,请替换为真实的环境 ID。\n`
+ ' 获取方式:登录腾讯云开发平台 → 环境管理 → 环境设置 → 环境 ID\n'
+ ' 建议通过环境变量配置:process.env.CLOUDBASE_ENV 或 import.meta.env.VITE_CLOUDBASE_ENV',
)
}
return !!config?.env
}
/** 设置 tcb api 的 endpoint */
public registerEndPoint(url: string, protocol?: 'http' | 'https') {
setEndPointInfo({ baseUrl: url, protocol, env: this.config.env, endPointKey: 'CLOUD_API' })
}
/** 设置网关/tcb api的 endPoint,通过 key 指定 */
public registerEndPointWithKey(props: ISetEndPointWithKey) {
setEndPointInfo({
env: this.config.env,
endPointKey: props.key,
baseUrl: props.url,
protocol: props.protocol,
})
}
/** 拿网关/tcb api的 endPoint,通过 key 指定 */
public getEndPointWithKey(key: EndPointKey) {
const info = getEndPointInfo(this.config.env, key)
return {
BASE_URL: info.baseUrl,
PROTOCOL: info.protocol,
}
}
// 解析URL参数
public parseCaptcha(url) {
return utils.parseCaptcha(url)
}
private useDefaultAdapter() {
const { adapter, runtime } = useDefaultAdapter.bind(this)()
Platform.adapter = adapter as SDKAdapterInterface
Platform.runtime = runtime as string
}
private formatTimeout(timeout: number) {
switch (true) {
case timeout > MAX_TIMEOUT:
printWarn(ERRORS.INVALID_PARAMS, 'timeout is greater than maximum value[10min]')
return MAX_TIMEOUT
case timeout < MIN_TIMEOUT:
printWarn(ERRORS.INVALID_PARAMS, 'timeout is less than maximum value[100ms]')
return MIN_TIMEOUT
default:
return timeout
}
}
private try2InitAuth(app) {
try {
app.auth()
} catch (error) {
console.log('try2InitAuth error:', error)
}
}
/**
* 处理 Node 环境下 adapter 的配置信息
* 在 Node 环境中,通过 adapter 自动获取密钥、环境ID、accessKey 等配置,
* 减少用户手动传入配置的负担
* @param config - 云开发初始化配置对象
* @returns 补充了 Node adapter 信息后的配置对象
*/
private dealNodeAdapterConfig(config: ICloudbaseConfig) {
// node环境可以从adapter获取默认的secretId和secretKey
if (
typeof process !== 'undefined'
&& typeof process.env !== 'undefined'
&& !process.env.IS_BROWSER_BUILD
&& Platform.adapter?.getSecretInfo
) {
const secretInfo = Platform.adapter?.getSecretInfo(config)
// 将 adapter 提供的密钥信息写入 config.auth
config.auth = {
...config.auth,
secretId: secretInfo.secretId,
secretKey: secretInfo.secretKey,
sessionToken: secretInfo.sessionToken,
secretType: secretInfo.secretType as any,
}
// node 环境可能可以从上下文获取env,用户未显式指定时自动填充
if (!config.env) {
config.env = secretInfo.env
}
// node adapter环境能读取到 process.env.CLOUDBASE_APIKEY 时,在没有传入accessKey时,使用 node adapter 提供的 accessKey(process.env.CLOUDBASE_APIKEY)
if (!config.accessKey && secretInfo.accessKey) {
config.accessKey = secretInfo.accessKey
}
}
return config
}
}
// 类型导出
export type { Cloudbase }
// 值导出
export const cloudbase: ICloudbase = new Cloudbase()
cloudbase.useAdapters(getWxDefaultAdapter())
// 默认导出实例
export default cloudbase