@cloudbase/app 2.26.0 → 2.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cjs/index.d.ts +2 -0
  2. package/dist/cjs/index.js +43 -7
  3. package/dist/cjs/index.node.d.ts +6 -0
  4. package/dist/cjs/index.node.js +21 -0
  5. package/dist/cjs/libs/adapter-node/constants.d.ts +33 -0
  6. package/dist/cjs/libs/adapter-node/constants.js +38 -0
  7. package/dist/cjs/libs/adapter-node/context.d.ts +13 -0
  8. package/dist/cjs/libs/adapter-node/context.js +162 -0
  9. package/dist/cjs/libs/adapter-node/index.d.ts +23 -0
  10. package/dist/cjs/libs/adapter-node/index.js +124 -0
  11. package/dist/cjs/libs/adapter-node/metadata.d.ts +14 -0
  12. package/dist/cjs/libs/adapter-node/metadata.js +130 -0
  13. package/dist/cjs/libs/adapter-node/request.d.ts +38 -0
  14. package/dist/cjs/libs/adapter-node/request.js +440 -0
  15. package/dist/cjs/libs/adapter-node/tool.d.ts +2 -0
  16. package/dist/cjs/libs/adapter-node/tool.js +227 -0
  17. package/dist/cjs/libs/adapter-node/types.d.ts +78 -0
  18. package/dist/cjs/libs/adapter-node/types.js +3 -0
  19. package/dist/cjs/libs/adapter-node/utils.d.ts +17 -0
  20. package/dist/cjs/libs/adapter-node/utils.js +221 -0
  21. package/dist/cjs/libs/request.d.ts +1 -0
  22. package/dist/cjs/libs/request.js +34 -19
  23. package/dist/esm/index.d.ts +2 -0
  24. package/dist/esm/index.js +43 -7
  25. package/dist/esm/index.node.d.ts +6 -0
  26. package/dist/esm/index.node.js +13 -0
  27. package/dist/esm/libs/adapter-node/constants.d.ts +33 -0
  28. package/dist/esm/libs/adapter-node/constants.js +35 -0
  29. package/dist/esm/libs/adapter-node/context.d.ts +13 -0
  30. package/dist/esm/libs/adapter-node/context.js +156 -0
  31. package/dist/esm/libs/adapter-node/index.d.ts +23 -0
  32. package/dist/esm/libs/adapter-node/index.js +118 -0
  33. package/dist/esm/libs/adapter-node/metadata.d.ts +14 -0
  34. package/dist/esm/libs/adapter-node/metadata.js +123 -0
  35. package/dist/esm/libs/adapter-node/request.d.ts +38 -0
  36. package/dist/esm/libs/adapter-node/request.js +437 -0
  37. package/dist/esm/libs/adapter-node/tool.d.ts +2 -0
  38. package/dist/esm/libs/adapter-node/tool.js +223 -0
  39. package/dist/esm/libs/adapter-node/types.d.ts +78 -0
  40. package/dist/esm/libs/adapter-node/types.js +2 -0
  41. package/dist/esm/libs/adapter-node/utils.d.ts +17 -0
  42. package/dist/esm/libs/adapter-node/utils.js +205 -0
  43. package/dist/esm/libs/request.d.ts +1 -0
  44. package/dist/esm/libs/request.js +34 -19
  45. package/dist/miniprogram/index.js +1 -1
  46. package/package.json +32 -4
  47. package/src/index.node.ts +22 -0
  48. package/src/index.ts +91 -7
  49. package/src/libs/adapter-node/constants.ts +42 -0
  50. package/src/libs/adapter-node/context.ts +238 -0
  51. package/src/libs/adapter-node/index.ts +166 -0
  52. package/src/libs/adapter-node/metadata.ts +69 -0
  53. package/src/libs/adapter-node/request.ts +486 -0
  54. package/src/libs/adapter-node/tool.ts +223 -0
  55. package/src/libs/adapter-node/types.ts +116 -0
  56. package/src/libs/adapter-node/utils.ts +182 -0
  57. package/src/libs/request.ts +125 -113
  58. package/webpack/web.prod.js +14 -13
  59. package/webpack/webpack.miniprogram.js +10 -3
