@questwork/q-utilities 0.1.17 → 0.1.18

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.
@@ -1,3537 +0,0 @@
1
-
2
- ;// ./lib/helpers/authorize/authorize.js
3
- function authorize({ allowCoordinator, allowOwner, query = {}, required, user }) {
4
- if (!user) {
5
- throw new Error('Require login.')
6
- }
7
- if (!user.permission) {
8
- // throw new Error('You do not have any permission.')
9
- }
10
- const scopes = user.permission.getScopes(required || {}) || []
11
- if (!scopes || scopes.length === 0) {
12
- // throw new Error('You are not allowed in this scope.')
13
- }
14
- if (!scopes.includes('*')) {
15
- query.tenantCode = user.tenantCode
16
- }
17
- if (!scopes.includes('TENANT')) {
18
- query.eventShortCode = user.eventShortCode
19
- }
20
- // if (!scopes.includes('EVENT')) {
21
- // query.eventRegistrationCode = user.eventRegistrationCode
22
- // }
23
- if (allowCoordinator) {
24
- if (query.registrationGroupCode && user.myManagedRegistrationGroupCodes.includes(query.registrationGroupCode)) {
25
- query.__ALLOW_COORDINATOR = true
26
- } else {
27
- if (!scopes.includes('EVENT')) {
28
- query.eventRegistrationCode = user.eventRegistrationCode
29
- }
30
- }
31
- } else {
32
- if (!scopes.includes('EVENT')) {
33
- query.eventRegistrationCode = user.eventRegistrationCode
34
- }
35
- }
36
- if (allowOwner) {
37
- query.__ALLOW_OWNER = true
38
- }
39
- // not good, just use it as example
40
- if (user.hasExcludedFields) {
41
- query.__EXCLUDED_FIELDS = user.getExcludedFields(required)
42
- }
43
- query.__LOGIN_SUBJECT_CODE = user.loginSubjectCode
44
- return query
45
- }
46
-
47
- ;// ./lib/helpers/authorize/index.js
48
-
49
-
50
- ;// ./lib/helpers/changeCreatorOwner/changeCreatorOwner.js
51
- function changeCreatorOwner(that, { source, target }) {
52
- if (that.meta) {
53
- if (!that.meta.creator || that.meta.creator === source.getId()) {
54
- that.meta.creator = target.getId()
55
- }
56
- if (!that.meta.owner || that.meta.owner === source.getId()) {
57
- that.meta.owner = target.getId()
58
- }
59
- } else {
60
- if (!that.creator || that.creator === source.getId()) {
61
- that.creator = target.getId()
62
- }
63
- if (!that.owner || that.owner === source.getId()) {
64
- that.owner = target.getId()
65
- }
66
- }
67
- return that
68
- }
69
-
70
- ;// ./lib/helpers/changeCreatorOwner/index.js
71
-
72
-
73
- ;// ./lib/helpers/getValidation/getValidation.js
74
- function getValidation(rule, data, getDataByKey, KeyValueObject) {
75
- if (!rule) {
76
- return true
77
- }
78
- if (typeof getDataByKey !== 'function' || (KeyValueObject && typeof KeyValueObject !== 'function')) {
79
- return false
80
- }
81
- const { key = '', value, placeholder, keyValuePath = '' } = rule
82
- const [valueAttribute] = Object.keys(value)
83
-
84
- if (!key && typeof placeholder === 'undefined') {
85
- switch (valueAttribute) {
86
- case '$and': {
87
- return value['$and'].reduce((acc, item) => (acc && getValidation(item, data, getDataByKey, KeyValueObject)), true)
88
- }
89
- case '$or': {
90
- return value['$or'].reduce((acc, item) => (acc || getValidation(item, data, getDataByKey, KeyValueObject)), false)
91
- }
92
- default:
93
- return false
94
- }
95
- }
96
- let rowValue = typeof placeholder === 'undefined' ? getDataByKey(key, data) : placeholder
97
-
98
- // if KeyValue object
99
- if (keyValuePath) {
100
- const rowValueData = KeyValueObject.toObject(rowValue)
101
- rowValue = getDataByKey(keyValuePath, rowValueData)
102
- }
103
-
104
- switch (valueAttribute) {
105
- case '$empty': {
106
- const isEmpty = rowValue === null || rowValue === undefined
107
- return isEmpty === value['$empty']
108
- }
109
- case '$eq': {
110
- return rowValue === value['$eq']
111
- }
112
- case '$gt': {
113
- return rowValue > value['$gt']
114
- }
115
- case '$gte': {
116
- return rowValue >= value['$gte']
117
- }
118
- case '$hasOverlap': {
119
- return _hasOverlap(rowValue, value['$hasOverlap'])
120
- }
121
- case '$lt': {
122
- return rowValue < value['$lt']
123
- }
124
- case '$lte': {
125
- return rowValue <= value['$lte']
126
- }
127
- case '$in': {
128
- if (Array.isArray(rowValue)) {
129
- return !!rowValue.find((e) => (value['$in'].includes(e)))
130
- }
131
- if (typeof rowValue !== 'object') {
132
- return !!value['$in'].includes(rowValue)
133
- }
134
- return false
135
- }
136
- case '$inValue': {
137
- const result = getDataByKey(value['$inValue'], data)
138
- const _value = Array.isArray(result) ? result : []
139
- if (Array.isArray(rowValue)) {
140
- return !!rowValue.find((e) => (_value.includes(e)))
141
- }
142
- if (typeof rowValue === 'string') {
143
- return !!_value.includes(rowValue)
144
- }
145
- return false
146
- }
147
- case '$ne': {
148
- return rowValue !== value['$ne']
149
- }
150
- case '$notIn': {
151
- if (Array.isArray(rowValue)) {
152
- return !rowValue.find((e) => (value['$notIn'].includes(e)))
153
- }
154
- if (typeof rowValue !== 'object') {
155
- return !value['$notIn'].includes(rowValue)
156
- }
157
- return false
158
- }
159
- case '$intervalTimeGt': {
160
- const now = new Date().getTime()
161
- const timestamp = new Date(rowValue).getTime()
162
- return (now - timestamp) > value['$intervalTimeGt']
163
- }
164
- case '$intervalTimeLt': {
165
- const now = new Date().getTime()
166
- const timestamp = new Date(rowValue).getTime()
167
- return (now - timestamp) < value['$intervalTimeLt']
168
- }
169
- case '$isToday': {
170
- const currentDate = new Date()
171
- const start = currentDate.setHours(0,0,0,0)
172
- const end = currentDate.setHours(23,59,59,59)
173
- const dateValue = new Date(rowValue).getTime()
174
- return (start <= dateValue && end >= dateValue) === value['$isToday']
175
- }
176
- case '$notInValue': {
177
- const result = getDataByKey(value['$notInValue'], data)
178
- const _value = Array.isArray(result) ? result : []
179
- if (Array.isArray(rowValue)) {
180
- return !rowValue.find((e) => (_value.includes(e)))
181
- }
182
- if (typeof rowValue !== 'object') {
183
- return !_value.includes(rowValue)
184
- }
185
- return false
186
- }
187
- case '$range': {
188
- const [min, max] = value['$range']
189
- if (typeof min === 'number' && typeof max === 'number' && rowValue >= min && rowValue <= max) {
190
- return true
191
- }
192
- return false
193
- }
194
- default:
195
- return false
196
- }
197
-
198
- }
199
-
200
- function _hasOverlap(item1, item2) {
201
- let arr1 = item1
202
- let arr2 = item2
203
- if (typeof arr1 === 'string') {
204
- arr1 = arr1.split(',')
205
- }
206
- if (typeof arr2 === 'string') {
207
- arr2 = arr2.split(',')
208
- }
209
- const set1 = new Set(arr1)
210
- return arr2.find((i) => (set1.has(i)))
211
- }
212
-
213
- /* harmony default export */ const getValidation_getValidation = ({
214
- getValidation
215
- });
216
-
217
-
218
- ;// ./lib/helpers/getValidation/index.js
219
-
220
-
221
- ;// ./lib/helpers/formatDate/formatDate.js
222
-
223
- function formatDate(date, format) {
224
- const _date = date && date instanceof Date ? date : new Date(date)
225
- const dayMapChi = ['日','一','二','三','四','五','六']
226
- const dayMapEng = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']
227
- const dayMapEngShort = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
228
- const _format = format || 'YYYY/MM/DD hh:mm'
229
- const e = _date.getDay()
230
- const ee = dayMapEngShort[e]
231
- const eee = dayMapChi[e]
232
- const eeee = dayMapEng[e]
233
- const y = _date.getFullYear()
234
- const m = _date.getMonth() + 1
235
- const d = _date.getDate()
236
- const h = _date.getHours()
237
- const mm = _date.getMinutes()
238
- const s = _date.getSeconds()
239
-
240
- return _format.replace('YYYY', y)
241
- .replace('MM', padding(m))
242
- .replace('MM', padding(m))
243
- .replace('DD', padding(d))
244
- .replace('hh', padding(h))
245
- .replace('mm', padding(mm))
246
- .replace('ss', padding(s))
247
- .replace('M', m)
248
- .replace('D', d)
249
- .replace('h', h)
250
- .replace('m', mm)
251
- .replace('s', s)
252
- .replace('EEEE', padding(eeee))
253
- .replace('EEE', padding(eee))
254
- .replace('EE', padding(ee))
255
- .replace('E', padding(e))
256
- }
257
-
258
- function padding(m) {
259
- return m < 10 ? `0${m}` : m
260
- }
261
-
262
-
263
- /* harmony default export */ const formatDate_formatDate = ({
264
- formatDate
265
- });
266
-
267
-
268
-
269
- ;// ./lib/helpers/formatDate/index.js
270
-
271
-
272
- ;// ./lib/helpers/getValueByKeys/getValueByKeys.js
273
- // keys can be array or object or string
274
- function getValueByKeys_getValueByKeys(keys, data) {
275
- let _keys = keys
276
- let _data = data
277
- if (typeof keys === 'string') {
278
- _keys = _keys.split('.')
279
- }
280
- if (!Array.isArray(keys) && typeof keys === 'object') {
281
- const { keys: keyArr, obj } = keys
282
- _keys = keyArr
283
- _data = obj
284
- }
285
- if (_keys.length === 0) {
286
- return _data
287
- }
288
- const firstKey = _keys.shift()
289
- if (_data && Object.prototype.hasOwnProperty.call(_data, firstKey)) {
290
- return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
291
- }
292
- if (_data && firstKey) {
293
- if (_keys.length > 0) {
294
- return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
295
- }
296
- return _data[firstKey]
297
- }
298
- return _data
299
-
300
- }
301
- /* harmony default export */ const getValueByKeys = ({
302
- getValueByKeys: getValueByKeys_getValueByKeys
303
- });
304
-
305
-
306
- ;// ./lib/helpers/getValueByKeys/index.js
307
-
308
-
309
- ;// ./lib/models/templateCompiler/templateCompilerException.js
310
- const TEMPLATE_COMPILER_EXCEPTION_TYPE = {
311
- argumentEmptyException: 'Argument is empty',
312
- argumentFormatException: 'Incorrect number or format of argument',
313
- invalidFuntionException: 'Function Name is invalid',
314
- invalidRegExpException: 'Invalid regular expression',
315
- isNotAFunctionException: 'Is not a function',
316
- notExistException: 'Key does not exist',
317
- resultEmptyException: 'Result is empty',
318
- resultMoreThanOneException: 'More than one result'
319
- }
320
-
321
- class TemplateCompilerException extends Error {
322
- constructor(message) {
323
- super(message)
324
- this.message = message
325
- }
326
- }
327
-
328
-
329
-
330
- ;// ./lib/models/templateCompiler/constants.js
331
- const _EMPTY = '_EMPTY'
332
- const _FN_NAMES = [
333
- 'concatIf',
334
- 'divide',
335
- 'eq',
336
- 'exec',
337
- 'filterAll',
338
- 'filterOne',
339
- 'formatDate',
340
- 'get',
341
- 'gt',
342
- 'gte',
343
- 'isEmpty',
344
- 'isNotEmpty',
345
- 'join',
346
- 'lt',
347
- 'lte',
348
- 'map',
349
- 'neq',
350
- 'removeHtml',
351
- 'toLowerCase',
352
- 'toUpperCase',
353
- ]
354
- const _HIDE = '_HIDE'
355
- const _NOT_EMPTY = '_NOT_EMPTY'
356
- const _SELF = '_SELF'
357
- const TAGS_EJS = ['<%=', '%>']
358
- const TAGS_HANDLEBAR = ['{{', '}}']
359
-
360
-
361
-
362
- ;// ./lib/models/templateCompiler/helpers/_concatIf.js
363
-
364
-
365
-
366
-
367
- function _concatIf(data, args) {
368
- if (typeof data !== 'string') {
369
- throw new TemplateCompilerException(`_concatIf: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: the data must be string :${data.join(', ')}`)
370
- }
371
- if (args.length !== 3) {
372
- throw new TemplateCompilerException(`_concatIf: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
373
- }
374
- if (data === null || (typeof data === 'undefined')) {
375
- return null
376
- }
377
- const [condition, success, failover] = args
378
- const validConditions = [_EMPTY, _NOT_EMPTY]
379
- if (validConditions.includes(condition) || success.length !== 2) {
380
- throw new TemplateCompilerException(`concatIf: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentEmptyException}: ${condition}, ${success}`)
381
- }
382
- if (data === '' && failover.includes(_HIDE)) {
383
- return ''
384
- }
385
- if (data !== '' && (data !== null || data !== undefined) && failover.includes(_HIDE)) {
386
- return `${success[0]}${data}${success[success.length - 1]}`
387
- }
388
- return failover
389
- }
390
-
391
-
392
-
393
- ;// ./lib/models/templateCompiler/helpers/_divide.js
394
- function _divide(value, divisor) {
395
- try {
396
- if (Number.isNaN(value)) {
397
- return value
398
- }
399
- return (value / divisor)
400
- } catch (e) {
401
- throw e
402
- }
403
- }
404
-
405
-
406
-
407
- ;// ./lib/models/templateCompiler/helpers/_eq.js
408
-
409
-
410
-
411
-
412
- function _eq(data, args) {
413
- if (args.length !== 3) {
414
- throw new TemplateCompilerException(`eq: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
415
- }
416
- if (data === null || (typeof data === 'undefined')) {
417
- return null
418
- }
419
- if (args.includes(_SELF)) {
420
- args = args.map((arg) => {
421
- return (arg === _SELF) ? data : arg
422
- })
423
- }
424
- const expected = args[0]
425
- return data === expected ? args[1] : args[2]
426
- }
427
-
428
-
429
-
430
- ;// ./lib/models/templateCompiler/helpers/_exec.js
431
- function _exec(data, args) {
432
- try {
433
- const [methodName, ..._args] = args
434
- return data[methodName](..._args)
435
- } catch (e) {
436
- throw e
437
- }
438
- }
439
-
440
-
441
-
442
- ;// ./lib/models/templateCompiler/helpers/_filterAll.js
443
-
444
-
445
-
446
- // const DELIMITER = '~~~'
447
-
448
- function _filterAll(data, args) {
449
- try {
450
- if (!Array.isArray(args) || args.length === 0) {
451
- throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentEmptyException)
452
- }
453
- if (!Array.isArray(data) || data.length === 0) {
454
- return []
455
- }
456
- if (typeof data[0] === 'object') {
457
- return _existObject(data, args)
458
- }
459
- if (typeof data[0] === 'string' || typeof data[0] === 'number') {
460
- return _exist(data, args)
461
- }
462
- return []
463
- } catch (e) {
464
- throw e
465
- }
466
- }
467
-
468
- function _exist(data, args) {
469
- const _args = args.flat()
470
- return data.filter((e) => _args.some((arg) => _performOperation(arg, e)))
471
- }
472
-
473
- function _existObject(data, args) {
474
- if (args.length === 1) {
475
- const arg = args[0]
476
- return data.filter((e) => {
477
- if (arg.includes('.')) {
478
- return getValueByKeys_getValueByKeys(arg.split('.'), e)
479
- }
480
- return Object.prototype.hasOwnProperty.call(e, arg)
481
- })
482
- }
483
-
484
- if (args.length > 2) {
485
- let res = data
486
- for (let i = 0; i < args.length; i += 2) {
487
- const group = [args[i], args[i + 1]]
488
- res = _existObject(res, group)
489
- }
490
- return res
491
- }
492
-
493
- const [key, ..._argsArr] = args
494
- const _args = _argsArr.flat()
495
- return data.filter((e) => {
496
- const value = key.includes('.') ? getValueByKeys_getValueByKeys(key.split('.'), e) : e[key]
497
- return _args.some((arg) => _performOperation(arg, value))
498
- })
499
- }
500
-
501
- function _performOperation(arg, value) {
502
- // the arg is undefined
503
- if (arg === undefined && value === undefined) return true
504
-
505
- // the arg is null
506
- if (arg === null && value === null) return true
507
-
508
- // the arg is boolean
509
- if (typeof arg === 'boolean') {
510
- return arg === value
511
- }
512
-
513
- // the arg is blank or *: Blank => Empty, * => Not Empty
514
- if (arg === '' || arg === '*') {
515
- // null and undefined are not included in either case
516
- if (value === null || value === undefined) {
517
- return false
518
- }
519
- if (typeof value === 'string') {
520
- return arg === '' ? value === '' : value !== ''
521
- }
522
- if (Array.isArray(value)) {
523
- return arg === '' ? value.length === 0 : value.length !== 0
524
- }
525
- return arg !== ''
526
- }
527
-
528
- // the arg is alphabetic or number
529
- if (_isPureStringOrNumber(arg)) {
530
- return arg === value
531
- }
532
-
533
- // the arg is array of [] or [*]: [] => Empty, [*] => Not Empty
534
- if (arg.startsWith('[') && arg.endsWith(']')) {
535
- if (arg === '[]') {
536
- return Array.isArray(value) && value.length === 0
537
- }
538
- if (arg === '[*]') {
539
- return Array.isArray(value) && value.length !== 0
540
- }
541
- return false
542
- }
543
-
544
- // the arg is 'operator + string | number'
545
- const { operator, value: argValue } = _splitOperator(arg)
546
- if (!operator || (argValue !== 0 && !argValue)) {
547
- return false
548
- }
549
- switch (operator) {
550
- case '>':
551
- return value > argValue
552
- case '<':
553
- return value < argValue
554
- case '!=':
555
- return value !== argValue
556
- case '>=':
557
- return value >= argValue
558
- case '<=':
559
- return value <= argValue
560
- default:
561
- return false
562
- }
563
- }
564
-
565
- function _isPureStringOrNumber(input) {
566
- if (typeof input === 'string') {
567
- if (input.startsWith('[') && input.endsWith(']')) {
568
- return false
569
- }
570
- if (/!=|>=|<=|>|</.test(input)) {
571
- return false
572
- }
573
- return true
574
- }
575
- return !Number.isNaN(input)
576
- }
577
-
578
- function _splitOperator(str) {
579
- const operators = ['!=', '>=', '<=', '>', '<']
580
-
581
- const matchedOp = operators.find((op) => str.startsWith(op))
582
- if (!matchedOp) return { operator: null, value: null }
583
-
584
- const remaining = str.slice(matchedOp.length)
585
-
586
- // '>Primary' or '<Primary' is invalid
587
- if (/^[a-zA-Z]*$/.test(remaining) && matchedOp !== '!=') {
588
- return { operator: null, value: null }
589
- }
590
-
591
- // if it is a number it is converted to a number
592
- const value = (!Number.isNaN(parseFloat(remaining)) && !Number.isNaN(remaining)) ? Number(remaining) : remaining
593
-
594
- return {
595
- operator: matchedOp,
596
- value
597
- }
598
- }
599
-
600
-
601
-
602
- ;// ./lib/models/templateCompiler/helpers/_filterOne.js
603
-
604
-
605
-
606
- function _filterOne(data, args) {
607
- try {
608
- const list = _filterAll(data, args)
609
- if (list.length === 1) {
610
- return list[0]
611
- }
612
- if (list.length === 0) {
613
- return null
614
- }
615
- throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.resultMoreThanOneException)
616
- } catch (e) {
617
- throw e
618
- }
619
- }
620
-
621
-
622
-
623
- ;// ./lib/models/templateCompiler/helpers/_formatDate.js
624
-
625
-
626
- function _formatDate(timestamp, format) {
627
- if (format.length === 0) {
628
- throw new TemplateCompilerException(`_formateDate: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: format parts must be not empty array`)
629
- }
630
-
631
- if (timestamp === null || timestamp === undefined) {
632
- return null
633
- }
634
-
635
- const date = new Date(timestamp)
636
-
637
- const partsMap = {
638
- yyyy: String(date.getFullYear()),
639
- mm: String(date.getMonth() + 1).padStart(2, '0'),
640
- dd: String(date.getDate()).padStart(2, '0')
641
- }
642
-
643
- // Check for invalid format tokens
644
- const validTokens = ['yyyy', 'mm', 'dd']
645
- const invalidTokens = format.filter((part) => part.length > 1 && !validTokens.includes(part))
646
-
647
- if (invalidTokens.length > 0) {
648
- throw new TemplateCompilerException(
649
- `_formateDate: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: the format type is not valid: ${format.join(', ')}`
650
- )
651
- }
652
-
653
- // Build the formatted string using reduce
654
- return format.reduce((result, part) => result + (partsMap[part] || part), '')
655
- }
656
-
657
-
658
-
659
- ;// ./lib/models/templateCompiler/helpers/_get.js
660
-
661
-
662
- function _get(data, key, failover = null) {
663
- try {
664
- if (key === null || (typeof key === 'undefined') || key === '') {
665
- throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentEmptyException)
666
- }
667
- if (data === null) {
668
- return null
669
- }
670
- if (key.includes('.')) {
671
- const parts = key.split('.')
672
- if (parts.length > 1) {
673
- const first = parts.shift()
674
- const remainingKey = parts.join('.')
675
- if (typeof data[first] !== 'undefined') {
676
- return _get(data[first], remainingKey, failover)
677
- }
678
- return _handleFailover(key, failover)
679
- }
680
- }
681
- if (typeof data[key] !== 'undefined') {
682
- return data[key]
683
- }
684
- return _handleFailover(key, failover)
685
- } catch (e) {
686
- throw e
687
- }
688
- }
689
-
690
- function _handleFailover(key, failover) {
691
- if (failover !== null) {
692
- return failover
693
- }
694
- return null
695
- // throw new TemplateCompilerException(`Key "${key}" does not exist and no failover`)
696
- }
697
-
698
-
699
-
700
- ;// ./lib/models/templateCompiler/helpers/_gt.js
701
-
702
-
703
-
704
-
705
- function _gt(data, args) {
706
- if (args.length !== 3) {
707
- throw new TemplateCompilerException(`_gt: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
708
- }
709
- if (data === null || (typeof data === 'undefined')) {
710
- return null
711
- }
712
- if (args.includes(_SELF)) {
713
- args = args.map((arg) => {
714
- return (arg === _SELF) ? data : arg
715
- })
716
- }
717
- const expected = args[0]
718
- return data > expected ? args[1] : args[2]
719
- }
720
-
721
-
722
-
723
- ;// ./lib/models/templateCompiler/helpers/_gte.js
724
-
725
-
726
-
727
-
728
- function _gte(data, args) {
729
- if (args.length !== 3) {
730
- throw new TemplateCompilerException(`_gte: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
731
- }
732
- if (data === null || (typeof data === 'undefined')) {
733
- return null
734
- }
735
- if (args.includes(_SELF)) {
736
- args = args.map((arg) => {
737
- return (arg === _SELF) ? data : arg
738
- })
739
- }
740
- const expected = args[0]
741
- return data >= expected ? args[1] : args[2]
742
- }
743
-
744
-
745
-
746
- ;// ./lib/models/templateCompiler/helpers/_isEmpty.js
747
-
748
-
749
-
750
-
751
- function _isEmpty(data, args) {
752
- if (args.length !== 2) {
753
- throw new TemplateCompilerException(`_isEmpty: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
754
- }
755
- // if (data === null || (typeof data === 'undefined')) {
756
- // return null
757
- // }
758
- if (args.includes(_SELF)) {
759
- args = args.map((arg) => {
760
- return (arg === _SELF) ? data : arg
761
- })
762
- }
763
- if (data !== null && typeof data === 'object' && Object.keys(data).length === 0) {
764
- return args[0]
765
- }
766
- return (data === '' || data === null || data === undefined || data.length === 0) ? args[0] : args[1]
767
- }
768
-
769
-
770
-
771
- ;// ./lib/models/templateCompiler/helpers/_isNotEmpty.js
772
-
773
-
774
-
775
-
776
- function _isNotEmpty(data, args) {
777
- if (args.length !== 2) {
778
- throw new TemplateCompilerException(`_isNotEmpty: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
779
- }
780
- // if (data === null || (typeof data === 'undefined')) {
781
- // return null
782
- // }
783
- if (args.includes(_SELF)) {
784
- args = args.map((arg) => {
785
- return (arg === _SELF) ? data : arg
786
- })
787
- }
788
- if (data !== null && typeof data === 'object' && Object.keys(data).length === 0) {
789
- return args[1]
790
- }
791
- if (Array.isArray(data) && data.length === 0) {
792
- return args[1]
793
- }
794
- if (typeof data === 'string' && data === '') {
795
- return args[1]
796
- }
797
- if (data === null || data === undefined) {
798
- return args[1]
799
- }
800
- return args[0]
801
- }
802
-
803
-
804
- ;// ./lib/models/templateCompiler/helpers/_join.js
805
- function _join(data, delimiter) {
806
- try {
807
- if (data.length === 0) return ''
808
- if (data.length === 1) return _stringifyObject(data[0])
809
- return data.map((item) => _stringifyObject(item)).join(delimiter)
810
- } catch (e) {
811
- throw e
812
- }
813
- }
814
-
815
- function _stringifyObject(obj) {
816
- return JSON.stringify(obj).replace(/"([^"]+)":/g, '$1: ').replace(/"([^"]+)"/g, '$1').replace(/,/g, ', ')
817
- }
818
-
819
-
820
-
821
- ;// ./lib/models/templateCompiler/helpers/_lt.js
822
-
823
-
824
-
825
-
826
- function _lt(data, args) {
827
- if (args.length !== 3) {
828
- throw new TemplateCompilerException(`_lt: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
829
- }
830
- if (data === null || (typeof data === 'undefined')) {
831
- return null
832
- }
833
- if (args.includes(_SELF)) {
834
- args = args.map((arg) => {
835
- return (arg === _SELF) ? data : arg
836
- })
837
- }
838
- const expected = args[0]
839
- return data < expected ? args[1] : args[2]
840
- }
841
-
842
-
843
-
844
- ;// ./lib/models/templateCompiler/helpers/_lte.js
845
-
846
-
847
-
848
-
849
- function _lte(data, args) {
850
- if (args.length !== 3) {
851
- throw new TemplateCompilerException(`_lte: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
852
- }
853
- if (data === null || (typeof data === 'undefined')) {
854
- return null
855
- }
856
- if (args.includes(_SELF)) {
857
- args = args.map((arg) => {
858
- return (arg === _SELF) ? data : arg
859
- })
860
- }
861
- const expected = args[0]
862
- return data <= expected ? args[1] : args[2]
863
- }
864
-
865
-
866
-
867
- ;// ./lib/models/templateCompiler/helpers/_map.js
868
-
869
-
870
-
871
- function _map(data, args) {
872
- try {
873
- if (args.length === 0) {
874
- throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentEmptyException)
875
- }
876
- if (data === null || (typeof data === 'undefined')) {
877
- return null
878
- }
879
-
880
- const result = data.reduce((acc, item) => {
881
- if (args.length === 1 && Array.isArray(args[0])) {
882
- args = args[0]
883
- acc.hasFormat = true
884
- }
885
- const list = args.map((key) => {
886
- if (key.includes('.')) {
887
- const parts = key.split('.')
888
- const first = parts[0]
889
- parts.shift()
890
- const remainingKey = parts.join('.')
891
- return _get(item[first], remainingKey)
892
- }
893
- if (typeof item[key] !== 'undefined') {
894
- return item[key]
895
- }
896
- return null
897
- })
898
- if (acc.hasFormat) {
899
- acc.content.push(list)
900
- } else {
901
- acc.content = acc.content.concat(list)
902
- }
903
- return acc
904
- }, {
905
- content: [],
906
- hasFormat: false
907
- })
908
- return result.content
909
- } catch (e) {
910
- throw e
911
- }
912
- }
913
-
914
-
915
-
916
- ;// ./lib/models/templateCompiler/helpers/_neq.js
917
-
918
-
919
-
920
-
921
- function _neq(data, args) {
922
- if (args.length !== 3) {
923
- throw new TemplateCompilerException(`_neq: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
924
- }
925
- if (data === null || (typeof data === 'undefined')) {
926
- return null
927
- }
928
- if (args.includes(_SELF)) {
929
- args = args.map((arg) => {
930
- return (arg === _SELF) ? data : arg
931
- })
932
- }
933
- const expected = args[0]
934
- return data !== expected ? args[1] : args[2]
935
- }
936
-
937
-
938
-
939
- ;// ./lib/models/templateCompiler/helpers/_removeHtml.js
940
-
941
-
942
- function _removeHtml(html, args) {
943
- if (html === null || html === undefined) {
944
- return null
945
- }
946
- if (!Array.isArray(args)) {
947
- throw new TemplateCompilerException(`_removeHtml: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: args parts must be array`)
948
- }
949
-
950
- return _htmlToPlainText(html, args[0])
951
- }
952
-
953
- function _htmlToPlainText(html, delimiter = '\n') {
954
- if (typeof delimiter !== 'string') {
955
- delimiter = '\n'; // Fallback to default if not a string
956
- }
957
-
958
- // First decode HTML entities and normalize whitespace
959
- const decodedHtml = html
960
- .replace(/&nbsp;/g, ' ')
961
- .replace(/\s+/g, ' '); // Collapse all whitespace to single spaces
962
-
963
- // Process HTML tags
964
- let text = decodedHtml
965
- // Replace block tags with temporary marker (~~~)
966
- .replace(/<\/?(p|div|h[1-6]|ul|ol|li|pre|section|article|table|tr|td|th)(\s[^>]*)?>/gi, '~~~')
967
- // Replace <br> tags with temporary marker (~~~)
968
- .replace(/<br\s*\/?>/gi, '~~~')
969
- // Remove all other tags
970
- .replace(/<[^>]+>/g, '')
971
- // Convert markers to specified delimiter
972
- .replace(/~~~+/g, delimiter)
973
- // Trim and clean whitespace
974
- .trim();
975
-
976
- // Special handling for empty delimiter
977
- if (delimiter === '') {
978
- // Collapse all whitespace to single space
979
- text = text.replace(/\s+/g, ' ');
980
- } else {
981
-
982
-
983
- // Collapse multiple delimiters to single
984
- text = text.replace(new RegExp(`${escapeRegExp(delimiter)}+`, 'g'), delimiter);
985
- // Remove leading/trailing delimiters
986
- text = text.replace(new RegExp(`^${escapeRegExp(delimiter)}|${escapeRegExp(delimiter)}$`, 'g'), '');
987
- }
988
-
989
- return text;
990
- }
991
-
992
-
993
- function escapeRegExp(string) {
994
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
995
- }
996
-
997
-
998
-
999
- ;// ./lib/models/templateCompiler/helpers/_toLowerCase.js
1000
-
1001
-
1002
- function _toLowerCase(data, args) {
1003
- if (args !== undefined) {
1004
- throw new TemplateCompilerException(`_toLowerCase: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: ${args.join(', ')}`)
1005
- }
1006
- if (data === null || (typeof data === 'undefined') || typeof data !== 'string') {
1007
- return null
1008
- }
1009
- return String(data).toLowerCase()
1010
- }
1011
-
1012
-
1013
-
1014
- ;// ./lib/models/templateCompiler/helpers/_toUpperCase.js
1015
-
1016
-
1017
- function _toUpperCase(data, args) {
1018
- if (typeof data !== 'string') {
1019
- throw new TemplateCompilerException(`_toUpperCase: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: the data must be string: ${data}`)
1020
- }
1021
- if (args !== undefined) {
1022
- throw new TemplateCompilerException(`_toUpperCase: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: the argument must be empty: ${args.join(', ')}`)
1023
- }
1024
- if (data === null || (typeof data === 'undefined') || typeof data !== 'string') {
1025
- return null
1026
- }
1027
- return String(data).toUpperCase()
1028
- }
1029
-
1030
-
1031
-
1032
- ;// ./lib/models/templateCompiler/helpers/index.js
1033
-
1034
-
1035
-
1036
-
1037
-
1038
-
1039
-
1040
-
1041
-
1042
-
1043
-
1044
-
1045
-
1046
-
1047
-
1048
-
1049
-
1050
-
1051
-
1052
-
1053
-
1054
-
1055
-
1056
- ;// ./lib/models/templateCompiler/templateCompiler.js
1057
-
1058
-
1059
-
1060
-
1061
- class TemplateCompiler {
1062
- constructor(data) {
1063
- this.data = data
1064
- }
1065
- static init(options) {
1066
- return new this(options)
1067
- }
1068
- static initFromArray(arr = []) {
1069
- if (Array.isArray(arr)) {
1070
- return arr.map((a) => this.init(a))
1071
- }
1072
- return []
1073
- }
1074
- static initOnlyValidFromArray(arr = []) {
1075
- return this.initFromArray(arr).filter((i) => i)
1076
- }
1077
- static concatIf(data, args) {
1078
- return _concatIf(data, args)
1079
- }
1080
- static divide(data, args) {
1081
- return _divide(data, args)
1082
- }
1083
- static eq(data, args) {
1084
- return _eq(data, args)
1085
- }
1086
-
1087
- static filterAll(data, args) {
1088
- return _filterAll(data, args)
1089
- }
1090
-
1091
- static formatDate(data, args) {
1092
- return _formatDate(data, args)
1093
- }
1094
- static get(data, key, failover = null) {
1095
- return _get(data, key, failover)
1096
- }
1097
- static gt(data, args) {
1098
- return _gt(data, args)
1099
- }
1100
- static gte(data, args) {
1101
- return _gte(data, args)
1102
- }
1103
- static isEmpty(data, args) {
1104
- return _isEmpty(data, args)
1105
- }
1106
- static isNotEmpty(data, args) {
1107
- return _isNotEmpty(data, args)
1108
- }
1109
- static join(data, separator = '') {
1110
- return _join(data, separator)
1111
- }
1112
- static lt(data, args) {
1113
- return _lt(data, args)
1114
- }
1115
- static lte(data, args) {
1116
- return _lte(data, args)
1117
- }
1118
- static map(data, args = []) {
1119
- return _map(data, args)
1120
- }
1121
- static neq(data, args) {
1122
- return _neq(data, args)
1123
- }
1124
- static removeHtml(data, args) {
1125
- return _removeHtml(data, args)
1126
- }
1127
- static toLowerCase(data, args) {
1128
- return _toLowerCase(data, args)
1129
- }
1130
- static toUpperCase(data, args) {
1131
- return _toUpperCase(data, args)
1132
- }
1133
- static parseFunction(expression) {
1134
- return _parseFunction(expression, _FN_NAMES)
1135
- }
1136
- static parseParams(parameters) {
1137
- return _parseParams(parameters)
1138
- }
1139
-
1140
- pipe(expression = '') {
1141
- this.delimiters = expression.substring(0, 2) === '{{' ? TAGS_HANDLEBAR : TAGS_EJS
1142
- const regex = new RegExp(`${this.delimiters[0]}\\s(.*?)\\s${this.delimiters[1]}`)
1143
- const match = expression.match(regex)
1144
- if (match !== null) {
1145
- try {
1146
- const functionList = _parseFunction(match[1], _FN_NAMES)
1147
- return functionList.reduce((acc, fn) => {
1148
- return _callFunction(acc, fn.name, fn.args)
1149
- }, this.data)
1150
- } catch (e) {
1151
- throw new TemplateCompilerException(`TemplateCompiler engine error: ${e.message}`)
1152
- }
1153
- }
1154
- throw new TemplateCompilerException(`TemplateCompiler engine error: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.invalidRegExpException}`)
1155
- }
1156
- }
1157
-
1158
- function _parseFunction(expression, existFunctionNames) {
1159
- const regExp = new RegExp(/(\w+)\(([^)]*)\)/)
1160
- let parts
1161
- if (expression.includes('|')) {
1162
- parts = expression.split('|')
1163
- } else {
1164
- parts = [expression]
1165
- }
1166
- return parts.reduce((acc, part) => {
1167
- const match = part.match(regExp)
1168
- if (match !== null) {
1169
- const functionName = match[1]
1170
- const parameters = match[2]
1171
- const paramList = _parseParams(parameters)
1172
- if (existFunctionNames.includes(functionName)) {
1173
- acc.push({
1174
- name: functionName,
1175
- args: paramList
1176
- })
1177
- } else {
1178
- throw new TemplateCompilerException(`${functionName} is not a valid function`)
1179
- }
1180
- }
1181
- return acc
1182
- }, [])
1183
- }
1184
-
1185
- function _parseParams(parameters) {
1186
- const _parameters = parameters.trim()
1187
- const regExp = new RegExp(/^[^\w\d\s]+$/)
1188
- const match = _parameters.match(regExp)
1189
- if (match !== null) {
1190
- return [_parameters.substring(1, _parameters.length - 1)]
1191
- }
1192
- if (_parameters.includes(',')) {
1193
- // 用正则表达式匹配逗号,但忽略方括号中的逗号
1194
- const parts = _splitIgnoringBrackets(_parameters)
1195
- return parts.map((part) => _parseSinglePart(part))
1196
- }
1197
- return [_parseSinglePart(_parameters)]
1198
- }
1199
-
1200
- function _splitIgnoringBrackets(input) {
1201
- const regExp2 = new RegExp(/^\d+(\.\d+)?$/)
1202
- const regExp = new RegExp(/(?![^\[]*\])\s*,\s*/)
1203
- const parts = input.split(regExp)
1204
- return parts.map((part) => {
1205
- const _part = part.trim()
1206
- if (_part !== '' && !!_part.match(regExp2)) {
1207
- // 如果是数字,转换为 num 类型
1208
- return Number.isNaN(_part) ? _part : parseInt(_part, 10)
1209
- }
1210
- // 否则当作字符串处理
1211
- return _part
1212
- })
1213
- }
1214
-
1215
- function _parseSinglePart(input) {
1216
- if (typeof input === 'string') {
1217
- if (input.startsWith('"') && input.endsWith('"')) {
1218
- // 去掉双引号,返回
1219
- return input.substring(1, input.length - 1)
1220
- }
1221
- if (input.startsWith("'") && input.endsWith("'")) {
1222
- // 去掉双引号,返回
1223
- return input.substring(1, input.length - 1)
1224
- }
1225
-
1226
- const _input = _toBasicType(input)
1227
-
1228
- if (typeof _input !== 'string') {
1229
- return _input
1230
- }
1231
-
1232
- // 如果是一个列表形式(例如 ["p", "d"] 或 [p, d])
1233
- if (_input.startsWith('[') && _input.endsWith(']')) {
1234
- const listContent = _input.substring(1, _input.length - 1).trim()
1235
- if (listContent !== '') {
1236
- return listContent.split(',').map((item) => {
1237
- return _toBasicType(item.trim())
1238
- })
1239
- }
1240
- return []
1241
- }
1242
- return input
1243
- }
1244
- return input
1245
- }
1246
-
1247
- function _toBasicType(input) {
1248
- if (input.startsWith('"') && input.endsWith('"')) {
1249
- // 去掉双引号,返回
1250
- return input.substring(1, input.length - 1)
1251
- }
1252
- if (input.startsWith("'") && input.endsWith("'")) {
1253
- // 去掉双引号,返回
1254
- return input.substring(1, input.length - 1)
1255
- }
1256
- if (input === 'true') {
1257
- return true
1258
- }
1259
- if (input === 'false') {
1260
- return false
1261
- }
1262
- if (input === 'undefined') {
1263
- return undefined
1264
- }
1265
- if (input === 'null') {
1266
- return null
1267
- }
1268
- if (!Number.isNaN(input) && !Number.isNaN(Number.parseFloat(input))) {
1269
- return Number(input)
1270
- }
1271
- return input
1272
- }
1273
-
1274
- function _callFunction(data, functionName, parameters) {
1275
- try {
1276
- let failover
1277
- switch (functionName) {
1278
- case 'concatIf':
1279
- return _concatIf(data, parameters)
1280
- case 'divide':
1281
- return _divide(data, parameters)
1282
- case 'eq':
1283
- return _eq(data, parameters)
1284
- case 'exec':
1285
- return _exec(data, parameters)
1286
- case 'filterAll':
1287
- return _filterAll(data, parameters)
1288
- case 'filterOne':
1289
- return _filterOne(data, parameters)
1290
- case 'formatDate':
1291
- return _formatDate(data, parameters)
1292
- case 'get':
1293
- if (parameters.length > 2) {
1294
- throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException)
1295
- }
1296
- if (parameters.length === 2) {
1297
- failover = parameters[parameters.length - 1]
1298
- }
1299
- return _get(data, parameters[0], failover)
1300
- case 'gt':
1301
- return _gt(data, parameters)
1302
- case 'gte':
1303
- return _gte(data, parameters)
1304
- case 'isEmpty':
1305
- return _isEmpty(data, parameters)
1306
- case 'isNotEmpty':
1307
- return _isNotEmpty(data, parameters)
1308
- case 'join':
1309
- return _join(data, parameters[0])
1310
- case 'lt':
1311
- return _lt(data, parameters)
1312
- case 'lte':
1313
- return _lte(data, parameters)
1314
- case 'map':
1315
- return _map(data, parameters)
1316
- case 'neq':
1317
- return _neq(data, parameters)
1318
- case 'removeHtml':
1319
- return _removeHtml(data, parameters)
1320
- case 'toLowerCase':
1321
- return _toLowerCase(data)
1322
- case 'toUpperCase':
1323
- return _toUpperCase(data)
1324
- default:
1325
- throw new Error(`${functionName} is not a valid function`)
1326
- }
1327
- } catch (e) {
1328
- throw e
1329
- }
1330
- }
1331
-
1332
-
1333
-
1334
- ;// ./lib/models/templateCompiler/index.js
1335
-
1336
-
1337
-
1338
-
1339
- ;// ./lib/helpers/concatStringByArray/concatStringByArray.js
1340
-
1341
-
1342
-
1343
-
1344
-
1345
- function concatStringByArray(arrTemplate, data) {
1346
- return arrTemplate.reduce((acc, item) => {
1347
- const { type, value = '', restriction, template, format, showMinutes } = item
1348
- switch (type) {
1349
- case('array'): {
1350
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1351
- const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || []
1352
- acc += _value.reduce((_acc, item) => {
1353
- return _acc += concatStringByArray(template, item)
1354
- }, '')
1355
- }
1356
- break
1357
- }
1358
- case ('date'): {
1359
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1360
- const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
1361
- acc += (formatDate(_value, format).toString())
1362
- }
1363
- break
1364
- }
1365
- case('ellipsis'): {
1366
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1367
- const { maxLength } = item
1368
- const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
1369
- if (_value.length <= maxLength) {
1370
- acc += (_value.toString())
1371
- } else {
1372
- acc += `${_value.substr(0, maxLength)}...`
1373
- }
1374
- }
1375
- break
1376
- }
1377
- case('group'): {
1378
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1379
- return concatStringByArray(value, data)
1380
- }
1381
- break
1382
- }
1383
- case('label'): {
1384
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1385
- acc += (value.toString())
1386
- }
1387
- break
1388
- }
1389
- case ('templateCompiler'): {
1390
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1391
- const templateCompiler = new TemplateCompiler({ data })
1392
- acc += templateCompiler.pipe(value)
1393
- }
1394
- break
1395
- }
1396
- case('value'): {
1397
- if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
1398
- const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
1399
- acc += (_value.toString())
1400
- }
1401
- break
1402
- }
1403
- }
1404
- return acc
1405
- }, '')
1406
- }
1407
- /* harmony default export */ const concatStringByArray_concatStringByArray = ({
1408
- concatStringByArray
1409
- });
1410
-
1411
-
1412
-
1413
- ;// ./lib/helpers/concatStringByArray/index.js
1414
-
1415
-
1416
- ;// ./lib/helpers/convertString/convertString.js
1417
-
1418
-
1419
- function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByKeys) {
1420
- if (!string) {
1421
- return ''
1422
- }
1423
- let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1424
- const reg = new RegExp(patternMatch, 'g')
1425
- return string.replace(reg, (match, key) => {
1426
- const result = _getValueByKeys({ keys: key.split('.'), obj: value })
1427
- if (result === null || result === undefined) {
1428
- return ''
1429
- }
1430
- return typeof result === 'object' ? JSON.stringify(result) : result
1431
- })
1432
- }
1433
-
1434
- /* harmony default export */ const convertString_convertString = ({
1435
- convertString
1436
- });
1437
-
1438
-
1439
- ;// ./lib/helpers/convertString/index.js
1440
-
1441
-
1442
- ;// ./lib/helpers/escapeRegex/escapeRegex.js
1443
- function escapeRegex(string) {
1444
- return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1445
- }
1446
-
1447
- ;// ./lib/helpers/escapeRegex/index.js
1448
-
1449
-
1450
- ;// ./lib/helpers/expressHelper/customHandler.js
1451
- function customHandler({ responseHelper, handler, ignoreError = false }) {
1452
- return async (req, res, next) => {
1453
- try {
1454
- await handler({ req, res })
1455
- await next()
1456
- } catch (err) {
1457
- if (ignoreError) {
1458
- await next()
1459
- } else {
1460
- res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1461
- }
1462
- }
1463
- }
1464
- }
1465
-
1466
- ;// ./lib/helpers/expressHelper/findAllResult.js
1467
- function findAllResult({ responseHelper, service }) {
1468
- return async (req, res, next) => {
1469
- try {
1470
- const { query } = req
1471
- const result = await service.findAll({ query })
1472
- res.locals.findAllResult = result
1473
- await next()
1474
- } catch (err) {
1475
- res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1476
- }
1477
- }
1478
- }
1479
-
1480
- ;// ./lib/helpers/expressHelper/findOneResult.js
1481
- function findOneResult({ responseHelper, service }) {
1482
- return async (req, res, next) => {
1483
- try {
1484
- const { params, query } = req
1485
- const { id } = params
1486
- const result = await service.findOne({
1487
- query: {
1488
- ...query,
1489
- id
1490
- }
1491
- })
1492
- res.locals.findOneResult = result
1493
- await next()
1494
- } catch (err) {
1495
- res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1496
- }
1497
- }
1498
- }
1499
-
1500
- ;// ./lib/helpers/expressHelper/postResult.js
1501
- function postResult({ responseHelper, service }) {
1502
- return async (req, res, next) => {
1503
- try {
1504
- const { body } = req
1505
- let result
1506
- if (Array.isArray(body)) {
1507
- result = await service.saveAll({ docs: body })
1508
- } else {
1509
- result = await service.saveOne({ doc: body })
1510
- }
1511
- res.locals.postResult = result
1512
- await next()
1513
- } catch (err) {
1514
- res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1515
- }
1516
- }
1517
- }
1518
-
1519
- ;// ./lib/helpers/expressHelper/updateOneResult.js
1520
- function updateOneResult({ responseHelper, service }) {
1521
- return async (req, res, next) => {
1522
- try {
1523
- const { body, params } = req
1524
- const { id } = params
1525
- if (id !== body.id) {
1526
- throw new Error('id in params and body must be same')
1527
- }
1528
- const { data } = await service.findOne({ query: { id } })
1529
- const doc = data[0]
1530
- doc.update(body)
1531
- const result = await service.saveOne({ doc })
1532
- res.locals.updateOneResult = result
1533
- await next()
1534
- } catch (err) {
1535
- res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1536
- }
1537
- }
1538
- }
1539
-
1540
- ;// ./lib/helpers/expressHelper/index.js
1541
-
1542
-
1543
-
1544
-
1545
-
1546
-
1547
-
1548
- const expressHelper = {
1549
- customHandler: customHandler,
1550
- findAllResult: findAllResult,
1551
- findOneResult: findOneResult,
1552
- postResult: postResult,
1553
- updateOneResult: updateOneResult,
1554
- }
1555
-
1556
- ;// ./lib/helpers/extractEmails/extractEmails.js
1557
- /**
1558
- * Extracts and normalizes unique email addresses from an array containing messy entries
1559
- * @param {Array} dirtyArray - Array that may contain emails in various formats (may include null/empty entries)
1560
- * @returns {Array} Sorted array of unique, lowercase email addresses
1561
- */
1562
- function extractEmails(dirtyArray) {
1563
- const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
1564
- const emails = new Set()
1565
-
1566
- // Handle null/undefined input array
1567
- if (!dirtyArray) return []
1568
-
1569
- dirtyArray.forEach(entry => {
1570
- // Skip null, undefined, empty, or whitespace-only entries
1571
- if (!entry || typeof entry !== 'string' || !entry.trim()) return
1572
-
1573
- try {
1574
- const cleanEntry = entry
1575
- .replace(/[\u200B-\u200D\uFEFF\u202A-\u202E]/g, '') // Remove hidden chars
1576
- .replace(/[<>]/g, ' ') // Convert email delimiters to spaces
1577
- .replace(/\s+/g, ' ') // Collapse multiple whitespace
1578
- .trim()
1579
-
1580
- // Extract all email matches
1581
- const matches = cleanEntry.match(emailRegex)
1582
- if (matches) {
1583
- matches.forEach(email => emails.add(email.toLowerCase())) // Normalize to lowercase
1584
- }
1585
- } catch (e) {
1586
- console.warn('Failed to process entry:', entry, e)
1587
- }
1588
- })
1589
-
1590
- // Convert Set to array and sort alphabetically
1591
- return Array.from(emails).sort((a, b) => a.localeCompare(b))
1592
- }
1593
-
1594
- ;// ./lib/helpers/extractEmails/index.js
1595
-
1596
-
1597
- ;// ./lib/helpers/objectHelper/objectHelper.js
1598
- const objectHelper = {
1599
- get(obj, path) {
1600
- const parts = path.split('.')
1601
- return parts.reduce((acc, part) => {
1602
- if (part.endsWith('[]')) {
1603
- // 处理数组遍历
1604
- const key = part.slice(0, -2) // 去掉 '[]' 得到属性名
1605
- if (Array.isArray(acc[key])) {
1606
- return acc[key] // 返回整个数组
1607
- }
1608
- return [] // 如果不是数组,返回空数组
1609
- }
1610
- if (part.includes('[') && part.includes(']')) {
1611
- // 处理数组索引
1612
- const arrayMatch = part.match(/(\w+)\[(\d+)\]/)
1613
- if (arrayMatch) {
1614
- const key = arrayMatch[1]
1615
- const index = arrayMatch[2]
1616
- return acc && acc[key] && acc[key][index]
1617
- }
1618
- } else if (acc && Array.isArray(acc)) {
1619
- // 如果当前值是数组,提取每个对象的指定属性
1620
- return acc.map((item) => item[part])
1621
- } else {
1622
- // 处理普通属性
1623
- return acc && acc[part]
1624
- }
1625
- }, obj)
1626
- },
1627
- merge,
1628
- set(obj, path, value) {
1629
- const parts = path.split('.');
1630
- let current = obj;
1631
-
1632
- // 处理所有中间部分
1633
- for (let i = 0; i < parts.length - 1; i++) {
1634
- const part = parts[i];
1635
- let key, index;
1636
-
1637
- // 检查是否是数组索引格式,如key[0]
1638
- const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1639
- if (arrayMatch) {
1640
- key = arrayMatch[1];
1641
- index = parseInt(arrayMatch[2], 10);
1642
- // 确保当前层级的数组存在
1643
- if (!current[key] || !Array.isArray(current[key])) {
1644
- current[key] = [];
1645
- }
1646
- // 扩展数组到足够大
1647
- while (current[key].length <= index) {
1648
- current[key].push(undefined);
1649
- }
1650
- // 如果当前位置未定义或为null,初始化为对象
1651
- if (current[key][index] == null) {
1652
- current[key][index] = {};
1653
- }
1654
- current = current[key][index];
1655
- } else {
1656
- // 处理普通属性
1657
- if (!current[part]) {
1658
- current[part] = {};
1659
- }
1660
- current = current[part];
1661
- }
1662
- }
1663
-
1664
- // 处理最后一部分
1665
- const lastPart = parts[parts.length - 1];
1666
- const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
1667
- if (arrayMatch) {
1668
- const key = arrayMatch[1];
1669
- const index = parseInt(arrayMatch[2], 10);
1670
- // 确保数组存在
1671
- if (!current[key] || !Array.isArray(current[key])) {
1672
- current[key] = [];
1673
- }
1674
- // 扩展数组到所需索引
1675
- while (current[key].length <= index) {
1676
- current[key].push(undefined);
1677
- }
1678
- current[key][index] = value;
1679
- } else {
1680
- current[lastPart] = value;
1681
- }
1682
- }
1683
- }
1684
-
1685
- function merge(target, ...sources) {
1686
- if (!sources.length) return target
1687
-
1688
- const source = sources.shift() // 取出第一个源对象
1689
-
1690
- if (_isObject(target) && _isObject(source)) {
1691
- for (const key in source) {
1692
- if (_isObject(source[key])) {
1693
- if (!target[key]) {
1694
- // 如果目标对象没有该属性,创建一个空对象
1695
- target[key] = {}
1696
- }
1697
- // 递归合并
1698
- merge(target[key], source[key])
1699
- } else {
1700
- // 直接覆盖
1701
- target[key] = source[key]
1702
- }
1703
- }
1704
- }
1705
-
1706
- // 继续合并剩余的源对象
1707
- return merge(target, ...sources)
1708
- }
1709
-
1710
- function _isObject(obj) {
1711
- return obj && typeof obj === 'object' && !Array.isArray(obj)
1712
- }
1713
-
1714
-
1715
-
1716
- ;// ./lib/helpers/objectHelper/index.js
1717
-
1718
-
1719
-
1720
-
1721
- ;// ./lib/helpers/pReduce/pReduce.js
1722
- async function pReduce(iterable, reducer, initialValue) {
1723
- return new Promise((resolve, reject) => {
1724
- const iterator = iterable[Symbol.iterator]()
1725
- let index = 0
1726
-
1727
- const next = async total => {
1728
- const element = iterator.next()
1729
-
1730
- if (element.done) {
1731
- resolve(total)
1732
- return
1733
- }
1734
-
1735
- try {
1736
- const [resolvedTotal, resolvedValue] = await Promise.all([total, element.value])
1737
- next(reducer(resolvedTotal, resolvedValue, index++))
1738
- } catch (error) {
1739
- reject(error)
1740
- }
1741
- }
1742
-
1743
- next(initialValue)
1744
- })
1745
- }
1746
-
1747
-
1748
-
1749
- ;// ./lib/models/repo/repo.js
1750
-
1751
-
1752
- class Repo {
1753
- constructor(options) {
1754
- options = options || {}
1755
- this.model = options.model
1756
- this._sharedOptions = options._sharedOptions // { session: this.dbTransaction }
1757
- this._queryOptions = options._queryOptions
1758
- this._saveOptions = options._saveOptions
1759
- this._Class = options._constructor && options._constructor._Class
1760
- ? options._constructor._Class
1761
- : null
1762
- }
1763
- static init(options = {}) {
1764
- return init(this, options)
1765
- }
1766
- static get _classname() {
1767
- return 'Repo'
1768
- }
1769
- static get _superclass() {
1770
- return 'Repo'
1771
- }
1772
-
1773
- get _classname() {
1774
- return 'Repo'
1775
- }
1776
-
1777
- get _superclass() {
1778
- return 'Repo'
1779
- }
1780
-
1781
- get isValid() {
1782
- return this.model
1783
- && (typeof this.model.deleteOne === 'function')
1784
- && (typeof this.model.findAll === 'function')
1785
- && (typeof this.model.saveOne === 'function')
1786
- }
1787
-
1788
- get queryOptions() {
1789
- return {
1790
- ...this._sharedOptions,
1791
- ...this._queryOptions,
1792
- }
1793
- }
1794
-
1795
- get saveOptions() {
1796
- return {
1797
- ...this._sharedOptions,
1798
- ...this._saveOptions,
1799
- }
1800
- }
1801
-
1802
- init(options) {
1803
- if (this._Class && typeof this._Class.init === 'function') {
1804
- return this._Class.init(options)
1805
- }
1806
- return options
1807
- }
1808
-
1809
- async deleteOne({ id }) {
1810
- try {
1811
- const result = await this.model.deleteOne({ _id: id })
1812
- return {
1813
- ...result, // { message: 'ok', total }
1814
- isNew: false,
1815
- data: []
1816
- }
1817
- } catch (err) {
1818
- throw err
1819
- }
1820
- }
1821
-
1822
- // systemLog is optional
1823
- findAll({ query, systemLog }) {
1824
- const log = _makeLog({
1825
- systemLog,
1826
- label: 'REPO_READ',
1827
- message: `fn ${this._classname}.prototype.findAll`,
1828
- input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1829
- })
1830
- return new Promise((resolve, reject) => {
1831
- this.model.findAll(query, this.queryOptions, (err, data, total) => {
1832
- if (err) {
1833
- log({ level: 'warn', output: err.toString() })
1834
- reject(err)
1835
- } else {
1836
- const result = {
1837
- isNew: false,
1838
- data,
1839
- total: total || data.length
1840
- }
1841
- log({ level: 'info', output: { ...result } })
1842
- resolve(result)
1843
- }
1844
- })
1845
- })
1846
- }
1847
-
1848
- findOne({ query, systemLog }) {
1849
- const log = _makeLog({
1850
- systemLog,
1851
- label: 'REPO_READ',
1852
- message: `fn ${this._classname}.prototype.findOne`,
1853
- input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1854
- })
1855
- return new Promise((resolve, reject) => {
1856
- this.model.findAll(query, this.queryOptions, (err, data) => {
1857
- if (err) {
1858
- reject(err)
1859
- } else if (data.length === 1) {
1860
- const result = {
1861
- isNew: false,
1862
- data,
1863
- total: 1
1864
- }
1865
- log({ level: 'info', output: { ...result } })
1866
- resolve(result)
1867
- } else if (data.length === 0) {
1868
- reject(new Error('record not found'))
1869
- } else {
1870
- reject(new Error('more than one is found'))
1871
- }
1872
- })
1873
- })
1874
- .catch((err) => {
1875
- log({ level: 'warn', output: err.toString() })
1876
- throw err
1877
- })
1878
- }
1879
-
1880
- saveAll({ config = {}, docs, systemLog }) {
1881
- let isNew
1882
- const log = _makeLog({
1883
- systemLog,
1884
- label: 'REPO_WRITE',
1885
- message: `fn ${this._classname}.prototype.saveAll`,
1886
- input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
1887
- })
1888
- const promise = typeof this.model.saveAll === 'function'
1889
- ? this.model.saveAll({ config, docs })
1890
- : Promise.all(docs.map(async (doc) => {
1891
- if (doc) {
1892
- const result = await this.saveOne({ config, doc })
1893
- isNew = result.isNew
1894
- const _data = result._data || result.data
1895
- return _data[0]
1896
- }
1897
- return null
1898
- }))
1899
- return promise.then((savedData) => {
1900
- if (savedData.length !== 1) isNew = null
1901
- const result = {
1902
- data: savedData,
1903
- isNew,
1904
- total: savedData.length
1905
- }
1906
- log({ level: 'info', output: { ...result } })
1907
- return result
1908
- }).catch((err) => {
1909
- log({ level: 'warn', output: err.toString() })
1910
- throw err
1911
- })
1912
- }
1913
-
1914
- saveOne({ config = {}, doc, systemLog }) {
1915
- const log = _makeLog({
1916
- systemLog,
1917
- label: 'REPO_WRITE',
1918
- message: `fn ${this._classname}.prototype.saveOne`,
1919
- input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
1920
- })
1921
- const saveOptions = {
1922
- ...this.saveOptions,
1923
- ...config,
1924
- }
1925
- return new Promise((resolve, reject) => {
1926
- this.model.saveOne(doc, saveOptions, (err, result) => {
1927
- if (err) {
1928
- log({ level: 'warn', output: err.toString() })
1929
- reject(err)
1930
- } else {
1931
- log({ level: 'info', output: { ...result } })
1932
- resolve(result)
1933
- }
1934
- })
1935
- })
1936
- }
1937
- }
1938
-
1939
- function _makeLog({ systemLog, label, message: message1, input } = {}) {
1940
- return ({ level, messgae: massage2, output } = {}) => {
1941
- if (systemLog && systemLog.systemLogHelper) {
1942
- systemLog.systemLogHelper.log({
1943
- batchId: systemLog.batchId,
1944
- label,
1945
- level,
1946
- message: massage2 || message1,
1947
- data: {
1948
- payload: {
1949
- input,
1950
- output
1951
- }
1952
- }
1953
- })
1954
- }
1955
- }
1956
- }
1957
-
1958
-
1959
-
1960
- ;// ./lib/models/repo/index.js
1961
-
1962
-
1963
-
1964
-
1965
- ;// ./lib/models/apiResponse/apiResponse.js
1966
- class ApiResponse {
1967
- constructor(options = {}) {
1968
- options = options || {}
1969
- this._data = options.data || options._data || []
1970
- this.err = options.err
1971
- this.isNew = options.isNew || false
1972
- this.message = options.message
1973
- this.total = options.total || 0
1974
- this._instanceBuilder = options._instanceBuilder
1975
- }
1976
-
1977
- static init(options = {}) {
1978
- if (options instanceof this) {
1979
- return options
1980
- }
1981
- const instance = new this(options)
1982
- return instance
1983
- }
1984
- static get _classname() {
1985
- return 'ApiResponse'
1986
- }
1987
- static get _superclass() {
1988
- return 'ApiResponse'
1989
- }
1990
-
1991
- // getters
1992
- get data() {
1993
- if (this._instanceBuilder && (typeof this._instanceBuilder === 'function')) {
1994
- return this._data.map(this._instanceBuilder)
1995
- }
1996
- return this._data
1997
- }
1998
- }
1999
-
2000
-
2001
-
2002
- ;// ./lib/models/apiResponse/makeApiResponse.js
2003
-
2004
-
2005
- function makeApiResponse({ repo, result }) {
2006
- return ApiResponse.init({
2007
- ...result,
2008
- _instanceBuilder: (i) => {
2009
- return repo.init(i)
2010
- }
2011
- })
2012
- }
2013
-
2014
-
2015
-
2016
- ;// ./lib/models/service/service.js
2017
-
2018
-
2019
-
2020
- class Service {
2021
- constructor({ repo }) {
2022
- this.repo = repo
2023
- }
2024
-
2025
- static get _classname() {
2026
- return 'Service'
2027
- }
2028
- static get _superclass() {
2029
- return 'Service'
2030
- }
2031
-
2032
- deleteOne({ id }) {
2033
- return this.repo.deleteOne({ id })
2034
- .catch(() => {
2035
- throw new Error(`Not found for query: ${id}`)
2036
- })
2037
- }
2038
-
2039
- async findAll({ query = {}, systemLog } = {}) {
2040
- const result = await this.repo.findAll({ query, systemLog })
2041
- return makeApiResponse({
2042
- repo: this.repo,
2043
- result
2044
- })
2045
- }
2046
-
2047
- async findOne({ query = {}, systemLog } = {}) {
2048
- const result = await this.repo.findOne({ query, systemLog })
2049
- return makeApiResponse({
2050
- repo: this.repo,
2051
- result
2052
- })
2053
- }
2054
-
2055
- init(options) {
2056
- return this.repo.init(options)
2057
- }
2058
- initFromArray(arr = []) {
2059
- if (Array.isArray(arr)) {
2060
- return arr.map((a) => this.init(a))
2061
- }
2062
- return []
2063
- }
2064
- initOnlyValidFromArray(arr = []) {
2065
- return this.initFromArray(arr).filter((i) => i)
2066
- }
2067
-
2068
- async saveAll({ config = {}, docs = [], systemLog } = {}) {
2069
- const copies = docs.map((doc) => {
2070
- return config.skipInit ? doc : this.init(doc)
2071
- })
2072
- const result = await this.repo.saveAll({ config, docs: copies, systemLog })
2073
- return makeApiResponse({
2074
- repo: this.repo,
2075
- result
2076
- })
2077
- }
2078
-
2079
- // set skipInit to true if we want to use POST for query
2080
- async saveOne({ config = {}, doc = {}, systemLog } = {}) {
2081
- const copy = config.skipInit ? doc : this.init(doc)
2082
- if (copy) {
2083
- const result = await this.repo.saveOne({ config, doc: copy, systemLog })
2084
- return makeApiResponse({
2085
- repo: this.repo,
2086
- result
2087
- })
2088
- }
2089
- return {
2090
- isNew: null,
2091
- data: [],
2092
- err: new Error('doc is not a valid instance')
2093
- }
2094
- }
2095
- }
2096
-
2097
- function makeService({ repo }) {
2098
- if (repo === undefined) {
2099
- throw new Error('repo is required.')
2100
- }
2101
- if (repo._superclass !== Repo._superclass) {
2102
- throw new Error('repo is not an instance of Repo.')
2103
- }
2104
- return new Service({ repo })
2105
- }
2106
-
2107
-
2108
-
2109
- ;// ./lib/models/service/index.js
2110
-
2111
-
2112
-
2113
-
2114
- ;// ./lib/helpers/generalPost/generalPost.js
2115
-
2116
-
2117
-
2118
-
2119
-
2120
- async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator, resourceInfo }) {
2121
- const { resources, data, globalShared = {}, shared = {}, relationship = {} } = body
2122
- const _resourceInfo = resourceInfo || body.resourceInfo
2123
- _attachShared(data, globalShared, shared)
2124
- const obj = await pReduce(resources, async (acc, resource) => {
2125
- const service = _makeService(resource, _resourceInfo, UniqueKeyGenerator, GeneralModel)
2126
- _createRelationship(data, relationship[resource], acc)
2127
- const _data = data[resource]
2128
- const result = await service.saveAll({ docs: [].concat(_data) })
2129
- acc[resource] = Array.isArray(_data) ? result._data : result._data[0]
2130
- return acc
2131
- }, {})
2132
- return obj
2133
- }
2134
-
2135
- function _attachShared(data, globalShared = {}, shared = {}) {
2136
- Object.keys(shared).forEach((key) => {
2137
- const _data = data[key]
2138
- if (Array.isArray(_data)) {
2139
- data[key] = _data.map((_dataItem) => {
2140
- return objectHelper.merge({}, _dataItem, globalShared, shared[key] || {})
2141
- })
2142
- } else {
2143
- data[key] = objectHelper.merge({}, _data, globalShared, shared[key] || {})
2144
- }
2145
- })
2146
- }
2147
-
2148
- function _createRelationship(data, relationship = {}, object) {
2149
- Object.keys(relationship).forEach((key) => {
2150
- const path = relationship[key]
2151
- const val = objectHelper.get(object, path)
2152
- objectHelper.set(data, key, val)
2153
- })
2154
- }
2155
-
2156
- function _makeService(resource, resourceInfo, UniqueKeyGenerator, GeneralModel) {
2157
- const { collectionName, fields } = resourceInfo[resource]
2158
- const uniqueKeyGenerator = UniqueKeyGenerator.makeGenerator(fields)
2159
- const model = new GeneralModel({ collectionName, uniqueKeyGenerator })
2160
- return makeService({
2161
- repo: new Repo({ model })
2162
- })
2163
- }
2164
-
2165
-
2166
-
2167
- ;// ./lib/helpers/generalPost/index.js
2168
-
2169
-
2170
-
2171
-
2172
- ;// ./lib/helpers/init/init.js
2173
- function init(_class, options) {
2174
- if (options instanceof _class) {
2175
- return options
2176
- }
2177
- try {
2178
- const instance = new _class(options)
2179
- return instance.isValid !== false ? instance : null
2180
- } catch (e) {
2181
- console.log(`init failed for class: ${_class._classname || 'no _classname'}`, e)
2182
- return null
2183
- }
2184
- }
2185
-
2186
- ;// ./lib/helpers/init/index.js
2187
-
2188
-
2189
- ;// ./lib/helpers/initFromArray/initFromArray.js
2190
-
2191
-
2192
- function initFromArray(_class, arr) {
2193
- if (Array.isArray(arr)) {
2194
- return arr.map((a) => init(_class, a))
2195
- }
2196
- return []
2197
- }
2198
-
2199
- ;// ./lib/helpers/initFromArray/index.js
2200
-
2201
-
2202
- ;// ./lib/helpers/initOnlyValidFromArray/initOnlyValidFromArray.js
2203
-
2204
-
2205
- function initOnlyValidFromArray(_class, arr) {
2206
- return initFromArray(_class, arr).filter((i) => i)
2207
- }
2208
-
2209
- ;// ./lib/helpers/initOnlyValidFromArray/index.js
2210
-
2211
-
2212
- ;// ./lib/helpers/mergeArraysByKey/mergeArraysByKey.js
2213
- function mergeArraysByKey(arr1, arr2) {
2214
- // Handle undefined/null inputs by defaulting to empty arrays
2215
- const safeArr1 = Array.isArray(arr1) ? arr1 : []
2216
- const safeArr2 = Array.isArray(arr2) ? arr2 : []
2217
-
2218
- const mergedMap = new Map()
2219
-
2220
- // Helper function to merge values based on their type
2221
- const mergeValues = (existingValue, newValue) => {
2222
- if (existingValue === undefined) return newValue
2223
-
2224
- // Handle arrays by concatenating
2225
- if (Array.isArray(existingValue) && Array.isArray(newValue)) {
2226
- return [...new Set([...existingValue, ...newValue])]
2227
- }
2228
-
2229
- // Handle objects by merging
2230
- if (typeof existingValue === 'object' && typeof newValue === 'object' &&
2231
- !Array.isArray(existingValue) && !Array.isArray(newValue)) {
2232
- return { ...existingValue, ...newValue }
2233
- }
2234
-
2235
- // // Handle numbers by adding
2236
- // if (typeof existingValue === 'number' && typeof newValue === 'number') {
2237
- // return existingValue
2238
- // }
2239
-
2240
- // // Handle strings by concatenating
2241
- // if (typeof existingValue === 'string' && typeof newValue === 'string') {
2242
- // return existingValue
2243
- // }
2244
-
2245
- // Default: use the new value
2246
- return newValue
2247
- }
2248
-
2249
- // Process first array
2250
- safeArr1.forEach(item => {
2251
- mergedMap.set(item.key, item.value)
2252
- })
2253
-
2254
- // Process second array and merge values
2255
- safeArr2.forEach(item => {
2256
- const existingValue = mergedMap.get(item.key)
2257
- mergedMap.set(item.key, mergeValues(existingValue, item.value))
2258
- })
2259
-
2260
- // Convert back to array format
2261
- return Array.from(mergedMap.entries()).map(([key, value]) => ({
2262
- key,
2263
- value
2264
- }))
2265
- }
2266
-
2267
- ;// ./lib/helpers/mergeArraysByKey/index.js
2268
-
2269
-
2270
- ;// ./lib/helpers/padZeros/padZeros.js
2271
- function padZeros(num, minLength = 6) {
2272
- num = num.toString()
2273
- if (num.length < minLength) {
2274
- return padZeros('0' + num, minLength)
2275
- }
2276
- return num
2277
- }
2278
-
2279
-
2280
-
2281
- ;// ./lib/helpers/padZeros/index.js
2282
-
2283
-
2284
-
2285
-
2286
- ;// ./lib/helpers/pReduce/index.js
2287
-
2288
-
2289
-
2290
-
2291
- ;// ./lib/helpers/replacePlaceholders/replacePlaceholders.js
2292
- function replacePlaceholders({ content, mapping }) {
2293
- let isObjectMode = false
2294
-
2295
- if (typeof content === 'object' && content !== null) {
2296
- content = JSON.stringify(content)
2297
- isObjectMode = true
2298
- }
2299
-
2300
- // [[ eventRegistration.eventRegistrationCode | 0 ]]
2301
- const regex = /(\[*)\[\[\s*([\w.\-_]+)(?:\s*\|\s*([^:\]\s]+))?(?:\s*:\s*([^\]\s]+))?\s*\]\](\]*)/g;
2302
-
2303
- const result = content.replace(regex, (match, leadingBrackets, path, defaultValue, type, trailingBrackets) => {
2304
-
2305
- // Split the path into parts
2306
- const keys = path.trim().split('.')
2307
-
2308
- // Traverse the nested object structure
2309
- let value = mapping
2310
- for (const key of keys) {
2311
- // Handle empty keys (in case of double dots or leading/trailing dots)
2312
- if (!key) continue
2313
-
2314
- value = value?.[key]
2315
- if (value === undefined) {
2316
- break
2317
- }
2318
- }
2319
-
2320
- // Apply default if missing
2321
- if (value === undefined) value = defaultValue?.trim();
2322
- if (value === undefined) return isObjectMode ? undefined : match;
2323
-
2324
- value = value !== undefined
2325
- ? leadingBrackets + value + trailingBrackets
2326
- : match
2327
-
2328
- // Return replacement or original if not found
2329
- return value
2330
- })
2331
-
2332
- if (isObjectMode) {
2333
- return JSON.parse(result)
2334
- }
2335
- return result
2336
- }
2337
-
2338
- ;// ./lib/helpers/replacePlaceholders/index.js
2339
-
2340
-
2341
- ;// ./lib/helpers/sanitizeText/sanitizeText.js
2342
- /**
2343
- * Sanitizes input by removing hidden/control characters with customizable whitespace handling.
2344
- * @param {string} input - The string to sanitize.
2345
- * @param {Object} [options] - Configuration options.
2346
- * @param {boolean} [options.normalizeWhitespace=true] - Collapse multiple spaces/tabs into one space.
2347
- * @param {boolean} [options.removeNewlines=false] - If true, replaces newlines with spaces.
2348
- * @param {boolean} [options.trim=true] - If true, trims leading/trailing whitespace.
2349
- * @returns {string} The sanitized string.
2350
- */
2351
- function sanitizeText(input, options = {}) {
2352
- const {
2353
- normalizeWhitespace = true,
2354
- removeNewlines = false,
2355
- trim = true,
2356
- preserveBasicWhitespace = true, // new option to keep tabs, newlines if removeNewlines=false
2357
- } = options
2358
-
2359
- if (typeof input !== 'string') {
2360
- return input
2361
- }
2362
-
2363
- let result = input
2364
-
2365
- // Phase 1: Remove all control characters except basic whitespace if requested
2366
- if (preserveBasicWhitespace && !removeNewlines) {
2367
- // Keep tab (\t), newline (\n), carriage return (\r)
2368
- result = result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2369
- } else {
2370
- // Remove all control characters including basic whitespace
2371
- result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2372
- }
2373
-
2374
- // Phase 2: Handle whitespace transformations
2375
- if (removeNewlines) {
2376
- result = result.replace(/[\r\n]+/g, ' ') // Convert newlines to spaces
2377
- }
2378
-
2379
- if (normalizeWhitespace) {
2380
- result = result.replace(/[ \t]+/g, ' ') // Collapse spaces/tabs to single space
2381
- }
2382
-
2383
- // Phase 3: Final trimming
2384
- if (trim) {
2385
- result = result.trim()
2386
- }
2387
-
2388
- return result
2389
- }
2390
-
2391
- ;// ./lib/helpers/sanitizeText/index.js
2392
-
2393
-
2394
- ;// ./lib/helpers/stringFormatter/stringFormatter.js
2395
- function stringFormatter(str, delimiter = '_') {
2396
- if (str === null || typeof str === 'undefined' || typeof str.toString === 'undefined') {
2397
- return null
2398
- }
2399
- return str.toString()
2400
- .trim()
2401
- .toUpperCase()
2402
- .replace('-', delimiter)
2403
- .replace(' ', delimiter)
2404
- }
2405
-
2406
-
2407
-
2408
- ;// ./lib/helpers/stringFormatter/index.js
2409
-
2410
-
2411
-
2412
-
2413
- ;// ./lib/helpers/stringHelper/stringHelper.js
2414
- function baseXEncode(num, base = 34) {
2415
- const charset = getBaseCharset(base)
2416
- return encode(num, charset)
2417
- }
2418
-
2419
- function encode(int, charset) {
2420
- const { byCode } = charset
2421
- if (int === 0) {
2422
- return byCode[0]
2423
- }
2424
-
2425
- let res = ''
2426
- const max = charset.length
2427
- while (int > 0) {
2428
- res = byCode[int % max] + res
2429
- int = Math.floor(int / max)
2430
- }
2431
- return res
2432
- }
2433
-
2434
- function getBaseCharset(base) {
2435
- let charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZ'
2436
- if (base === 58) {
2437
- charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz'
2438
- }
2439
- return indexCharset(charset)
2440
- }
2441
-
2442
- function indexCharset(str) {
2443
- const byCode = {}
2444
- const byChar = {}
2445
- const { length } = str
2446
- let char
2447
- for (let i = 0; i < length; i++) {
2448
- char = str[i]
2449
- byCode[i] = char
2450
- byChar[char] = i;
2451
- }
2452
- return { byCode, byChar, length }
2453
- }
2454
-
2455
- function isSame(str1, str2) {
2456
- if (typeof str1 !== 'string' || typeof str2 !== 'string') {
2457
- return false
2458
- }
2459
- return str1.trim().toUpperCase() === str2.trim().toUpperCase()
2460
- }
2461
-
2462
- function randomString({ len = 16, pattern = 'a1' } = {}) {
2463
- const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
2464
- const a = 'abcdefghijklmnopqrstuvwxyz'
2465
- const num = '1234567890'
2466
- const mark = '~!@#$%^&*_+-='
2467
- let str = ''
2468
- if (pattern.includes('A')) {
2469
- str += A
2470
- }
2471
- if (pattern.includes('a')) {
2472
- str += a
2473
- }
2474
- if (pattern.includes('1')) {
2475
- str += num
2476
- }
2477
- if (pattern.includes('#')) {
2478
- str += mark
2479
- }
2480
- const chars = [...str]
2481
- return [...Array(len)].map(i => {
2482
- return chars[(Math.random() * chars.length) | 0]
2483
- }).join``
2484
- }
2485
-
2486
- function reverse(str) {
2487
- const _str = (typeof str !== 'string') ? str.toString() : str
2488
- const splitString = _str.split('')
2489
- const reverseArray = splitString.reverse()
2490
- return reverseArray.join('')
2491
- }
2492
-
2493
- function setCode(base = 34) {
2494
- const now = (new Date()).valueOf()
2495
- const random = randomString({
2496
- len: 8,
2497
- pattern: '1'
2498
- })
2499
- const str = reverse(`${now}${random}`)
2500
- // const str = `${now}${random}`
2501
- return baseXEncode(str, base)
2502
- }
2503
-
2504
- function toCamelCase(str) {
2505
- if (!str) return ''
2506
- return str
2507
- .trim()
2508
- .split(/\s+/)
2509
- .map((word, index) => {
2510
- if (!word) return ''
2511
- if (index === 0) {
2512
- return word.toLowerCase()
2513
- }
2514
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
2515
- })
2516
- .join('')
2517
- }
2518
-
2519
- function toLowerCase(str) {
2520
- if (!str) return ''
2521
- return str
2522
- .trim()
2523
- .toLowerCase()
2524
- }
2525
-
2526
- const stringHelper = {
2527
- isSame,
2528
- setCode,
2529
- toCamelCase,
2530
- toLowerCase,
2531
- }
2532
-
2533
-
2534
-
2535
- ;// ./lib/helpers/stringHelper/index.js
2536
-
2537
-
2538
-
2539
-
2540
- ;// ./lib/helpers/trackingPlugin/trackingPlugin.js
2541
- function trackingPlugin(schema, options) {
2542
- // Add meta fields
2543
- schema.add({
2544
- meta: {
2545
- active: { type: Boolean, default: true },
2546
- created: { type: Number },
2547
- creator: { type: String },
2548
- deleted: { type: Boolean, default: false },
2549
- modified: { type: Number },
2550
- owner: { type: String },
2551
- }
2552
- })
2553
-
2554
- // Auto-update hook
2555
- schema.pre('save', function(next) {
2556
- this.meta.modified = Date.now()
2557
- next()
2558
- })
2559
-
2560
- // Add core indexes
2561
- schema.index({
2562
- 'meta.active': 1,
2563
- 'meta.deleted': 1
2564
- }, {
2565
- name: 'tracking_status_index',
2566
- background: true,
2567
- partialFilterExpression: {
2568
- 'meta.active': true,
2569
- 'meta.deleted': false
2570
- }
2571
- })
2572
-
2573
- // Optional: Add helper methods
2574
- // schema.methods.touch = function(userId) {
2575
- // this.meta.updatedAt = new Date()
2576
- // this.meta.updatedBy = userId
2577
- // }
2578
- }
2579
-
2580
- ;// ./lib/helpers/tenantPlugin/tenantPlugin.js
2581
-
2582
-
2583
- function tenantPlugin(schema, options) {
2584
- // Apply tracking plugin first if not already present
2585
- if (!schema.path('meta')) {
2586
- trackingPlugin(schema, options)
2587
- }
2588
-
2589
- // Add tenant-specific fields
2590
- schema.add({
2591
- metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
2592
- remarks: [{ type: Object }],
2593
- tenantCode: { type: String, required: true }
2594
- })
2595
-
2596
- // Add core indexes
2597
- schema.index({
2598
- 'tenantCode': 1
2599
- }, {
2600
- name: 'tenant_core_index',
2601
- background: true
2602
- })
2603
-
2604
- // 1. ENHANCE EXISTING TRACKING INDEXES
2605
- const existingIndexes = schema.indexes()
2606
-
2607
- // Check if tracking_status_index exists
2608
- const hasTenantStatusIndex = existingIndexes.some(idx =>
2609
- idx.name === 'tenant_status_index' // Check by name for reliability
2610
- )
2611
-
2612
- if (!hasTenantStatusIndex) {
2613
- schema.index({
2614
- 'tenantCode': 1, // Unique field first
2615
- _type: 1, // Low-cardinality field last
2616
- }, {
2617
- name: 'tenant_status_index',
2618
- background: true,
2619
- partialFilterExpression: {
2620
- '_type': 'Tenant',
2621
- 'meta.active': true,
2622
- 'meta.deleted': false
2623
- }
2624
- })
2625
- }
2626
- }
2627
-
2628
- ;// ./lib/helpers/tenantPlugin/index.js
2629
-
2630
-
2631
- ;// ./lib/helpers/trackingPlugin/index.js
2632
-
2633
-
2634
- ;// ./lib/helpers/index.js
2635
-
2636
-
2637
-
2638
-
2639
-
2640
-
2641
-
2642
-
2643
-
2644
-
2645
-
2646
-
2647
-
2648
-
2649
-
2650
-
2651
-
2652
-
2653
-
2654
-
2655
-
2656
-
2657
-
2658
-
2659
-
2660
- ;// ./lib/models/apiResponse/index.js
2661
-
2662
-
2663
-
2664
-
2665
-
2666
- ;// ./lib/models/awsStsS3Client/awsStsS3Client.js
2667
- class AwsStsS3Client {
2668
- constructor(options) {
2669
- options = options || {}
2670
-
2671
- this.expiration = options.expiration || null
2672
- this.s3Client = options.s3Client || null
2673
- this.getIdToken = options.getIdToken
2674
- this.region = options.region || 'ap-east-1'
2675
- this.roleArn = options.roleArn
2676
- this.roleSessionName = options.roleSessionName || 'web-identity-session'
2677
- this.durationSession = options.durationSession || 3600
2678
- this.awsClientSts = options.awsClientSts
2679
- this.awsClientS3 = options.awsClientS3
2680
- }
2681
-
2682
- static dummyData() {
2683
- return {
2684
- getIdToken: () => 'mock-web-identity-token',
2685
- roleArn: 'arn:aws:iam::846252828949:role/oidcS3Jccpa',
2686
- awsClientSts: {
2687
- STSClient: class {},
2688
- AssumeRoleWithWebIdentityCommand: class {}
2689
- },
2690
- awsClientS3: {
2691
- S3Client: class {},
2692
- PutObjectCommand: class {},
2693
- GetObjectCommand: class {},
2694
- DeleteObjectCommand: class {}
2695
- }
2696
- }
2697
- }
2698
-
2699
- static init(options = {}) {
2700
- if (options instanceof this) {
2701
- return options
2702
- }
2703
- try {
2704
- const instance = new this(options)
2705
- if (!instance.isValid) {
2706
- return null
2707
- }
2708
- return instance
2709
- } catch (error) {
2710
- return null
2711
- }
2712
- }
2713
-
2714
- get isExpired() {
2715
- if (!this.expiration) return true;
2716
- const now = new Date();
2717
- const bufferMs = 1 * 60 * 1000; // 一分钟缓冲
2718
- return now >= new Date(this.expiration.getTime() - bufferMs);
2719
- }
2720
-
2721
- get isValid() {
2722
- if (!this.getIdToken) {
2723
- throw new Error('Missing required configuration: getIdToken function')
2724
- }
2725
- if (!this.roleArn) {
2726
- throw new Error('Missing required configuration: roleArn')
2727
- }
2728
- if (!this.awsClientSts) {
2729
- throw new Error('Missing required AWS awsClientSts client configuration')
2730
- }
2731
- if (!this.awsClientSts.STSClient) {
2732
- throw new Error('Missing STSClient in AWS awsClientSts client configuration')
2733
- }
2734
- if (!this.awsClientSts.AssumeRoleWithWebIdentityCommand) {
2735
- throw new Error('Missing AssumeRoleWithWebIdentityCommand in AWS awsClientSts client configuration')
2736
- }
2737
- if (!this.awsClientS3) {
2738
- throw new Error('Missing required AWS awsClientS3 client configuration')
2739
- }
2740
-
2741
- const requiredS3Components = [
2742
- 'S3Client',
2743
- 'PutObjectCommand',
2744
- 'GetObjectCommand',
2745
- 'DeleteObjectCommand'
2746
- ]
2747
-
2748
- for (const component of requiredS3Components) {
2749
- if (!this.awsClientS3[component]) {
2750
- throw new Error(`Missing ${component} in AWS awsClientS3 client configuration`)
2751
- }
2752
- }
2753
-
2754
- return true
2755
- }
2756
-
2757
- async refreshCredentials() {
2758
- try {
2759
- const webIdentityToken = await this.getIdToken()
2760
- if (!webIdentityToken) {
2761
- throw new Error('getIdToken function returned empty or invalid token')
2762
- }
2763
-
2764
- const stsClient = new this.awsClientSts.STSClient({ region: this.region })
2765
-
2766
- const stsResponse = await stsClient.send(
2767
- new this.awsClientSts.AssumeRoleWithWebIdentityCommand({
2768
- RoleArn: this.roleArn,
2769
- RoleSessionName: this.roleSessionName,
2770
- WebIdentityToken: await this.getIdToken(),
2771
- DurationSeconds: this.durationSession,
2772
- })
2773
- )
2774
-
2775
- const credentials = stsResponse.Credentials
2776
- if (!credentials) {
2777
- throw new Error('No credentials returned from awsClientSts')
2778
- }
2779
-
2780
- this.expiration = credentials.Expiration
2781
-
2782
- this.s3Client = new this.awsClientS3.S3Client({
2783
- region: this.region,
2784
- credentials: {
2785
- accessKeyId: credentials.AccessKeyId,
2786
- secretAccessKey: credentials.SecretAccessKey,
2787
- sessionToken: credentials.SessionToken,
2788
- }
2789
- })
2790
-
2791
- return this
2792
- } catch (error) {
2793
- throw new Error(`Failed to refresh credentials: ${error.message}`)
2794
- }
2795
- }
2796
-
2797
- async getS3Client() {
2798
- if (this.isExpired || !this.s3Client) {
2799
- await this.refreshCredentials()
2800
- }
2801
- return this.s3Client
2802
- }
2803
-
2804
- async putObject(params) {
2805
- try {
2806
- const client = await this.getS3Client()
2807
- const command = new this.awsClientS3.PutObjectCommand(params)
2808
- await client.send(command)
2809
- const fileArr = params.Key.split('/')
2810
- return {
2811
- url: `https://s3.${this.region}.amazonaws.com/${params.Bucket}/${params.Key}`,
2812
- filename: fileArr.pop(),
2813
- folder: params.Bucket,
2814
- subFolders: fileArr
2815
- }
2816
- } catch (error) {
2817
- throw new Error(`Failed to put object: ${error.message}`)
2818
- }
2819
- }
2820
-
2821
- async getObject(params) {
2822
- try {
2823
- const client = await this.getS3Client()
2824
- const command = new this.awsClientS3.GetObjectCommand(params)
2825
- const response = await client.send(command)
2826
- return {
2827
- body: response.Body,
2828
- contentType: response.ContentType,
2829
- lastModified: response.LastModified,
2830
- contentLength: response.ContentLength,
2831
- }
2832
- } catch (error) {
2833
- throw new Error(`Failed to get object: ${error.message}`)
2834
- }
2835
- }
2836
-
2837
- async deleteObject(params) {
2838
- try {
2839
- const client = await this.getS3Client()
2840
- const command = new this.awsClientS3.DeleteObjectCommand(params)
2841
- await client.send(command)
2842
- return true
2843
- } catch (error) {
2844
- throw new Error(`Failed to delete object: ${error.message}`)
2845
- }
2846
- }
2847
- }
2848
-
2849
-
2850
-
2851
- ;// ./lib/models/awsStsS3Client/index.js
2852
-
2853
-
2854
-
2855
-
2856
- ;// ./lib/models/keyValueObject/keyValueObject.js
2857
- class KeyValueObject {
2858
- constructor(options = {}) {
2859
- options = options || {}
2860
- this.key = options.key || null
2861
- this.value = (typeof options.value !== 'undefined') ? options.value : ''
2862
- }
2863
-
2864
- // Class methods
2865
- static init(options = {}) {
2866
- if (options instanceof this) {
2867
- return options
2868
- }
2869
- const instance = new this(options)
2870
- return instance.isValid ? instance : null
2871
- }
2872
- static initFromArray(arr = []) {
2873
- if (Array.isArray(arr)) {
2874
- return arr.map((a) => this.init(a))
2875
- }
2876
- return []
2877
- }
2878
- static initOnlyValidFromArray(arr = []) {
2879
- return this.initFromArray(arr).filter((i) => i)
2880
- }
2881
- static get _classname() {
2882
- return 'KeyValueObject'
2883
- }
2884
- static get _superclass() {
2885
- return 'KeyValueObject'
2886
- }
2887
-
2888
- static addItem(arr, key, value) {
2889
- arr.push(this.init({ key, value }))
2890
- }
2891
- static addRecord(arr = [], key, value) {
2892
- if (!this.hasKeyValue(arr, key, value)) {
2893
- arr.push(this.init({ key, value }))
2894
- }
2895
- return arr
2896
- }
2897
- static appendRecord(arr = [], key, value) {
2898
- return arr.map((item) => {
2899
- if (this.sameKey(item, key)) {
2900
- item.value = [...item.value, ...value]
2901
- }
2902
- return item
2903
- })
2904
- }
2905
- static appendValueArray(arr = [], key, value) {
2906
- return arr.map((item) => {
2907
- if (this.sameKey(item, key)) {
2908
- item.value = [...item.value, ...value]
2909
- }
2910
- return item
2911
- })
2912
- }
2913
- static foundByKey(arr = [], key) {
2914
- const found = arr.find((m) => {
2915
- return this.sameKey(m, key)
2916
- })
2917
- return found || null
2918
- }
2919
- static foundValueByKey(arr = [], key) {
2920
- const found = this.foundByKey(arr, key)
2921
- return found ? found.value : null
2922
- }
2923
- static fromObject(options = {}) {
2924
- return Object.keys(options).reduce((acc, key) => {
2925
- acc.push(this.init({ key, value: options[key] }))
2926
- return acc
2927
- }, [])
2928
- }
2929
- static getValueByKey(arr = [], key) {
2930
- return this.foundValueByKey(arr, key)
2931
- }
2932
- static getValueByKeyFromArray(arr = [], key) {
2933
- if (arr.length === 0) {
2934
- return null
2935
- }
2936
- const firstArr = arr.shift()
2937
- const found = firstArr.find((i) => {
2938
- return this.sameKey(i, key)
2939
- })
2940
- if (found && found.value) {
2941
- return found.value
2942
- }
2943
- return this.getValueByKeyFromArray(arr, key)
2944
- }
2945
- static getValuesByKey(arr = [], key) {
2946
- return arr.reduce((acc, item) => {
2947
- if (this.sameKey(item, key)) {
2948
- acc.push(item.value)
2949
- }
2950
- return acc
2951
- }, [])
2952
- }
2953
- static hasKeyValue(arr = [], key, value) {
2954
- if (typeof value === 'undefined') {
2955
- return arr.filter((item) => this.sameKey(item, key)).length > 0
2956
- }
2957
- return arr.filter((item) => (this.sameKey(item, key) && _isSame(item.value, value))).length > 0
2958
- }
2959
- static insertOrUpdateRecord(arr = [], key, value) {
2960
- let copy = [...arr]
2961
- if (!this.hasKeyValue(arr, key)) {
2962
- copy.push(this.init({ key, value }))
2963
- } else {
2964
- copy = this.updateRecord(arr, key, value)
2965
- }
2966
- return copy
2967
- }
2968
- static keys(arr = []) {
2969
- if (Array.isArray(arr)) {
2970
- return arr.reduce((acc, item) => {
2971
- acc.push(item.key)
2972
- return acc
2973
- }, [])
2974
- }
2975
- return []
2976
- }
2977
- static merge(toArr, fromArr) {
2978
- (fromArr || []).map((from) => {
2979
- const found = toArr.find((to) => {
2980
- return to.key === from.key
2981
- })
2982
- if (found) {
2983
- found.value = _mergeValues(from.value, found.value)
2984
- } else {
2985
- toArr.push(from)
2986
- }
2987
- })
2988
- return toArr
2989
- }
2990
- static removeByKey(arr, key) {
2991
- return arr.reduce((acc, item) => {
2992
- if (!this.sameKey(item, key)) {
2993
- acc.push(item)
2994
- }
2995
- return acc
2996
- }, [])
2997
- }
2998
- static sameKey(item, key) {
2999
- if (item) {
3000
- return _isSame(item.key, key)
3001
- }
3002
- return false
3003
- }
3004
- static toObject(arr = []) {
3005
- if (Array.isArray(arr)) {
3006
- return arr.reduce((acc, item) => {
3007
- acc[item.key] = item.value
3008
- return acc
3009
- }, {})
3010
- }
3011
- return {}
3012
- }
3013
- static toString(arr = [], delimiter = '; ') {
3014
- if (Array.isArray(arr)) {
3015
- return arr.reduce((acc, item) => {
3016
- acc.push(`${item.key}: ${item.value}`)
3017
- return acc
3018
- }, []).join(delimiter)
3019
- }
3020
- return ''
3021
- }
3022
- static updateRecord(arr = [], key, value) {
3023
- return arr.map((item) => {
3024
- if (this.sameKey(item, key)) {
3025
- return {
3026
- ...item,
3027
- value
3028
- }
3029
- }
3030
- return item
3031
- })
3032
- }
3033
- static updateOrInsertRecord(arr = [], key, value) {
3034
- return this.insertOrUpdateRecord(arr, key, value)
3035
- }
3036
- static updateRecordsFromArray(arr = [], updateArr = []) {
3037
- if (Array.isArray(arr) && Array.isArray(updateArr)) {
3038
- const obj1 = this.toObject(arr)
3039
- const obj2 = this.toObject(updateArr)
3040
- return this.fromObject({
3041
- ...obj1,
3042
- ...obj2
3043
- })
3044
- }
3045
- return []
3046
- }
3047
- static values(arr = []) {
3048
- if (Array.isArray(arr)) {
3049
- return arr.reduce((acc, item) => {
3050
- acc.push(item.value)
3051
- return acc
3052
- }, [])
3053
- }
3054
- return []
3055
- }
3056
-
3057
- // getters
3058
- get isValid() {
3059
- return !!this.key
3060
- }
3061
-
3062
- get toObject() {
3063
- const obj = {}
3064
- if (this.isValid) {
3065
- obj[this.key] = this.value
3066
- }
3067
- return obj
3068
- }
3069
- }
3070
-
3071
- function _mergeValues(existingValue, newValue) {
3072
- if (existingValue === undefined) return newValue
3073
-
3074
- // Handle arrays by concatenating
3075
- if (Array.isArray(existingValue) && Array.isArray(newValue)) {
3076
- return [...new Set([...existingValue, ...newValue])]
3077
- }
3078
-
3079
- // Handle objects by merging
3080
- if (typeof existingValue === 'object' && typeof newValue === 'object' &&
3081
- !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3082
- return { ...existingValue, ...newValue }
3083
- }
3084
-
3085
- // // Handle numbers by adding
3086
- // if (typeof existingValue === 'number' && typeof newValue === 'number') {
3087
- // return existingValue
3088
- // }
3089
-
3090
- // // Handle strings by concatenating
3091
- // if (typeof existingValue === 'string' && typeof newValue === 'string') {
3092
- // return existingValue
3093
- // }
3094
-
3095
- // Default: use the new value
3096
- return newValue
3097
- }
3098
-
3099
- function _isSame(key1, key2) {
3100
- return key1 === key2
3101
- }
3102
-
3103
-
3104
-
3105
- ;// ./lib/models/keyValueObject/index.js
3106
-
3107
-
3108
-
3109
-
3110
- ;// ./lib/models/metadata/metadata.js
3111
-
3112
-
3113
-
3114
- const DELIMITER = '_'
3115
-
3116
- class Metadata extends KeyValueObject {
3117
- static init(options = {}) {
3118
- if (options instanceof this) {
3119
- return options
3120
- }
3121
- const instance = new this({
3122
- ...options,
3123
- key: stringFormatter(options.key, DELIMITER),
3124
- })
3125
- return instance.isValid ? instance : null
3126
- }
3127
- static get _classname() {
3128
- return 'Metadata'
3129
- }
3130
-
3131
- static merge(toArr, fromArr) {
3132
- (fromArr || []).map((from) => {
3133
- const found = toArr.find((to) => {
3134
- return metadata_isSame(to.key, from.key)
3135
- })
3136
- if (found) {
3137
- found.value = metadata_mergeValues(from.value, found.value)
3138
- } else {
3139
- toArr.push(from)
3140
- }
3141
- })
3142
- return toArr
3143
- }
3144
- static sameKey(item, key) {
3145
- return metadata_isSame(item.key, key)
3146
- }
3147
- }
3148
-
3149
- function metadata_isSame(key1, key2) {
3150
- return stringFormatter(key1, DELIMITER) === stringFormatter(key2, DELIMITER)
3151
- }
3152
-
3153
- function metadata_mergeValues(existingValue, newValue) {
3154
- if (existingValue === undefined) return newValue
3155
-
3156
- // Handle arrays by concatenating
3157
- if (Array.isArray(existingValue) && Array.isArray(newValue)) {
3158
- return [...new Set([...existingValue, ...newValue])]
3159
- }
3160
-
3161
- // Handle objects by merging
3162
- if (typeof existingValue === 'object' && typeof newValue === 'object' &&
3163
- !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3164
- return { ...existingValue, ...newValue }
3165
- }
3166
-
3167
- // // Handle numbers by adding
3168
- // if (typeof existingValue === 'number' && typeof newValue === 'number') {
3169
- // return existingValue
3170
- // }
3171
-
3172
- // // Handle strings by concatenating
3173
- // if (typeof existingValue === 'string' && typeof newValue === 'string') {
3174
- // return existingValue
3175
- // }
3176
-
3177
- // Default: use the new value
3178
- return newValue
3179
- }
3180
-
3181
-
3182
-
3183
- ;// ./lib/models/metadata/index.js
3184
-
3185
-
3186
-
3187
-
3188
- ;// ./lib/models/qMeta/qMeta.js
3189
-
3190
-
3191
- const updateAllowedProps = [
3192
- 'attributes',
3193
- 'ref'
3194
- ]
3195
-
3196
- class QMeta {
3197
- constructor(options = {}) {
3198
- options = options || {}
3199
- this.attributes = KeyValueObject.initOnlyValidFromArray(options.attributes)
3200
- this.ref = options.ref || {}
3201
- }
3202
-
3203
- static get _classname() {
3204
- return 'QMeta'
3205
- }
3206
- static get _superclass() {
3207
- return 'QMeta'
3208
- }
3209
-
3210
- // Class methods
3211
- static init(options = {}) {
3212
- if (options instanceof QMeta) {
3213
- return options
3214
- }
3215
- return new QMeta(options)
3216
- }
3217
-
3218
- // instance methods
3219
- addAttribute(obj) {
3220
- const kvObject = KeyValueObject.init(obj)
3221
- if (!kvObject) {
3222
- throw new Error('invalid meta attribute')
3223
- }
3224
- this.attributes.push(kvObject)
3225
- return this
3226
- }
3227
-
3228
- update(obj) {
3229
- Object.keys(obj).forEach((key) => {
3230
- if (updateAllowedProps.includes(key)) {
3231
- if (key === 'attributes') {
3232
- this[key] = KeyValueObject.initOnlyValidFromArray(obj[key])
3233
- } else {
3234
- this[key] = obj[key]
3235
- }
3236
- }
3237
- })
3238
- return this
3239
- }
3240
- }
3241
-
3242
-
3243
-
3244
- ;// ./lib/models/qMeta/index.js
3245
-
3246
-
3247
-
3248
-
3249
- ;// ./lib/models/trackedEntity/trackedEntity.js
3250
-
3251
-
3252
- class TrackedEntity {
3253
- constructor(options = {}) {
3254
- options = options || {}
3255
- const timestamp = Date.now()
3256
- this.meta = {
3257
- active: options.meta?.active ?? options.active ?? true,
3258
- created: options.meta?.created ?? (options.created
3259
- ? new Date(options.created).getTime()
3260
- : timestamp),
3261
- creator: options.meta?.creator ?? options.creator ?? '',
3262
- deleted: options.meta?.deleted ?? options.deleted ?? false,
3263
- modified: options.meta?.modified ?? (options.modified
3264
- ? new Date(options.modified).getTime()
3265
- : timestamp),
3266
- owner: options.meta?.owner ?? options.owner ?? '',
3267
- }
3268
-
3269
- // if (trackFlat) {
3270
- // Object.assign(this, _tracking)
3271
- // } else {
3272
- // this.meta = { ..._tracking, ...options.meta }
3273
- // }
3274
- }
3275
-
3276
- // Class methods
3277
- static get _classname() {
3278
- return 'TrackedEntity'
3279
- }
3280
- static get _superclass() {
3281
- return 'TrackedEntity'
3282
- }
3283
-
3284
- static init(options = {}) {
3285
- return init(this, options)
3286
- }
3287
- static initFromArray(arr = []) {
3288
- return initFromArray(this, arr)
3289
- }
3290
- static initOnlyValidFromArray(arr = []) {
3291
- return initOnlyValidFromArray(this, arr)
3292
- }
3293
- // static nest(entity) {
3294
- // const { active, created, creator, deleted, modified, owner, ...rest } = entity
3295
- // return { ...rest, meta: { active, created, creator, deleted, modified, owner } }
3296
- // }
3297
-
3298
- // getters
3299
- get isValid() {
3300
- return !!this
3301
- }
3302
- get active() {
3303
- return this.meta?.active ?? this.active
3304
- }
3305
- get created() {
3306
- return this.meta?.created ?? this.created
3307
- }
3308
- get creator() {
3309
- return this.meta?.creator ?? this.creator
3310
- }
3311
- get deleted() {
3312
- return this.meta?.deleted ?? this.deleted
3313
- }
3314
- get modified() {
3315
- return this.meta?.modified ?? this.modified
3316
- }
3317
- get owner() {
3318
- return this.meta?.owner ?? this.owner
3319
- }
3320
- changeCreatorOwner({ source, target }) {
3321
- return changeCreatorOwner(this, { source, target }).setModified()
3322
- }
3323
- delete() {
3324
- return this.setDeleted()
3325
- }
3326
- setActive() {
3327
- if (this.meta) {
3328
- this.meta.active = true
3329
- } else {
3330
- this.active = true
3331
- }
3332
- return this
3333
- }
3334
- setDeleted() {
3335
- if (this.meta) {
3336
- this.meta.deleted = true
3337
- } else {
3338
- this.deleted = true
3339
- }
3340
- return this
3341
- }
3342
- setModified() {
3343
- const timestamp = Date.now()
3344
- if (this.meta) {
3345
- this.meta.modified = timestamp
3346
- } else {
3347
- this.modified = timestamp
3348
- }
3349
- return this
3350
- }
3351
- setOwner(owner) {
3352
- if (!owner) {
3353
- return this
3354
- }
3355
- if (this.meta) {
3356
- this.meta.owner = owner
3357
- } else {
3358
- this.owner = owner
3359
- }
3360
- return this
3361
- }
3362
- unsetActive() {
3363
- if (this.meta) {
3364
- this.meta.active = false
3365
- } else {
3366
- this.active = false
3367
- }
3368
- return this
3369
- }
3370
- unsetDeleted() {
3371
- if (this.meta) {
3372
- this.meta.deleted = false
3373
- } else {
3374
- this.deleted = false
3375
- }
3376
- return this
3377
- }
3378
-
3379
- update(update) {
3380
- if (update.meta) {
3381
- this.meta = { ...this.meta, ...update.meta }
3382
- }
3383
- return this.setModified()
3384
- }
3385
- }
3386
-
3387
- ;// ./lib/models/trackedEntity/index.js
3388
-
3389
- // Explicit named export (optional)
3390
-
3391
- ;// ./lib/models/tenantAwareEntity/tenantAwareEntity.js
3392
-
3393
-
3394
-
3395
-
3396
- class TenantAwareEntity extends TrackedEntity {
3397
- constructor(options = {}) {
3398
- options = options || {}
3399
-
3400
- /**
3401
- * instead of throw error, we choose to implement the isValid checking
3402
- */
3403
- // if (!options.tenantCode) {
3404
- // throw new Error('tenantCode required')
3405
- // }
3406
-
3407
- super(options)
3408
-
3409
- this._tenant = options._tenant
3410
-
3411
- this.metadata = Metadata.initOnlyValidFromArray(options.metadata)
3412
- this.remarks = KeyValueObject.initOnlyValidFromArray(options.remarks)
3413
- this.tenantCode = options.tenantCode // Required for multi-tenancy
3414
- }
3415
-
3416
- // Class methods
3417
- static get _classname() {
3418
- return 'TenantAwareEntity'
3419
- }
3420
- static get _superclass() {
3421
- return 'TenantAwareEntity'
3422
- }
3423
-
3424
- // getters
3425
- get isValid() {
3426
- return super.isValid && !!this.tenantCode // Required for multi-tenancy
3427
- }
3428
-
3429
- // instance methods
3430
- getMetadata() {
3431
- return this.metadata
3432
- }
3433
- getMetadataByKey(key) {
3434
- return Metadata.foundByKey(this.metadata, key)
3435
- }
3436
- getMetadataValueByKey(key) {
3437
- const found = this.getMetadataByKey(key)
3438
- return found ? found.value : null
3439
- }
3440
- getRemarks() {
3441
- return this.remarks
3442
- }
3443
- getRemarkByKey(key) {
3444
- return KeyValueObject.foundByKey(this.remarks, key)
3445
- }
3446
- getRemarksValueByKey(key) {
3447
- const found = this.getRemarkByKey(key)
3448
- return found ? found.value : null
3449
- }
3450
- getTenantCode() {
3451
- return this.tenantCode
3452
- }
3453
-
3454
- update(update) {
3455
- if (update.metadata && Array.isArray(update.metadata)) {
3456
- this.metadata = Metadata.initOnlyValidFromArray(update.metadata)
3457
- }
3458
- if (update.remarks && Array.isArray(update.remarks)) {
3459
- this.remarks = KeyValueObject.initOnlyValidFromArray(update.remarks)
3460
- }
3461
- return super.update(update)
3462
- }
3463
- }
3464
-
3465
- ;// ./lib/models/tenantAwareEntity/index.js
3466
-
3467
-
3468
-
3469
- ;// ./lib/models/uniqueKeyGenerator/uniqueKeyGenerator.js
3470
-
3471
-
3472
- class UniqueKeyGenerator {
3473
- static get _classname() {
3474
- return 'UniqueKeyGenerator'
3475
- }
3476
- static get _superclass() {
3477
- return 'UniqueKeyGenerator'
3478
- }
3479
- static makeFormatter({ fieldName, format, options }) {
3480
- switch (format) {
3481
- case 'set_code':
3482
- return _makeSetCode(fieldName, options)
3483
- default:
3484
- return _makeSetCode(fieldName, options)
3485
- }
3486
- }
3487
- static makeGenerator(arr) {
3488
- const fns = arr.map((item) => this.makeFormatter(item))
3489
- return async (obj) => {
3490
- const output = await pReduce(fns, async (acc, fn) => {
3491
- const _obj = await fn(obj)
3492
- return Object.assign(acc, _obj)
3493
- }, obj)
3494
- return output
3495
- }
3496
- }
3497
- }
3498
-
3499
- function _makeSetCode(fieldName, options) {
3500
- return async (obj = {}) => {
3501
- if (obj[fieldName]) {
3502
- return {}
3503
- }
3504
- return {
3505
- [fieldName]: stringHelper.setCode()
3506
- }
3507
- }
3508
- }
3509
-
3510
-
3511
-
3512
- ;// ./lib/models/uniqueKeyGenerator/index.js
3513
-
3514
-
3515
-
3516
-
3517
- ;// ./lib/models/index.js
3518
-
3519
-
3520
-
3521
-
3522
-
3523
-
3524
-
3525
-
3526
-
3527
-
3528
-
3529
-
3530
- ;// ./lib/index.js
3531
-
3532
-
3533
-
3534
- ;// ./index.js
3535
-
3536
-
3537
- export { ApiResponse, AwsStsS3Client, KeyValueObject, Metadata, QMeta, Repo, Service, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, changeCreatorOwner, concatStringByArray, convertString, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, init, initFromArray, initOnlyValidFromArray, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pReduce, padZeros, replacePlaceholders, sanitizeText, stringFormatter, stringHelper, tenantPlugin, trackingPlugin };