@cloudbase/container 2.5.29-beta.0
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.
- package/.eslintignore +2 -0
- package/.eslintrc.js +15 -0
- package/LICENSE +202 -0
- package/dist/cjs/go_wams_exec.d.ts +2 -0
- package/dist/cjs/go_wams_exec.js +538 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +383 -0
- package/dist/esm/go_wams_exec.d.ts +2 -0
- package/dist/esm/go_wams_exec.js +536 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +376 -0
- package/package.json +38 -0
- package/src/go_wams_exec.ts +590 -0
- package/src/index.ts +330 -0
- package/tsconfig.esm.json +16 -0
- package/tsconfig.json +16 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { ICloudbase } from '@cloudbase/types'
|
|
2
|
+
import { ICloudbaseComponent } from '@cloudbase/types/component'
|
|
3
|
+
import { ICallContainerOptions, IContianerConfig } from '@cloudbase/types/container'
|
|
4
|
+
import { constants, utils, helpers } from '@cloudbase/utilities'
|
|
5
|
+
// import brotliPromise from 'brotli-dec-wasm' // Import the default export
|
|
6
|
+
import decompress from 'brotli/decompress'
|
|
7
|
+
|
|
8
|
+
import Go from './go_wams_exec'
|
|
9
|
+
|
|
10
|
+
declare const cloudbase: ICloudbase
|
|
11
|
+
|
|
12
|
+
const { getSdkName, ERRORS, COMMUNITY_SITE_URL } = constants
|
|
13
|
+
const { execCallback, printWarn } = utils
|
|
14
|
+
const { catchErrorsDecorator } = helpers
|
|
15
|
+
|
|
16
|
+
const COMPONENT_NAME = 'container'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 封装对齐 wx.request
|
|
20
|
+
*/
|
|
21
|
+
async function request({
|
|
22
|
+
url,
|
|
23
|
+
method = 'GET',
|
|
24
|
+
header = {},
|
|
25
|
+
data,
|
|
26
|
+
timeout = 60000,
|
|
27
|
+
dataType = 'json',
|
|
28
|
+
responseType = 'text',
|
|
29
|
+
}: {
|
|
30
|
+
url: string
|
|
31
|
+
data?: string | object | ArrayBuffer
|
|
32
|
+
header?: Record<string, string>
|
|
33
|
+
timeout?: number
|
|
34
|
+
method?: string
|
|
35
|
+
dataType?: 'json' | string
|
|
36
|
+
responseType?: 'text' | 'arrayBuffer'
|
|
37
|
+
}) {
|
|
38
|
+
const headers = { ...header }
|
|
39
|
+
|
|
40
|
+
if (typeof data === 'object' && !(data instanceof ArrayBuffer || ArrayBuffer.isView(data))) {
|
|
41
|
+
if (!headers['Content-Type'] && !headers['content-type']) {
|
|
42
|
+
headers['Content-Type'] = 'application/json'
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
data = JSON.stringify(data)
|
|
46
|
+
} catch (e) {
|
|
47
|
+
throw new Error(JSON.stringify({
|
|
48
|
+
code: ERRORS.INVALID_PARAMS,
|
|
49
|
+
msg: `[${COMPONENT_NAME}.callContainer] invalid data with content-type: application/json`,
|
|
50
|
+
}),)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { signal, abort } = new AbortController()
|
|
55
|
+
|
|
56
|
+
return Promise.race([
|
|
57
|
+
new Promise((_, reject) => {
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
const error = new Error('请求超时')
|
|
60
|
+
;(error as any).code = 'SERVER_TIMEOUT'
|
|
61
|
+
reject(error)
|
|
62
|
+
abort()
|
|
63
|
+
}, timeout)
|
|
64
|
+
}),
|
|
65
|
+
fetch(url, {
|
|
66
|
+
method,
|
|
67
|
+
headers,
|
|
68
|
+
body: data,
|
|
69
|
+
signal,
|
|
70
|
+
}).then((response) => {
|
|
71
|
+
if (responseType === 'arrayBuffer') {
|
|
72
|
+
return response.arrayBuffer()
|
|
73
|
+
}
|
|
74
|
+
if (dataType === 'json') {
|
|
75
|
+
try {
|
|
76
|
+
return response.json()
|
|
77
|
+
} catch (e) {
|
|
78
|
+
throw new Error(`[${getSdkName()}][${ERRORS.INVALID_PARAMS}][${COMPONENT_NAME}.callContainer] response data must be json`,)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return response.text()
|
|
82
|
+
}),
|
|
83
|
+
])
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class InvalieParamsError extends Error {
|
|
87
|
+
constructor(scope, message) {
|
|
88
|
+
const msg = JSON.stringify({
|
|
89
|
+
code: ERRORS.INVALID_PARAMS,
|
|
90
|
+
msg: `[${scope}] ${message}`,
|
|
91
|
+
})
|
|
92
|
+
super(msg)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface ICallTcbContainerOptions extends ICallContainerOptions {
|
|
97
|
+
success: (res: { statusCode: string | number; header: ICallContainerOptions['header']; data: any }) => void
|
|
98
|
+
fail: (res: { data: { code: number; message: string } }) => void
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface IInitTcbContainerOptions {
|
|
102
|
+
config: Omit<IContianerConfig, 'wasmUrl'>
|
|
103
|
+
success: (res: { statusCode: string | number; header: ICallContainerOptions['header']; data: any }) => void
|
|
104
|
+
fail: (res: { data: { code: number; message: string } }) => void
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class CloudbaseContainers {
|
|
108
|
+
private readonly config: IContianerConfig
|
|
109
|
+
private wasm: Promise<WebAssembly.Exports>
|
|
110
|
+
private containerInitPromise: /* Promise<any>*/ any | null
|
|
111
|
+
|
|
112
|
+
constructor(config: IContianerConfig) {
|
|
113
|
+
this.config = config
|
|
114
|
+
if (!this.config.wasmUrl) {
|
|
115
|
+
throw new Error(JSON.stringify({
|
|
116
|
+
code: ERRORS.INVALID_PARAMS,
|
|
117
|
+
msg: `[${COMPONENT_NAME}] 缺少 wasmUrl`,
|
|
118
|
+
}),)
|
|
119
|
+
}
|
|
120
|
+
this.wasm = getExportFunction(this.config.wasmUrl, {
|
|
121
|
+
utils: {
|
|
122
|
+
info(e) {
|
|
123
|
+
console.log(e)
|
|
124
|
+
},
|
|
125
|
+
request,
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@catchErrorsDecorator({
|
|
131
|
+
customInfo: {
|
|
132
|
+
className: 'Cloudbase',
|
|
133
|
+
methodName: 'callContainer',
|
|
134
|
+
},
|
|
135
|
+
title: '调用失败',
|
|
136
|
+
messages: [
|
|
137
|
+
'请确认以下各项:',
|
|
138
|
+
' 1 - 调用 callContainer() 的语法或参数是否正确',
|
|
139
|
+
' 2 - 域名 & 路径是否存在',
|
|
140
|
+
`如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
|
|
141
|
+
],
|
|
142
|
+
})
|
|
143
|
+
public async callContainer(options: ICallContainerOptions, callback?: Function) {
|
|
144
|
+
await this.initContainer(this.config)
|
|
145
|
+
const { callContainer }: { callContainer: (options: ICallTcbContainerOptions) => void } = (await this.wasm) as any
|
|
146
|
+
|
|
147
|
+
const {
|
|
148
|
+
url,
|
|
149
|
+
method = 'GET',
|
|
150
|
+
header = {},
|
|
151
|
+
data,
|
|
152
|
+
timeout = 60000,
|
|
153
|
+
dataType = 'json',
|
|
154
|
+
responseType = 'text',
|
|
155
|
+
...restOptions
|
|
156
|
+
} = options
|
|
157
|
+
|
|
158
|
+
if (!url) {
|
|
159
|
+
throw new InvalieParamsError(`${COMPONENT_NAME}.callContainer`, 'invalid request url')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// @ts-ignore
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const res = await new Promise((resolve, reject) => {
|
|
166
|
+
const params = { url, method, header, data, timeout, dataType, responseType, ...restOptions }
|
|
167
|
+
|
|
168
|
+
callContainer({
|
|
169
|
+
...params,
|
|
170
|
+
success: resolve,
|
|
171
|
+
fail: (res) => {
|
|
172
|
+
const { data } = res || {}
|
|
173
|
+
const error = new Error(data?.message || 'call container error')
|
|
174
|
+
;(error as any).code = data?.code || 'UNKNOWN_ERROR'
|
|
175
|
+
reject(error)
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
return execCallback(callback, null, res)
|
|
180
|
+
} catch (e) {
|
|
181
|
+
execCallback(callback, e)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async initContainer(config: Omit<IContianerConfig, 'wasmUrl'>) {
|
|
186
|
+
const { initContainer }: { initContainer: (options: IInitTcbContainerOptions) => void } = (await this.wasm) as any
|
|
187
|
+
if (!this.containerInitPromise) {
|
|
188
|
+
this.containerInitPromise = new Promise((resolve, reject) => {
|
|
189
|
+
initContainer({
|
|
190
|
+
config,
|
|
191
|
+
success(res) {
|
|
192
|
+
if (String(res.statusCode) !== '200') {
|
|
193
|
+
reject(new Error(JSON.stringify({
|
|
194
|
+
code: 'INIT_ERROR',
|
|
195
|
+
msg: `[${COMPONENT_NAME}] ${res.statusCode} ${JSON.stringify(res.data)}`,
|
|
196
|
+
}),),)
|
|
197
|
+
}
|
|
198
|
+
resolve(res)
|
|
199
|
+
},
|
|
200
|
+
fail(res) {
|
|
201
|
+
reject(new Error(JSON.stringify({
|
|
202
|
+
code: 'INIT_ERROR',
|
|
203
|
+
msg: `[${COMPONENT_NAME}] ${JSON.stringify(res.data)}`,
|
|
204
|
+
}),),)
|
|
205
|
+
this._containerInitPromis = null
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return this.containerInitPromise
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const component: ICloudbaseComponent = {
|
|
216
|
+
name: COMPONENT_NAME,
|
|
217
|
+
namespace: 'container',
|
|
218
|
+
entity(config: IContianerConfig) {
|
|
219
|
+
if (this.containerInstance) {
|
|
220
|
+
printWarn(ERRORS.INVALID_OPERATION, 'every cloudbase instance should has only one container object')
|
|
221
|
+
return this.containerInstance
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const instance = new CloudbaseContainers(config)
|
|
225
|
+
this.containerInstance = instance
|
|
226
|
+
this.callContainer = instance.callContainer.bind(instance)
|
|
227
|
+
|
|
228
|
+
return this.containerInstance
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
cloudbase.registerComponent(component)
|
|
233
|
+
} catch (e) {}
|
|
234
|
+
|
|
235
|
+
export function registerContainers(app: Pick<ICloudbase, 'registerComponent'>) {
|
|
236
|
+
try {
|
|
237
|
+
app.registerComponent(component)
|
|
238
|
+
} catch (e) {
|
|
239
|
+
console.warn(e)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function getExportFunction(url, module, mode = 'go') {
|
|
244
|
+
async function instantiateStreaming(resp, importObject, brotliCompressed = false) {
|
|
245
|
+
if (brotliCompressed || !WebAssembly.instantiateStreaming) {
|
|
246
|
+
const source = await (await resp).arrayBuffer()
|
|
247
|
+
const buffer = brotliCompressed ? decompress(new Int8Array(source)) : source
|
|
248
|
+
return WebAssembly.instantiate(buffer, importObject)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return WebAssembly.instantiateStreaming(resp, importObject)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let importObject = {
|
|
255
|
+
env: {
|
|
256
|
+
memoryBase: 0,
|
|
257
|
+
tableBase: 0,
|
|
258
|
+
memory: new WebAssembly.Memory({
|
|
259
|
+
initial: 256,
|
|
260
|
+
}),
|
|
261
|
+
// table: new WebAssembly.Table({
|
|
262
|
+
// initial: 2,
|
|
263
|
+
// element: 'anyfunc',
|
|
264
|
+
// }),
|
|
265
|
+
},
|
|
266
|
+
...module,
|
|
267
|
+
}
|
|
268
|
+
let go
|
|
269
|
+
const brotliCompressed = isBrotliCompressed(url)
|
|
270
|
+
|
|
271
|
+
if (mode === 'go') {
|
|
272
|
+
go = new Go()
|
|
273
|
+
go._initedModulePromise = new Promise((resolve) => {
|
|
274
|
+
go._initedModuleResolve = resolve
|
|
275
|
+
})
|
|
276
|
+
importObject = {
|
|
277
|
+
...go.importObject,
|
|
278
|
+
env: {
|
|
279
|
+
...go.importObject.env,
|
|
280
|
+
exportModule(module) {
|
|
281
|
+
return go._initedModuleResolve(module)
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const reuslt = await instantiateStreaming(fetch(url), importObject, brotliCompressed)
|
|
288
|
+
|
|
289
|
+
if (mode === 'go') {
|
|
290
|
+
await Promise.race([
|
|
291
|
+
go.run(reuslt.instance),
|
|
292
|
+
new Promise(resolve => setTimeout(() => {
|
|
293
|
+
resolve(null)
|
|
294
|
+
}, 500),),
|
|
295
|
+
])
|
|
296
|
+
|
|
297
|
+
return { callContainer: (window as any).callContainer, initContainer: (window as any).initContainer }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// if (mode === 'go') {
|
|
301
|
+
// go.run(reuslt.instance)
|
|
302
|
+
// const module = await go._initedModulePromise
|
|
303
|
+
// return module
|
|
304
|
+
// }
|
|
305
|
+
|
|
306
|
+
return reuslt.instance.exports
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isBrotliCompressed(url) {
|
|
310
|
+
let brotliCompressed = false
|
|
311
|
+
|
|
312
|
+
let pathname = ''
|
|
313
|
+
try {
|
|
314
|
+
const a = document.createElement('a')
|
|
315
|
+
a.href = url
|
|
316
|
+
pathname = a.pathname
|
|
317
|
+
} catch (e) {
|
|
318
|
+
try {
|
|
319
|
+
const res = new URL(url)
|
|
320
|
+
pathname = res?.pathname
|
|
321
|
+
} catch (e) {
|
|
322
|
+
pathname = url
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (/\.br$/.test(pathname)) {
|
|
327
|
+
brotliCompressed = true
|
|
328
|
+
}
|
|
329
|
+
return brotliCompressed
|
|
330
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"outDir": "dist/cjs",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
},
|
|
8
|
+
"include": [
|
|
9
|
+
"src/**/*.ts",
|
|
10
|
+
"package.json"
|
|
11
|
+
],
|
|
12
|
+
"exclude": [
|
|
13
|
+
"node_modules",
|
|
14
|
+
"dist"
|
|
15
|
+
]
|
|
16
|
+
}
|