@questwork/q-utilities 0.1.19 → 0.1.21

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.
@@ -24,14 +24,14 @@ function authorize({ allowCoordinator, allowOwner, query = {}, required, user })
24
24
  if (query.registrationGroupCode && user.myManagedRegistrationGroupCodes.includes(query.registrationGroupCode)) {
25
25
  query.__ALLOW_COORDINATOR = true
26
26
  } else {
27
- if (!scopes.includes('EVENT')) {
28
- query.eventRegistrationCode = user.eventRegistrationCode
29
- }
30
- }
31
- } else {
32
27
  if (!scopes.includes('EVENT')) {
33
28
  query.eventRegistrationCode = user.eventRegistrationCode
34
29
  }
30
+ }
31
+ } else {
32
+ if (!scopes.includes('EVENT')) {
33
+ query.eventRegistrationCode = user.eventRegistrationCode
34
+ }
35
35
  }
36
36
  if (allowOwner) {
37
37
  query.__ALLOW_OWNER = true
@@ -63,14 +63,14 @@ function calculateAge(timestamp, reference) {
63
63
  }
64
64
 
65
65
  // Calculate raw difference
66
- let age = refDate.getFullYear() - birthDate.getFullYear();
67
- const monthDiff = refDate.getMonth() - birthDate.getMonth();
68
-
66
+ let age = refDate.getFullYear() - birthDate.getFullYear()
67
+ const monthDiff = refDate.getMonth() - birthDate.getMonth()
68
+
69
69
  // Adjust if birthday hasn't occurred yet this year
70
70
  if (monthDiff < 0 || (monthDiff === 0 && refDate.getDate() < birthDate.getDate())) {
71
71
  age--
72
72
  }
73
-
73
+
74
74
  return age
75
75
  }
76
76
 
