@questwork/q-utilities 0.1.18 → 0.1.20

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