package/package.json CHANGED
@@ -1,9 +1,21 @@
1
1
  {
2
2
  "name": "@cloudbase/app",
3
- "version": "2.26.0",
3
+ "version": "2.26.2",
4
4
  "description": "cloudbase javascript sdk core",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "node": {
10
+ "import": "./dist/esm/index.node.js",
11
+ "require": "./dist/cjs/index.node.js"
12
+ },
13
+ "import": "./dist/esm/index.js",
14
+ "require": "./dist/cjs/index.js",
15
+ "default": "./dist/cjs/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
7
19
  "scripts": {
8
20
  "tsc": "rm -rf dist/ && tsc -p tsconfig.esm.json && tsc -p tsconfig.json",
9
21
  "lint": "eslint --fix \"./src/**/*.ts\"",
@@ -31,8 +43,24 @@
31
43
  "dependencies": {
32
44
  "@cloudbase/adapter-interface": "^0.7.1",
33
45
  "@cloudbase/adapter-wx_mp": "^1.3.1",
34
- "@cloudbase/types": "2.26.0",
35
- "@cloudbase/utilities": "2.26.0"
46
+ "@cloudbase/types": "2.26.2",
47
+ "@cloudbase/utilities": "2.26.2"
48
+ },
49
+ "peerDependencies": {
50
+ "@cloudbase/signature-nodejs": ">=1.0.0",
51
+ "jsonwebtoken": ">=8.0.0",
52
+ "ws": "^8.18.0"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "@cloudbase/signature-nodejs": {
56
+ "optional": true
57
+ },
58
+ "jsonwebtoken": {
59
+ "optional": true
60
+ },
61
+ "ws": {
62
+ "optional": true
63
+ }
36
64
  },
37
- "gitHead": "48764fb5aa33114d65b3607ab97cc95be3f558cc"
65
+ "gitHead": "af397dd45339057868b98ac09c417d352e56a217"
38
66
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Node.js 入口
3
+ * 引用 base 模块的全部导出,并额外加载 Node adapter
4
+ */
5
+ import cloudbase from './index'
6
+
7
+ export { getBaseEndPoint } from './constants/common'
8
+ export { LANGS } from './libs/lang'
9
+ export type { Cloudbase } from './index'
10
+ export { cloudbase }
11
+
12
+ // Node 环境下加载 Node adapter
13
+ try {
14
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
15
+ const nodeAdapter = require('./libs/adapter-node').default
16
+ cloudbase.useAdapters(nodeAdapter)
17
+ } catch (error) {
18
+ throw error
19
+ }
20
+
21
+ // 默认导出实例
22
+ export default cloudbase
package/src/index.ts CHANGED
@@ -101,24 +101,31 @@ class Cloudbase implements ICloudbase {
101
101
  ],
102
102
  })
