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