mm_expand 2.3.4 → 2.3.5
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/lib/array.js +36 -36
- package/lib/base.js +15 -15
- package/lib/date.js +9 -8
- package/lib/errors.js +99 -99
- package/lib/event.js +45 -45
- package/lib/eventer.js +166 -165
- package/lib/file.js +126 -14
- package/lib/global.js +51 -53
- package/lib/lang.js +10 -10
- package/lib/logger.js +2 -2
- package/lib/number.js +10 -10
- package/lib/object.js +72 -75
- package/lib/req.js +42 -42
- package/lib/ret.js +248 -249
- package/lib/string.js +87 -93
- package/lib/timer.js +15 -15
- package/lib/validator.js +11 -11
- package/package.json +6 -6
package/lib/errors.js
CHANGED
|
@@ -7,137 +7,137 @@ const { Base } = require('./base');
|
|
|
7
7
|
class Errors extends Base {
|
|
8
8
|
static config = {
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* 错误码语言
|
|
11
11
|
* @type {string}
|
|
12
12
|
* @default "zh-CN"
|
|
13
13
|
*/
|
|
14
|
-
lang:
|
|
14
|
+
lang: 'zh-CN'
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* @
|
|
20
|
-
* @param {
|
|
18
|
+
* 构造函数
|
|
19
|
+
* @class
|
|
20
|
+
* @param {object} config 配置项
|
|
21
21
|
*/
|
|
22
22
|
constructor(config) {
|
|
23
23
|
super(config);
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* 错误码语言包
|
|
26
26
|
*/
|
|
27
27
|
this._langs = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
28
|
+
'zh-CN': {
|
|
29
|
+
'10000': '业务逻辑错误',
|
|
30
|
+
'0': '成功',
|
|
31
|
+
'100': '继续',
|
|
32
|
+
'101': '切换协议',
|
|
33
|
+
'200': '确定',
|
|
34
|
+
'201': '已创建',
|
|
35
|
+
'202': '已接受',
|
|
36
|
+
'204': '无内容',
|
|
37
|
+
'301': '已永久移动',
|
|
38
|
+
'302': '已找到',
|
|
39
|
+
'303': '见其他',
|
|
40
|
+
'304': '未修改',
|
|
41
|
+
'307': '临时重定向',
|
|
42
|
+
'400': '错误请求',
|
|
43
|
+
'401': '未授权',
|
|
44
|
+
'403': '禁止访问',
|
|
45
|
+
'404': '未找到',
|
|
46
|
+
'405': '方法不允许',
|
|
47
|
+
'408': '请求超时',
|
|
48
|
+
'413': 'Payload Too Large',
|
|
49
|
+
'415': '不支持的媒体类型',
|
|
50
|
+
'429': '请求过多',
|
|
51
|
+
'500': '内部服务器错误',
|
|
52
|
+
'501': '未实现',
|
|
53
|
+
'502': '错误网关',
|
|
54
|
+
'503': '服务不可用',
|
|
55
|
+
'504': '网关超时',
|
|
56
|
+
'-32700': '格式错误',
|
|
57
|
+
'-32701': '不支持的编码',
|
|
58
|
+
'-32702': '编码中包含无效字符',
|
|
59
|
+
'-32600': '无效的json-rpc. 不符合规范',
|
|
60
|
+
'-32601': '请求的方法未找到',
|
|
61
|
+
'-32602': '无效的方法参数',
|
|
62
|
+
'-32603': '内部json-rpc错误',
|
|
63
|
+
'-32500': '应用错误',
|
|
64
|
+
'-32400': '系统错误',
|
|
65
|
+
'-32300': '传输错误',
|
|
66
|
+
'30000': '身份验证失败',
|
|
67
|
+
'40000': '数据库执行错误',
|
|
68
|
+
'50000': '服务端执行错误',
|
|
69
|
+
'70000': '参数不正确'
|
|
70
70
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
71
|
+
'en-US': {
|
|
72
|
+
'10000': 'business logic error',
|
|
73
|
+
'0': 'success',
|
|
74
|
+
'100': 'Continue',
|
|
75
|
+
'101': 'Switching Protocols',
|
|
76
|
+
'200': 'OK',
|
|
77
|
+
'201': 'Created',
|
|
78
|
+
'202': 'Accepted',
|
|
79
|
+
'204': 'No Content',
|
|
80
|
+
'301': 'Moved Permanently',
|
|
81
|
+
'302': 'Found',
|
|
82
|
+
'303': 'See Other',
|
|
83
|
+
'304': 'Not Modified',
|
|
84
|
+
'307': 'Temporary Redirect',
|
|
85
|
+
'400': 'Bad Request',
|
|
86
|
+
'401': 'Unauthorized',
|
|
87
|
+
'403': 'Forbidden',
|
|
88
|
+
'404': 'Not Found',
|
|
89
|
+
'405': 'Method Not Allowed',
|
|
90
|
+
'408': 'Request Timeout',
|
|
91
|
+
'413': 'Payload Too Large',
|
|
92
|
+
'415': 'Unsupported Media Type',
|
|
93
|
+
'429': 'Too Many Requests',
|
|
94
|
+
'500': 'Internal Server Errors',
|
|
95
|
+
'501': 'Not Implemented',
|
|
96
|
+
'502': 'Bad Gateway',
|
|
97
|
+
'503': 'Service Unavailable',
|
|
98
|
+
'504': 'Gateway Timeout',
|
|
99
|
+
'-32700': 'not well formed',
|
|
100
|
+
'-32701': 'unsupported encoding',
|
|
101
|
+
'-32702': 'invalid character for encoding',
|
|
102
|
+
'-32600': 'invalid json-rpc. not conforming to spec',
|
|
103
|
+
'-32601': 'requested method not found',
|
|
104
|
+
'-32602': 'invalid method parameters',
|
|
105
|
+
'-32603': 'internal json-rpc error',
|
|
106
|
+
'-32500': 'application error',
|
|
107
|
+
'-32400': 'system error',
|
|
108
|
+
'-32300': 'transport error',
|
|
109
|
+
'30000': 'authentication failed',
|
|
110
|
+
'40000': 'database execution error',
|
|
111
|
+
'50000': 'server execution error',
|
|
112
|
+
'70000': 'parameter is incorrect'
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
*
|
|
119
|
+
* 根据错误码获取错误信息
|
|
120
120
|
* @param {string|number} code 错误码
|
|
121
121
|
* @returns {string|null} 错误信息
|
|
122
122
|
*/
|
|
123
123
|
Errors.prototype.get = function (code) {
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
let lang = this.config.lang;
|
|
125
|
+
let lang_pack = this._langs[lang];
|
|
126
126
|
|
|
127
127
|
if (lang_pack && lang_pack[code]) {
|
|
128
128
|
return lang_pack[code];
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// 如果当前语言包没有,尝试英文包
|
|
132
|
-
if (lang !==
|
|
133
|
-
return this._langs[
|
|
132
|
+
if (lang !== 'en-US' && this._langs['en-US'][code]) {
|
|
133
|
+
return this._langs['en-US'][code];
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
return null;
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
|
-
*
|
|
140
|
+
* 设置语言
|
|
141
141
|
* @param {string} lang 语言代码
|
|
142
142
|
*/
|
|
143
143
|
Errors.prototype.setLang = function (lang) {
|
|
@@ -145,7 +145,7 @@ Errors.prototype.setLang = function (lang) {
|
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
|
-
*
|
|
148
|
+
* 获取当前语言
|
|
149
149
|
* @returns {string} 当前语言代码
|
|
150
150
|
*/
|
|
151
151
|
Errors.prototype.getLang = function () {
|
|
@@ -153,8 +153,8 @@ Errors.prototype.getLang = function () {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
*
|
|
156
|
+
* 导出错误管理类
|
|
157
157
|
*/
|
|
158
158
|
module.exports = {
|
|
159
159
|
Errors
|
|
160
|
-
}
|
|
160
|
+
};
|
package/lib/event.js
CHANGED
|
@@ -6,9 +6,9 @@ require('./global.js');
|
|
|
6
6
|
*/
|
|
7
7
|
class Event {
|
|
8
8
|
static config = {
|
|
9
|
-
|
|
9
|
+
max_list: 100,
|
|
10
10
|
auto_clean: true,
|
|
11
|
-
|
|
11
|
+
error: null
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
// 日志对象
|
|
@@ -66,10 +66,10 @@ class Event {
|
|
|
66
66
|
_getEventType(event_name) {
|
|
67
67
|
if (typeof event_name !== 'string') return 'main';
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
let parts = event_name.split(':');
|
|
70
70
|
if (parts.length < 2) return 'main';
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
let suffix = parts[parts.length - 1];
|
|
73
73
|
const special_types = ['before', 'check', 'after', 'error', 'render', 'success'];
|
|
74
74
|
|
|
75
75
|
if (special_types.includes(suffix)) {
|
|
@@ -86,7 +86,7 @@ class Event {
|
|
|
86
86
|
* @private
|
|
87
87
|
*/
|
|
88
88
|
_getEventStore(event_name) {
|
|
89
|
-
|
|
89
|
+
let event_type = this._getEventType(event_name);
|
|
90
90
|
|
|
91
91
|
if (event_type === 'main') {
|
|
92
92
|
return {
|
|
@@ -113,13 +113,13 @@ class Event {
|
|
|
113
113
|
throw new TypeError('监听器必须是函数');
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
let store = this._getEventStore(event_name);
|
|
117
117
|
|
|
118
118
|
if (!store.map.has(event_name)) {
|
|
119
119
|
store.map.set(event_name, []);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
let listeners = store.map.get(event_name);
|
|
123
123
|
|
|
124
124
|
// 检查监听器数量限制
|
|
125
125
|
if (listeners.length >= this.#event_config.max_listeners) {
|
|
@@ -146,7 +146,7 @@ class Event {
|
|
|
146
146
|
* @returns {Event} 返回this,支持链式调用
|
|
147
147
|
*/
|
|
148
148
|
once(event_name, listener) {
|
|
149
|
-
|
|
149
|
+
once_wrapper = (...args) => {
|
|
150
150
|
this.off(event_name, once_wrapper);
|
|
151
151
|
return listener(...args);
|
|
152
152
|
};
|
|
@@ -172,17 +172,17 @@ class Event {
|
|
|
172
172
|
* @returns {Event} 返回this,支持链式调用
|
|
173
173
|
*/
|
|
174
174
|
off(event_name, listener) {
|
|
175
|
-
|
|
175
|
+
let store = this._getEventStore(event_name);
|
|
176
176
|
|
|
177
177
|
if (!store.map.has(event_name)) {
|
|
178
178
|
return this;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
let listeners = store.map.get(event_name);
|
|
182
182
|
|
|
183
183
|
if (listener) {
|
|
184
184
|
// 移除特定监听器
|
|
185
|
-
|
|
185
|
+
let index = listeners.findIndex(fn => fn === listener || fn._original === listener);
|
|
186
186
|
if (index !== -1) {
|
|
187
187
|
listeners.splice(index, 1);
|
|
188
188
|
|
|
@@ -195,7 +195,7 @@ class Event {
|
|
|
195
195
|
}
|
|
196
196
|
} else {
|
|
197
197
|
// 移除所有监听器
|
|
198
|
-
|
|
198
|
+
let count = listeners.length;
|
|
199
199
|
store.map.delete(event_name);
|
|
200
200
|
|
|
201
201
|
// 更新计数器
|
|
@@ -221,15 +221,15 @@ class Event {
|
|
|
221
221
|
* @returns {boolean} 是否成功触发事件
|
|
222
222
|
*/
|
|
223
223
|
emit(event_name, ...args) {
|
|
224
|
-
|
|
224
|
+
let store = this._getEventStore(event_name);
|
|
225
225
|
|
|
226
226
|
if (!store.map.has(event_name)) {
|
|
227
227
|
return false;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
let listeners = store.map.get(event_name).slice();
|
|
231
231
|
|
|
232
|
-
for (
|
|
232
|
+
for (let listener of listeners) {
|
|
233
233
|
try {
|
|
234
234
|
listener(...args);
|
|
235
235
|
} catch (error) {
|
|
@@ -247,15 +247,15 @@ class Event {
|
|
|
247
247
|
* @returns {Promise} 事件触发结果
|
|
248
248
|
*/
|
|
249
249
|
async emitWait(event_name, ...args) {
|
|
250
|
-
|
|
250
|
+
let store = this._getEventStore(event_name);
|
|
251
251
|
|
|
252
252
|
if (!store.map.has(event_name)) {
|
|
253
253
|
return;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
let listeners = store.map.get(event_name).slice();
|
|
257
257
|
|
|
258
|
-
for (
|
|
258
|
+
for (let listener of listeners) {
|
|
259
259
|
try {
|
|
260
260
|
await listener(...args);
|
|
261
261
|
} catch (error) {
|
|
@@ -271,13 +271,13 @@ class Event {
|
|
|
271
271
|
* @returns {boolean} 是否成功触发事件
|
|
272
272
|
*/
|
|
273
273
|
emitAsync(event_name, ...args) {
|
|
274
|
-
|
|
274
|
+
let store = this._getEventStore(event_name);
|
|
275
275
|
|
|
276
276
|
if (!store.map.has(event_name)) {
|
|
277
277
|
return false;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
let listeners = store.map.get(event_name).slice();
|
|
281
281
|
|
|
282
282
|
listeners.forEach(listener => {
|
|
283
283
|
try {
|
|
@@ -300,14 +300,14 @@ class Event {
|
|
|
300
300
|
* @returns {Promise} 事件触发结果
|
|
301
301
|
*/
|
|
302
302
|
async emitAll(event_name, ...args) {
|
|
303
|
-
|
|
303
|
+
let store = this._getEventStore(event_name);
|
|
304
304
|
|
|
305
305
|
if (!store.map.has(event_name)) {
|
|
306
306
|
return [];
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
let listeners = store.map.get(event_name).slice();
|
|
310
|
+
let promises = listeners.map(listener => {
|
|
311
311
|
try {
|
|
312
312
|
return Promise.resolve(listener(...args));
|
|
313
313
|
} catch (error) {
|
|
@@ -325,14 +325,14 @@ class Event {
|
|
|
325
325
|
* @returns {Promise} 第一个完成的结果
|
|
326
326
|
*/
|
|
327
327
|
async emitRace(event_name, ...args) {
|
|
328
|
-
|
|
328
|
+
let store = this._getEventStore(event_name);
|
|
329
329
|
|
|
330
330
|
if (!store.map.has(event_name)) {
|
|
331
331
|
return null;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
let listeners = store.map.get(event_name).slice();
|
|
335
|
+
let promises = listeners.map((listener, index) =>
|
|
336
336
|
Promise.resolve(listener(...args))
|
|
337
337
|
.then(result => ({ index, result, status: 'fulfilled' }))
|
|
338
338
|
.catch(error => ({ index, error, status: 'rejected' }))
|
|
@@ -348,17 +348,17 @@ class Event {
|
|
|
348
348
|
* @param {...any} args 事件参数
|
|
349
349
|
* @returns {Promise} 最后一个事件监听者的结果
|
|
350
350
|
*/
|
|
351
|
-
async
|
|
352
|
-
|
|
351
|
+
async emitWate(event_name, value, ...args) {
|
|
352
|
+
let store = this._getEventStore(event_name);
|
|
353
353
|
|
|
354
354
|
if (!store.map.has(event_name)) {
|
|
355
355
|
return value;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
|
|
358
|
+
let listeners = store.map.get(event_name).slice();
|
|
359
359
|
let val = value;
|
|
360
360
|
|
|
361
|
-
for (
|
|
361
|
+
for (let listener of listeners) {
|
|
362
362
|
try {
|
|
363
363
|
val = await listener(val, ...args);
|
|
364
364
|
} catch (error) {
|
|
@@ -376,7 +376,7 @@ class Event {
|
|
|
376
376
|
* @returns {number} 监听器数量
|
|
377
377
|
*/
|
|
378
378
|
listenerCount(event_name) {
|
|
379
|
-
|
|
379
|
+
let store = this._getEventStore(event_name);
|
|
380
380
|
|
|
381
381
|
if (!store.map.has(event_name)) {
|
|
382
382
|
return 0;
|
|
@@ -388,10 +388,10 @@ class Event {
|
|
|
388
388
|
/**
|
|
389
389
|
* 获取所有事件监听器
|
|
390
390
|
* @param {string} event_name 事件名称
|
|
391
|
-
* @returns {
|
|
391
|
+
* @returns {Array} 监听器数组
|
|
392
392
|
*/
|
|
393
393
|
listeners(event_name) {
|
|
394
|
-
|
|
394
|
+
let store = this._getEventStore(event_name);
|
|
395
395
|
|
|
396
396
|
if (!store.map.has(event_name)) {
|
|
397
397
|
return [];
|
|
@@ -407,10 +407,10 @@ class Event {
|
|
|
407
407
|
*/
|
|
408
408
|
removeAllListeners(event_name) {
|
|
409
409
|
if (event_name) {
|
|
410
|
-
|
|
410
|
+
let store = this._getEventStore(event_name);
|
|
411
411
|
|
|
412
412
|
if (store.map.has(event_name)) {
|
|
413
|
-
|
|
413
|
+
let count = store.map.get(event_name).length;
|
|
414
414
|
store.map.delete(event_name);
|
|
415
415
|
|
|
416
416
|
// 更新计数器
|
|
@@ -433,19 +433,19 @@ class Event {
|
|
|
433
433
|
|
|
434
434
|
/**
|
|
435
435
|
* 获取所有事件名称
|
|
436
|
-
* @returns {
|
|
436
|
+
* @returns {Array} 事件名称数组
|
|
437
437
|
*/
|
|
438
438
|
eventNames() {
|
|
439
439
|
const event_names = [];
|
|
440
440
|
|
|
441
441
|
// 添加 main 类型事件
|
|
442
|
-
for (
|
|
442
|
+
for (let event_name of this.#events.main.keys()) {
|
|
443
443
|
event_names.push(event_name);
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
// 添加特殊类型事件
|
|
447
|
-
for (const [
|
|
448
|
-
for (
|
|
447
|
+
for (const [, map] of Object.entries(this.#events.special)) {
|
|
448
|
+
for (let event_name of map.keys()) {
|
|
449
449
|
event_names.push(event_name);
|
|
450
450
|
}
|
|
451
451
|
}
|
|
@@ -464,13 +464,13 @@ class Event {
|
|
|
464
464
|
throw new TypeError('监听器必须是函数');
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
-
|
|
467
|
+
let store = this._getEventStore(event_name);
|
|
468
468
|
|
|
469
469
|
if (!store.map.has(event_name)) {
|
|
470
470
|
store.map.set(event_name, []);
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
|
|
473
|
+
let listeners = store.map.get(event_name);
|
|
474
474
|
|
|
475
475
|
// 检查监听器数量限制
|
|
476
476
|
if (listeners.length >= this.#event_config.max_listeners) {
|
|
@@ -498,7 +498,7 @@ class Event {
|
|
|
498
498
|
* @returns {Event} 返回this,支持链式调用
|
|
499
499
|
*/
|
|
500
500
|
prependOnceListener(event_name, listener) {
|
|
501
|
-
|
|
501
|
+
once_wrapper = (...args) => {
|
|
502
502
|
this.off(event_name, once_wrapper);
|
|
503
503
|
return listener(...args);
|
|
504
504
|
};
|
|
@@ -510,10 +510,10 @@ class Event {
|
|
|
510
510
|
/**
|
|
511
511
|
* 获取原始监听器数组
|
|
512
512
|
* @param {string} event_name 事件名称
|
|
513
|
-
* @returns {
|
|
513
|
+
* @returns {Array} 原始监听器数组
|
|
514
514
|
*/
|
|
515
515
|
rawListeners(event_name) {
|
|
516
|
-
|
|
516
|
+
let store = this._getEventStore(event_name);
|
|
517
517
|
|
|
518
518
|
if (!store.map.has(event_name)) {
|
|
519
519
|
return [];
|
|
@@ -547,7 +547,7 @@ class Event {
|
|
|
547
547
|
* 错误处理
|
|
548
548
|
* @param {Error} error 错误对象
|
|
549
549
|
* @param {string} event_name 事件名称
|
|
550
|
-
* @param {
|
|
550
|
+
* @param {Array} args 事件参数
|
|
551
551
|
* @private
|
|
552
552
|
*/
|
|
553
553
|
_handleError(error, event_name, args) {
|