103
103
  public init(config: ICloudbaseConfig & { lang?: LANGS }): Cloudbase {
104
+ config.endPointMode = config.endPointMode || 'CLOUD_API'
105
+
106
+ // 初始化时若未兼容平台,则使用默认adapter
107
+ if (!Platform.adapter) {
108
+ this.useDefaultAdapter()
109
+ }
110
+
111
+ // 在判断config.env之前,先处理node环境的配置
112
+ config = this.dealNodeAdapterConfig(config)
113
+
104
114
  if (!config.env) {
105
115
  throw new Error(JSON.stringify({
106
116
  code: ERRORS.INVALID_PARAMS,
107
117
  msg: 'env must not be specified',
108
118
  }),)
109
119
  }
110
- // config.endPointMode = config.endPointMode || 'GATEWAY'
111
120
 
112
- // 初始化时若未兼容平台,则使用默认adapter
113
- if (!Platform.adapter) {
114
- this.useDefaultAdapter()
115
- }
121
+ this.isInitialized(config)
116
122
 
117
- const reqConfig: IRequestConfig = {
118
- timeout: config.timeout || 5000,
123
+ const reqConfig: IRequestConfig & { auth: ICloudbaseConfig['auth'] } = {
124
+ timeout: config.timeout || 15000,
119
125
  timeoutMsg: `[${getSdkName()}][REQUEST TIMEOUT] request had been abort since didn't finished within${
120
126
  config.timeout / 1000
121
127
  }s`,
128
+ auth: config.auth,
122
129
  }
123
130
 
124
131
  this.requestClient = new Platform.adapter.reqClass(reqConfig)
@@ -148,12 +155,18 @@ class Cloudbase implements ICloudbase {
148
155
  _fromApp: app,
149
156
  i18n,
150
157
  endPointMode: config.endPointMode,
158
+ auth: config.auth,
151
159
  })
152
160
  app.requestClient = this.requestClient
153
161
  ;(this as any)?.fire?.('cloudbase_init', app)
154
162
 
155
163
  this.try2InitAuth(app)
156
164
 
165
+ // node环境可以从adapter获取nodeTool的方法
166
+ if (Platform.adapter?.nodeTool) {
167
+ Platform.adapter?.nodeTool(app, this.cloudbaseConfig)
168
+ }
169
+
157
170
  return app
158
171
  }
159
172
 
@@ -218,6 +231,43 @@ class Cloudbase implements ICloudbase {
218
231
  setSdkName(name)
219
232
  }
220
233
 
234
+ /**
235
+ * 检查当前 Cloudbase 实例是否已完成初始化
236
+ *
237
+ * @example
238
+ * ```javascript
239
+ * import cloudbase from '@cloudbase/js-sdk'
240
+ *
241
+ * // 初始化前
242
+ * console.log(cloudbase.isInitialized()) // false
243
+ *
244
+ * const app = cloudbase.init({ env: 'your-env-id' })
245
+ *
246
+ * // 初始化后
247
+ * console.log(app.isInitialized()) // true
248
+ * ```
249
+ *
250
+ * @returns {boolean} 是否已初始化
251
+ */
252
+ public isInitialized(config = this.cloudbaseConfig): boolean {
253
+ // 检测常见占位符 env 值,在开发阶段给出友好提示
254
+ const PLACEHOLDER_PATTERNS = [
255
+ 'your-env-id', 'your-envid', 'your_env_id',
256
+ 'xxx-yyy', 'xxxx-yyy', 'envId', 'env-id',
257
+ 'your-environment-id', 'REPLACE_ME',
258
+ ]
259
+ if (PLACEHOLDER_PATTERNS.some(p => config.env.toLowerCase() === p.toLowerCase())) {
260
+ printWarn(
261
+ ERRORS.INVALID_PARAMS,
262
+ `[CloudBase] 检测到环境 ID "${config.env}" 可能是占位符,请替换为真实的环境 ID。\n`
263
+ + ' 获取方式:登录腾讯云开发平台 → 环境管理 → 环境设置 → 环境 ID\n'
264
+ + ' 建议通过环境变量配置:process.env.CLOUDBASE_ENV 或 import.meta.env.VITE_CLOUDBASE_ENV',
265
+ )
266
+ }
267
+
268
+ return !!(config?.env)
269
+ }
270
+
221
271
  /** 设置 tcb api 的 endpoint */
222
272
  public registerEndPoint(url: string, protocol?: 'http' | 'https') {
223
273
  setEndPointInfo({ baseUrl: url, protocol, env: this.config.env, endPointKey: 'CLOUD_API' })
@@ -273,6 +323,40 @@ class Cloudbase implements ICloudbase {
273
323
  console.log('try2InitAuth error:', error)
274
324
  }
275
325
  }
