@questwork/q-utilities 0.1.29 → 0.1.32

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