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