326
+
327
+ /**
328
+ * 处理 Node 环境下 adapter 的配置信息
329
+ * 在 Node 环境中,通过 adapter 自动获取密钥、环境ID、accessKey 等配置,
330
+ * 减少用户手动传入配置的负担
331
+ * @param config - 云开发初始化配置对象
332
+ * @returns 补充了 Node adapter 信息后的配置对象
333
+ */
334
+ private dealNodeAdapterConfig(config: ICloudbaseConfig) {
335
+ // node环境可以从adapter获取默认的secretId和secretKey
336
+ if (typeof process !== 'undefined' && typeof process.env !== 'undefined' && !process.env.IS_BROWSER_BUILD && Platform.adapter?.getSecretInfo) {
337
+ const secretInfo = Platform.adapter?.getSecretInfo(config)
338
+ // 将 adapter 提供的密钥信息写入 config.auth
339
+ config.auth = {
340
+ ...config.auth,
341
+ secretId: secretInfo.secretId,
342
+ secretKey: secretInfo.secretKey,
343
+ sessionToken: secretInfo.sessionToken,
344
+ secretType: secretInfo.secretType as any,
345
+ }
346
+
347
+ // node 环境可能可以从上下文获取env,用户未显式指定时自动填充
348
+ if (!config.env) {
349
+ config.env = secretInfo.env
350
+ }
351
+
352
+ // node adapter环境能读取到 process.env.CLOUDBASE_APIKEY 时,在没有传入accessKey时,使用 node adapter 提供的 accessKey(process.env.CLOUDBASE_APIKEY)
353
+ if (!config.accessKey && secretInfo.accessKey) {
354
+ config.accessKey = secretInfo.accessKey
355
+ }
356
+ }
357
+
358
+ return config
359
+ }
276
360
  }
277
361
 
278
362
  // 类型导出