@@ -100,7 +100,18 @@ function changeCreatorOwner(that, { source, target }) {
100
100
  ;// ./lib/helpers/changeCreatorOwner/index.js
101
101
 
102
102
 
103
+ ;// ./lib/helpers/isConvertibleToNumber/isConvertibleToNumber.js
104
+ function isConvertibleToNumber(value) {
105
+ return value !== null
106
+ && value !== undefined
107
+ && typeof value !== 'boolean'
108
+ && String(value).trim() !== ''
109
+ && !isNaN(Number(value))
110
+ }
111
+
103
112
  ;// ./lib/helpers/getValidation/getValidation.js
113
+
114
+
104
115
  function getValidation(rule, data, getDataByKey, KeyValueObject) {
105
116
  if (!rule) {
106
117
  return true
@@ -114,10 +125,10 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
114
125
  if (!key && typeof placeholder === 'undefined') {
115
126
  switch (valueAttribute) {
116
127
  case '$and': {
117
- return value['$and'].reduce((acc, item) => (acc && getValidation(item, data, getDataByKey, KeyValueObject)), true)
128
+ return value.$and.reduce((acc, item) => (acc && getValidation(item, data, getDataByKey, KeyValueObject)), true)
118
129
  }
119
130
  case '$or': {
120
- return value['$or'].reduce((acc, item) => (acc || getValidation(item, data, getDataByKey, KeyValueObject)), false)
131
+ return value.$or.reduce((acc, item) => (acc || getValidation(item, data, getDataByKey, KeyValueObject)), false)
121
132
  }
122
133
  default:
123
134
  return false
@@ -132,39 +143,53 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
132
143
  }
133
144
 
134
145
  switch (valueAttribute) {
146
+ case '$after': {
147
+ if (isConvertibleToNumber(value?.$after)) {
148
+ const _value = Number(String(value?.$after))
149
+ return Date.now() > _value
150
+ }
151
+ return false
152
+ }
153
+ case '$before': {
154
+ if (isConvertibleToNumber(value?.$before)) {
155
+ const _value = Number(String(value?.$before))
156
+ return Date.now() < _value
157
+ }
158
+ return false
159
+ }
135
160
  case '$empty': {
136
161
  const isEmpty = rowValue === null || rowValue === undefined
137
- return isEmpty === value['$empty']
162
+ return isEmpty === value.$empty
138
163
  }
139
164
  case '$eq': {
140
- return rowValue === value['$eq']
165
+ return rowValue === value.$eq
141
166
  }
142
167
  case '$gt': {
143
- return rowValue > value['$gt']
168
+ return rowValue > value.$gt
144
169
  }
145
170
  case '$gte': {
146
- return rowValue >= value['$gte']
171
+ return rowValue >= value.$gte
147
172
  }
148
173
  case '$hasOverlap': {
149
- return _hasOverlap(rowValue, value['$hasOverlap'])
174
+ return _hasOverlap(rowValue, value.$hasOverlap)
150
175
  }
151
176
  case '$lt': {
152
- return rowValue < value['$lt']
177
+ return rowValue < value.$lt
153
178
  }
154
179
  case '$lte': {
155
- return rowValue <= value['$lte']
180
+ return rowValue <= value.$lte
156
181
  }
157
182
  case '$in': {
158
183
  if (Array.isArray(rowValue)) {
159
- return !!rowValue.find((e) => (value['$in'].includes(e)))
184
+ return !!rowValue.find((e) => (value.$in.includes(e)))
160
185
  }
161
186
  if (typeof rowValue !== 'object') {
162
- return !!value['$in'].includes(rowValue)
187
+ return !!value.$in.includes(rowValue)
163
188
  }
164
189
  return false
165
190
  }
166
191
  case '$inValue': {
167
- const result = getDataByKey(value['$inValue'], data)
192
+ const result = getDataByKey(value.$inValue, data)
168
193
  const _value = Array.isArray(result) ? result : []
169
194
  if (Array.isArray(rowValue)) {
170
195
  return !!rowValue.find((e) => (_value.includes(e)))
@@ -175,36 +200,36 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
175
200
  return false
176
201
  }
177
202
  case '$ne': {
178
- return rowValue !== value['$ne']
203
+ return rowValue !== value.$ne
179
204
  }
180
205
  case '$notIn': {
181
206
  if (Array.isArray(rowValue)) {
182
- return !rowValue.find((e) => (value['$notIn'].includes(e)))
207
+ return !rowValue.find((e) => (value.$notIn.includes(e)))
183
208
  }
184
209
  if (typeof rowValue !== 'object') {
185
- return !value['$notIn'].includes(rowValue)
210
+ return !value.$notIn.includes(rowValue)
186
211
  }
187
212
  return false
188
213
  }
189
214
  case '$intervalTimeGt': {
190
215
  const now = new Date().getTime()
191
216
  const timestamp = new Date(rowValue).getTime()
192
- return (now - timestamp) > value['$intervalTimeGt']
217
+ return (now - timestamp) > value.$intervalTimeGt
193
218
  }
194
219
  case '$intervalTimeLt': {
195
220
  const now = new Date().getTime()
196
221
  const timestamp = new Date(rowValue).getTime()
197
- return (now - timestamp) < value['$intervalTimeLt']
222
+ return (now - timestamp) < value.$intervalTimeLt
198
223
  }
199
224
  case '$isToday': {
200
225
  const currentDate = new Date()
201
- const start = currentDate.setHours(0,0,0,0)
202
- const end = currentDate.setHours(23,59,59,59)
226
+ const start = currentDate.setHours(0, 0, 0, 0)
227
+ const end = currentDate.setHours(23, 59, 59, 59)
203
228
  const dateValue = new Date(rowValue).getTime()
204
- return (start <= dateValue && end >= dateValue) === value['$isToday']
229
+ return (start <= dateValue && end >= dateValue) === value.$isToday
205
230
  }
206
231
  case '$notInValue': {
207
- const result = getDataByKey(value['$notInValue'], data)
232
+ const result = getDataByKey(value.$notInValue, data)
208
233
  const _value = Array.isArray(result) ? result : []
209
234
  if (Array.isArray(rowValue)) {
210
235
  return !rowValue.find((e) => (_value.includes(e)))
@@ -215,7 +240,7 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
215
240
  return false
216
241
  }
217
242
  case '$range': {
218
- const [min, max] = value['$range']
243
+ const [min, max] = value.$range
219
244
  if (typeof min === 'number' && typeof max === 'number' && rowValue >= min && rowValue <= max) {
220
245
  return true
221
246
  }
@@ -224,7 +249,6 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
224
249
  default:
225
250
  return false
226
251
  }
227
-
228
252
  }
229
253
 
230
254
  function _hasOverlap(item1, item2) {
@@ -249,12 +273,11 @@ function _hasOverlap(item1, item2) {
249
273
 
250
274
 
251
275
  ;// ./lib/helpers/formatDate/formatDate.js
252
-
253
276
  function formatDate(date, format) {
254
277
  const _date = date && date instanceof Date ? date : new Date(date)
255
- const dayMapChi = ['日','一','二','三','四','五','六']
256
- const dayMapEng = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']
257
- const dayMapEngShort = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
278
+ const dayMapChi = ['日', '一', '二', '三', '四', '五', '六']
279
+ const dayMapEng = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
280
+ const dayMapEngShort = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
258
281
  const _format = format || 'YYYY/MM/DD hh:mm'
259
282
  const e = _date.getDay()
260
283
  const ee = dayMapEngShort[e]
@@ -289,13 +312,11 @@ function padding(m) {
289
312
  return m < 10 ? `0${m}` : m
290
313
  }
291
314
 
292
-
293
315
  /* harmony default export */ const formatDate_formatDate = ({
294
316
  formatDate
295
317
  });
296
318
 
297
319
 
298
-
299
320
  ;// ./lib/helpers/formatDate/index.js
300
321
 
301
322
 
@@ -326,7 +347,6 @@ function getValueByKeys_getValueByKeys(keys, data) {
326
347
  return _data[firstKey]
327
348
  }
328
349
  return _data
329
-
330
350
  }
331
351
  /* harmony default export */ const getValueByKeys = ({
332
352
  getValueByKeys: getValueByKeys_getValueByKeys
@@ -378,6 +398,7 @@ const _FN_NAMES = [
378
398
  'map',
379
399
  'neq',
380
400
  'removeHtml',
401
+ 'sum',
381
402
  'toLowerCase',
382
403
  'toUpperCase',
383
404
  ]
@@ -530,10 +551,12 @@ function _existObject(data, args) {
530
551
 
531
552
  function _performOperation(arg, value) {
532
553
  // the arg is undefined
533
- if (arg === undefined && value === undefined) return true
554
+ if (arg === undefined && value === undefined)
555
+ return true
534
556
 
535
557
  // the arg is null
536
- if (arg === null && value === null) return true
558
+ if (arg === null && value === null)
559
+ return true
537
560
 
538
561
  // the arg is boolean
539
562
  if (typeof arg === 'boolean') {
@@ -609,17 +632,18 @@ function _splitOperator(str) {
609
632
  const operators = ['!=', '>=', '<=', '>', '<']
610
633
 
611
634
  const matchedOp = operators.find((op) => str.startsWith(op))
612
- if (!matchedOp) return { operator: null, value: null }
635
+ if (!matchedOp)
636
+ return { operator: null, value: null }
613
637
 
614
638
  const remaining = str.slice(matchedOp.length)
615
639
 
616
640
  // '>Primary' or '<Primary' is invalid
617
- if (/^[a-zA-Z]*$/.test(remaining) && matchedOp !== '!=') {
641
+ if (/^[a-z]*$/i.test(remaining) && matchedOp !== '!=') {
618
642
  return { operator: null, value: null }
619
643
  }
620
644
 
621
645
  // if it is a number it is converted to a number
622
- const value = (!Number.isNaN(parseFloat(remaining)) && !Number.isNaN(remaining)) ? Number(remaining) : remaining
646
+ const value = (!Number.isNaN(Number.parseFloat(remaining)) && !Number.isNaN(remaining)) ? Number(remaining) : remaining
623
647
 
624
648
  return {
625
649
  operator: matchedOp,
@@ -831,11 +855,14 @@ function _isNotEmpty(data, args) {
831
855
  }
832
856
 
833
857
 
858
+
834
859
  ;// ./lib/models/templateCompiler/helpers/_join.js
835
860
  function _join(data, delimiter) {
836
861
  try {
837
- if (data.length === 0) return ''
838
- if (data.length === 1) return _stringifyObject(data[0])
862
+ if (data.length === 0)
863
+ return ''
864
+ if (data.length === 1)
865
+ return _stringifyObject(data[0])
839
866
  return data.map((item) => _stringifyObject(item)).join(delimiter)
840
867
  } catch (e) {
841
868
  throw e
@@ -982,13 +1009,13 @@ function _removeHtml(html, args) {
982
1009
 
983
1010
  function _htmlToPlainText(html, delimiter = '\n') {
984
1011
  if (typeof delimiter !== 'string') {
985
- delimiter = '\n'; // Fallback to default if not a string
1012
+ delimiter = '\n' // Fallback to default if not a string
986
1013
  }
987
1014
 
988
1015
  // First decode HTML entities and normalize whitespace
989
1016
  const decodedHtml = html
990
1017
  .replace(/&nbsp;/g, ' ')
991
- .replace(/\s+/g, ' '); // Collapse all whitespace to single spaces
1018
+ .replace(/\s+/g, ' ') // Collapse all whitespace to single spaces
992
1019
 
993
1020
  // Process HTML tags
994
1021
  let text = decodedHtml
@@ -999,29 +1026,36 @@ function _htmlToPlainText(html, delimiter = '\n') {
999
1026
  // Remove all other tags
1000
1027
  .replace(/<[^>]+>/g, '')
1001
1028
  // Convert markers to specified delimiter
1002
- .replace(/~~~+/g, delimiter)
1029
+ .replace(/~{3,}/g, delimiter)
1003
1030
  // Trim and clean whitespace
1004
- .trim();
1031
+ .trim()
1005
1032
 
1006
1033
  // Special handling for empty delimiter
1007
1034
  if (delimiter === '') {
1008
1035
  // Collapse all whitespace to single space
1009
- text = text.replace(/\s+/g, ' ');
1036
+ text = text.replace(/\s+/g, ' ')
1010
1037
  } else {
1011
-
1012
-
1013
1038
  // Collapse multiple delimiters to single
1014
- text = text.replace(new RegExp(`${escapeRegExp(delimiter)}+`, 'g'), delimiter);
1039
+ text = text.replace(new RegExp(`${escapeRegExp(delimiter)}+`, 'g'), delimiter)
1015
1040
  // Remove leading/trailing delimiters
1016
- text = text.replace(new RegExp(`^${escapeRegExp(delimiter)}|${escapeRegExp(delimiter)}$`, 'g'), '');
1041
+ text = text.replace(new RegExp(`^${escapeRegExp(delimiter)}|${escapeRegExp(delimiter)}$`, 'g'), '')
1017
1042
  }
1018
1043
 
1019
- return text;
1044
+ return text
1020
1045
  }
1021
1046
 
1022
-
1023
1047
  function escapeRegExp(string) {
1024
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1048
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
1049
+ }
1050
+
1051
+
1052
+
1053
+ ;// ./lib/models/templateCompiler/helpers/_sum.js
1054
+ function _sum(data, args) {
1055
+ if (Number.isNaN(data) || data === null || (typeof data === 'undefined') || data === '') {
1056
+ return data
1057
+ }
1058
+ return args.reduce((acc, e) => (acc + e), data)
1025
1059
  }
1026
1060
 
1027
1061
 
@@ -1081,6 +1115,7 @@ function _toUpperCase(data, args) {
1081
1115
 
1082
1116
 
1083
1117
 
1118
+
1084
1119
 
1085
1120
 
1086
1121
  ;// ./lib/models/templateCompiler/templateCompiler.js
@@ -1154,6 +1189,9 @@ class TemplateCompiler {
1154
1189
  static removeHtml(data, args) {
1155
1190
  return _removeHtml(data, args)
1156
1191
  }
1192
+ static sum(data, args) {
1193
+ return _sum(data, args)
1194
+ }
1157
1195
  static toLowerCase(data, args) {
1158
1196
  return _toLowerCase(data, args)
1159
1197
  }
@@ -1214,7 +1252,7 @@ function _parseFunction(expression, existFunctionNames) {
1214
1252
 
1215
1253
  function _parseParams(parameters) {
1216
1254
  const _parameters = parameters.trim()
1217
- const regExp = new RegExp(/^[^\w\d\s]+$/)
1255
+ const regExp = new RegExp(/^[^\w\s]+$/)
1218
1256
  const match = _parameters.match(regExp)
1219
1257
  if (match !== null) {
1220
1258
  return [_parameters.substring(1, _parameters.length - 1)]
@@ -1229,13 +1267,13 @@ function _parseParams(parameters) {
1229
1267
 
1230
1268
  function _splitIgnoringBrackets(input) {
1231
1269
  const regExp2 = new RegExp(/^\d+(\.\d+)?$/)
1232
- const regExp = new RegExp(/(?![^\[]*\])\s*,\s*/)
1270
+ const regExp = new RegExp(/(?![^[]*\])\s*,\s*/)
1233
1271
  const parts = input.split(regExp)
1234
1272
  return parts.map((part) => {
1235
1273
  const _part = part.trim()
1236
1274
  if (_part !== '' && !!_part.match(regExp2)) {
1237
1275
  // 如果是数字,转换为 num 类型
1238
- return Number.isNaN(_part) ? _part : parseInt(_part, 10)
1276
+ return Number.isNaN(_part) ? _part : Number.parseInt(_part, 10)
1239
1277
  }
1240
1278
  // 否则当作字符串处理
1241
1279
  return _part
@@ -1248,7 +1286,7 @@ function _parseSinglePart(input) {
1248
1286
  // 去掉双引号,返回
1249
1287
  return input.substring(1, input.length - 1)
1250
1288
  }
1251
- if (input.startsWith("'") && input.endsWith("'")) {
1289
+ if (input.startsWith('\'') && input.endsWith('\'')) {
1252
1290
  // 去掉双引号,返回
1253
1291
  return input.substring(1, input.length - 1)
1254
1292
  }
@@ -1279,7 +1317,7 @@ function _toBasicType(input) {
1279
1317
  // 去掉双引号,返回
1280
1318
  return input.substring(1, input.length - 1)
1281
1319
  }
1282
- if (input.startsWith("'") && input.endsWith("'")) {
1320
+ if (input.startsWith('\'') && input.endsWith('\'')) {
1283
1321
  // 去掉双引号,返回
1284
1322
  return input.substring(1, input.length - 1)
1285
1323
  }
@@ -1347,6 +1385,8 @@ function _callFunction(data, functionName, parameters) {
1347
1385
  return _neq(data, parameters)
1348
1386
  case 'removeHtml':
1349
1387
  return _removeHtml(data, parameters)
1388
+ case 'sum':
1389
+ return _sum(data, parameters)
1350
1390
  case 'toLowerCase':
1351
1391
  return _toLowerCase(data)
1352
1392
  case 'toUpperCase':
@@ -1376,14 +1416,14 @@ function concatStringByArray(arrTemplate, data) {
1376
1416
  return arrTemplate.reduce((acc, item) => {
1377
1417
  const { type, value = '', restriction, template, format, showMinutes } = item
1378
1418
  switch (type) {
1379
- case('array'): {
1419
+ case ('array'): {
1380
1420
  if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1381
1421
  const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || []
1382
1422
  acc += _value.reduce((_acc, item) => {
1383
1423
  return _acc += concatStringByArray(template, item)
1384
1424
  }, '')
1385
1425
  }
1386
- break
1426
+ break
1387
1427
  }
1388
1428
  case ('date'): {
1389
1429
  if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
@@ -1392,7 +1432,7 @@ function concatStringByArray(arrTemplate, data) {
1392
1432
  }
1393
1433
  break
1394
1434
  }
1395
- case('ellipsis'): {
1435
+ case ('ellipsis'): {
1396
1436
  if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1397
1437
  const { maxLength } = item
1398
1438
  const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
@@ -1402,15 +1442,15 @@ function concatStringByArray(arrTemplate, data) {
1402
1442
  acc += `${_value.substr(0, maxLength)}...`
1403
1443
  }
1404
1444
  }
1405
- break
1445
+ break
1406
1446
  }
1407
- case('group'): {
1447
+ case ('group'): {
1408
1448
  if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1409
1449
  return concatStringByArray(value, data)
1410
1450
  }
1411
1451
  break
1412
1452
  }
1413
- case('label'): {
1453
+ case ('label'): {
1414
1454
  if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1415
1455
  acc += (value.toString())
1416
1456
  }
@@ -1423,12 +1463,12 @@ function concatStringByArray(arrTemplate, data) {
1423
1463
  }
1424
1464
  break
1425
1465
  }
1426
- case('value'): {
1466
+ case ('value'): {
1427
1467
  if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1428
1468
  const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
1429
1469
  acc += (_value.toString())
1430
1470
  }
1431
- break
1471
+ break
1432
1472
  }
1433
1473
  }
1434
1474
  return acc
@@ -1439,7 +1479,6 @@ function concatStringByArray(arrTemplate, data) {
1439
1479
  });
1440
1480
 
1441
1481
 
1442
-
1443
1482
  ;// ./lib/helpers/concatStringByArray/index.js
1444
1483
 
1445
1484
 
@@ -1450,7 +1489,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1450
1489
  if (!string) {
1451
1490
  return ''
1452
1491
  }
1453
- let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1492
+ const _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1454
1493
  const reg = new RegExp(patternMatch, 'g')
1455
1494
  return string.replace(reg, (match, key) => {
1456
1495
  const result = _getValueByKeys({ keys: key.split('.'), obj: value })
@@ -1469,9 +1508,110 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1469
1508
  ;// ./lib/helpers/convertString/index.js
1470
1509
 
1471
1510
 
1511
+ ;// ./lib/helpers/detectControlCharacters/detectControlCharacters.js
1512
+ /**
1513
+ * Detects and reports hidden/control characters in a string without modifying it.
1514
+ * @param {string} input - The string to analyze.
1515
+ * @param {Object} [options] - Configuration options.
1516
+ * @param {boolean} [options.preserveBasicWhitespace=true] - Whether to consider basic whitespace as valid.
1517
+ * @returns {Object} Report object with detection results.
1518
+ */
1519
+ function detectControlCharacters(input, options = {}) {
1520
+ const {
1521
+ preserveBasicWhitespace = true,
1522
+ removeNewlines = false
1523
+ } = options
1524
+
1525
+ if (typeof input !== 'string') {
1526
+ return {
1527
+ hasControlChars: false,
1528
+ matches: [],
1529
+ inputType: typeof input,
1530
+ message: 'Input is not a string'
1531
+ }
1532
+ }
1533
+
1534
+ const matches = []
1535
+ let regex
1536
+
1537
+ if (preserveBasicWhitespace && !removeNewlines) {
1538
+ // Same regex as Phase 1 preserve mode - keep tab (\t), newline (\n), carriage return (\r)
1539
+ regex = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g
1540
+ } else {
1541
+ // Same regex as Phase 1 full removal mode - use consistent escape sequences
1542
+ regex = /[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g
1543
+ }
1544
+
1545
+ // Use a replacer function to capture matches without modifying the string
1546
+ input.replace(regex, (match, offset) => {
1547
+ matches.push({
1548
+ character: match,
1549
+ code: match.charCodeAt(0),
1550
+ hex: '0x' + match.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0'),
1551
+ position: offset,
1552
+ context: getContext(input, offset, 10) // Show surrounding text
1553
+ })
1554
+ return '' // Return empty but we don't use the result
1555
+ })
1556
+
1557
+ return {
1558
+ hasControlChars: matches.length > 0,
1559
+ matches: matches,
1560
+ totalFound: matches.length,
1561
+ inputPreview: input.length > 50 ? input.substring(0, 50) + '...' : input,
1562
+ inputLength: input.length,
1563
+ optionsUsed: { preserveBasicWhitespace },
1564
+ regexPattern: regex.toString()
1565
+ }
1566
+ }
1567
+
1568
+ /**
1569
+ * Helper function to get context around a match
1570
+ */
1571
+ function getContext(str, position, contextLength = 10) {
1572
+ const start = Math.max(0, position - contextLength)
1573
+ const end = Math.min(str.length, position + contextLength + 1)
1574
+ let context = str.substring(start, end)
1575
+
1576
+ // Replace control characters with their escape sequences for readability
1577
+ context = context.replace(/[\x00-\x1F\x7F-\x9F]/g, (match) => {
1578
+ return '\\u' + match.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')
1579
+ })
1580
+
1581
+ return context
1582
+ }
1583
+
1584
+ /**
1585
+ * Pretty print the detection results to console
1586
+ */
1587
+ function printControlCharReport(report) {
1588
+ console.log('=== Control Character Detection Report ===')
1589
+ console.log(`Input: "${report.inputPreview}" (${report.inputLength} chars)`)
1590
+ console.log(`Options: preserveBasicWhitespace = ${report.optionsUsed.preserveBasicWhitespace}`)
1591
+ console.log(`Regex pattern: ${report.regexPattern}`)
1592
+ console.log(`Control characters found: ${report.totalFound}`)
1593
+
1594
+ if (report.hasControlChars) {
1595
+ console.log('\n📋 Matches found:')
1596
+ report.matches.forEach((match, index) => {
1597
+ console.log(`\n${index + 1}. Character: ${JSON.stringify(match.character)}`)
1598
+ console.log(` Code: ${match.code} (${match.hex})`)
1599
+ console.log(` Position: ${match.position}`)
1600
+ console.log(` Context: "...${match.context}..."`)
1601
+ })
1602
+ } else {
1603
+ console.log('✅ No control characters detected in Phase 1 range')
1604
+ }
1605
+
1606
+ console.log('=== End Report ===')
1607
+ }
1608
+
1609
+ ;// ./lib/helpers/detectControlCharacters/index.js
1610
+
1611
+
1472
1612
  ;// ./lib/helpers/escapeRegex/escapeRegex.js
1473
1613
  function escapeRegex(string) {
1474
- return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1614
+ return String(string).replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
1475
1615
  }
1476
1616
 
1477
1617
  ;// ./lib/helpers/escapeRegex/index.js
@@ -1574,7 +1714,6 @@ function updateOneResult({ responseHelper, service }) {
1574
1714
 
1575
1715
 
1576
1716
 
1577
-
1578
1717
  const expressHelper = {
1579
1718
  customHandler: customHandler,
1580
1719
  findAllResult: findAllResult,
@@ -1590,27 +1729,29 @@ const expressHelper = {
1590
1729
  * @returns {Array} Sorted array of unique, lowercase email addresses
1591
1730
  */
1592
1731
  function extractEmails(dirtyArray) {
1593
- const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
1732
+ const emailRegex = /[\w.%+-]+@[a-z0-9.-]+\.[a-z]{2,}/gi
1594
1733
  const emails = new Set()
1595
1734
 
1596
1735
  // Handle null/undefined input array
1597
- if (!dirtyArray) return []
1736
+ if (!dirtyArray)
1737
+ return []
1598
1738
 
1599
- dirtyArray.forEach(entry => {
1739
+ dirtyArray.forEach((entry) => {
1600
1740
  // Skip null, undefined, empty, or whitespace-only entries
1601
- if (!entry || typeof entry !== 'string' || !entry.trim()) return
1741
+ if (!entry || typeof entry !== 'string' || !entry.trim())
1742
+ return
1602
1743
 
1603
1744
  try {
1604
1745
  const cleanEntry = entry
1605
1746
  .replace(/[\u200B-\u200D\uFEFF\u202A-\u202E]/g, '') // Remove hidden chars
1606
- .replace(/[<>]/g, ' ') // Convert email delimiters to spaces
1607
- .replace(/\s+/g, ' ') // Collapse multiple whitespace
1747
+ .replace(/[<>]/g, ' ') // Convert email delimiters to spaces
1748
+ .replace(/\s+/g, ' ') // Collapse multiple whitespace
1608
1749
  .trim()
1609
1750
 
1610
1751
  // Extract all email matches
1611
1752
  const matches = cleanEntry.match(emailRegex)
1612
1753
  if (matches) {
1613
- matches.forEach(email => emails.add(email.toLowerCase())) // Normalize to lowercase
1754
+ matches.forEach((email) => emails.add(email.toLowerCase())) // Normalize to lowercase
1614
1755
  }
1615
1756
  } catch (e) {
1616
1757
  console.warn('Failed to process entry:', entry, e)
@@ -1656,64 +1797,65 @@ const objectHelper = {
1656
1797
  },
1657
1798
  merge,
1658
1799
  set(obj, path, value) {
1659
- const parts = path.split('.');
1660
- let current = obj;
1800
+ const parts = path.split('.')
1801
+ let current = obj
1661
1802
 
1662
1803
  // 处理所有中间部分
1663
1804
  for (let i = 0; i < parts.length - 1; i++) {
1664
- const part = parts[i];
1665
- let key, index;
1805
+ const part = parts[i]
1806
+ let key, index
1666
1807
 
1667
1808
  // 检查是否是数组索引格式,如key[0]
1668
- const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1809
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/)
1669
1810
  if (arrayMatch) {
1670
- key = arrayMatch[1];
1671
- index = parseInt(arrayMatch[2], 10);
1811
+ key = arrayMatch[1]
1812
+ index = Number.parseInt(arrayMatch[2], 10)
1672
1813
  // 确保当前层级的数组存在
1673
1814
  if (!current[key] || !Array.isArray(current[key])) {
1674
- current[key] = [];
1815
+ current[key] = []
1675
1816
  }
1676
1817
  // 扩展数组到足够大
1677
1818
  while (current[key].length <= index) {
1678
- current[key].push(undefined);
1819
+ current[key].push(undefined)
1679
1820
  }
1680
1821
  // 如果当前位置未定义或为null,初始化为对象
1681
1822
  if (current[key][index] == null) {
1682
- current[key][index] = {};
1823
+ current[key][index] = {}
1683
1824
  }
1684
- current = current[key][index];
1825
+ current = current[key][index]
1685
1826
  } else {
1686
1827
  // 处理普通属性
1687
1828
  if (!current[part]) {
1688
- current[part] = {};
1829
+ current[part] = {}
1689
1830
  }
1690
- current = current[part];
1831
+ current = current[part]
1691
1832
  }
1692
1833
  }
1693
1834
 
1694
1835
  // 处理最后一部分
1695
- const lastPart = parts[parts.length - 1];
1696
- const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
1836
+ const lastPart = parts[parts.length - 1]
1837
+ const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/)
1697
1838
  if (arrayMatch) {
1698
- const key = arrayMatch[1];
1699
- const index = parseInt(arrayMatch[2], 10);
1839
+ const key = arrayMatch[1]
1840
+ const index = Number.parseInt(arrayMatch[2], 10)
1700
1841
  // 确保数组存在
1701
1842
  if (!current[key] || !Array.isArray(current[key])) {
1702
- current[key] = [];
1843
+ current[key] = []
1703
1844
  }
1704
1845
  // 扩展数组到所需索引
1705
1846
  while (current[key].length <= index) {
1706
- current[key].push(undefined);
1847
+ current[key].push(undefined)
1707
1848
  }
1708
- current[key][index] = value;
1849
+ current[key][index] = value
1709
1850
  } else {
1710
- current[lastPart] = value;
1851
+ current[lastPart] = value
1711
1852
  }
1712
1853
  }
1713
1854
  }
1714
1855
 
1715
1856
  function merge(target, ...sources) {
1716
- if (!sources.length) return target
1857
+ if (!sources.length)
1858
+ return target
1717
1859
 
1718
1860
  const source = sources.shift() // 取出第一个源对象
1719
1861
 
@@ -1750,28 +1892,28 @@ function _isObject(obj) {
1750
1892
 
1751
1893
  ;// ./lib/helpers/pReduce/pReduce.js
1752
1894
  async function pReduce(iterable, reducer, initialValue) {
1753
- return new Promise((resolve, reject) => {
1754
- const iterator = iterable[Symbol.iterator]()
1755
- let index = 0
1895
+ return new Promise((resolve, reject) => {
1896
+ const iterator = iterable[Symbol.iterator]()
1897
+ let index = 0
1756
1898
 
1757
- const next = async total => {
1758
- const element = iterator.next()
1899
+ const next = async (total) => {
1900
+ const element = iterator.next()
1759
1901
 
1760
- if (element.done) {
1761
- resolve(total)
1762
- return
1763
- }
1902
+ if (element.done) {
1903
+ resolve(total)
1904
+ return
1905
+ }
1764
1906
 
1765
- try {
1766
- const [resolvedTotal, resolvedValue] = await Promise.all([total, element.value])
1767
- next(reducer(resolvedTotal, resolvedValue, index++))
1768
- } catch (error) {
1769
- reject(error)
1770
- }
1771
- }
1907
+ try {
1908
+ const [resolvedTotal, resolvedValue] = await Promise.all([total, element.value])
1909
+ next(reducer(resolvedTotal, resolvedValue, index++))
1910
+ } catch (error) {
1911
+ reject(error)
1912
+ }
1913
+ }
1772
1914
 
1773
- next(initialValue)
1774
- })
1915
+ next(initialValue)
1916
+ })
1775
1917
  }
1776
1918
 
1777
1919
 
@@ -1918,16 +2060,17 @@ class Repo {
1918
2060
  const promise = typeof this.model.saveAll === 'function'
1919
2061
  ? this.model.saveAll({ config, docs })
1920
2062
  : Promise.all(docs.map(async (doc) => {
1921
- if (doc) {
1922
- const result = await this.saveOne({ config, doc })
1923
- isNew = result.isNew
1924
- const _data = result._data || result.data
1925
- return _data[0]
1926
- }
1927
- return null
1928
- }))
2063
+ if (doc) {
2064
+ const result = await this.saveOne({ config, doc })
2065
+ isNew = result.isNew
2066
+ const _data = result._data || result.data
2067
+ return _data[0]
2068
+ }
2069
+ return null
2070
+ }))
1929
2071
  return promise.then((savedData) => {
1930
- if (savedData.length !== 1) isNew = null
2072
+ if (savedData.length !== 1)
2073
+ isNew = null
1931
2074
  const result = {
1932
2075
  data: savedData,
1933
2076
  isNew,
@@ -2261,6 +2404,9 @@ function initOnlyValidFromArray(_class, arr) {
2261
2404
  ;// ./lib/helpers/initOnlyValidFromArray/index.js
2262
2405
 
2263
2406
 
2407
+ ;// ./lib/helpers/isConvertibleToNumber/index.js
2408
+
2409
+
2264
2410
  ;// ./lib/helpers/mergeArraysByKey/mergeArraysByKey.js
2265
2411
  function mergeArraysByKey(arr1, arr2) {
2266
2412
  // Handle undefined/null inputs by defaulting to empty arrays
@@ -2271,40 +2417,41 @@ function mergeArraysByKey(arr1, arr2) {
2271
2417
 
2272
2418
  // Helper function to merge values based on their type
2273
2419
  const mergeValues = (existingValue, newValue) => {
2274
- if (existingValue === undefined) return newValue
2275
-
2420
+ if (existingValue === undefined)
2421
+ return newValue
2422
+
2276
2423
  // Handle arrays by concatenating
2277
2424
  if (Array.isArray(existingValue) && Array.isArray(newValue)) {
2278
2425
  return [...new Set([...existingValue, ...newValue])]
2279
2426
  }
2280
-
2427
+
2281
2428
  // Handle objects by merging
2282
- if (typeof existingValue === 'object' && typeof newValue === 'object' &&
2283
- !Array.isArray(existingValue) && !Array.isArray(newValue)) {
2429
+ if (typeof existingValue === 'object' && typeof newValue === 'object'
2430
+ && !Array.isArray(existingValue) && !Array.isArray(newValue)) {
2284
2431
  return { ...existingValue, ...newValue }
2285
2432
  }
2286
-
2433
+
2287
2434
  // // Handle numbers by adding
2288
2435
  // if (typeof existingValue === 'number' && typeof newValue === 'number') {
2289
2436
  // return existingValue
2290
2437
  // }
2291
-
2438
+
2292
2439
  // // Handle strings by concatenating
2293
2440
  // if (typeof existingValue === 'string' && typeof newValue === 'string') {
2294
2441
  // return existingValue
2295
2442
  // }
2296
-
2443
+
2297
2444
  // Default: use the new value
2298
2445
  return newValue
2299
2446
  }
2300
2447
 
2301
2448
  // Process first array
2302
- safeArr1.forEach(item => {
2449
+ safeArr1.forEach((item) => {
2303
2450
  mergedMap.set(item.key, item.value)
2304
2451
  })
2305
2452
 
2306
2453
  // Process second array and merge values
2307
- safeArr2.forEach(item => {
2454
+ safeArr2.forEach((item) => {
2308
2455
  const existingValue = mergedMap.get(item.key)
2309
2456
  mergedMap.set(item.key, mergeValues(existingValue, item.value))
2310
2457
  })
@@ -2323,7 +2470,7 @@ function mergeArraysByKey(arr1, arr2) {
2323
2470
  function padZeros(num, minLength = 6) {
2324
2471
  num = num.toString()
2325
2472
  if (num.length < minLength) {
2326
- return padZeros('0' + num, minLength)
2473
+ return padZeros(`0${num}`, minLength)
2327
2474
  }
2328
2475
  return num
2329
2476
  }
@@ -2343,26 +2490,26 @@ function padZeros(num, minLength = 6) {
2343
2490
  ;// ./lib/helpers/replacePlaceholders/replacePlaceholders.js
2344
2491
  function replacePlaceholders({ content, mapping }) {
2345
2492
  let isObjectMode = false
2346
-
2493
+
2347
2494
  if (typeof content === 'object' && content !== null) {
2348
2495
  content = JSON.stringify(content)
2349
2496
  isObjectMode = true
2350
2497
  }
2351
2498
 
2352
2499
  // [[ eventRegistration.eventRegistrationCode | 0 ]]
2353
- const regex = /(\[*)\[\[\s*([\w.\-_]+)(?:\s*\|\s*([^:\]\s]+))?(?:\s*:\s*([^\]\s]+))?\s*\]\](\]*)/g;
2500
+ const regex = /(\[*)\[\[\s*([\w.\-]+)(?:\s*\|\s*([^:\]\s]+))?(?:\s*:\s*([^\]\s]+))?\s*\]\](\]*)/g
2354
2501
 
2355
2502
  const result = content.replace(regex, (match, leadingBrackets, path, defaultValue, type, trailingBrackets) => {
2356
-
2357
2503
  // Split the path into parts
2358
2504
  const keys = path.trim().split('.')
2359
-
2505
+
2360
2506
  // Traverse the nested object structure
2361
2507
  let value = mapping
2362
2508
  for (const key of keys) {
2363
2509
  // Handle empty keys (in case of double dots or leading/trailing dots)
2364
- if (!key) continue
2365
-
2510
+ if (!key)
2511
+ continue
2512
+
2366
2513
  value = value?.[key]
2367
2514
  if (value === undefined) {
2368
2515
  break
@@ -2370,10 +2517,12 @@ function replacePlaceholders({ content, mapping }) {
2370
2517
  }
2371
2518
 
2372
2519
  // Apply default if missing
2373
- if (value === undefined) value = defaultValue?.trim();
2374
- if (value === undefined) return isObjectMode ? undefined : match;
2375
-
2376
- value = value !== undefined
2520
+ if (value === undefined)
2521
+ value = defaultValue?.trim()
2522
+ if (value === undefined)
2523
+ return isObjectMode ? undefined : match
2524
+
2525
+ value = value !== undefined
2377
2526
  ? leadingBrackets + value + trailingBrackets
2378
2527
  : match
2379
2528
 
@@ -2394,10 +2543,11 @@ function replacePlaceholders({ content, mapping }) {
2394
2543
  /**
2395
2544
  * Sanitizes input by removing hidden/control characters with customizable whitespace handling.
2396
2545
  * @param {string} input - The string to sanitize.
2397
- * @param {Object} [options] - Configuration options.
2398
- * @param {boolean} [options.normalizeWhitespace=true] - Collapse multiple spaces/tabs into one space.
2399
- * @param {boolean} [options.removeNewlines=false] - If true, replaces newlines with spaces.
2400
- * @param {boolean} [options.trim=true] - If true, trims leading/trailing whitespace.
2546
+ * @param {object} [options] - Configuration options.
2547
+ * @param {boolean} [options.normalizeWhitespace] - Collapse multiple spaces/tabs into one space.
2548
+ * @param {boolean} [options.removeNewlines] - If true, replaces newlines with spaces.
2549
+ * @param {boolean} [options.trim] - If true, trims leading/trailing whitespace.
2550
+ * @param {boolean} [options.debug] - If true, logs debug information about removed characters.
2401
2551
  * @returns {string} The sanitized string.
2402
2552
  */
2403
2553
  function sanitizeText(input, options = {}) {
@@ -2405,7 +2555,8 @@ function sanitizeText(input, options = {}) {
2405
2555
  normalizeWhitespace = true,
2406
2556
  removeNewlines = false,
2407
2557
  trim = true,
2408
- preserveBasicWhitespace = true, // new option to keep tabs, newlines if removeNewlines=false
2558
+ preserveBasicWhitespace = true,
2559
+ debug = false, // new option for debugging
2409
2560
  } = options
2410
2561
 
2411
2562
  if (typeof input !== 'string') {
@@ -2414,27 +2565,106 @@ function sanitizeText(input, options = {}) {
2414
2565
 
2415
2566
  let result = input
2416
2567
 
2568
+ if (debug) {
2569
+ console.log('Original input:', JSON.stringify(input))
2570
+ console.log('Options:', { normalizeWhitespace, removeNewlines, trim, preserveBasicWhitespace })
2571
+ }
2572
+
2417
2573
  // Phase 1: Remove all control characters except basic whitespace if requested
2418
2574
  if (preserveBasicWhitespace && !removeNewlines) {
2419
- // Keep tab (\t), newline (\n), carriage return (\r)
2420
- result = result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2575
+ if (debug) {
2576
+ const before = result
2577
+ const matches = []
2578
+ // Use a replacer function to capture what's being removed
2579
+ result = result.replace(/[\x00-\x08\v\f\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, (match) => {
2580
+ matches.push({
2581
+ char: match,
2582
+ code: match.charCodeAt(0),
2583
+ hex: `0x${match.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`
2584
+ })
2585
+ return ''
2586
+ })
2587
+ if (matches.length > 0) {
2588
+ console.log('Phase 1 (preserve mode) - Removed characters:')
2589
+ matches.forEach((m) => {
2590
+ console.log(` - Character: ${JSON.stringify(m.char)}, Code: ${m.code}, Hex: ${m.hex}`)
2591
+ })
2592
+ console.log(`Removed ${matches.length} control character(s)`)
2593
+ } else {
2594
+ console.log('Phase 1 (preserve mode) - No control characters found')
2595
+ }
2596
+ } else {
2597
+ result = result.replace(/[\x00-\x08\v\f\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2598
+ }
2421
2599
  } else {
2422
- // Remove all control characters including basic whitespace
2423
- result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2600
+ if (debug) {
2601
+ const before = result
2602
+ const matches = []
2603
+ result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, (match) => {
2604
+ matches.push({
2605
+ char: match,
2606
+ code: match.charCodeAt(0),
2607
+ hex: `0x${match.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`
2608
+ })
2609
+ return ''
2610
+ })
2611
+ if (matches.length > 0) {
2612
+ console.log('Phase 1 (full removal mode) - Removed characters:')
2613
+ matches.forEach((m) => {
2614
+ console.log(` - Character: ${JSON.stringify(m.char)}, Code: ${m.code}, Hex: ${m.hex}`)
2615
+ })
2616
+ console.log(`Removed ${matches.length} control character(s)`)
2617
+ } else {
2618
+ console.log('Phase 1 (full removal mode) - No control characters found')
2619
+ }
2620
+ } else {
2621
+ result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2622
+ }
2623
+ }
2624
+
2625
+ if (debug) {
2626
+ console.log('After Phase 1:', JSON.stringify(result))
2424
2627
  }
2425
2628
 
2426
2629
  // Phase 2: Handle whitespace transformations
2427
2630
  if (removeNewlines) {
2428
- result = result.replace(/[\r\n]+/g, ' ') // Convert newlines to spaces
2631
+ if (debug) {
2632
+ const before = result
2633
+ result = result.replace(/[\r\n]+/g, ' ')
2634
+ console.log('Phase 2 - Converted newlines to spaces')
2635
+ } else {
2636
+ result = result.replace(/[\r\n]+/g, ' ')
2637
+ }
2429
2638
  }
2430
2639
 
2431
2640
  if (normalizeWhitespace) {
2432
- result = result.replace(/[ \t]+/g, ' ') // Collapse spaces/tabs to single space
2641
+ if (debug) {
2642
+ const before = result
2643
+ result = result.replace(/[ \t]+/g, ' ')
2644
+ console.log('Phase 2 - Normalized whitespace')
2645
+ } else {
2646
+ result = result.replace(/[ \t]+/g, ' ')
2647
+ }
2648
+ }
2649
+
2650
+ if (debug) {
2651
+ console.log('After Phase 2:', JSON.stringify(result))
2433
2652
  }
2434
2653
 
2435
2654
  // Phase 3: Final trimming
2436
2655
  if (trim) {
2437
- result = result.trim()
2656
+ if (debug) {
2657
+ const before = result
2658
+ result = result.trim()
2659
+ console.log('Phase 3 - Trimmed leading/trailing whitespace')
2660
+ } else {
2661
+ result = result.trim()
2662
+ }
2663
+ }
2664
+
2665
+ if (debug) {
2666
+ console.log('Final result:', JSON.stringify(result))
2667
+ console.log('--- Sanitization complete ---')
2438
2668
  }
2439
2669
 
2440
2670
  return result
@@ -2445,12 +2675,12 @@ function sanitizeText(input, options = {}) {
2445
2675
 
2446
2676
  ;// ./lib/helpers/shuffleArray/shuffleArray.js
2447
2677
  function shuffleArray(array) {
2448
- const arr = [...array];
2449
- for (let i = arr.length - 1; i >= 0; i--) { // Changed `i > 0` to `i >= 0`
2678
+ const arr = [...array]
2679
+ for (let i = arr.length - 1; i >= 0; i--) { // Changed `i > 0` to `i >= 0`
2450
2680
  const j = Math.floor(Math.random() * (i + 1));
2451
- [arr[i], arr[j]] = [arr[j], arr[i]];
2681
+ [arr[i], arr[j]] = [arr[j], arr[i]]
2452
2682
  }
2453
- return arr;
2683
+ return arr
2454
2684
  }
2455
2685
 
2456
2686
  ;// ./lib/helpers/shuffleArray/index.js
@@ -2512,7 +2742,7 @@ function indexCharset(str) {
2512
2742
  for (let i = 0; i < length; i++) {
2513
2743
  char = str[i]
2514
2744
  byCode[i] = char
2515
- byChar[char] = i;
2745
+ byChar[char] = i
2516
2746
  }
2517
2747
  return { byCode, byChar, length }
2518
2748
  }
@@ -2543,7 +2773,7 @@ function randomString({ len = 16, pattern = 'a1' } = {}) {
2543
2773
  str += mark
2544
2774
  }
2545
2775
  const chars = [...str]
2546
- return [...Array(len)].map(i => {
2776
+ return [...new Array(len)].map((i) => {
2547
2777
  return chars[(Math.random() * chars.length) | 0]
2548
2778
  }).join``
2549
2779
  }
@@ -2567,12 +2797,14 @@ function setCode(base = 34) {
2567
2797
  }
2568
2798
 
2569
2799
  function toCamelCase(str) {
2570
- if (!str) return ''
2800
+ if (!str)
2801
+ return ''
2571
2802
  return str
2572
2803
  .trim()
2573
2804
  .split(/\s+/)
2574
2805
  .map((word, index) => {
2575
- if (!word) return ''
2806
+ if (!word)
2807
+ return ''
2576
2808
  if (index === 0) {
2577
2809
  return word.toLowerCase()
2578
2810
  }
@@ -2582,7 +2814,8 @@ function toCamelCase(str) {
2582
2814
  }
2583
2815
 
2584
2816
  function toLowerCase(str) {
2585
- if (!str) return ''
2817
+ if (!str)
2818
+ return ''
2586
2819
  return str
2587
2820
  .trim()
2588
2821
  .toLowerCase()
@@ -2617,7 +2850,7 @@ function trackingPlugin(schema, options) {
2617
2850
  })
2618
2851
 
2619
2852
  // Auto-update hook
2620
- schema.pre('save', function(next) {
2853
+ schema.pre('save', function (next) {
2621
2854
  this.meta.modified = Date.now()
2622
2855
  next()
2623
2856
  })
@@ -2629,9 +2862,9 @@ function trackingPlugin(schema, options) {
2629
2862
  }, {
2630
2863
  name: 'tracking_status_index',
2631
2864
  background: true,
2632
- partialFilterExpression: {
2865
+ partialFilterExpression: {
2633
2866
  'meta.active': true,
2634
- 'meta.deleted': false
2867
+ 'meta.deleted': false
2635
2868
  }
2636
2869
  })
2637
2870
 
@@ -2660,7 +2893,7 @@ function tenantPlugin(schema, options) {
2660
2893
 
2661
2894
  // Add core indexes
2662
2895
  schema.index({
2663
- 'tenantCode': 1
2896
+ tenantCode: 1
2664
2897
  }, {
2665
2898
  name: 'tenant_core_index',
2666
2899
  background: true
@@ -2668,15 +2901,15 @@ function tenantPlugin(schema, options) {
2668
2901
 
2669
2902
  // 1. ENHANCE EXISTING TRACKING INDEXES
2670
2903
  const existingIndexes = schema.indexes()
2671
-
2904
+
2672
2905
  // Check if tracking_status_index exists
2673
- const hasTenantStatusIndex = existingIndexes.some(idx =>
2906
+ const hasTenantStatusIndex = existingIndexes.some((idx) =>
2674
2907
  idx.name === 'tenant_status_index' // Check by name for reliability
2675
2908
  )
2676
2909
 
2677
2910
  if (!hasTenantStatusIndex) {
2678
2911
  schema.index({
2679
- 'tenantCode': 1, // Unique field first
2912
+ tenantCode: 1, // Unique field first
2680
2913
  _type: 1, // Low-cardinality field last
2681
2914
  }, {
2682
2915
  name: 'tenant_status_index',
@@ -2684,7 +2917,7 @@ function tenantPlugin(schema, options) {
2684
2917
  partialFilterExpression: {
2685
2918
  '_type': 'Tenant',
2686
2919
  'meta.active': true,
2687
- 'meta.deleted': false
2920
+ 'meta.deleted': false
2688
2921
  }
2689
2922
  })
2690
2923
  }
@@ -2722,6 +2955,8 @@ function tenantPlugin(schema, options) {
2722
2955
 
2723
2956
 
2724
2957
 
2958
+
2959
+
2725
2960
 
2726
2961
 
2727
2962
 
@@ -2780,10 +3015,11 @@ class AwsStsS3Client {
2780
3015
  }
2781
3016
 
2782
3017
  get isExpired() {
2783
- if (!this.expiration) return true;
2784
- const now = new Date();
2785
- const bufferMs = 1 * 60 * 1000; // 一分钟缓冲
2786
- return now >= new Date(this.expiration.getTime() - bufferMs);
3018
+ if (!this.expiration)
3019
+ return true
3020
+ const now = new Date()
3021
+ const bufferMs = 1 * 60 * 1000 // 一分钟缓冲
3022
+ return now >= new Date(this.expiration.getTime() - bufferMs)
2787
3023
  }
2788
3024
 
2789
3025
  get isValid() {
@@ -2818,7 +3054,7 @@ class AwsStsS3Client {
2818
3054
  throw new Error(`Missing ${component} in AWS awsClientS3 client configuration`)
2819
3055
  }
2820
3056
  }
2821
-
3057
+
2822
3058
  return true
2823
3059
  }
2824
3060
 
@@ -2828,9 +3064,9 @@ class AwsStsS3Client {
2828
3064
  if (!webIdentityToken) {
2829
3065
  throw new Error('getIdToken function returned empty or invalid token')
2830
3066
  }
2831
-
3067
+
2832
3068
  const stsClient = new this.awsClientSts.STSClient({ region: this.region })
2833
-
3069
+
2834
3070
  const stsResponse = await stsClient.send(
2835
3071
  new this.awsClientSts.AssumeRoleWithWebIdentityCommand({
2836
3072
  RoleArn: this.roleArn,
@@ -3137,29 +3373,30 @@ class KeyValueObject {
3137
3373
  }
3138
3374
 
3139
3375
  function _mergeValues(existingValue, newValue) {
3140
- if (existingValue === undefined) return newValue
3141
-
3376
+ if (existingValue === undefined)
3377
+ return newValue
3378
+
3142
3379
  // Handle arrays by concatenating
3143
3380
  if (Array.isArray(existingValue) && Array.isArray(newValue)) {
3144
3381
  return [...new Set([...existingValue, ...newValue])]
3145
3382
  }
3146
-
3383
+
3147
3384
  // Handle objects by merging
3148
- if (typeof existingValue === 'object' && typeof newValue === 'object' &&
3149
- !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3385
+ if (typeof existingValue === 'object' && typeof newValue === 'object'
3386
+ && !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3150
3387
  return { ...existingValue, ...newValue }
3151
3388
  }
3152
-
3389
+
3153
3390
  // // Handle numbers by adding
3154
3391
  // if (typeof existingValue === 'number' && typeof newValue === 'number') {
3155
3392
  // return existingValue
3156
3393
  // }
3157
-
3394
+
3158
3395
  // // Handle strings by concatenating
3159
3396
  // if (typeof existingValue === 'string' && typeof newValue === 'string') {
3160
3397
  // return existingValue
3161
3398
  // }
3162
-
3399
+
3163
3400
  // Default: use the new value
3164
3401
  return newValue
3165
3402
  }
@@ -3219,29 +3456,30 @@ function metadata_isSame(key1, key2) {
3219
3456
  }
3220
3457
 
3221
3458
  function metadata_mergeValues(existingValue, newValue) {
3222
- if (existingValue === undefined) return newValue
3223
-
3459
+ if (existingValue === undefined)
3460
+ return newValue
3461
+
3224
3462
  // Handle arrays by concatenating
3225
3463
  if (Array.isArray(existingValue) && Array.isArray(newValue)) {
3226
3464
  return [...new Set([...existingValue, ...newValue])]
3227
3465
  }
3228
-
3466
+
3229
3467
  // Handle objects by merging
3230
- if (typeof existingValue === 'object' && typeof newValue === 'object' &&
3231
- !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3468
+ if (typeof existingValue === 'object' && typeof newValue === 'object'
3469
+ && !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3232
3470
  return { ...existingValue, ...newValue }
3233
3471
  }
3234
-
3472
+
3235
3473
  // // Handle numbers by adding
3236
3474
  // if (typeof existingValue === 'number' && typeof newValue === 'number') {
3237
3475
  // return existingValue
3238
3476
  // }
3239
-
3477
+
3240
3478
  // // Handle strings by concatenating
3241
3479
  // if (typeof existingValue === 'string' && typeof newValue === 'string') {
3242
3480
  // return existingValue
3243
3481
  // }
3244
-
3482
+
3245
3483
  // Default: use the new value
3246
3484
  return newValue
3247
3485
  }
@@ -3262,14 +3500,14 @@ class TrackedEntity {
3262
3500
  const timestamp = Date.now()
3263
3501
  this.meta = {
3264
3502
  active: options.meta?.active ?? options.active ?? true,
3265
- created: options.meta?.created ?? (options.created
3266
- ? new Date(options.created).getTime()
3267
- : timestamp),
3503
+ created: options.meta?.created ?? (options.created
3504
+ ? new Date(options.created).getTime()
3505
+ : timestamp),
3268
3506
  creator: options.meta?.creator ?? options.creator ?? '',
3269
3507
  deleted: options.meta?.deleted ?? options.deleted ?? false,
3270
- modified: options.meta?.modified ?? (options.modified
3271
- ? new Date(options.modified).getTime()
3272
- : timestamp),
3508
+ modified: options.meta?.modified ?? (options.modified
3509
+ ? new Date(options.modified).getTime()
3510
+ : timestamp),
3273
3511
  owner: options.meta?.owner ?? options.owner ?? '',
3274
3512
  }
3275
3513
 
@@ -3436,7 +3674,6 @@ class PushEnvelope extends TrackedEntity {
3436
3674
  get isValid() {
3437
3675
  return super.isValid && this.data
3438
3676
  }
3439
-
3440
3677
  }
3441
3678
 
3442
3679
  ;// ./lib/models/pushEnvelope/index.js
@@ -3503,6 +3740,191 @@ class QMeta {
3503
3740
 
3504
3741
 
3505
3742
 
3743
+ ;// ./lib/models/status/actionRecord.js
3744
+
3745
+
3746
+ class ActionRecord {
3747
+ constructor(options) {
3748
+ options = options || {}
3749
+
3750
+ const { _Actor } = options._constructor || {}
3751
+ this._Actor = _Actor
3752
+
3753
+ this._actor = options._actor
3754
+
3755
+ this.actorCode = typeof options === 'number' ? null : (options.actorCode || null)
3756
+ this.timestamp = typeof options === 'number' ? options : (options.timestamp || null)
3757
+ }
3758
+
3759
+ static get _classname() {
3760
+ return 'ActionRecord'
3761
+ }
3762
+ static get _superclass() {
3763
+ return 'ActionRecord'
3764
+ }
3765
+ static dummyData() {
3766
+ return {
3767
+ timestamp: (new Date()).valueOf(),
3768
+ }
3769
+ }
3770
+ static init(options = {}) {
3771
+ return init(this, options)
3772
+ }
3773
+
3774
+ get _classname() {
3775
+ return 'ActionRecord'
3776
+ }
3777
+
3778
+ get _superclass() {
3779
+ return 'ActionRecord'
3780
+ }
3781
+
3782
+ get actor() {
3783
+ return this._Actor && typeof this._Actor.init === 'function' ? this._Actor.init(this._actor) : this._actor
3784
+ }
3785
+
3786
+ get isValid() {
3787
+ return !!this.timestamp
3788
+ }
3789
+
3790
+ update(update) {
3791
+ if (typeof update === 'number') {
3792
+ this.timestamp = update
3793
+ return this
3794
+ }
3795
+ Object.keys(update).forEach((key) => {
3796
+ this[key] = update[key]
3797
+ })
3798
+ return this
3799
+ }
3800
+ }
3801
+
3802
+ ;// ./lib/models/status/status.js
3803
+
3804
+
3805
+
3806
+ const notUpdateAllowedProps = [
3807
+ 'created',
3808
+ // 'statusType'
3809
+ ]
3810
+
3811
+ class Status {
3812
+ constructor(options) {
3813
+ options = options || {}
3814
+
3815
+ const { _ActionRecord } = options._constructor || {}
3816
+ this._ActionRecord = _ActionRecord && (_ActionRecord._superclass === ActionRecord._superclass) ? _ActionRecord : ActionRecord
3817
+
3818
+ this.created = this._ActionRecord.init(options.created || { timestamp: (new Date()).valueOf() })
3819
+ // this.statusType = options.statusType || 'Status'
3820
+ }
3821
+
3822
+ static get _classname() {
3823
+ return 'Status'
3824
+ }
3825
+ static get _superclass() {
3826
+ return 'Status'
3827
+ }
3828
+ static dummyData() {
3829
+ return {}
3830
+ }
3831
+ static init(options = {}) {
3832
+ return init(this, options)
3833
+ }
3834
+ static initFromArray(arr = []) {
3835
+ return initFromArray(this, arr)
3836
+ }
3837
+ static initOnlyValidFromArray(arr = []) {
3838
+ return initOnlyValidFromArray(this, arr)
3839
+ }
3840
+
3841
+ get _classname() {
3842
+ return 'Status'
3843
+ }
3844
+ get _superclass() {
3845
+ return 'Status'
3846
+ }
3847
+ get isCreated() {
3848
+ return this.created?.timestamp !== null
3849
+ }
3850
+ get isValid() {
3851
+ return !!this
3852
+ }
3853
+
3854
+ setValue(t, actorCode, key) {
3855
+ const timestamp = t || Date.now()
3856
+ this[key] = this[key] instanceof this._ActionRecord ? this[key].update({ actorCode, timestamp }) : this._ActionRecord.init({ actorCode, timestamp })
3857
+ return this
3858
+ }
3859
+
3860
+ update(update) {
3861
+ Object.keys(update).forEach((key) => {
3862
+ if (!notUpdateAllowedProps.includes(key)) {
3863
+ this[key] = this[key] instanceof this._ActionRecord ? this[key].update(update[key]) : this._ActionRecord.init(update[key])
3864
+ }
3865
+ })
3866
+ return this
3867
+ }
3868
+ }
3869
+
3870
+ ;// ./lib/models/status/statusDocument.js
3871
+
3872
+
3873
+ class StatusDocument extends Status {
3874
+ constructor(options) {
3875
+ options = options || {}
3876
+ super(options)
3877
+
3878
+ this.archived = this._ActionRecord.init(options.archived)
3879
+ this.completed = this._ActionRecord.init(options.completed)
3880
+ this.discarded = this._ActionRecord.init(options.discarded)
3881
+ this.drafted = this._ActionRecord.init(options.drafted)
3882
+ // this.statusType = 'StatusDocument'
3883
+ }
3884
+
3885
+ static get _classname() {
3886
+ return 'StatusDocument'
3887
+ }
3888
+ get _classname() {
3889
+ return 'StatusDocument'
3890
+ }
3891
+ get isArchived() {
3892
+ return this.created?.timestamp !== null
3893
+ }
3894
+ get isCompleted() {
3895
+ return this.completed?.timestamp !== null
3896
+ }
3897
+ get isDiscarded() {
3898
+ return this.discarded?.timestamp !== null
3899
+ }
3900
+ get isDrafted() {
3901
+ return this.drafted?.timestamp !== null
3902
+ }
3903
+ get isValid() {
3904
+ return super.isValid
3905
+ }
3906
+ setArchived(value, actorCode) {
3907
+ // const timestamp = value || Date.now()
3908
+ // this.archived = this.archived instanceof this._ActionRecord ? this.archived.update({ actorCode, timestamp }) : this._ActionRecord.init({ actorCode, timestamp })
3909
+ // return this
3910
+ return this.setValue(value, actorCode, 'archived')
3911
+ }
3912
+ setCompleted(value, actorCode) {
3913
+ return this.setValue(value, actorCode, 'completed')
3914
+ }
3915
+ setDiscarded(value, actorCode) {
3916
+ return this.setValue(value, actorCode, 'discarded')
3917
+ }
3918
+ setDrafted(value, actorCode) {
3919
+ return this.setValue(value, actorCode, 'drafted')
3920
+ }
3921
+ }
3922
+
3923
+ ;// ./lib/models/status/index.js
3924
+
3925
+
3926
+
3927
+
3506
3928
  ;// ./lib/models/tenantAwareEntity/tenantAwareEntity.js
3507
3929
 
3508
3930
 
@@ -3643,6 +4065,7 @@ function _makeSetCode(fieldName, options) {
3643
4065
 
3644
4066
 
3645
4067
 
4068
+
3646
4069
  ;// ./lib/index.js
3647
4070
 
3648
4071
 
@@ -3650,4 +4073,4 @@ function _makeSetCode(fieldName, options) {
3650
4073
  ;// ./index.js
3651
4074
 
3652
4075
 
3653
- export { ApiResponse, AwsStsS3Client, KeyValueObject, Metadata, PushEnvelope, QMeta, Repo, Service, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, calculateAge, changeCreatorOwner, concatStringByArray, convertString, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, groupArrayByKey, init, initFromArray, initOnlyValidFromArray, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pReduce, padZeros, replacePlaceholders, sanitizeText, shuffleArray, stringFormatter, stringHelper, tenantPlugin, trackingPlugin };
4076
+ export { ActionRecord, ApiResponse, AwsStsS3Client, KeyValueObject, Metadata, PushEnvelope, QMeta, Repo, Service, Status, StatusDocument, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, calculateAge, changeCreatorOwner, concatStringByArray, convertString, detectControlCharacters, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, groupArrayByKey, init, initFromArray, initOnlyValidFromArray, isConvertibleToNumber, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pReduce, padZeros, printControlCharReport, replacePlaceholders, sanitizeText, shuffleArray, stringFormatter, stringHelper, tenantPlugin, trackingPlugin };