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