@@ -0,0 +1,42 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ /** 统一错误码定义 */
3
+ export const ERROR = {
4
+ INVALID_PARAM: {
5
+ code: 'INVALID_PARAM',
6
+ message: 'invalid param',
7
+ },
8
+ SYS_ERR: {
9
+ code: 'SYS_ERR',
10
+ message: 'system error',
11
+ },
12
+ STORAGE_REQUEST_FAIL: {
13
+ code: 'STORAGE_REQUEST_FAIL',
14
+ message: 'storage request fail',
15
+ },
16
+ STORAGE_FILE_NONEXIST: {
17
+ code: 'STORAGE_FILE_NONEXIST',
18
+ message: 'storage file not exist',
19
+ },
20
+ TCB_CLS_UNOPEN: {
21
+ code: 'TCB_CLS_UNOPEN',
22
+ message: '需要先开通日志检索功能',
23
+ },
24
+ INVALID_CONTEXT: {
25
+ code: 'INVALID_CONTEXT',
26
+ message: '无效的context对象,请使用 云函数入口的context参数',
27
+ },
28
+ }
29
+
30
+ /** 管理端 API 路径,用于替换 /web 路径(需要密钥签名时) */
31
+ export const ADMIN_PATH = '/admin'
32
+
33
+ /** 管理端获取客户端凭证 access_token,为管理员权限 */
34
+ export const CLIENT_AUTH_PATH = '/auth/v1/token/clientCredential'
35
+
36
+ /** 自定义用户 ID 校验规则:4~32 位,允许字母、数字及部分特殊字符 */
37
+ export const checkCustomUserIdRegex = /^[a-zA-Z0-9_\-#@~=*(){}[\]:.,<>+]{4,32}$/
38
+
39
+ export enum SECRET_TYPE {
40
+ 'SECRET' = 'SECRET',
41
+ 'SESSION_SECRET' = 'SESSION_SECRET',
42
+ }
@@ -0,0 +1,238 @@
1
+ /* eslint-disable import/no-mutable-exports */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
3
+ /* eslint-disable no-restricted-syntax */
4
+ import { ERROR, SECRET_TYPE } from './constants'
5
+ import { checkIsInScf, getEnv } from './utils'
6
+ import type { IContextParam, ICompleteCloudbaseContext } from './types'
7
+ import { ICloudbaseConfig } from '@cloudbase/types'
8
+
9
+ /**
10
+ * 解析并校验云函数入口的 Context 参数
11
+ * 将下划线命名的字段转为驼峰格式,并解析环境变量
12
+ *
13
+ * @param context - 云函数入口传入的 context 对象
14
+ * @returns 解析后的上下文对象,包含 environment(新架构)或 environ(老架构)
15
+ * @throws INVALID_CONTEXT - context 不是对象或解析失败时
16
+ */
17
+ export function parseContext(context: IContextParam) {
18
+ if (typeof context !== 'object') {
19
+ throw { ...ERROR.INVALID_CONTEXT, message: 'context 必须为对象类型' }
20
+ }
21
+
22
+ const parseResult: any = {}
23
+
24
+ const {
25
+ memory_limit_in_mb,
26
+ time_limit_in_ms,
27
+ request_id,
28
+ function_version,
29
+ namespace,
30
+ function_name,
31
+
32
+ environ,
33
+ environment,
34
+ } = context
35
+
36
+ try {
37
+ parseResult.memoryLimitInMb = memory_limit_in_mb
38
+ parseResult.timeLimitIns = time_limit_in_ms
39
+ parseResult.requestId = request_id
40
+ parseResult.functionVersion = function_version
41
+ parseResult.namespace = namespace
42
+ parseResult.functionName = function_name
43
+
44
+ if (environment) {
45
+ // 新架构:environment 为 JSON 字符串,直接解析
46
+ parseResult.environment = JSON.parse(environment)
47
+ } else {
48
+ // TODO: 考虑移除老架构的兼容逻辑
49
+ // 老架构:environ 为分号分隔的 key=value 字符串
50
+ // 存在 bug:value 含特殊字符时可能影响解析
51
+ const parseEnviron: any = environ?.split(';')
52
+ const parseEnvironObj: any = {}
53
+
54
+ // eslint-disable-next-line @typescript-eslint/no-for-in-array
55
+ for (const i in parseEnviron) {
56
+ if (parseEnviron[i].includes('=')) {
57
+ // 只按第一个 = 切割,保留 value 中的 =
58
+ const equalIndex = parseEnviron[i].indexOf('=')
59
+ const key = parseEnviron[i].slice(0, equalIndex)
60
+ let value: any = parseEnviron[i].slice(equalIndex + 1)
61
+
62
+ // value 含逗号时视为数组
63
+ if (value.indexOf(',') >= 0) {
64
+ value = value.split(',')
65
+ }
66
+ parseEnvironObj[key] = value
67
+ }
68
+ }
69
+
70
+ parseResult.environ = parseEnvironObj
71
+ }
72
+ } catch (_err: any) {
73
+ throw { ...ERROR.INVALID_CONTEXT }
74
+ }
75
+
76
+ return parseResult
77
+ }
78
+
79
+ /**
80
+ * 获取当前云函数内的所有环境变量
81
+ * 统一从 process.env 和 context 两个来源合并取值
82
+ *
83
+ * 取值优先级:context > process.env
84
+ * 通过 TCB_CONTEXT_KEYS / WX_CONTEXT_KEYS 动态扩展需要读取的环境变量列表
85
+ *
86
+ * @param context - 可选,云函数入口的 context 参数
87
+ * @returns 合并后的完整环境变量上下文
88
+ */
89
+ export const getCloudbaseContext = (context?: IContextParam): ICompleteCloudbaseContext => {
90
+ // 云函数环境下校验必要环境变量
91
+ if (checkIsInScf()) {
92
+ if (!getEnv('TENCENTCLOUD_REGION')) {
93
+ console.error('[TCB][ERROR] missing `TENCENTCLOUD_REGION` environment')
94
+ }
95
+ if (!getEnv('SCF_NAMESPACE')) {
96
+ console.error('[TCB][ERROR] missing `SCF_NAMESPACE` environment')
97
+ }
98
+ }
99
+
100
+ // 第一步:从 process.env 读取基础环境变量
101
+ const envObj = getEnv()
102
+ const envFromProcessEnv: any = {
103
+ TRIGGER_SRC: envObj.TRIGGER_SRC,
104
+ _SCF_TCB_LOG: envObj._SCF_TCB_LOG,
105
+ SCF_NAMESPACE: envObj.SCF_NAMESPACE,
106
+ TENCENTCLOUD_RUNENV: envObj.TENCENTCLOUD_RUNENV,
107
+ TENCENTCLOUD_SECRETID: envObj.TENCENTCLOUD_SECRETID,
108
+ TENCENTCLOUD_SECRETKEY: envObj.TENCENTCLOUD_SECRETKEY,
109
+ TENCENTCLOUD_SESSIONTOKEN: envObj.TENCENTCLOUD_SESSIONTOKEN,
110
+ WX_CONTEXT_KEYS: envObj.WX_CONTEXT_KEYS,
111
+ WX_TRIGGER_API_TOKEN_V0: envObj.WX_TRIGGER_API_TOKEN_V0,
112
+ WX_CLIENTIP: envObj.WX_CLIENTIP,
113
+ WX_CLIENTIPV6: envObj.WX_CLIENTIPV6,
114
+ TCB_CONTEXT_KEYS: envObj.TCB_CONTEXT_KEYS,
115
+ TCB_CONTEXT_CNFG: envObj.TCB_CONTEXT_CNFG,
116
+ LOGINTYPE: envObj.LOGINTYPE,
117
+ }
118
+
119
+ // 第二步:从 context 中解析环境变量
120
+ let envFromContext: any = {}
121
+ if (context) {
122
+ const { environment, environ } = parseContext(context)
123
+ envFromContext = environment || environ || {}
124
+ }
125
+
126
+ // 第三步:根据 CONTEXT_KEYS 动态扩展变量列表(优先取 context 中的值)
127
+ const tcbContextKeys = envFromContext.TCB_CONTEXT_KEYS || envObj.TCB_CONTEXT_KEYS
128
+ const wxContextKeys = envFromContext.WX_CONTEXT_KEYS || envObj.WX_CONTEXT_KEYS
129
+
130
+ if (tcbContextKeys) {
131
+ try {
132
+ const tcbKeysList = tcbContextKeys.split(',')
133
+ for (const item of tcbKeysList) {
134
+ envFromProcessEnv[item] = envFromContext[item] || getEnv(item)
135
+ }
136
+ } catch (_e) {
137
+ //
138
+ }
139
+ }
140
+
141
+ if (wxContextKeys) {
142
+ try {
143
+ const wxKeysList = wxContextKeys.split(',')
144
+ for (const item of wxKeysList) {
145
+ envFromProcessEnv[item] = envFromContext[item] || getEnv(item)
146
+ }
147
+ } catch (_e) {
148
+ //
149
+ }
150
+ }
151
+
152
+ // 第四步:合并所有来源,context 的值覆盖 process.env,过滤 undefined
153
+ const allContext = { ...envFromProcessEnv, ...envFromContext }
154
+
155
+ const finalContext: any = {}
156
+ for (const key in allContext) {
157
+ if (allContext[key] !== undefined) {
158
+ finalContext[key] = allContext[key]
159
+ }
160
+ }
161
+
162
+ return finalContext
163
+ }
164
+
165
+ /** 缓存 extendedContext,供其他模块读取(如 tool.ts 中获取 userId) */
166
+ export let INIT_CONFIG: ICloudbaseConfig = {} as unknown as ICloudbaseConfig
167
+
168
+ /**
169
+ * 从环境变量中获取腾讯云临时密钥信息
170
+ * 用于 API 请求签名
171
+ */
172
+ export const getSecretInfo = (config?: ICloudbaseConfig) => {
173
+ INIT_CONFIG = JSON.parse(JSON.stringify(config || {}))
174
+
175
+ const { TCB_ENV, SCF_NAMESPACE } = getCloudbaseContext()
176
+
177
+ let env = config?.env || TCB_ENV || SCF_NAMESPACE || ''
178
+ const defaultInfo = {
179
+ env,
180
+ secretId: '',
181
+ secretKey: '',
182
+ sessionToken: '',
183
+ accessKey: '',
184
+ secretType: '',
185
+ }
186
+
187
+ if (config?.accessKey) {
188
+ return defaultInfo
189
+ }
190
+
191
+ if (config?.auth?.secretId && config?.auth?.secretKey) {
192
+ return {
193
+ ...defaultInfo,
194
+ secretId: config.auth.secretId,
195
+ secretKey: config.auth.secretKey,
196
+ secretType: SECRET_TYPE.SECRET,
197
+ }
198
+ }
199
+
200
+ const secretEnv = getEnv()
201
+ const { TENCENTCLOUD_SECRETID } = secretEnv
202
+ const { TENCENTCLOUD_SECRETKEY } = secretEnv
203
+ const { TENCENTCLOUD_SESSIONTOKEN } = secretEnv
204
+ const { CLOUDBASE_APIKEY } = secretEnv
205
+
206
+ if (CLOUDBASE_APIKEY) {
207
+ return {
208
+ ...defaultInfo,
209
+ accessKey: CLOUDBASE_APIKEY,
210
+ }
211
+ }
212
+
213
+ if (TENCENTCLOUD_SECRETID && TENCENTCLOUD_SECRETKEY) {
214
+ return {
215
+ ...defaultInfo,
216
+ secretId: TENCENTCLOUD_SECRETID,
217
+ secretKey: TENCENTCLOUD_SECRETKEY,
218
+ sessionToken: TENCENTCLOUD_SESSIONTOKEN || '',
219
+ secretType: SECRET_TYPE.SESSION_SECRET,
220
+ }
221
+ }
222
+
223
+ if (config?.context?.extendedContext) {
224
+ const { extendedContext } = config.context
225
+ if (!env) {
226
+ env = extendedContext.envId || ''
227
+ }
228
+
229
+ return {
230
+ ...defaultInfo,
231
+ secretId: extendedContext?.tmpSecret?.secretId || '',
232
+ secretKey: extendedContext?.tmpSecret?.secretKey || '',
233
+ sessionToken: extendedContext?.tmpSecret?.token || '',
234
+ }
235
+ }
236
+
237
+ return { ...defaultInfo }
238
+ }
@@ -0,0 +1,166 @@
1
+ /* eslint-disable prefer-destructuring */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
3
+ import {
4
+ SDKAdapterInterface,
5
+ StorageInterface,
6
+ StorageType,
7
+ WebSocketContructor as WebSocketConstructor,
8
+ } from '@cloudbase/adapter-interface'
9
+
10
+ import { NodeRequest } from './request'
11
+ import { nodeTool } from './tool'
12
+ import { getSecretInfo } from './context'
13
+ import { parseQueryString } from './utils'
14
+ import { ICloudbaseConfig } from '@cloudbase/types'
15
+
16
+ // ws 延迟加载,避免非 Node 环境打包时引入
17
+ let _WS: any = null
18
+ function getWS() {
19
+ if (!_WS) {
20
+ try {
21
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
22
+ _WS = require('ws')
23
+ } catch (e) {
24
+ throw new Error('缺少依赖 ws,请执行以下命令安装:\n\n'
25
+ + ' npm install ws\n\n'
26
+ + '该依赖用于 Node 环境下的 WebSocket 连接。',)
27
+ }
28
+ }
29
+ return _WS
30
+ }
31
+
32
+ // Re-export for external consumers
33
+ export { NodeRequest } from './request'
34
+ export { parseQueryString, createWebStreamFromNodeReadableStream } from './utils'
35
+ export type {
36
+ ICreateTicketOpts,
37
+ IGetUserInfoResult,
38
+ IGetEndUserInfoResult,
39
+ IUserInfoQuery,
40
+ IContextParam,
41
+ ICompleteCloudbaseContext,
42
+ } from './types'
43
+
44
+ /**
45
+ * 环境匹配检测:判断当前运行环境是否为 Node.js
46
+ * SDK 通过此方法决定是否使用当前 adapter
47
+ */
48
+ function isMatch(): boolean {
49
+ // eslint-disable-next-line eqeqeq
50
+ return typeof process !== 'undefined' && process.versions != null && process.versions.node != null
51
+ }
52
+
53
+ /**
54
+ * 基于 Map 的内存存储实现
55
+ * Node.js 环境下无浏览器 Storage API,使用内存 Map 模拟
56
+ */
57
+ const storage: StorageInterface = (() => {
58
+ const db = new Map()
59
+ return {
60
+ mode: 'sync',
61
+ setItem(key, value) {
62
+ db.set(key, value)
63
+ },
64
+ getItem(key) {
65
+ return db.get(key)
66
+ },
67
+ removeItem(key) {
68
+ db.delete(key)
69
+ },
70
+ clear() {
71
+ db.clear()
72
+ },
73
+ }
74
+ })()
75
+
76
+ /**
77
+ * 生成 Node.js 环境的 SDK Adapter
78
+ * 组装请求类、存储、WebSocket、认证等能力
79
+ *
80
+ * @param options - 配置项,包含 EventBus(用于验证码回调)
81
+ * @returns SDK Adapter 实例
82
+ */
83
+ function genAdapter(options: any) {
84
+ const adapter: SDKAdapterInterface & {
85
+ getSecretInfo: (config?: ICloudbaseConfig) => {
86
+ env: string
87
+ secretId: string
88
+ secretKey: string
89
+ sessionToken: string
90
+ accessKey: string
91
+ secretType: string
92
+ }
93
+ nodeTool: (app: any, config: ICloudbaseConfig) => void
94
+ } = {
95
+ nodeTool,
96
+ getSecretInfo,
97
+ root: { globalThis: {} },
98
+ reqClass: NodeRequest,
99
+ wsClass: getWS() as WebSocketConstructor,
100
+ sessionStorage: storage,
101
+ localStorage: storage,
102
+ primaryStorage: StorageType.session,
103
+ captchaOptions: {
104
+ /**
105
+ * 验证码处理回调
106
+ * 解析 data: URI 中的验证码参数,通过 EventBus 通知业务层
107
+ * 等待业务层返回验证结果后 resolve
108
+ */
109
+ openURIWithCallback: (_url: string) => {
110
+ const { EventBus } = options
111
+ let queryObj: Record<string, string | string[]> = {}
112
+ let url = _url
113
+
114
+ // 从 data: URI 中提取查询参数
115
+ const matched = _url.match(/^(data:.*?)(\?[^#\s]*)?$/)
116
+ if (matched) {
117
+ url = matched[1]
118
+ const search = matched[2]
119
+ if (search) {
120
+ queryObj = parseQueryString(search)
121
+ }
122
+ }
123
+
124
+ const { token, ...restQueryObj } = queryObj
125
+
126
+ // data: 协议但无 token,视为无效验证码数据
127
+ if (/^data:/.test(url) && !token) {
128
+ return Promise.reject({
129
+ error: 'invalid_argument',
130
+ error_description: `invalie captcha data: ${_url}`,
131
+ })
132
+ }
133
+ if (!token) {
134
+ return Promise.reject({
135
+ error: 'unimplemented',
136
+ error_description: 'need to impl captcha data',
137
+ })
138
+ }
139
+
140
+ return new Promise((resolve) => {
141
+ // 通知业务层展示验证码
142
+ EventBus.$emit('CAPTCHA_DATA_CHANGE', {
143
+ ...restQueryObj,
144
+ token,
145
+ url,
146
+ })
147
+
148
+ // 等待业务层返回验证结果
149
+ EventBus.$once('RESOLVE_CAPTCHA_DATA', (res: { captcha_token: string; expires_in: number }) => {
150
+ resolve(res)
151
+ })
152
+ })
153
+ },
154
+ },
155
+ }
156
+ return adapter
157
+ }
158
+
159
+ /** Node.js Adapter 入口导出 */
160
+ const adapter = {
161
+ genAdapter,
162
+ isMatch,
163
+ runtime: 'node-adapter',
164
+ }
165
+
166
+ export default adapter