@questwork/q-utilities 0.1.13 → 0.1.15

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.
@@ -50,6 +50,7 @@ __webpack_require__.r(__webpack_exports__);
50
50
  // EXPORTS
51
51
  __webpack_require__.d(__webpack_exports__, {
52
52
  ApiResponse: () => (/* reexport */ ApiResponse),
53
+ AwsStsS3Client: () => (/* reexport */ AwsStsS3Client),
53
54
  KeyValueObject: () => (/* reexport */ KeyValueObject),
54
55
  Metadata: () => (/* reexport */ Metadata),
55
56
  QMeta: () => (/* reexport */ QMeta),
@@ -59,8 +60,13 @@ __webpack_require__.d(__webpack_exports__, {
59
60
  TenantAwareEntity: () => (/* reexport */ TenantAwareEntity),
60
61
  TrackedEntity: () => (/* reexport */ TrackedEntity),
61
62
  UniqueKeyGenerator: () => (/* reexport */ UniqueKeyGenerator),
63
+ authorize: () => (/* reexport */ authorize),
64
+ changeCreatorOwner: () => (/* reexport */ changeCreatorOwner),
62
65
  concatStringByArray: () => (/* reexport */ concatStringByArray),
63
66
  convertString: () => (/* reexport */ convertString),
67
+ escapeRegex: () => (/* reexport */ escapeRegex),
68
+ expressHelper: () => (/* reexport */ expressHelper),
69
+ extractEmails: () => (/* reexport */ extractEmails),
64
70
  formatDate: () => (/* reexport */ formatDate),
65
71
  generalPost: () => (/* reexport */ generalPost),
66
72
  getValidation: () => (/* reexport */ getValidation),
@@ -70,13 +76,75 @@ __webpack_require__.d(__webpack_exports__, {
70
76
  initOnlyValidFromArray: () => (/* reexport */ initOnlyValidFromArray),
71
77
  makeApiResponse: () => (/* reexport */ makeApiResponse),
72
78
  makeService: () => (/* reexport */ makeService),
79
+ mergeArraysByKey: () => (/* reexport */ mergeArraysByKey),
73
80
  objectHelper: () => (/* reexport */ objectHelper),
74
81
  pReduce: () => (/* reexport */ pReduce),
75
82
  padZeros: () => (/* reexport */ padZeros),
83
+ replacePlaceholders: () => (/* reexport */ replacePlaceholders),
84
+ sanitizeText: () => (/* reexport */ sanitizeText),
76
85
  stringFormatter: () => (/* reexport */ stringFormatter),
77
- stringHelper: () => (/* reexport */ stringHelper)
86
+ stringHelper: () => (/* reexport */ stringHelper),
87
+ trackingPlugin: () => (/* reexport */ trackingPlugin)
78
88
  });
79
89
 
90
+ ;// ./lib/helpers/authorize/authorize.js
91
+ function authorize({ allowOwner, query = {}, required, user }) {
92
+ if (!user) {
93
+ throw new Error('Require login.')
94
+ }
95
+ if (!user.permission) {
96
+ throw new Error('You do not have any permission.')
97
+ }
98
+ const scopes = user.permission.getScopes(required || {})
99
+ if (!scopes || scopes.length === 0) {
100
+ throw new Error('You are not allowed in this scope.')
101
+ }
102
+ if (!scopes.includes('*')) {
103
+ query.tenantCode = user.tenantCode
104
+ }
105
+ if (!scopes.includes('TENANT')) {
106
+ query.eventShortCode = user.eventShortCode
107
+ }
108
+ if (!scopes.includes('EVENT')) {
109
+ query.eventRegistrationCode = user.eventRegistrationCode
110
+ }
111
+ if (allowOwner) {
112
+ query.__ALLOW_OWNER = true
113
+ }
114
+ // not good, just use it as example
115
+ if (user.hasExcludedFields) {
116
+ query.__EXCLUDED_FIELDS = user.getExcludedFields(required)
117
+ }
118
+ query.__LOGIN_SUBJECT_CODE = user.loginSubjectCode
119
+ return query
120
+ }
121
+
122
+ ;// ./lib/helpers/authorize/index.js
123
+
124
+
125
+ ;// ./lib/helpers/changeCreatorOwner/changeCreatorOwner.js
126
+ function changeCreatorOwner(that, { source, target }) {
127
+ if (that.meta) {
128
+ if (!that.meta.creator || that.meta.creator === source.getId()) {
129
+ that.meta.creator = target.getId()
130
+ }
131
+ if (!that.meta.owner || that.meta.owner === source.getId()) {
132
+ that.meta.owner = target.getId()
133
+ }
134
+ } else {
135
+ if (!that.creator || that.creator === source.getId()) {
136
+ that.creator = target.getId()
137
+ }
138
+ if (!that.owner || that.owner === source.getId()) {
139
+ that.owner = target.getId()
140
+ }
141
+ }
142
+ return that
143
+ }
144
+
145
+ ;// ./lib/helpers/changeCreatorOwner/index.js
146
+
147
+
80
148
  ;// ./lib/helpers/getValidation/getValidation.js
81
149
  function getValidation(rule, data, getDataByKey, KeyValueObject) {
82
150
  if (!rule) {
@@ -85,10 +153,10 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
85
153
  if (typeof getDataByKey !== 'function' || (KeyValueObject && typeof KeyValueObject !== 'function')) {
86
154
  return false
87
155
  }
88
- const { key = '', value, keyValuePath = '' } = rule
156
+ const { key = '', value, placeholder, keyValuePath = '' } = rule
89
157
  const [valueAttribute] = Object.keys(value)
90
158
 
91
- if (!key) {
159
+ if (!key && typeof placeholder === 'undefined') {
92
160
  switch (valueAttribute) {
93
161
  case '$and': {
94
162
  return value['$and'].reduce((acc, item) => (acc && getValidation(item, data, getDataByKey, KeyValueObject)), true)
@@ -100,14 +168,10 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
100
168
  return false
101
169
  }
102
170
  }
103
-
104
- let rowValue = getDataByKey(key, data)
105
-
106
- // debugger
171
+ let rowValue = typeof placeholder === 'undefined' ? getDataByKey(key, data) : placeholder
107
172
 
108
173
  // if KeyValue object
109
174
  if (keyValuePath) {
110
- console.log('keyValuePath', keyValuePath)
111
175
  const rowValueData = KeyValueObject.toObject(rowValue)
112
176
  rowValue = getDataByKey(keyValuePath, rowValueData)
113
177
  }
@@ -126,6 +190,9 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
126
190
  case '$gte': {
127
191
  return rowValue >= value['$gte']
128
192
  }
193
+ case '$hasOverlap': {
194
+ return _hasOverlap(rowValue, value['$hasOverlap'])
195
+ }
129
196
  case '$lt': {
130
197
  return rowValue < value['$lt']
131
198
  }
@@ -202,6 +269,20 @@ function getValidation(rule, data, getDataByKey, KeyValueObject) {
202
269
  default:
203
270
  return false
204
271
  }
272
+
273
+ }
274
+
275
+ function _hasOverlap(item1, item2) {
276
+ let arr1 = item1
277
+ let arr2 = item2
278
+ if (typeof arr1 === 'string') {
279
+ arr1 = arr1.split(',')
280
+ }
281
+ if (typeof arr2 === 'string') {
282
+ arr2 = arr2.split(',')
283
+ }
284
+ const set1 = new Set(arr1)
285
+ return arr2.find((i) => (set1.has(i)))
205
286
  }
206
287
 
207
288
  /* harmony default export */ const getValidation_getValidation = ({
@@ -338,6 +419,7 @@ const _FN_NAMES = [
338
419
  'lte',
339
420
  'map',
340
421
  'neq',
422
+ 'removeHtml',
341
423
  'toLowerCase',
342
424
  'toUpperCase',
343
425
  ]
@@ -926,6 +1008,66 @@ function _neq(data, args) {
926
1008
 
927
1009
 
928
1010
 
1011
+ ;// ./lib/models/templateCompiler/helpers/_removeHtml.js
1012
+
1013
+
1014
+ function _removeHtml(html, args) {
1015
+ if (html === null || html === undefined) {
1016
+ return null
1017
+ }
1018
+ if (!Array.isArray(args)) {
1019
+ throw new TemplateCompilerException(`_removeHtml: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: args parts must be array`)
1020
+ }
1021
+
1022
+ return _htmlToPlainText(html, args[0])
1023
+ }
1024
+
1025
+ function _htmlToPlainText(html, delimiter = '\n') {
1026
+ if (typeof delimiter !== 'string') {
1027
+ delimiter = '\n'; // Fallback to default if not a string
1028
+ }
1029
+
1030
+ // First decode HTML entities and normalize whitespace
1031
+ const decodedHtml = html
1032
+ .replace(/&nbsp;/g, ' ')
1033
+ .replace(/\s+/g, ' '); // Collapse all whitespace to single spaces
1034
+
1035
+ // Process HTML tags
1036
+ let text = decodedHtml
1037
+ // Replace block tags with temporary marker (~~~)
1038
+ .replace(/<\/?(p|div|h[1-6]|ul|ol|li|pre|section|article|table|tr|td|th)(\s[^>]*)?>/gi, '~~~')
1039
+ // Replace <br> tags with temporary marker (~~~)
1040
+ .replace(/<br\s*\/?>/gi, '~~~')
1041
+ // Remove all other tags
1042
+ .replace(/<[^>]+>/g, '')
1043
+ // Convert markers to specified delimiter
1044
+ .replace(/~~~+/g, delimiter)
1045
+ // Trim and clean whitespace
1046
+ .trim();
1047
+
1048
+ // Special handling for empty delimiter
1049
+ if (delimiter === '') {
1050
+ // Collapse all whitespace to single space
1051
+ text = text.replace(/\s+/g, ' ');
1052
+ } else {
1053
+
1054
+
1055
+ // Collapse multiple delimiters to single
1056
+ text = text.replace(new RegExp(`${escapeRegExp(delimiter)}+`, 'g'), delimiter);
1057
+ // Remove leading/trailing delimiters
1058
+ text = text.replace(new RegExp(`^${escapeRegExp(delimiter)}|${escapeRegExp(delimiter)}$`, 'g'), '');
1059
+ }
1060
+
1061
+ return text;
1062
+ }
1063
+
1064
+
1065
+ function escapeRegExp(string) {
1066
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1067
+ }
1068
+
1069
+
1070
+
929
1071
  ;// ./lib/models/templateCompiler/helpers/_toLowerCase.js
930
1072
 
931
1073
 
@@ -980,6 +1122,7 @@ function _toUpperCase(data, args) {
980
1122
 
981
1123
 
982
1124
 
1125
+
983
1126
 
984
1127
 
985
1128
  ;// ./lib/models/templateCompiler/templateCompiler.js
@@ -1050,6 +1193,9 @@ class TemplateCompiler {
1050
1193
  static neq(data, args) {
1051
1194
  return _neq(data, args)
1052
1195
  }
1196
+ static removeHtml(data, args) {
1197
+ return _removeHtml(data, args)
1198
+ }
1053
1199
  static toLowerCase(data, args) {
1054
1200
  return _toLowerCase(data, args)
1055
1201
  }
@@ -1059,6 +1205,9 @@ class TemplateCompiler {
1059
1205
  static parseFunction(expression) {
1060
1206
  return _parseFunction(expression, _FN_NAMES)
1061
1207
  }
1208
+ static parseParams(parameters) {
1209
+ return _parseParams(parameters)
1210
+ }
1062
1211
 
1063
1212
  pipe(expression = '') {
1064
1213
  this.delimiters = expression.substring(0, 2) === '{{' ? TAGS_HANDLEBAR : TAGS_EJS
@@ -1238,6 +1387,8 @@ function _callFunction(data, functionName, parameters) {
1238
1387
  return _map(data, parameters)
1239
1388
  case 'neq':
1240
1389
  return _neq(data, parameters)
1390
+ case 'removeHtml':
1391
+ return _removeHtml(data, parameters)
1241
1392
  case 'toLowerCase':
1242
1393
  return _toLowerCase(data)
1243
1394
  case 'toUpperCase':
@@ -1354,6 +1505,161 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1354
1505
  ;// ./lib/helpers/convertString/index.js
1355
1506
 
1356
1507
 
1508
+ ;// ./lib/helpers/escapeRegex/escapeRegex.js
1509
+ function escapeRegex(string) {
1510
+ return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1511
+ }
1512
+
1513
+ ;// ./lib/helpers/escapeRegex/index.js
1514
+
1515
+
1516
+ ;// ./lib/helpers/expressHelper/customHandler.js
1517
+ function customHandler({ responseHelper, handler, ignoreError = false }) {
1518
+ return async (req, res, next) => {
1519
+ try {
1520
+ await handler({ req, res })
1521
+ await next()
1522
+ } catch (err) {
1523
+ if (ignoreError) {
1524
+ await next()
1525
+ } else {
1526
+ res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1527
+ }
1528
+ }
1529
+ }
1530
+ }
1531
+
1532
+ ;// ./lib/helpers/expressHelper/findAllResult.js
1533
+ function findAllResult({ responseHelper, service }) {
1534
+ return async (req, res, next) => {
1535
+ try {
1536
+ const { query } = req
1537
+ const result = await service.findAll({ query })
1538
+ res.locals.findAllResult = result
1539
+ await next()
1540
+ } catch (err) {
1541
+ res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1542
+ }
1543
+ }
1544
+ }
1545
+
1546
+ ;// ./lib/helpers/expressHelper/findOneResult.js
1547
+ function findOneResult({ responseHelper, service }) {
1548
+ return async (req, res, next) => {
1549
+ try {
1550
+ const { params, query } = req
1551
+ const { id } = params
1552
+ const result = await service.findOne({
1553
+ query: {
1554
+ ...query,
1555
+ id
1556
+ }
1557
+ })
1558
+ res.locals.findOneResult = result
1559
+ await next()
1560
+ } catch (err) {
1561
+ res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1562
+ }
1563
+ }
1564
+ }
1565
+
1566
+ ;// ./lib/helpers/expressHelper/postResult.js
1567
+ function postResult({ responseHelper, service }) {
1568
+ return async (req, res, next) => {
1569
+ try {
1570
+ const { body } = req
1571
+ let result
1572
+ if (Array.isArray(body)) {
1573
+ result = await service.saveAll({ docs: body })
1574
+ } else {
1575
+ result = await service.saveOne({ doc: body })
1576
+ }
1577
+ res.locals.postResult = result
1578
+ await next()
1579
+ } catch (err) {
1580
+ res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1581
+ }
1582
+ }
1583
+ }
1584
+
1585
+ ;// ./lib/helpers/expressHelper/updateOneResult.js
1586
+ function updateOneResult({ responseHelper, service }) {
1587
+ return async (req, res, next) => {
1588
+ try {
1589
+ const { body, params } = req
1590
+ const { id } = params
1591
+ if (id !== body.id) {
1592
+ throw new Error('id in params and body must be same')
1593
+ }
1594
+ const { data } = await service.findOne({ query: { id } })
1595
+ const doc = data[0]
1596
+ doc.update(body)
1597
+ const result = await service.saveOne({ doc })
1598
+ res.locals.updateOneResult = result
1599
+ await next()
1600
+ } catch (err) {
1601
+ res.status(400).json(responseHelper.standardizeResponse({ err, message: err.message || err }))
1602
+ }
1603
+ }
1604
+ }
1605
+
1606
+ ;// ./lib/helpers/expressHelper/index.js
1607
+
1608
+
1609
+
1610
+
1611
+
1612
+
1613
+
1614
+ const expressHelper = {
1615
+ customHandler: customHandler,
1616
+ findAllResult: findAllResult,
1617
+ findOneResult: findOneResult,
1618
+ postResult: postResult,
1619
+ updateOneResult: updateOneResult,
1620
+ }
1621
+
1622
+ ;// ./lib/helpers/extractEmails/extractEmails.js
1623
+ /**
1624
+ * Extracts and normalizes unique email addresses from an array containing messy entries
1625
+ * @param {Array} dirtyArray - Array that may contain emails in various formats (may include null/empty entries)
1626
+ * @returns {Array} Sorted array of unique, lowercase email addresses
1627
+ */
1628
+ function extractEmails(dirtyArray) {
1629
+ const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
1630
+ const emails = new Set()
1631
+
1632
+ // Handle null/undefined input array
1633
+ if (!dirtyArray) return []
1634
+
1635
+ dirtyArray.forEach(entry => {
1636
+ // Skip null, undefined, empty, or whitespace-only entries
1637
+ if (!entry || typeof entry !== 'string' || !entry.trim()) return
1638
+
1639
+ try {
1640
+ const cleanEntry = entry
1641
+ .replace(/[\u200B-\u200D\uFEFF\u202A-\u202E]/g, '') // Remove hidden chars
1642
+ .replace(/[<>]/g, ' ') // Convert email delimiters to spaces
1643
+ .replace(/\s+/g, ' ') // Collapse multiple whitespace
1644
+ .trim()
1645
+
1646
+ // Extract all email matches
1647
+ const matches = cleanEntry.match(emailRegex)
1648
+ if (matches) {
1649
+ matches.forEach(email => emails.add(email.toLowerCase())) // Normalize to lowercase
1650
+ }
1651
+ } catch (e) {
1652
+ console.warn('Failed to process entry:', entry, e)
1653
+ }
1654
+ })
1655
+
1656
+ // Convert Set to array and sort alphabetically
1657
+ return Array.from(emails).sort((a, b) => a.localeCompare(b))
1658
+ }
1659
+
1660
+ ;// ./lib/helpers/extractEmails/index.js
1661
+
1662
+
1357
1663
  ;// ./lib/helpers/objectHelper/objectHelper.js
1358
1664
  const objectHelper = {
1359
1665
  get(obj, path) {
@@ -1386,42 +1692,59 @@ const objectHelper = {
1386
1692
  },
1387
1693
  merge,
1388
1694
  set(obj, path, value) {
1389
- const parts = path.split('.')
1390
- let current = obj
1695
+ const parts = path.split('.');
1696
+ let current = obj;
1697
+
1698
+ // 处理所有中间部分
1391
1699
  for (let i = 0; i < parts.length - 1; i++) {
1392
- const part = parts[i]
1393
- if (part.endsWith('[]')) {
1394
- // 处理数组遍历
1395
- const key = part.slice(0, -2) // 去掉 '[]' 得到属性名
1396
- if (Array.isArray(current[key])) {
1397
- current[key].forEach((item) => set(item, parts.slice(i + 1).join('.'), value))
1700
+ const part = parts[i];
1701
+ let key, index;
1702
+
1703
+ // 检查是否是数组索引格式,如key[0]
1704
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1705
+ if (arrayMatch) {
1706
+ key = arrayMatch[1];
1707
+ index = parseInt(arrayMatch[2], 10);
1708
+ // 确保当前层级的数组存在
1709
+ if (!current[key] || !Array.isArray(current[key])) {
1710
+ current[key] = [];
1398
1711
  }
1399
- return // 处理完数组后直接返回
1400
- }
1401
- if (part.includes('[') && part.includes(']')) {
1402
- // 处理数组索引
1403
- const arrayMatch = part.match(/(\w+)\[(\d+)\]/)
1404
- if (arrayMatch) {
1405
- const key = arrayMatch[1]
1406
- const index = arrayMatch[2]
1407
- if (Array.isArray(current[key]) && current[key][index]) {
1408
- current = current[key][index]
1409
- } else {
1410
- return // 如果数组或索引不存在,直接返回
1411
- }
1712
+ // 扩展数组到足够大
1713
+ while (current[key].length <= index) {
1714
+ current[key].push(undefined);
1412
1715
  }
1716
+ // 如果当前位置未定义或为null,初始化为对象
1717
+ if (current[key][index] == null) {
1718
+ current[key][index] = {};
1719
+ }
1720
+ current = current[key][index];
1413
1721
  } else {
1414
1722
  // 处理普通属性
1415
1723
  if (!current[part]) {
1416
- current[part] = {} // 如果属性不存在,创建一个空对象
1724
+ current[part] = {};
1417
1725
  }
1418
- current = current[part]
1726
+ current = current[part];
1419
1727
  }
1420
1728
  }
1421
-
1422
- // 设置最终属性值
1423
- const lastPart = parts[parts.length - 1]
1424
- current[lastPart] = value
1729
+
1730
+ // 处理最后一部分
1731
+ const lastPart = parts[parts.length - 1];
1732
+ const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
1733
+ if (arrayMatch) {
1734
+ const key = arrayMatch[1];
1735
+ const index = parseInt(arrayMatch[2], 10);
1736
+ // 确保数组存在
1737
+ if (!current[key] || !Array.isArray(current[key])) {
1738
+ current[key] = [];
1739
+ }
1740
+ // 扩展数组到所需索引
1741
+ while (current[key].length <= index) {
1742
+ current[key].push(undefined);
1743
+ }
1744
+ current[key][index] = value;
1745
+ } else {
1746
+ current[lastPart] = value;
1747
+ }
1425
1748
  }
1426
1749
  }
1427
1750
 
@@ -1490,6 +1813,8 @@ async function pReduce(iterable, reducer, initialValue) {
1490
1813
 
1491
1814
 
1492
1815
  ;// ./lib/models/repo/repo.js
1816
+
1817
+
1493
1818
  class Repo {
1494
1819
  constructor(options) {
1495
1820
  options = options || {}
@@ -1502,11 +1827,7 @@ class Repo {
1502
1827
  : null
1503
1828
  }
1504
1829
  static init(options = {}) {
1505
- if (options instanceof this) {
1506
- return options
1507
- }
1508
- const instance = new this(options)
1509
- return instance.isValid ? instance : null
1830
+ return init(this, options)
1510
1831
  }
1511
1832
  static get _classname() {
1512
1833
  return 'Repo'
@@ -1698,6 +2019,11 @@ function _makeLog({ systemLog, label, message: message1, input } = {}) {
1698
2019
 
1699
2020
 
1700
2021
 
2022
+ ;// ./lib/models/repo/index.js
2023
+
2024
+
2025
+
2026
+
1701
2027
  ;// ./lib/models/apiResponse/apiResponse.js
1702
2028
  class ApiResponse {
1703
2029
  constructor(options = {}) {
@@ -1842,6 +2168,11 @@ function makeService({ repo }) {
1842
2168
 
1843
2169
 
1844
2170
 
2171
+ ;// ./lib/models/service/index.js
2172
+
2173
+
2174
+
2175
+
1845
2176
  ;// ./lib/helpers/generalPost/generalPost.js
1846
2177
 
1847
2178
 
@@ -1866,7 +2197,13 @@ async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator, resour
1866
2197
  function _attachShared(data, globalShared = {}, shared = {}) {
1867
2198
  Object.keys(shared).forEach((key) => {
1868
2199
  const _data = data[key]
1869
- data[key] = objectHelper.merge({}, _data, globalShared, shared[key] || {})
2200
+ if (Array.isArray(_data)) {
2201
+ data[key] = _data.map((_dataItem) => {
2202
+ return objectHelper.merge({}, _dataItem, globalShared, shared[key] || {})
2203
+ })
2204
+ } else {
2205
+ data[key] = objectHelper.merge({}, _data, globalShared, shared[key] || {})
2206
+ }
1870
2207
  })
1871
2208
  }
1872
2209
 
@@ -1901,7 +2238,7 @@ function init(_class, options) {
1901
2238
  }
1902
2239
  try {
1903
2240
  const instance = new _class(options)
1904
- return instance.isValid ? instance : null
2241
+ return instance.isValid !== false ? instance : null
1905
2242
  } catch (e) {
1906
2243
  console.log(`init failed for class: ${_class._classname || 'no _classname'}`, e)
1907
2244
  return null
@@ -1934,6 +2271,64 @@ function initOnlyValidFromArray(_class, arr) {
1934
2271
  ;// ./lib/helpers/initOnlyValidFromArray/index.js
1935
2272
 
1936
2273
 
2274
+ ;// ./lib/helpers/mergeArraysByKey/mergeArraysByKey.js
2275
+ function mergeArraysByKey(arr1, arr2) {
2276
+ // Handle undefined/null inputs by defaulting to empty arrays
2277
+ const safeArr1 = Array.isArray(arr1) ? arr1 : []
2278
+ const safeArr2 = Array.isArray(arr2) ? arr2 : []
2279
+
2280
+ const mergedMap = new Map()
2281
+
2282
+ // Helper function to merge values based on their type
2283
+ const mergeValues = (existingValue, newValue) => {
2284
+ if (existingValue === undefined) return newValue
2285
+
2286
+ // Handle arrays by concatenating
2287
+ if (Array.isArray(existingValue) && Array.isArray(newValue)) {
2288
+ return [...new Set([...existingValue, ...newValue])]
2289
+ }
2290
+
2291
+ // Handle objects by merging
2292
+ if (typeof existingValue === 'object' && typeof newValue === 'object' &&
2293
+ !Array.isArray(existingValue) && !Array.isArray(newValue)) {
2294
+ return { ...existingValue, ...newValue }
2295
+ }
2296
+
2297
+ // // Handle numbers by adding
2298
+ // if (typeof existingValue === 'number' && typeof newValue === 'number') {
2299
+ // return existingValue
2300
+ // }
2301
+
2302
+ // // Handle strings by concatenating
2303
+ // if (typeof existingValue === 'string' && typeof newValue === 'string') {
2304
+ // return existingValue
2305
+ // }
2306
+
2307
+ // Default: use the new value
2308
+ return newValue
2309
+ }
2310
+
2311
+ // Process first array
2312
+ safeArr1.forEach(item => {
2313
+ mergedMap.set(item.key, item.value)
2314
+ })
2315
+
2316
+ // Process second array and merge values
2317
+ safeArr2.forEach(item => {
2318
+ const existingValue = mergedMap.get(item.key)
2319
+ mergedMap.set(item.key, mergeValues(existingValue, item.value))
2320
+ })
2321
+
2322
+ // Convert back to array format
2323
+ return Array.from(mergedMap.entries()).map(([key, value]) => ({
2324
+ key,
2325
+ value
2326
+ }))
2327
+ }
2328
+
2329
+ ;// ./lib/helpers/mergeArraysByKey/index.js
2330
+
2331
+
1937
2332
  ;// ./lib/helpers/padZeros/padZeros.js
1938
2333
  function padZeros(num, minLength = 6) {
1939
2334
  num = num.toString()
@@ -1955,6 +2350,102 @@ function padZeros(num, minLength = 6) {
1955
2350
 
1956
2351
 
1957
2352
 
2353
+ ;// ./lib/helpers/replacePlaceholders/replacePlaceholders.js
2354
+ function replacePlaceholders({ content, mapping }) {
2355
+ let isObjectMode = false
2356
+
2357
+ if (typeof content === 'object' && content !== null) {
2358
+ content = JSON.stringify(content)
2359
+ isObjectMode = true
2360
+ }
2361
+
2362
+ // [[ eventRegistration.eventRegistrationCode | 0 ]]
2363
+ const regex = /(\[*)\[\[\s*([\w.\-_]+)(?:\s*\|\s*([^:\]\s]+))?(?:\s*:\s*([^\]\s]+))?\s*\]\](\]*)/g;
2364
+
2365
+ const result = content.replace(regex, (match, leadingBrackets, path, defaultValue, type, trailingBrackets) => {
2366
+
2367
+ // Split the path into parts
2368
+ const keys = path.trim().split('.')
2369
+
2370
+ // Traverse the nested object structure
2371
+ let value = mapping
2372
+ for (const key of keys) {
2373
+ // Handle empty keys (in case of double dots or leading/trailing dots)
2374
+ if (!key) continue
2375
+
2376
+ value = value?.[key]
2377
+ if (value === undefined) {
2378
+ break
2379
+ }
2380
+ }
2381
+
2382
+ // Apply default if missing
2383
+ if (value === undefined) value = defaultValue?.trim();
2384
+ if (value === undefined) return isObjectMode ? undefined : match;
2385
+
2386
+ value = value !== undefined
2387
+ ? leadingBrackets + value + trailingBrackets
2388
+ : match
2389
+
2390
+ // Return replacement or original if not found
2391
+ return value
2392
+ })
2393
+
2394
+ if (isObjectMode) {
2395
+ return JSON.parse(result)
2396
+ }
2397
+ return result
2398
+ }
2399
+
2400
+ ;// ./lib/helpers/replacePlaceholders/index.js
2401
+
2402
+
2403
+ ;// ./lib/helpers/sanitizeText/sanitizeText.js
2404
+ /**
2405
+ * Sanitizes input by removing hidden/control characters with customizable whitespace handling.
2406
+ * @param {string} input - The string to sanitize.
2407
+ * @param {Object} [options] - Configuration options.
2408
+ * @param {boolean} [options.normalizeWhitespace=true] - Collapse multiple spaces/tabs into one space.
2409
+ * @param {boolean} [options.removeNewlines=false] - If true, replaces newlines with spaces.
2410
+ * @param {boolean} [options.trim=true] - If true, trims leading/trailing whitespace.
2411
+ * @returns {string} The sanitized string.
2412
+ */
2413
+ function sanitizeText(input, options = {}) {
2414
+ const {
2415
+ normalizeWhitespace = true,
2416
+ removeNewlines = false,
2417
+ trim = true,
2418
+ } = options
2419
+
2420
+ if (typeof input !== 'string') {
2421
+ return input
2422
+ }
2423
+
2424
+ let result = input
2425
+
2426
+ // Phase 1: Remove hidden/control characters
2427
+ result = result.replace(/[\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
2428
+
2429
+ // Phase 2: Handle whitespace transformations
2430
+ if (removeNewlines) {
2431
+ result = result.replace(/[\r\n]+/g, ' ') // Convert newlines to spaces
2432
+ }
2433
+
2434
+ if (normalizeWhitespace) {
2435
+ result = result.replace(/[ \t]+/g, ' ') // Collapse spaces/tabs to single space
2436
+ }
2437
+
2438
+ // Phase 3: Final trimming
2439
+ if (trim) {
2440
+ result = result.trim()
2441
+ }
2442
+
2443
+ return result
2444
+ }
2445
+
2446
+ ;// ./lib/helpers/sanitizeText/index.js
2447
+
2448
+
1958
2449
  ;// ./lib/helpers/stringFormatter/stringFormatter.js
1959
2450
  function stringFormatter(str, delimiter = '_') {
1960
2451
  if (str === null || typeof str === 'undefined' || typeof str.toString === 'undefined') {
@@ -2080,10 +2571,18 @@ function toCamelCase(str) {
2080
2571
  .join('')
2081
2572
  }
2082
2573
 
2574
+ function toLowerCase(str) {
2575
+ if (!str) return ''
2576
+ return str
2577
+ .trim()
2578
+ .toLowerCase()
2579
+ }
2580
+
2083
2581
  const stringHelper = {
2084
2582
  isSame,
2085
2583
  setCode,
2086
2584
  toCamelCase,
2585
+ toLowerCase,
2087
2586
  }
2088
2587
 
2089
2588
 
@@ -2093,6 +2592,36 @@ const stringHelper = {
2093
2592
 
2094
2593
 
2095
2594
 
2595
+ ;// ./lib/helpers/trackingPlugin/trackingPlugin.js
2596
+ function trackingPlugin(schema, options) {
2597
+ // Add meta fields
2598
+ schema.add({
2599
+ meta: {
2600
+ active: { type: Boolean, default: true },
2601
+ created: { type: Number },
2602
+ creator: { type: String },
2603
+ deleted: { type: Boolean, default: false },
2604
+ modified: { type: Number },
2605
+ owner: { type: String },
2606
+ }
2607
+ })
2608
+
2609
+ // Auto-update hook
2610
+ schema.pre('save', function(next) {
2611
+ this.meta.modified = Date.now()
2612
+ next()
2613
+ })
2614
+
2615
+ // Optional: Add helper methods
2616
+ // schema.methods.touch = function(userId) {
2617
+ // this.meta.updatedAt = new Date()
2618
+ // this.meta.updatedBy = userId
2619
+ // }
2620
+ }
2621
+
2622
+ ;// ./lib/helpers/trackingPlugin/index.js
2623
+
2624
+
2096
2625
  ;// ./lib/helpers/index.js
2097
2626
 
2098
2627
 
@@ -2105,6 +2634,15 @@ const stringHelper = {
2105
2634
 
2106
2635
 
2107
2636
 
2637
+
2638
+
2639
+
2640
+
2641
+
2642
+
2643
+
2644
+
2645
+
2108
2646
 
2109
2647
 
2110
2648
 
@@ -2115,6 +2653,196 @@ const stringHelper = {
2115
2653
 
2116
2654
 
2117
2655
 
2656
+ ;// ./lib/models/awsStsS3Client/awsStsS3Client.js
2657
+ class AwsStsS3Client {
2658
+ constructor(options) {
2659
+ options = options || {}
2660
+
2661
+ this.expiration = options.expiration || null
2662
+ this.s3Client = options.s3Client || null
2663
+ this.getIdToken = options.getIdToken
2664
+ this.region = options.region || 'ap-east-1'
2665
+ this.roleArn = options.roleArn
2666
+ this.roleSessionName = options.roleSessionName || 'web-identity-session'
2667
+ this.durationSession = options.durationSession || 3600
2668
+ this.awsClientSts = options.awsClientSts
2669
+ this.awsClientS3 = options.awsClientS3
2670
+ }
2671
+
2672
+ static dummyData() {
2673
+ return {
2674
+ getIdToken: () => 'mock-web-identity-token',
2675
+ roleArn: 'arn:aws:iam::846252828949:role/oidcS3Jccpa',
2676
+ awsClientSts: {
2677
+ STSClient: class {},
2678
+ AssumeRoleWithWebIdentityCommand: class {}
2679
+ },
2680
+ awsClientS3: {
2681
+ S3Client: class {},
2682
+ PutObjectCommand: class {},
2683
+ GetObjectCommand: class {},
2684
+ DeleteObjectCommand: class {}
2685
+ }
2686
+ }
2687
+ }
2688
+
2689
+ static init(options = {}) {
2690
+ if (options instanceof this) {
2691
+ return options
2692
+ }
2693
+ try {
2694
+ const instance = new this(options)
2695
+ if (!instance.isValid) {
2696
+ return null
2697
+ }
2698
+ return instance
2699
+ } catch (error) {
2700
+ return null
2701
+ }
2702
+ }
2703
+
2704
+ get isExpired() {
2705
+ if (!this.expiration) return true;
2706
+ const now = new Date();
2707
+ const bufferMs = 1 * 60 * 1000; // 一分钟缓冲
2708
+ return now >= new Date(this.expiration.getTime() - bufferMs);
2709
+ }
2710
+
2711
+ get isValid() {
2712
+ if (!this.getIdToken) {
2713
+ throw new Error('Missing required configuration: getIdToken function')
2714
+ }
2715
+ if (!this.roleArn) {
2716
+ throw new Error('Missing required configuration: roleArn')
2717
+ }
2718
+ if (!this.awsClientSts) {
2719
+ throw new Error('Missing required AWS awsClientSts client configuration')
2720
+ }
2721
+ if (!this.awsClientSts.STSClient) {
2722
+ throw new Error('Missing STSClient in AWS awsClientSts client configuration')
2723
+ }
2724
+ if (!this.awsClientSts.AssumeRoleWithWebIdentityCommand) {
2725
+ throw new Error('Missing AssumeRoleWithWebIdentityCommand in AWS awsClientSts client configuration')
2726
+ }
2727
+ if (!this.awsClientS3) {
2728
+ throw new Error('Missing required AWS awsClientS3 client configuration')
2729
+ }
2730
+
2731
+ const requiredS3Components = [
2732
+ 'S3Client',
2733
+ 'PutObjectCommand',
2734
+ 'GetObjectCommand',
2735
+ 'DeleteObjectCommand'
2736
+ ]
2737
+
2738
+ for (const component of requiredS3Components) {
2739
+ if (!this.awsClientS3[component]) {
2740
+ throw new Error(`Missing ${component} in AWS awsClientS3 client configuration`)
2741
+ }
2742
+ }
2743
+
2744
+ return true
2745
+ }
2746
+
2747
+ async refreshCredentials() {
2748
+ try {
2749
+ const webIdentityToken = await this.getIdToken()
2750
+ if (!webIdentityToken) {
2751
+ throw new Error('getIdToken function returned empty or invalid token')
2752
+ }
2753
+
2754
+ const stsClient = new this.awsClientSts.STSClient({ region: this.region })
2755
+
2756
+ const stsResponse = await stsClient.send(
2757
+ new this.awsClientSts.AssumeRoleWithWebIdentityCommand({
2758
+ RoleArn: this.roleArn,
2759
+ RoleSessionName: this.roleSessionName,
2760
+ WebIdentityToken: await this.getIdToken(),
2761
+ DurationSeconds: this.durationSession,
2762
+ })
2763
+ )
2764
+
2765
+ const credentials = stsResponse.Credentials
2766
+ if (!credentials) {
2767
+ throw new Error('No credentials returned from awsClientSts')
2768
+ }
2769
+
2770
+ this.expiration = credentials.Expiration
2771
+
2772
+ this.s3Client = new this.awsClientS3.S3Client({
2773
+ region: this.region,
2774
+ credentials: {
2775
+ accessKeyId: credentials.AccessKeyId,
2776
+ secretAccessKey: credentials.SecretAccessKey,
2777
+ sessionToken: credentials.SessionToken,
2778
+ }
2779
+ })
2780
+
2781
+ return this
2782
+ } catch (error) {
2783
+ throw new Error(`Failed to refresh credentials: ${error.message}`)
2784
+ }
2785
+ }
2786
+
2787
+ async getS3Client() {
2788
+ if (this.isExpired || !this.s3Client) {
2789
+ await this.refreshCredentials()
2790
+ }
2791
+ return this.s3Client
2792
+ }
2793
+
2794
+ async putObject(params) {
2795
+ try {
2796
+ const client = await this.getS3Client()
2797
+ const command = new this.awsClientS3.PutObjectCommand(params)
2798
+ await client.send(command)
2799
+ const fileArr = params.Key.split('/')
2800
+ return {
2801
+ url: `https://s3.${this.region}.amazonaws.com/${params.Bucket}/${params.Key}`,
2802
+ filename: fileArr.pop(),
2803
+ folder: params.Bucket,
2804
+ subFolders: fileArr
2805
+ }
2806
+ } catch (error) {
2807
+ throw new Error(`Failed to put object: ${error.message}`)
2808
+ }
2809
+ }
2810
+
2811
+ async getObject(params) {
2812
+ try {
2813
+ const client = await this.getS3Client()
2814
+ const command = new this.awsClientS3.GetObjectCommand(params)
2815
+ const response = await client.send(command)
2816
+ return {
2817
+ body: response.Body,
2818
+ contentType: response.ContentType,
2819
+ lastModified: response.LastModified,
2820
+ contentLength: response.ContentLength,
2821
+ }
2822
+ } catch (error) {
2823
+ throw new Error(`Failed to get object: ${error.message}`)
2824
+ }
2825
+ }
2826
+
2827
+ async deleteObject(params) {
2828
+ try {
2829
+ const client = await this.getS3Client()
2830
+ const command = new this.awsClientS3.DeleteObjectCommand(params)
2831
+ await client.send(command)
2832
+ return true
2833
+ } catch (error) {
2834
+ throw new Error(`Failed to delete object: ${error.message}`)
2835
+ }
2836
+ }
2837
+ }
2838
+
2839
+
2840
+
2841
+ ;// ./lib/models/awsStsS3Client/index.js
2842
+
2843
+
2844
+
2845
+
2118
2846
  ;// ./lib/models/keyValueObject/keyValueObject.js
2119
2847
  class KeyValueObject {
2120
2848
  constructor(options = {}) {
@@ -2242,7 +2970,7 @@ class KeyValueObject {
2242
2970
  return to.key === from.key
2243
2971
  })
2244
2972
  if (found) {
2245
- found.value = (found.value || []).concat(from.value)
2973
+ found.value = _mergeValues(from.value, found.value)
2246
2974
  } else {
2247
2975
  toArr.push(from)
2248
2976
  }
@@ -2327,6 +3055,34 @@ class KeyValueObject {
2327
3055
  }
2328
3056
  }
2329
3057
 
3058
+ function _mergeValues(existingValue, newValue) {
3059
+ if (existingValue === undefined) return newValue
3060
+
3061
+ // Handle arrays by concatenating
3062
+ if (Array.isArray(existingValue) && Array.isArray(newValue)) {
3063
+ return [...new Set([...existingValue, ...newValue])]
3064
+ }
3065
+
3066
+ // Handle objects by merging
3067
+ if (typeof existingValue === 'object' && typeof newValue === 'object' &&
3068
+ !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3069
+ return { ...existingValue, ...newValue }
3070
+ }
3071
+
3072
+ // // Handle numbers by adding
3073
+ // if (typeof existingValue === 'number' && typeof newValue === 'number') {
3074
+ // return existingValue
3075
+ // }
3076
+
3077
+ // // Handle strings by concatenating
3078
+ // if (typeof existingValue === 'string' && typeof newValue === 'string') {
3079
+ // return existingValue
3080
+ // }
3081
+
3082
+ // Default: use the new value
3083
+ return newValue
3084
+ }
3085
+
2330
3086
  function _isSame(key1, key2) {
2331
3087
  return key1 === key2
2332
3088
  }
@@ -2365,7 +3121,7 @@ class Metadata extends KeyValueObject {
2365
3121
  return metadata_isSame(to.key, from.key)
2366
3122
  })
2367
3123
  if (found) {
2368
- found.value = (found.value || []).concat(from.value)
3124
+ found.value = metadata_mergeValues(from.value, found.value)
2369
3125
  } else {
2370
3126
  toArr.push(from)
2371
3127
  }
@@ -2381,6 +3137,34 @@ function metadata_isSame(key1, key2) {
2381
3137
  return stringFormatter(key1, DELIMITER) === stringFormatter(key2, DELIMITER)
2382
3138
  }
2383
3139
 
3140
+ function metadata_mergeValues(existingValue, newValue) {
3141
+ if (existingValue === undefined) return newValue
3142
+
3143
+ // Handle arrays by concatenating
3144
+ if (Array.isArray(existingValue) && Array.isArray(newValue)) {
3145
+ return [...new Set([...existingValue, ...newValue])]
3146
+ }
3147
+
3148
+ // Handle objects by merging
3149
+ if (typeof existingValue === 'object' && typeof newValue === 'object' &&
3150
+ !Array.isArray(existingValue) && !Array.isArray(newValue)) {
3151
+ return { ...existingValue, ...newValue }
3152
+ }
3153
+
3154
+ // // Handle numbers by adding
3155
+ // if (typeof existingValue === 'number' && typeof newValue === 'number') {
3156
+ // return existingValue
3157
+ // }
3158
+
3159
+ // // Handle strings by concatenating
3160
+ // if (typeof existingValue === 'string' && typeof newValue === 'string') {
3161
+ // return existingValue
3162
+ // }
3163
+
3164
+ // Default: use the new value
3165
+ return newValue
3166
+ }
3167
+
2384
3168
 
2385
3169
 
2386
3170
  ;// ./lib/models/metadata/index.js
@@ -2449,21 +3233,12 @@ class QMeta {
2449
3233
 
2450
3234
 
2451
3235
 
2452
- ;// ./lib/models/repo/index.js
2453
-
2454
-
2455
-
2456
-
2457
- ;// ./lib/models/service/index.js
2458
-
2459
-
2460
-
2461
-
2462
3236
  ;// ./lib/models/trackedEntity/trackedEntity.js
2463
3237
 
2464
3238
 
2465
3239
  class TrackedEntity {
2466
- constructor(options = {}, { trackFlat = false } = {}) {
3240
+ constructor(options = {}) {
3241
+ options = options || {}
2467
3242
  const timestamp = Date.now()
2468
3243
  const _tracking = {
2469
3244
  active: options.active ?? true,
@@ -2474,11 +3249,13 @@ class TrackedEntity {
2474
3249
  owner: options.owner ?? '',
2475
3250
  }
2476
3251
 
2477
- if (trackFlat) {
2478
- Object.assign(this, _tracking)
2479
- } else {
2480
- this.meta = { ..._tracking, ...options.meta }
2481
- }
3252
+ this.meta = { ..._tracking, ...options.meta }
3253
+
3254
+ // if (trackFlat) {
3255
+ // Object.assign(this, _tracking)
3256
+ // } else {
3257
+ // this.meta = { ..._tracking, ...options.meta }
3258
+ // }
2482
3259
  }
2483
3260
 
2484
3261
  // Class methods
@@ -2498,16 +3275,55 @@ class TrackedEntity {
2498
3275
  static initOnlyValidFromArray(arr = []) {
2499
3276
  return initOnlyValidFromArray(this, arr)
2500
3277
  }
2501
- static nest(entity) {
2502
- const { active, created, creator, deleted, modified, owner, ...rest } = entity
2503
- return { ...rest, meta: { active, created, creator, deleted, modified, owner } }
2504
- }
3278
+ // static nest(entity) {
3279
+ // const { active, created, creator, deleted, modified, owner, ...rest } = entity
3280
+ // return { ...rest, meta: { active, created, creator, deleted, modified, owner } }
3281
+ // }
2505
3282
 
2506
3283
  // getters
2507
3284
  get isValid() {
2508
3285
  return !!this
2509
3286
  }
2510
-
3287
+ get active() {
3288
+ return this.meta?.active ?? this.active
3289
+ }
3290
+ get created() {
3291
+ return this.meta?.created ?? this.created
3292
+ }
3293
+ get creator() {
3294
+ return this.meta?.creator ?? this.creator
3295
+ }
3296
+ get deleted() {
3297
+ return this.meta?.deleted ?? this.deleted
3298
+ }
3299
+ get modified() {
3300
+ return this.meta?.modified ?? this.modified
3301
+ }
3302
+ get owner() {
3303
+ return this.meta?.owner ?? this.owner
3304
+ }
3305
+ changeCreatorOwner({ source, target }) {
3306
+ return changeCreatorOwner(this, { source, target }).setModified()
3307
+ }
3308
+ delete() {
3309
+ return this.setDeleted()
3310
+ }
3311
+ setActive() {
3312
+ if (this.meta) {
3313
+ this.meta.active = true
3314
+ } else {
3315
+ this.active = true
3316
+ }
3317
+ return this
3318
+ }
3319
+ setDeleted() {
3320
+ if (this.meta) {
3321
+ this.meta.deleted = true
3322
+ } else {
3323
+ this.deleted = true
3324
+ }
3325
+ return this
3326
+ }
2511
3327
  setModified() {
2512
3328
  const timestamp = Date.now()
2513
3329
  if (this.meta) {
@@ -2515,6 +3331,41 @@ class TrackedEntity {
2515
3331
  } else {
2516
3332
  this.modified = timestamp
2517
3333
  }
3334
+ return this
3335
+ }
3336
+ setOwner(owner) {
3337
+ if (!owner) {
3338
+ return this
3339
+ }
3340
+ if (this.meta) {
3341
+ this.meta.owner = owner
3342
+ } else {
3343
+ this.owner = owner
3344
+ }
3345
+ return this
3346
+ }
3347
+ unsetActive() {
3348
+ if (this.meta) {
3349
+ this.meta.active = false
3350
+ } else {
3351
+ this.active = false
3352
+ }
3353
+ return this
3354
+ }
3355
+ unsetDeleted() {
3356
+ if (this.meta) {
3357
+ this.meta.deleted = false
3358
+ } else {
3359
+ this.deleted = false
3360
+ }
3361
+ return this
3362
+ }
3363
+
3364
+ update(update) {
3365
+ if (update.meta) {
3366
+ this.meta = { ...this.meta, ...update.meta }
3367
+ }
3368
+ return this.setModified()
2518
3369
  }
2519
3370
  }
2520
3371
 
@@ -2525,6 +3376,8 @@ class TrackedEntity {
2525
3376
  ;// ./lib/models/tenantAwareEntity/tenantAwareEntity.js
2526
3377
 
2527
3378
 
3379
+
3380
+
2528
3381
  class TenantAwareEntity extends TrackedEntity {
2529
3382
  constructor(options = {}) {
2530
3383
  options = options || {}
@@ -2539,6 +3392,9 @@ class TenantAwareEntity extends TrackedEntity {
2539
3392
  super(options)
2540
3393
 
2541
3394
  this._tenant = options._tenant
3395
+
3396
+ this.metadata = Metadata.initOnlyValidFromArray(options.metadata)
3397
+ this.remarks = KeyValueObject.initOnlyValidFromArray(options.remarks)
2542
3398
  this.tenantCode = options.tenantCode // Required for multi-tenancy
2543
3399
  }
2544
3400
 
@@ -2554,6 +3410,41 @@ class TenantAwareEntity extends TrackedEntity {
2554
3410
  get isValid() {
2555
3411
  return super.isValid && !!this.tenantCode // Required for multi-tenancy
2556
3412
  }
3413
+
3414
+ // instance methods
3415
+ getMetadata() {
3416
+ return this.metadata
3417
+ }
3418
+ getMetadataByKey(key) {
3419
+ return Metadata.foundByKey(this.metadata, key)
3420
+ }
3421
+ getMetadataValueByKey(key) {
3422
+ const found = this.getMetadataByKey(key)
3423
+ return found ? found.value : null
3424
+ }
3425
+ getRemarks() {
3426
+ return this.remarks
3427
+ }
3428
+ getRemarkByKey(key) {
3429
+ return KeyValueObject.foundByKey(this.remarks, key)
3430
+ }
3431
+ getRemarksValueByKey(key) {
3432
+ const found = this.getRemarkByKey(key)
3433
+ return found ? found.value : null
3434
+ }
3435
+ getTenantCode() {
3436
+ return this.tenantCode
3437
+ }
3438
+
3439
+ update(update) {
3440
+ if (update.metadata && Array.isArray(update.metadata)) {
3441
+ this.metadata = Metadata.initOnlyValidFromArray(update.metadata)
3442
+ }
3443
+ if (update.remarks && Array.isArray(update.remarks)) {
3444
+ this.remarks = KeyValueObject.initOnlyValidFromArray(update.remarks)
3445
+ }
3446
+ return super.update(update)
3447
+ }
2557
3448
  }
2558
3449
 
2559
3450
  ;// ./lib/models/tenantAwareEntity/index.js
@@ -2620,6 +3511,7 @@ function _makeSetCode(fieldName, options) {
2620
3511
 
2621
3512
 
2622
3513
 
3514
+
2623
3515
  ;// ./lib/index.js
2624
3516
 
2625
3517