@wiajs/core 1.1.33 → 1.2.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.
package/dist/core.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * wia core v1.1.33
2
+ * wia core v1.2.1
3
3
  * (c) 2015-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
package/dist/core.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * wia core v1.1.33
2
+ * wia core v1.2.1
3
3
  * (c) 2015-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
package/dist/core.min.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /*!
2
- * wia core v1.1.33
2
+ * wia core v1.2.1
3
3
  * (c) 2015-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
6
6
  /*!
7
- * wia core v1.1.33
7
+ * wia core v1.2.1
8
8
  * (c) 2015-2025 Sibyl Yu and contributors
9
9
  * Released under the MIT License.
10
10
  */
package/dist/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * wia core v1.1.33
2
+ * wia core v1.2.1
3
3
  * (c) 2015-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * wia core v1.1.33
2
+ * wia core v1.2.1
3
3
  * (c) 2015-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
@@ -10,6 +10,15 @@ Object.defineProperty(exports, '__esModule', { value: true });
10
10
  var jsxRuntime = {};
11
11
 
12
12
  // runtime: 'automatic', // automatic or classic automatic 使用 JSX 运行时(在React 17 中引入)
13
+ // HTML 属性值转义函数
14
+ function escapeAttrValue(value) {
15
+ return String(value)
16
+ .replace(/&/g, "&")
17
+ .replace(/"/g, """)
18
+ .replace(/</g, "&lt;")
19
+ .replace(/>/g, "&gt;");
20
+ }
21
+
13
22
  function jsx(tag, {children, ...props} = {}) {
14
23
  let R = '';
15
24
 
@@ -20,12 +29,12 @@ function jsx(tag, {children, ...props} = {}) {
20
29
 
21
30
  // 特殊处理 className 和 htmlFor
22
31
  if (attrs.className) {
23
- attrsArray.push(`class="${attrs.className}"`);
32
+ attrsArray.push(`class="${escapeAttrValue(attrs.className)}"`);
24
33
  delete attrs.className;
25
34
  }
26
35
 
27
36
  if (attrs.htmlFor) {
28
- attrsArray.push(`for="${attrs.htmlFor}"`);
37
+ attrsArray.push(`for="${escapeAttrValue(attrs.htmlFor)}"`);
29
38
  delete attrs.htmlFor;
30
39
  }
31
40
 
@@ -48,7 +57,7 @@ function jsx(tag, {children, ...props} = {}) {
48
57
 
49
58
  styleArray.push(`${cssProp}:${cssValue}`);
50
59
  }
51
- attrsArray.push(`style="${styleArray.join(';')}"`);
60
+ attrsArray.push(`style="${escapeAttrValue(styleArray.join(';'))}"`);
52
61
  delete attrs.style;
53
62
  }
54
63
 
@@ -59,13 +68,11 @@ function jsx(tag, {children, ...props} = {}) {
59
68
 
60
69
  // 处理布尔属性(如 disabled, checked 等)
61
70
  if (typeof value === 'boolean') {
62
- if (value) {
63
- attrsArray.push(key);
64
- }
71
+ if (value) attrsArray.push(key);
65
72
  }
66
73
  // 处理动态值属性
67
74
  else if (value != null) {
68
- attrsArray.push(`${key}="${value}"`);
75
+ attrsArray.push(`${key}="${escapeAttrValue(value)}"`);
69
76
  }
70
77
  }
71
78
 
@@ -85,7 +92,7 @@ function jsx(tag, {children, ...props} = {}) {
85
92
  // 自闭合标签处理
86
93
  const voidElements = ['input', 'img', 'br', 'hr', 'meta', 'link', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr', 'path', 'circle', 'polygon', 'line', 'rect', 'ellipse', 'use', 'stop'];
87
94
  if (voidElements.includes(tag))
88
- R = `<${tag} ${attrsString} />`.trim();
95
+ R = `<${tag}${attrsString ? ' ' + attrsString : ''} />`;
89
96
  else {
90
97
  const childrenContent = Array.isArray(children)
91
98
  ? children
@@ -103,7 +110,7 @@ function jsx(tag, {children, ...props} = {}) {
103
110
  ? (typeof children === 'boolean' ? '' : children)
104
111
  : '';
105
112
 
106
- R = `<${tag} ${attrsString}>${childrenContent}</${tag}>`.trim();
113
+ R = `<${tag}${attrsString ? ' ' + attrsString : ''}>${childrenContent}</${tag}>`;
107
114
  }
108
115
  }
109
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiajs/core",
3
- "version": "1.1.33",
3
+ "version": "1.2.2",
4
4
  "description": "wia app core package",
5
5
  "main": "./dist/core.cjs",
6
6
  "module": "./dist/core.mjs",
package/util/tool.js CHANGED
@@ -200,6 +200,220 @@ function serObj(obj) {
200
200
  .join('&');
201
201
  }
202
202
 
203
+ /**
204
+ * 金额千位分隔符字符串转换为 Number,单位分
205
+ * 避免运算误差!
206
+ * @param {String} v 金额千位分隔符金额字符串
207
+ * @returns Number 整型金额,单位分
208
+ */
209
+ function toFen(v) {
210
+ let R = v
211
+ if (typeof v === 'string') R = Math.round(Number.parseFloat(v.replaceAll(',', '')) * 100)
212
+ return R
213
+ }
214
+
215
+ /**
216
+ * 金额千位分隔符字符串
217
+ * @param {Number} v 金额,单位分
218
+ * @returns
219
+ */
220
+ function toYuan(v) {
221
+ let R = v
222
+
223
+ if (typeof v === 'number') {
224
+ v = `${(v * 0.01).toFixed(2)}` // 分到元
225
+ // 千分符的正则
226
+ const reg = v.includes('.') ? /(\d{1,3})(?=(?:\d{3})+\.)/g : /(\d{1,3})(?=(?:\d{3})+$)/g
227
+ R = v.replace(reg, '$1,') // 千分位格式化
228
+ }
229
+
230
+ return R
231
+ }
232
+
233
+ /**
234
+ * 延迟promise,可以 await 或 then
235
+ * @param {number} ms - 毫秒
236
+ */
237
+ function delay(ms) {
238
+ return new Promise(succ => {
239
+ setTimeout(succ, ms)
240
+ })
241
+ }
242
+
243
+ /**
244
+ * 简单异步函数转换为promise函数
245
+ * @param {*} f
246
+ * @param {*} type
247
+ * 0:一个回调,一个参数,无失败参数,仅仅返回执行结果
248
+ * 1: 一个回调函数,第一个参数为 err,第二个参数为成功结果
249
+ * 2:两个回调函数,第一个为成功回调,第二个为失败回调
250
+ * 3:两个回调函数,第一个为失败回调,第二个为成功回调
251
+ * @returns
252
+ */
253
+ function promisify(f, type = 1) {
254
+ return (...arg) =>
255
+ new Promise((res, rej) => {
256
+ if (type == null || type === 1)
257
+ f(...arg, (err, rs) => {
258
+ if (err) rej(err)
259
+ else res(rs)
260
+ })
261
+ else if (type === 0) f(...arg, rs => res(rs))
262
+ else if (type === 2)
263
+ f(
264
+ ...arg,
265
+ rs => res(rs),
266
+ rs => rej(rs || new Error('reject'))
267
+ )
268
+ else if (type === 3)
269
+ f(
270
+ ...arg,
271
+ rs => res(rs),
272
+ rs => rej(rs || new Error('reject'))
273
+ )
274
+ })
275
+ }
276
+
277
+ /**
278
+ * 格式化数字:保留 cnt 位小数并添加千位分隔符
279
+ * @param {number} num - 需要格式化的数字
280
+ * @param {number} [cnt] - 小数位数
281
+ * @returns {string} 格式化后的字符串
282
+ */
283
+ function formatNum(num, cnt = 2) {
284
+ if (typeof num !== 'number' || isNaN(num)) {
285
+ return '0.00' // 如果不是数字,返回默认值
286
+ }
287
+ return num.toLocaleString('en-US', {
288
+ minimumFractionDigits: cnt, // 最少保留 2 位小数
289
+ maximumFractionDigits: cnt, // 最多保留 2 位小数
290
+ })
291
+ }
292
+
293
+ /**
294
+ * 判断参数是否为空
295
+ * @param {*} p
296
+ * @returns {boolean}
297
+ * true: undefined/null/''/空对象/空数组/空Map/空Set
298
+ * false: false/0/非空对象、非空数组、值
299
+ */
300
+ function isEmpty(p) {
301
+ if (p === undefined || p === null || p === '') {
302
+ return true
303
+ }
304
+
305
+ if (typeof p === 'string' && p.trim() === '') {
306
+ return true
307
+ }
308
+
309
+ if (Array.isArray(p) && p.length === 0) {
310
+ return true
311
+ }
312
+
313
+ if (p instanceof Map && p.size === 0) {
314
+ return true
315
+ }
316
+
317
+ if (p instanceof Set && p.size === 0) {
318
+ return true
319
+ }
320
+
321
+ // 检查普通空对象
322
+ if (typeof p === 'object' && p !== null && p.constructor === Object && Object.keys(p).length === 0) {
323
+ return true
324
+ }
325
+
326
+ return false
327
+ }
328
+
329
+ /**
330
+ * 日期判断
331
+ * @param {*} v
332
+ * @returns
333
+ */
334
+ function isDate(v) {
335
+ return v instanceof Date
336
+ }
337
+
338
+ /**
339
+ * 是否为数字
340
+ * @param {*} value
341
+ * @returns {boolean}
342
+ */
343
+ function isNumber(value) {
344
+ let R = false
345
+ if (typeof value === 'number') R = true
346
+ else R = Number.isFinite(Number(value)) && value.trim() !== ''
347
+
348
+ return R
349
+ }
350
+
351
+ /**
352
+ * 检查参数,不满足条件抛出异常
353
+ * 1. 非数组:null/undefined/''/空对象
354
+ * 2. 数组:其中一个元素 null/undefined/''/空对象
355
+ * @param {*} p 参数
356
+ * @param {string=} msg
357
+ */
358
+ function needParam(p, msg) {
359
+ if (isEmpty(p)) throw new Err(Err.MissParam, msg)
360
+
361
+ if (Array.isArray(p)) {
362
+ if (p.some(m => isEmpty(m))) throw new Err(Err.MissParam, msg)
363
+ }
364
+ }
365
+
366
+ /**
367
+ * 签名,添加 signTime 和 sign 属性
368
+ * 签名120/30秒内有效,只能使用一次,防止重放攻击或重复提交
369
+ * signTime:1970/1/1 以来的【毫秒数】,不用秒,避免重复
370
+ * post 签名,参数需排序,signTime、Sign 不参与排序放最后,其他字段按字母排序
371
+ * 不同时刻,不同用户pid,不同订单编号,可避免签名重复
372
+ * 同用户pid的高密集并发重复请求可能导致签名重复
373
+ * 可在入参中增加一个nonce随机数,避免签名重复
374
+ * 【注意】日期类型,自动转换字符串为:Sat Jul 08 2023 21:48:24 GMT+0800 (China Standard Time)
375
+ * 需手动 toISOString 转换为 "2011-10-05T14:48:00.000Z"
376
+ * @param {*} r 被签名对象
377
+ * @param {*} secret 签名密钥
378
+ * @param {{start?: string, nonce?: boolean}} [opts={}] 起始日期,默认 1970
379
+ * @returns 带签名的字符串
380
+ */
381
+ async function sign(r, secret, opts = {nonce: true}) {
382
+ let R = ''
383
+
384
+ const {start, nonce} = opts
385
+ // 签名时刻,默认 1970/1/1 以来的毫秒
386
+ let signTime = Date.now()
387
+
388
+ if (start) signTime = Math.trunc(Date.now() - Date.parse(start))
389
+
390
+ // 可选,无实际用途的随机参数,避免签名重复
391
+ if (nonce && !r.nonce) r.nonce = Math.floor(Math.random() * 1000000) // 6位随机数
392
+
393
+ R = Object.keys(r)
394
+ .sort()
395
+ .map(k => (isDate(r[k]) ? `${k}=${r[k].toISOString()}` : `${k}=${r[k]}`))
396
+ .join('&')
397
+
398
+ // 兼容 DSC 旧的签名模式(UAir,大写开头)
399
+ if (start) {
400
+ R += `&SignTime=${signTime}`
401
+ r.SignTime = signTime
402
+ r.Sign = (await sha1(`${R}${secret}`)).toUpperCase()
403
+ R += `&Sign=${r.Sign}`
404
+ } else {
405
+ R += `&signTime=${signTime}`
406
+ r.signTime = signTime
407
+ r.sign = (await sha1(`${R}${secret}`)).toUpperCase()
408
+ R += `&sign=${r.sign}`
409
+ }
410
+
411
+ // 调试时使用,生产务必关闭,避免泄露密码!!!
412
+ // log.debug(`sign r:${JSON.stringify(r)} R:${R} secret: ${secret}`)
413
+
414
+ return R
415
+ }
416
+
203
417
  export {
204
418
  StringBuf,
205
419
  format,
@@ -211,4 +425,5 @@ export {
211
425
  sortObj,
212
426
  serObj,
213
427
  setTitle,
428
+ toYuan, toFen, promisify, delay, formatNum, isNumber, isDate, needParam, sign
214
429
  };