@smart100/spu-web-plugin 1.0.5 → 1.0.7
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/dist/spu-web-plugin.mjs +8066 -404
- package/package.json +4 -1
- package/src/apaasSpuTrack.ts +3 -4
- package/src/axios.ts +11 -0
- package/src/core.js +44 -91
- package/src/crypt.ts +62 -0
- package/src/envService.ts +124 -0
- package/src/index.ts +8 -6
- package/src/login.ts +109 -93
- package/src/storageCache.ts +25 -0
- package/src/utils.ts +38 -1
- package/src/nativeApi.ts +0 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smart100/spu-web-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "npm run build:types && rollup -c -w",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
30
30
|
"@rollup/plugin-terser": "^0.4.4",
|
|
31
31
|
"@rollup/plugin-typescript": "^11.1.5",
|
|
32
|
+
"@types/crypto-js": "^4.2.2",
|
|
32
33
|
"@types/lodash-es": "^4.17.10",
|
|
33
34
|
"@types/node": "^20.8.10",
|
|
34
35
|
"postcss": "^8.4.31",
|
|
@@ -42,9 +43,11 @@
|
|
|
42
43
|
"aws-sdk": "^2.1692.0",
|
|
43
44
|
"axios": "^1.6.0",
|
|
44
45
|
"co": "^4.6.0",
|
|
46
|
+
"crypto-js": "^4.2.0",
|
|
45
47
|
"dayjs": "^1.11.10",
|
|
46
48
|
"jwt-decode": "^3.1.2",
|
|
47
49
|
"lit": "^3.1.4",
|
|
50
|
+
"localforage": "^1.10.0",
|
|
48
51
|
"lodash-es": "^4.17.21",
|
|
49
52
|
"uuid": "^9.0.1",
|
|
50
53
|
"vconsole": "^3.15.1"
|
package/src/apaasSpuTrack.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { globalOptions } from './index'
|
|
2
|
-
import
|
|
2
|
+
import core from './core'
|
|
3
3
|
import { cloneDeep, merge, set } from 'lodash-es'
|
|
4
4
|
import { getUser, checkLogin } from './login'
|
|
5
|
-
import
|
|
5
|
+
import { getEnvname } from './envService'
|
|
6
6
|
|
|
7
7
|
// @ts-ignore
|
|
8
8
|
import ApaasSpuTrack from './package/apaas-track/apaas-spu/index.umd.js'
|
|
9
9
|
// import ApaasSpuTrack from './package/apaas-track/apaas-spu/index.js'
|
|
10
10
|
// import * as ApaasSpuTrack from './package/apaas-track/apaas-spu/index.mjs'
|
|
11
|
-
|
|
12
11
|
// console.log(ApaasSpuTrack)
|
|
13
12
|
// console.log(window.ApaasSpuTrack)
|
|
14
13
|
|
|
15
14
|
const getWebInitParams = async () => {
|
|
16
15
|
const user = getUser()
|
|
17
|
-
const envname = await
|
|
16
|
+
const envname = await getEnvname()
|
|
18
17
|
return {
|
|
19
18
|
project: globalOptions.modulename,
|
|
20
19
|
appid: globalOptions.modulekey,
|
package/src/axios.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { loadding } from './components/loadding'
|
|
|
5
5
|
import { getToken, updateToken, getLoginState } from './login'
|
|
6
6
|
import core from './core'
|
|
7
7
|
import { urlquery } from './urlquery'
|
|
8
|
+
import { getTecode } from './envService'
|
|
8
9
|
|
|
9
10
|
interface Response {
|
|
10
11
|
code: number | string
|
|
@@ -74,6 +75,16 @@ const createAxiosInstance = (type: 'spu' | 'normal' = 'spu', options: any) => {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
if (type !== 'spu') {
|
|
79
|
+
const isSendTecode = get(config, 'isSendTecode', false)
|
|
80
|
+
if (isSendTecode) {
|
|
81
|
+
const tecode = getTecode()
|
|
82
|
+
if (config?.headers && tecode) {
|
|
83
|
+
config.headers.tecode = tecode
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
77
88
|
return config
|
|
78
89
|
},
|
|
79
90
|
(error) => {
|
package/src/core.js
CHANGED
|
@@ -2,35 +2,8 @@ import { globalOptions } from './index'
|
|
|
2
2
|
import { axios } from './axios'
|
|
3
3
|
import { get, cloneDeep } from 'lodash-es'
|
|
4
4
|
import { urlquery } from './urlquery'
|
|
5
|
-
import { getToken,
|
|
6
|
-
|
|
7
|
-
const urlIsIp = (url) => {
|
|
8
|
-
const hostname = url.split('://')[1].split(':')[0].split('/')[0]
|
|
9
|
-
const arr = hostname.split('.')
|
|
10
|
-
if (arr.length !== 4) return false
|
|
11
|
-
let flag = true
|
|
12
|
-
for (let i = 0, len = arr.length; i < len; i++) {
|
|
13
|
-
if (!Number.isInteger(Number(arr[i]))) {
|
|
14
|
-
flag = false
|
|
15
|
-
break
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return flag
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// 如果是非ip地址 则切换为与主页面一样的 location.protocol 前缀
|
|
22
|
-
const toggleHttpOrHttps = (url) => {
|
|
23
|
-
let res = url
|
|
24
|
-
|
|
25
|
-
if (urlIsIp(url)) return res
|
|
26
|
-
|
|
27
|
-
if (!res.startsWith(location.protocol)) {
|
|
28
|
-
const arr = res.split('//')
|
|
29
|
-
arr[0] = location.protocol
|
|
30
|
-
res = arr.join('//')
|
|
31
|
-
}
|
|
32
|
-
return res
|
|
33
|
-
}
|
|
5
|
+
import { getToken, getUser, getRefreshToken, getTokenExpires } from './login'
|
|
6
|
+
import { getEnvname, getEnvdata } from './envService'
|
|
34
7
|
|
|
35
8
|
class Core {
|
|
36
9
|
loadStatus = 0 // 0未开始 1正在加载 2加载完成
|
|
@@ -42,15 +15,48 @@ class Core {
|
|
|
42
15
|
webDefineData: null
|
|
43
16
|
}
|
|
44
17
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
18
|
+
// isinit = false
|
|
19
|
+
// async init() {
|
|
20
|
+
// if (this.isinit) return
|
|
21
|
+
// this.cache.tenantCode = getUser('tenantcode') || ''
|
|
22
|
+
// this.cache.envName = await getEnvname()
|
|
23
|
+
// this.cache.envData = await getEnvdata()
|
|
24
|
+
// this.cache.webDefineData = await this.requestWebDefineData()
|
|
25
|
+
// this.isinit = true
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
// async getCache() {
|
|
29
|
+
// if (this.isinit) {
|
|
30
|
+
// return this.cache
|
|
31
|
+
// } else {
|
|
32
|
+
// await this.init()
|
|
33
|
+
// return this.cache
|
|
34
|
+
// }
|
|
35
|
+
// }
|
|
36
|
+
|
|
37
|
+
requestDataPromise = null
|
|
38
|
+
|
|
39
|
+
async initGetData() {
|
|
40
|
+
const nowEnvname = await getEnvname()
|
|
41
|
+
const nowTenantCode = getUser('tenantcode') || ''
|
|
42
|
+
// console.log(tenantCode)
|
|
43
|
+
if (this.cache.envName === nowEnvname && this.cache.tenantCode === nowTenantCode && this.loadStatus === 2) {
|
|
44
|
+
return this.cache
|
|
52
45
|
}
|
|
53
|
-
|
|
46
|
+
|
|
47
|
+
// 兼容同时间发起多个
|
|
48
|
+
if (this.loadStatus === 1 && this.requestDataPromise) {
|
|
49
|
+
// console.error(2122)
|
|
50
|
+
// console.log(this.requestDataPromise)
|
|
51
|
+
// debugger
|
|
52
|
+
return this.requestDataPromise
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.loadStatus = 1
|
|
56
|
+
this.requestDataPromise = this.requestData()
|
|
57
|
+
await this.requestDataPromise
|
|
58
|
+
this.loadStatus = 2
|
|
59
|
+
return this.cache
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
// 请求实时G3数据
|
|
@@ -59,40 +65,11 @@ class Core {
|
|
|
59
65
|
const nowTenantCode = getUser('tenantcode') || ''
|
|
60
66
|
this.cache.envName = nowEnvname
|
|
61
67
|
this.cache.tenantCode = nowTenantCode
|
|
62
|
-
this.cache.envData = await
|
|
68
|
+
this.cache.envData = await getEnvdata()
|
|
63
69
|
this.cache.webDefineData = await this.requestWebDefineData()
|
|
64
70
|
return this.cache
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
async requestEnvData(envName) {
|
|
68
|
-
// envName = '产品运营中心验证'
|
|
69
|
-
let result = null
|
|
70
|
-
if (envName) {
|
|
71
|
-
const hostsRoot =
|
|
72
|
-
document.location.protocol === 'https:'
|
|
73
|
-
? 'https://mconfig.xuantongkeji.com'
|
|
74
|
-
: 'http://mconfig.xuantongkeji.com:8015'
|
|
75
|
-
let response
|
|
76
|
-
try {
|
|
77
|
-
response = await axios.get(`${hostsRoot}/multiplatconfig/env/${envName}`, {
|
|
78
|
-
isShowLoading: false,
|
|
79
|
-
isSendToken: false
|
|
80
|
-
})
|
|
81
|
-
// console.log(response)
|
|
82
|
-
// debugger
|
|
83
|
-
result = get(response, 'data.0')
|
|
84
|
-
|
|
85
|
-
// 如果是非ip地址 则切换为与主页面一样的 location.protocol 前缀
|
|
86
|
-
result?.business && (result.business = toggleHttpOrHttps(result.business))
|
|
87
|
-
result?.smartcenter && (result.smartcenter = toggleHttpOrHttps(result.smartcenter))
|
|
88
|
-
// debugger
|
|
89
|
-
} catch (err) {
|
|
90
|
-
console.error(err)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return result
|
|
94
|
-
}
|
|
95
|
-
|
|
96
73
|
async requestWebDefineData() {
|
|
97
74
|
const envId = this.cache.envData?.envid
|
|
98
75
|
const tenantCode = this.cache.tenantCode
|
|
@@ -142,30 +119,6 @@ class Core {
|
|
|
142
119
|
return result
|
|
143
120
|
}
|
|
144
121
|
|
|
145
|
-
requestDataPromise = null
|
|
146
|
-
async initGetData() {
|
|
147
|
-
const nowEnvname = await getEnvname()
|
|
148
|
-
const nowTenantCode = getUser('tenantcode') || ''
|
|
149
|
-
// console.log(tenantCode)
|
|
150
|
-
if (this.cache.envName === nowEnvname && this.cache.tenantCode === nowTenantCode && this.loadStatus === 2) {
|
|
151
|
-
return this.cache
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// 兼容同时间发起多个
|
|
155
|
-
if (this.loadStatus === 1 && this.requestDataPromise) {
|
|
156
|
-
// console.error(2122)
|
|
157
|
-
// console.log(this.requestDataPromise)
|
|
158
|
-
// debugger
|
|
159
|
-
return this.requestDataPromise
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
this.loadStatus = 1
|
|
163
|
-
this.requestDataPromise = this.requestData()
|
|
164
|
-
await this.requestDataPromise
|
|
165
|
-
this.loadStatus = 2
|
|
166
|
-
return this.cache
|
|
167
|
-
}
|
|
168
|
-
|
|
169
122
|
async getEnvData() {
|
|
170
123
|
const res = {
|
|
171
124
|
errorMsg: '',
|
package/src/crypt.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import CryptoJS from 'crypto-js'
|
|
2
|
+
|
|
3
|
+
// 二进制转字符串
|
|
4
|
+
function byteToString(arr: any[]): string {
|
|
5
|
+
let str = ''
|
|
6
|
+
const _arr = arr
|
|
7
|
+
for (let i = 0; i < _arr.length; i++) {
|
|
8
|
+
const one = _arr[i].toString(2)
|
|
9
|
+
const v = one.match(/^1+(?=0)/)
|
|
10
|
+
if (v && one.length === 8) {
|
|
11
|
+
const bytesLength = v[0].length
|
|
12
|
+
let store = _arr[i].toString(2).slice(7 - bytesLength)
|
|
13
|
+
for (let st = 1; st < bytesLength; st++) {
|
|
14
|
+
store += _arr[st + i].toString(2).slice(2)
|
|
15
|
+
}
|
|
16
|
+
str += String.fromCharCode(Number.parseInt(store, 2))
|
|
17
|
+
i += bytesLength - 1
|
|
18
|
+
} else {
|
|
19
|
+
str += String.fromCharCode(_arr[i])
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return str
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const key = byteToString([0x36, 0x31, 0x33, 0x30, 0x32, 0x32, 0x32, 0x32, 0x32])
|
|
26
|
+
|
|
27
|
+
// 加密算法
|
|
28
|
+
function encrypt(dataString: string) {
|
|
29
|
+
// ENC加密
|
|
30
|
+
const keyHex = CryptoJS.enc.Utf8.parse(key)
|
|
31
|
+
const encrypted = CryptoJS.DES.encrypt(dataString, keyHex, {
|
|
32
|
+
mode: CryptoJS.mode.ECB,
|
|
33
|
+
padding: CryptoJS.pad.Pkcs7
|
|
34
|
+
})
|
|
35
|
+
// base64加密
|
|
36
|
+
const base64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
|
37
|
+
return base64
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 解密算法
|
|
41
|
+
function decrypt(base64: string) {
|
|
42
|
+
// base64解密
|
|
43
|
+
const encryptedString = CryptoJS.enc.Base64.parse(base64)
|
|
44
|
+
const ciphertext = encryptedString.toString()
|
|
45
|
+
// ENC解密
|
|
46
|
+
const keyHex = CryptoJS.enc.Utf8.parse(key)
|
|
47
|
+
|
|
48
|
+
const decrypted = CryptoJS.DES.decrypt(
|
|
49
|
+
{
|
|
50
|
+
ciphertext: CryptoJS.enc.Hex.parse(ciphertext)
|
|
51
|
+
} as any,
|
|
52
|
+
keyHex,
|
|
53
|
+
{
|
|
54
|
+
mode: CryptoJS.mode.ECB,
|
|
55
|
+
padding: CryptoJS.pad.Pkcs7
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
const result = decrypted.toString(CryptoJS.enc.Utf8)
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { encrypt, decrypt }
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { get, cloneDeep } from 'lodash-es'
|
|
2
|
+
import { lsProxy } from './storageProxy'
|
|
3
|
+
import { axios } from './axios'
|
|
4
|
+
import { decrypt } from './crypt'
|
|
5
|
+
import { toggleHttpOrHttps } from './utils'
|
|
6
|
+
import { getData, setData, removeData } from './storageCache'
|
|
7
|
+
|
|
8
|
+
async function getEnvname(): Promise<string> {
|
|
9
|
+
let envname = ''
|
|
10
|
+
|
|
11
|
+
// web 查 context 的 envname
|
|
12
|
+
let context: any = lsProxy.getItem('context')
|
|
13
|
+
context && (context = JSON.parse(context))
|
|
14
|
+
const contextEnvname = context?.envname || ''
|
|
15
|
+
|
|
16
|
+
// 链接有些spu可能会传 envname
|
|
17
|
+
const queryEnvname = getQueryEnvname()
|
|
18
|
+
|
|
19
|
+
if (contextEnvname) {
|
|
20
|
+
envname = contextEnvname
|
|
21
|
+
} else if (queryEnvname) {
|
|
22
|
+
envname = queryEnvname
|
|
23
|
+
} else if (window?.aPaaS?.getWebInitParams && window?.Native?.setNavigationBarReturnButton) {
|
|
24
|
+
// 手机端 查 envname
|
|
25
|
+
// 只有手机端有 setNavigationBarReturnButton 方法
|
|
26
|
+
envname = await new Promise((resolve, reject) => {
|
|
27
|
+
window.aPaaS.getWebInitParams((params: any) => {
|
|
28
|
+
resolve(params?.envname || '')
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return envname
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function setQueryEnvname(value: string) {
|
|
37
|
+
setData('envname', value)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getQueryEnvname() {
|
|
41
|
+
return getData('envname') || ''
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function removeQueryEnvname() {
|
|
45
|
+
removeData('envname')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function requestEnvdata(envName: string): Promise<IAny | null> {
|
|
49
|
+
// envName = '产品运营中心验证'
|
|
50
|
+
let result = null
|
|
51
|
+
if (envName) {
|
|
52
|
+
const hostsRoot =
|
|
53
|
+
document.location.protocol === 'https:'
|
|
54
|
+
? 'https://mconfig.xuantongkeji.com'
|
|
55
|
+
: 'http://mconfig.xuantongkeji.com:8015'
|
|
56
|
+
try {
|
|
57
|
+
const res = await axios.get(`${hostsRoot}/multiplatconfig/env/${envName}`, {
|
|
58
|
+
isShowLoading: false,
|
|
59
|
+
isSendToken: false
|
|
60
|
+
})
|
|
61
|
+
// console.log(res)
|
|
62
|
+
// debugger
|
|
63
|
+
result = get(res, 'data.0')
|
|
64
|
+
|
|
65
|
+
// 如果是非ip地址 则切换为与主页面一样的 location.protocol 前缀
|
|
66
|
+
result?.business && (result.business = toggleHttpOrHttps(result.business))
|
|
67
|
+
result?.smartcenter && (result.smartcenter = toggleHttpOrHttps(result.smartcenter))
|
|
68
|
+
// debugger
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error(err)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function saveEnvdata(envdata: IAny) {
|
|
77
|
+
lsProxy.setItem('envdata', JSON.stringify(envdata))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function removeEnvdata() {
|
|
81
|
+
lsProxy.removeItem('envdata')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function getEnvdata(): Promise<IAny | null> {
|
|
85
|
+
const envdataStr = lsProxy.getItem('envdata')
|
|
86
|
+
|
|
87
|
+
if (envdataStr) {
|
|
88
|
+
return JSON.parse(envdataStr)
|
|
89
|
+
} else {
|
|
90
|
+
const envname = await getEnvname()
|
|
91
|
+
if (envname) {
|
|
92
|
+
const envdata = await requestEnvdata(envname)
|
|
93
|
+
return envdata
|
|
94
|
+
} else {
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function setTecode(tecode: string) {
|
|
101
|
+
setData('tecode', tecode)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function removeTecode() {
|
|
105
|
+
removeData('tecode')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getTecode() {
|
|
109
|
+
return getData('tecode')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
getEnvname,
|
|
114
|
+
setQueryEnvname,
|
|
115
|
+
getQueryEnvname,
|
|
116
|
+
removeQueryEnvname,
|
|
117
|
+
requestEnvdata,
|
|
118
|
+
saveEnvdata,
|
|
119
|
+
removeEnvdata,
|
|
120
|
+
getEnvdata,
|
|
121
|
+
setTecode,
|
|
122
|
+
removeTecode,
|
|
123
|
+
getTecode
|
|
124
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -42,6 +42,7 @@ const getDefaultGlobalOptions = () => {
|
|
|
42
42
|
modulekey: arr[1] || '',
|
|
43
43
|
moduleversion: arr[2] || '',
|
|
44
44
|
storageproxyprefix: '',
|
|
45
|
+
singleLoginCallback: null,
|
|
45
46
|
router: null
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -52,11 +53,6 @@ const install = (app: any, options: any) => {
|
|
|
52
53
|
// console.log(app)
|
|
53
54
|
// console.log(app.version)
|
|
54
55
|
merge(globalOptions, options)
|
|
55
|
-
console.log(`@smart100/spu-web-plugin@${version} install!`)
|
|
56
|
-
console.log(`@smart100/spu-web-plugin@${version} userOptions`)
|
|
57
|
-
console.log(options)
|
|
58
|
-
console.log(`@smart100/spu-web-plugin@${version} globalOptions`)
|
|
59
|
-
console.log(globalOptions)
|
|
60
56
|
|
|
61
57
|
installStorageProxy(globalOptions)
|
|
62
58
|
installUrlquery()
|
|
@@ -67,8 +63,14 @@ const install = (app: any, options: any) => {
|
|
|
67
63
|
installApaasSpuTrack()
|
|
68
64
|
installTest(globalOptions)
|
|
69
65
|
|
|
66
|
+
console.log(`@smart100/spu-web-plugin@${version} install!`)
|
|
67
|
+
console.log(`@smart100/spu-web-plugin@${version} userOptions`)
|
|
68
|
+
console.log(options)
|
|
69
|
+
console.log(`@smart100/spu-web-plugin@${version} globalOptions`)
|
|
70
|
+
console.log(globalOptions)
|
|
71
|
+
|
|
70
72
|
// setTimeout(() => {
|
|
71
|
-
// console.error('
|
|
73
|
+
// console.error('5416546544')
|
|
72
74
|
// }, 1000)
|
|
73
75
|
|
|
74
76
|
// if (install.installed) return
|