@questwork/q-utilities 0.1.29 → 0.1.30
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.
- package/dist/index.min.cjs +351 -17
- package/dist/q-utilities.esm.js +349 -18
- package/dist/q-utilities.min.js +351 -17
- package/package.json +2 -1
package/dist/index.min.cjs
CHANGED
|
@@ -89,6 +89,9 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
89
89
|
makeService: () => (/* reexport */ makeService),
|
|
90
90
|
mergeArraysByKey: () => (/* reexport */ mergeArraysByKey),
|
|
91
91
|
objectHelper: () => (/* reexport */ objectHelper),
|
|
92
|
+
pMap: () => (/* reexport */ pMap),
|
|
93
|
+
pMapIterable: () => (/* reexport */ pMapIterable),
|
|
94
|
+
pMapSkip: () => (/* reexport */ pMapSkip),
|
|
92
95
|
pReduce: () => (/* reexport */ pReduce),
|
|
93
96
|
padZeros: () => (/* reexport */ padZeros),
|
|
94
97
|
printControlCharReport: () => (/* reexport */ printControlCharReport),
|
|
@@ -1349,15 +1352,19 @@ class Repo {
|
|
|
1349
1352
|
}
|
|
1350
1353
|
|
|
1351
1354
|
// systemLog is optional
|
|
1352
|
-
findAll({ query, systemLog }) {
|
|
1355
|
+
findAll({ config = {}, query, systemLog }) {
|
|
1353
1356
|
const log = _makeLog({
|
|
1354
1357
|
systemLog,
|
|
1355
1358
|
label: 'REPO_READ',
|
|
1356
1359
|
message: `fn ${this._classname}.prototype.findAll`,
|
|
1357
1360
|
input: [{ query: { ...query }, systemLog: { ...systemLog } }]
|
|
1358
1361
|
})
|
|
1362
|
+
const queryOptions = {
|
|
1363
|
+
...this.queryOptions,
|
|
1364
|
+
...config,
|
|
1365
|
+
}
|
|
1359
1366
|
return new Promise((resolve, reject) => {
|
|
1360
|
-
this.model.findAll(query,
|
|
1367
|
+
this.model.findAll(query, queryOptions, (err, data, total) => {
|
|
1361
1368
|
if (err) {
|
|
1362
1369
|
log({ level: 'warn', output: err.toString() })
|
|
1363
1370
|
reject(err)
|
|
@@ -1374,15 +1381,19 @@ class Repo {
|
|
|
1374
1381
|
})
|
|
1375
1382
|
}
|
|
1376
1383
|
|
|
1377
|
-
findOne({ query, systemLog }) {
|
|
1384
|
+
findOne({ config = {}, query, systemLog }) {
|
|
1378
1385
|
const log = _makeLog({
|
|
1379
1386
|
systemLog,
|
|
1380
1387
|
label: 'REPO_READ',
|
|
1381
1388
|
message: `fn ${this._classname}.prototype.findOne`,
|
|
1382
1389
|
input: [{ query: { ...query }, systemLog: { ...systemLog } }]
|
|
1383
1390
|
})
|
|
1391
|
+
const queryOptions = {
|
|
1392
|
+
...this.queryOptions,
|
|
1393
|
+
...config,
|
|
1394
|
+
}
|
|
1384
1395
|
return new Promise((resolve, reject) => {
|
|
1385
|
-
this.model.findAll(query,
|
|
1396
|
+
this.model.findAll(query, queryOptions, (err, data) => {
|
|
1386
1397
|
if (err) {
|
|
1387
1398
|
reject(err)
|
|
1388
1399
|
} else if (data.length === 1) {
|
|
@@ -1416,15 +1427,16 @@ class Repo {
|
|
|
1416
1427
|
})
|
|
1417
1428
|
const promise = typeof this.model.saveAll === 'function'
|
|
1418
1429
|
? this.model.saveAll({ config, docs })
|
|
1419
|
-
:
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1430
|
+
: _saveAll({ config, docs, service: this })
|
|
1431
|
+
// : Promise.all(docs.map(async (doc) => {
|
|
1432
|
+
// if (doc) {
|
|
1433
|
+
// const result = await this.saveOne({ config, doc })
|
|
1434
|
+
// isNew = result.isNew
|
|
1435
|
+
// const _data = result._data || result.data
|
|
1436
|
+
// return _data[0]
|
|
1437
|
+
// }
|
|
1438
|
+
// return null
|
|
1439
|
+
// }))
|
|
1428
1440
|
return promise.then((savedData) => {
|
|
1429
1441
|
if (savedData.length !== 1)
|
|
1430
1442
|
isNew = null
|
|
@@ -1485,6 +1497,32 @@ function _makeLog({ systemLog, label, message: message1, input } = {}) {
|
|
|
1485
1497
|
}
|
|
1486
1498
|
}
|
|
1487
1499
|
|
|
1500
|
+
async function _saveAll({ config = {}, docs, service }) {
|
|
1501
|
+
let _result = null
|
|
1502
|
+
if (config.session || service.saveOptions.session) {
|
|
1503
|
+
_result = await pMap(docs, async (doc) => {
|
|
1504
|
+
if (doc) {
|
|
1505
|
+
const result = await service.saveOne({ config, doc })
|
|
1506
|
+
// isNew = result.isNew
|
|
1507
|
+
const _data = result._data || result.data
|
|
1508
|
+
return _data[0]
|
|
1509
|
+
}
|
|
1510
|
+
return null
|
|
1511
|
+
}, { concurrency: 1 })
|
|
1512
|
+
} else {
|
|
1513
|
+
_result = await Promise.all(docs.map(async (doc) => {
|
|
1514
|
+
if (doc) {
|
|
1515
|
+
const result = await service.saveOne({ config, doc })
|
|
1516
|
+
// isNew = result.isNew
|
|
1517
|
+
const _data = result._data || result.data
|
|
1518
|
+
return _data[0]
|
|
1519
|
+
}
|
|
1520
|
+
return null
|
|
1521
|
+
}))
|
|
1522
|
+
}
|
|
1523
|
+
return _result
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1488
1526
|
|
|
1489
1527
|
|
|
1490
1528
|
;// ./lib/models/repo/index.js
|
|
@@ -1520,16 +1558,16 @@ class Service {
|
|
|
1520
1558
|
// })
|
|
1521
1559
|
}
|
|
1522
1560
|
|
|
1523
|
-
async findAll({ query = {}, systemLog } = {}) {
|
|
1524
|
-
const result = await this.repo.findAll({ query, systemLog })
|
|
1561
|
+
async findAll({ config = {}, query = {}, systemLog } = {}) {
|
|
1562
|
+
const result = await this.repo.findAll({ config, query, systemLog })
|
|
1525
1563
|
return makeApiResponse({
|
|
1526
1564
|
repo: this.repo,
|
|
1527
1565
|
result
|
|
1528
1566
|
})
|
|
1529
1567
|
}
|
|
1530
1568
|
|
|
1531
|
-
async findOne({ query = {}, systemLog } = {}) {
|
|
1532
|
-
const result = await this.repo.findOne({ query, systemLog })
|
|
1569
|
+
async findOne({ config = {}, query = {}, systemLog } = {}) {
|
|
1570
|
+
const result = await this.repo.findOne({ config, query, systemLog })
|
|
1533
1571
|
return makeApiResponse({
|
|
1534
1572
|
repo: this.repo,
|
|
1535
1573
|
result
|
|
@@ -3899,6 +3937,293 @@ function padZeros(num, minLength = 6) {
|
|
|
3899
3937
|
|
|
3900
3938
|
|
|
3901
3939
|
|
|
3940
|
+
;// ./lib/helpers/pMap/pMap.js
|
|
3941
|
+
async function pMap(
|
|
3942
|
+
iterable,
|
|
3943
|
+
mapper,
|
|
3944
|
+
{
|
|
3945
|
+
concurrency = Number.POSITIVE_INFINITY,
|
|
3946
|
+
stopOnError = true,
|
|
3947
|
+
signal,
|
|
3948
|
+
} = {},
|
|
3949
|
+
) {
|
|
3950
|
+
return new Promise((resolve_, reject_) => {
|
|
3951
|
+
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
|
|
3952
|
+
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`)
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
if (typeof mapper !== 'function') {
|
|
3956
|
+
throw new TypeError('Mapper function is required')
|
|
3957
|
+
}
|
|
3958
|
+
|
|
3959
|
+
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
|
|
3960
|
+
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`)
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
const result = []
|
|
3964
|
+
const errors = []
|
|
3965
|
+
const skippedIndexesMap = new Map()
|
|
3966
|
+
let isRejected = false
|
|
3967
|
+
let isResolved = false
|
|
3968
|
+
let isIterableDone = false
|
|
3969
|
+
let resolvingCount = 0
|
|
3970
|
+
let currentIndex = 0
|
|
3971
|
+
const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]()
|
|
3972
|
+
|
|
3973
|
+
const signalListener = () => {
|
|
3974
|
+
reject(signal.reason)
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
const cleanup = () => {
|
|
3978
|
+
signal?.removeEventListener('abort', signalListener)
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
const resolve = (value) => {
|
|
3982
|
+
resolve_(value)
|
|
3983
|
+
cleanup()
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
const reject = (reason) => {
|
|
3987
|
+
isRejected = true
|
|
3988
|
+
isResolved = true
|
|
3989
|
+
reject_(reason)
|
|
3990
|
+
cleanup()
|
|
3991
|
+
}
|
|
3992
|
+
|
|
3993
|
+
if (signal) {
|
|
3994
|
+
if (signal.aborted) {
|
|
3995
|
+
reject(signal.reason)
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
signal.addEventListener('abort', signalListener, { once: true })
|
|
3999
|
+
}
|
|
4000
|
+
|
|
4001
|
+
const next = async () => {
|
|
4002
|
+
if (isResolved) {
|
|
4003
|
+
return
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
const nextItem = await iterator.next()
|
|
4007
|
+
|
|
4008
|
+
const index = currentIndex
|
|
4009
|
+
currentIndex++
|
|
4010
|
+
|
|
4011
|
+
// Note: `iterator.next()` can be called many times in parallel.
|
|
4012
|
+
// This can cause multiple calls to this `next()` function to
|
|
4013
|
+
// receive a `nextItem` with `done === true`.
|
|
4014
|
+
// The shutdown logic that rejects/resolves must be protected
|
|
4015
|
+
// so it runs only one time as the `skippedIndex` logic is
|
|
4016
|
+
// non-idempotent.
|
|
4017
|
+
if (nextItem.done) {
|
|
4018
|
+
isIterableDone = true
|
|
4019
|
+
|
|
4020
|
+
if (resolvingCount === 0 && !isResolved) {
|
|
4021
|
+
if (!stopOnError && errors.length > 0) {
|
|
4022
|
+
reject(new AggregateError(errors)) // eslint-disable-line unicorn/error-message
|
|
4023
|
+
return
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
isResolved = true
|
|
4027
|
+
|
|
4028
|
+
if (skippedIndexesMap.size === 0) {
|
|
4029
|
+
resolve(result)
|
|
4030
|
+
return
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
const pureResult = []
|
|
4034
|
+
|
|
4035
|
+
// Support multiple `pMapSkip`'s.
|
|
4036
|
+
for (const [index, value] of result.entries()) {
|
|
4037
|
+
if (skippedIndexesMap.get(index) === pMapSkip) {
|
|
4038
|
+
continue
|
|
4039
|
+
}
|
|
4040
|
+
|
|
4041
|
+
pureResult.push(value)
|
|
4042
|
+
}
|
|
4043
|
+
|
|
4044
|
+
resolve(pureResult)
|
|
4045
|
+
}
|
|
4046
|
+
|
|
4047
|
+
return
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
resolvingCount++;
|
|
4051
|
+
|
|
4052
|
+
// Intentionally detached
|
|
4053
|
+
(async () => {
|
|
4054
|
+
try {
|
|
4055
|
+
const element = await nextItem.value
|
|
4056
|
+
|
|
4057
|
+
if (isResolved) {
|
|
4058
|
+
return
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
const value = await mapper(element, index)
|
|
4062
|
+
|
|
4063
|
+
// Use Map to stage the index of the element.
|
|
4064
|
+
if (value === pMapSkip) {
|
|
4065
|
+
skippedIndexesMap.set(index, value)
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
result[index] = value
|
|
4069
|
+
|
|
4070
|
+
resolvingCount--
|
|
4071
|
+
await next()
|
|
4072
|
+
} catch (error) {
|
|
4073
|
+
if (stopOnError) {
|
|
4074
|
+
reject(error)
|
|
4075
|
+
} else {
|
|
4076
|
+
errors.push(error)
|
|
4077
|
+
resolvingCount--
|
|
4078
|
+
|
|
4079
|
+
// In that case we can't really continue regardless of `stopOnError` state
|
|
4080
|
+
// since an iterable is likely to continue throwing after it throws once.
|
|
4081
|
+
// If we continue calling `next()` indefinitely we will likely end up
|
|
4082
|
+
// in an infinite loop of failed iteration.
|
|
4083
|
+
try {
|
|
4084
|
+
await next()
|
|
4085
|
+
} catch (error) {
|
|
4086
|
+
reject(error)
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
})()
|
|
4091
|
+
};
|
|
4092
|
+
|
|
4093
|
+
// Create the concurrent runners in a detached (non-awaited)
|
|
4094
|
+
// promise. We need this so we can await the `next()` calls
|
|
4095
|
+
// to stop creating runners before hitting the concurrency limit
|
|
4096
|
+
// if the iterable has already been marked as done.
|
|
4097
|
+
// NOTE: We *must* do this for async iterators otherwise we'll spin up
|
|
4098
|
+
// infinite `next()` calls by default and never start the event loop.
|
|
4099
|
+
(async () => {
|
|
4100
|
+
for (let index = 0; index < concurrency; index++) {
|
|
4101
|
+
try {
|
|
4102
|
+
await next()
|
|
4103
|
+
} catch (error) {
|
|
4104
|
+
reject(error)
|
|
4105
|
+
break
|
|
4106
|
+
}
|
|
4107
|
+
|
|
4108
|
+
if (isIterableDone || isRejected) {
|
|
4109
|
+
break
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
})()
|
|
4113
|
+
})
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
function pMapIterable(
|
|
4117
|
+
iterable,
|
|
4118
|
+
mapper,
|
|
4119
|
+
{
|
|
4120
|
+
concurrency = Number.POSITIVE_INFINITY,
|
|
4121
|
+
backpressure = concurrency,
|
|
4122
|
+
} = {},
|
|
4123
|
+
) {
|
|
4124
|
+
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
|
|
4125
|
+
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`)
|
|
4126
|
+
}
|
|
4127
|
+
|
|
4128
|
+
if (typeof mapper !== 'function') {
|
|
4129
|
+
throw new TypeError('Mapper function is required')
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
|
|
4133
|
+
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`)
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
if (!((Number.isSafeInteger(backpressure) && backpressure >= concurrency) || backpressure === Number.POSITIVE_INFINITY)) {
|
|
4137
|
+
throw new TypeError(`Expected \`backpressure\` to be an integer from \`concurrency\` (${concurrency}) and up or \`Infinity\`, got \`${backpressure}\` (${typeof backpressure})`)
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4140
|
+
return {
|
|
4141
|
+
async* [Symbol.asyncIterator]() {
|
|
4142
|
+
const iterator = iterable[Symbol.asyncIterator] === undefined ? iterable[Symbol.iterator]() : iterable[Symbol.asyncIterator]()
|
|
4143
|
+
|
|
4144
|
+
const promises = []
|
|
4145
|
+
let pendingPromisesCount = 0
|
|
4146
|
+
let isDone = false
|
|
4147
|
+
let index = 0
|
|
4148
|
+
|
|
4149
|
+
function trySpawn() {
|
|
4150
|
+
if (isDone || !(pendingPromisesCount < concurrency && promises.length < backpressure)) {
|
|
4151
|
+
return
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
pendingPromisesCount++
|
|
4155
|
+
|
|
4156
|
+
const promise = (async () => {
|
|
4157
|
+
const { done, value } = await iterator.next()
|
|
4158
|
+
|
|
4159
|
+
if (done) {
|
|
4160
|
+
pendingPromisesCount--
|
|
4161
|
+
return { done: true }
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4164
|
+
// Spawn if still below concurrency and backpressure limit
|
|
4165
|
+
trySpawn()
|
|
4166
|
+
|
|
4167
|
+
try {
|
|
4168
|
+
const returnValue = await mapper(await value, index++)
|
|
4169
|
+
|
|
4170
|
+
pendingPromisesCount--
|
|
4171
|
+
|
|
4172
|
+
if (returnValue === pMapSkip) {
|
|
4173
|
+
const index = promises.indexOf(promise)
|
|
4174
|
+
|
|
4175
|
+
if (index > 0) {
|
|
4176
|
+
promises.splice(index, 1)
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
|
|
4180
|
+
// Spawn if still below backpressure limit and just dropped below concurrency limit
|
|
4181
|
+
trySpawn()
|
|
4182
|
+
|
|
4183
|
+
return { done: false, value: returnValue }
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
pendingPromisesCount--
|
|
4186
|
+
isDone = true
|
|
4187
|
+
return { error }
|
|
4188
|
+
}
|
|
4189
|
+
})()
|
|
4190
|
+
|
|
4191
|
+
promises.push(promise)
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
trySpawn()
|
|
4195
|
+
|
|
4196
|
+
while (promises.length > 0) {
|
|
4197
|
+
const { error, done, value } = await promises[0]
|
|
4198
|
+
|
|
4199
|
+
promises.shift()
|
|
4200
|
+
|
|
4201
|
+
if (error) {
|
|
4202
|
+
throw error
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
if (done) {
|
|
4206
|
+
return
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
// Spawn if just dropped below backpressure limit and below the concurrency limit
|
|
4210
|
+
trySpawn()
|
|
4211
|
+
|
|
4212
|
+
if (value === pMapSkip) {
|
|
4213
|
+
continue
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
yield value
|
|
4217
|
+
}
|
|
4218
|
+
},
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
const pMapSkip = Symbol('skip')
|
|
4223
|
+
|
|
4224
|
+
;// ./lib/helpers/pMap/index.js
|
|
4225
|
+
|
|
4226
|
+
|
|
3902
4227
|
;// ./lib/helpers/pReduce/index.js
|
|
3903
4228
|
|
|
3904
4229
|
|
|
@@ -4255,11 +4580,19 @@ function toLowerCase(str) {
|
|
|
4255
4580
|
.toLowerCase()
|
|
4256
4581
|
}
|
|
4257
4582
|
|
|
4583
|
+
function toScreamingSnakeCase(str) {
|
|
4584
|
+
return str
|
|
4585
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
4586
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
|
4587
|
+
.toUpperCase()
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4258
4590
|
const stringHelper = {
|
|
4259
4591
|
isSame,
|
|
4260
4592
|
setCode,
|
|
4261
4593
|
toCamelCase,
|
|
4262
4594
|
toLowerCase,
|
|
4595
|
+
toScreamingSnakeCase,
|
|
4263
4596
|
}
|
|
4264
4597
|
|
|
4265
4598
|
|
|
@@ -4395,6 +4728,7 @@ function tenantPlugin(schema, options) {
|
|
|
4395
4728
|
|
|
4396
4729
|
|
|
4397
4730
|
|
|
4731
|
+
|
|
4398
4732
|
|
|
4399
4733
|
|
|
4400
4734
|
;// ./lib/index.js
|
package/dist/q-utilities.esm.js
CHANGED
|
@@ -1274,15 +1274,19 @@ class Repo {
|
|
|
1274
1274
|
}
|
|
1275
1275
|
|
|
1276
1276
|
// systemLog is optional
|
|
1277
|
-
findAll({ query, systemLog }) {
|
|
1277
|
+
findAll({ config = {}, query, systemLog }) {
|
|
1278
1278
|
const log = _makeLog({
|
|
1279
1279
|
systemLog,
|
|
1280
1280
|
label: 'REPO_READ',
|
|
1281
1281
|
message: `fn ${this._classname}.prototype.findAll`,
|
|
1282
1282
|
input: [{ query: { ...query }, systemLog: { ...systemLog } }]
|
|
1283
1283
|
})
|
|
1284
|
+
const queryOptions = {
|
|
1285
|
+
...this.queryOptions,
|
|
1286
|
+
...config,
|
|
1287
|
+
}
|
|
1284
1288
|
return new Promise((resolve, reject) => {
|
|
1285
|
-
this.model.findAll(query,
|
|
1289
|
+
this.model.findAll(query, queryOptions, (err, data, total) => {
|
|
1286
1290
|
if (err) {
|
|
1287
1291
|
log({ level: 'warn', output: err.toString() })
|
|
1288
1292
|
reject(err)
|
|
@@ -1299,15 +1303,19 @@ class Repo {
|
|
|
1299
1303
|
})
|
|
1300
1304
|
}
|
|
1301
1305
|
|
|
1302
|
-
findOne({ query, systemLog }) {
|
|
1306
|
+
findOne({ config = {}, query, systemLog }) {
|
|
1303
1307
|
const log = _makeLog({
|
|
1304
1308
|
systemLog,
|
|
1305
1309
|
label: 'REPO_READ',
|
|
1306
1310
|
message: `fn ${this._classname}.prototype.findOne`,
|
|
1307
1311
|
input: [{ query: { ...query }, systemLog: { ...systemLog } }]
|
|
1308
1312
|
})
|
|
1313
|
+
const queryOptions = {
|
|
1314
|
+
...this.queryOptions,
|
|
1315
|
+
...config,
|
|
1316
|
+
}
|
|
1309
1317
|
return new Promise((resolve, reject) => {
|
|
1310
|
-
this.model.findAll(query,
|
|
1318
|
+
this.model.findAll(query, queryOptions, (err, data) => {
|
|
1311
1319
|
if (err) {
|
|
1312
1320
|
reject(err)
|
|
1313
1321
|
} else if (data.length === 1) {
|
|
@@ -1341,15 +1349,16 @@ class Repo {
|
|
|
1341
1349
|
})
|
|
1342
1350
|
const promise = typeof this.model.saveAll === 'function'
|
|
1343
1351
|
? this.model.saveAll({ config, docs })
|
|
1344
|
-
:
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1352
|
+
: _saveAll({ config, docs, service: this })
|
|
1353
|
+
// : Promise.all(docs.map(async (doc) => {
|
|
1354
|
+
// if (doc) {
|
|
1355
|
+
// const result = await this.saveOne({ config, doc })
|
|
1356
|
+
// isNew = result.isNew
|
|
1357
|
+
// const _data = result._data || result.data
|
|
1358
|
+
// return _data[0]
|
|
1359
|
+
// }
|
|
1360
|
+
// return null
|
|
1361
|
+
// }))
|
|
1353
1362
|
return promise.then((savedData) => {
|
|
1354
1363
|
if (savedData.length !== 1)
|
|
1355
1364
|
isNew = null
|
|
@@ -1410,6 +1419,32 @@ function _makeLog({ systemLog, label, message: message1, input } = {}) {
|
|
|
1410
1419
|
}
|
|
1411
1420
|
}
|
|
1412
1421
|
|
|
1422
|
+
async function _saveAll({ config = {}, docs, service }) {
|
|
1423
|
+
let _result = null
|
|
1424
|
+
if (config.session || service.saveOptions.session) {
|
|
1425
|
+
_result = await pMap(docs, async (doc) => {
|
|
1426
|
+
if (doc) {
|
|
1427
|
+
const result = await service.saveOne({ config, doc })
|
|
1428
|
+
// isNew = result.isNew
|
|
1429
|
+
const _data = result._data || result.data
|
|
1430
|
+
return _data[0]
|
|
1431
|
+
}
|
|
1432
|
+
return null
|
|
1433
|
+
}, { concurrency: 1 })
|
|
1434
|
+
} else {
|
|
1435
|
+
_result = await Promise.all(docs.map(async (doc) => {
|
|
1436
|
+
if (doc) {
|
|
1437
|
+
const result = await service.saveOne({ config, doc })
|
|
1438
|
+
// isNew = result.isNew
|
|
1439
|
+
const _data = result._data || result.data
|
|
1440
|
+
return _data[0]
|
|
1441
|
+
}
|
|
1442
|
+
return null
|
|
1443
|
+
}))
|
|
1444
|
+
}
|
|
1445
|
+
return _result
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1413
1448
|
|
|
1414
1449
|
|
|
1415
1450
|
;// ./lib/models/repo/index.js
|
|
@@ -1445,16 +1480,16 @@ class Service {
|
|
|
1445
1480
|
// })
|
|
1446
1481
|
}
|
|
1447
1482
|
|
|
1448
|
-
async findAll({ query = {}, systemLog } = {}) {
|
|
1449
|
-
const result = await this.repo.findAll({ query, systemLog })
|
|
1483
|
+
async findAll({ config = {}, query = {}, systemLog } = {}) {
|
|
1484
|
+
const result = await this.repo.findAll({ config, query, systemLog })
|
|
1450
1485
|
return makeApiResponse({
|
|
1451
1486
|
repo: this.repo,
|
|
1452
1487
|
result
|
|
1453
1488
|
})
|
|
1454
1489
|
}
|
|
1455
1490
|
|
|
1456
|
-
async findOne({ query = {}, systemLog } = {}) {
|
|
1457
|
-
const result = await this.repo.findOne({ query, systemLog })
|
|
1491
|
+
async findOne({ config = {}, query = {}, systemLog } = {}) {
|
|
1492
|
+
const result = await this.repo.findOne({ config, query, systemLog })
|
|
1458
1493
|
return makeApiResponse({
|
|
1459
1494
|
repo: this.repo,
|
|
1460
1495
|
result
|
|
@@ -3824,6 +3859,293 @@ function padZeros(num, minLength = 6) {
|
|
|
3824
3859
|
|
|
3825
3860
|
|
|
3826
3861
|
|
|
3862
|
+
;// ./lib/helpers/pMap/pMap.js
|
|
3863
|
+
async function pMap(
|
|
3864
|
+
iterable,
|
|
3865
|
+
mapper,
|
|
3866
|
+
{
|
|
3867
|
+
concurrency = Number.POSITIVE_INFINITY,
|
|
3868
|
+
stopOnError = true,
|
|
3869
|
+
signal,
|
|
3870
|
+
} = {},
|
|
3871
|
+
) {
|
|
3872
|
+
return new Promise((resolve_, reject_) => {
|
|
3873
|
+
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
|
|
3874
|
+
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`)
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3877
|
+
if (typeof mapper !== 'function') {
|
|
3878
|
+
throw new TypeError('Mapper function is required')
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
|
|
3882
|
+
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`)
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
const result = []
|
|
3886
|
+
const errors = []
|
|
3887
|
+
const skippedIndexesMap = new Map()
|
|
3888
|
+
let isRejected = false
|
|
3889
|
+
let isResolved = false
|
|
3890
|
+
let isIterableDone = false
|
|
3891
|
+
let resolvingCount = 0
|
|
3892
|
+
let currentIndex = 0
|
|
3893
|
+
const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]()
|
|
3894
|
+
|
|
3895
|
+
const signalListener = () => {
|
|
3896
|
+
reject(signal.reason)
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
const cleanup = () => {
|
|
3900
|
+
signal?.removeEventListener('abort', signalListener)
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
const resolve = (value) => {
|
|
3904
|
+
resolve_(value)
|
|
3905
|
+
cleanup()
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
const reject = (reason) => {
|
|
3909
|
+
isRejected = true
|
|
3910
|
+
isResolved = true
|
|
3911
|
+
reject_(reason)
|
|
3912
|
+
cleanup()
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
if (signal) {
|
|
3916
|
+
if (signal.aborted) {
|
|
3917
|
+
reject(signal.reason)
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
signal.addEventListener('abort', signalListener, { once: true })
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
const next = async () => {
|
|
3924
|
+
if (isResolved) {
|
|
3925
|
+
return
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
const nextItem = await iterator.next()
|
|
3929
|
+
|
|
3930
|
+
const index = currentIndex
|
|
3931
|
+
currentIndex++
|
|
3932
|
+
|
|
3933
|
+
// Note: `iterator.next()` can be called many times in parallel.
|
|
3934
|
+
// This can cause multiple calls to this `next()` function to
|
|
3935
|
+
// receive a `nextItem` with `done === true`.
|
|
3936
|
+
// The shutdown logic that rejects/resolves must be protected
|
|
3937
|
+
// so it runs only one time as the `skippedIndex` logic is
|
|
3938
|
+
// non-idempotent.
|
|
3939
|
+
if (nextItem.done) {
|
|
3940
|
+
isIterableDone = true
|
|
3941
|
+
|
|
3942
|
+
if (resolvingCount === 0 && !isResolved) {
|
|
3943
|
+
if (!stopOnError && errors.length > 0) {
|
|
3944
|
+
reject(new AggregateError(errors)) // eslint-disable-line unicorn/error-message
|
|
3945
|
+
return
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
isResolved = true
|
|
3949
|
+
|
|
3950
|
+
if (skippedIndexesMap.size === 0) {
|
|
3951
|
+
resolve(result)
|
|
3952
|
+
return
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
const pureResult = []
|
|
3956
|
+
|
|
3957
|
+
// Support multiple `pMapSkip`'s.
|
|
3958
|
+
for (const [index, value] of result.entries()) {
|
|
3959
|
+
if (skippedIndexesMap.get(index) === pMapSkip) {
|
|
3960
|
+
continue
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
pureResult.push(value)
|
|
3964
|
+
}
|
|
3965
|
+
|
|
3966
|
+
resolve(pureResult)
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3969
|
+
return
|
|
3970
|
+
}
|
|
3971
|
+
|
|
3972
|
+
resolvingCount++;
|
|
3973
|
+
|
|
3974
|
+
// Intentionally detached
|
|
3975
|
+
(async () => {
|
|
3976
|
+
try {
|
|
3977
|
+
const element = await nextItem.value
|
|
3978
|
+
|
|
3979
|
+
if (isResolved) {
|
|
3980
|
+
return
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
const value = await mapper(element, index)
|
|
3984
|
+
|
|
3985
|
+
// Use Map to stage the index of the element.
|
|
3986
|
+
if (value === pMapSkip) {
|
|
3987
|
+
skippedIndexesMap.set(index, value)
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3990
|
+
result[index] = value
|
|
3991
|
+
|
|
3992
|
+
resolvingCount--
|
|
3993
|
+
await next()
|
|
3994
|
+
} catch (error) {
|
|
3995
|
+
if (stopOnError) {
|
|
3996
|
+
reject(error)
|
|
3997
|
+
} else {
|
|
3998
|
+
errors.push(error)
|
|
3999
|
+
resolvingCount--
|
|
4000
|
+
|
|
4001
|
+
// In that case we can't really continue regardless of `stopOnError` state
|
|
4002
|
+
// since an iterable is likely to continue throwing after it throws once.
|
|
4003
|
+
// If we continue calling `next()` indefinitely we will likely end up
|
|
4004
|
+
// in an infinite loop of failed iteration.
|
|
4005
|
+
try {
|
|
4006
|
+
await next()
|
|
4007
|
+
} catch (error) {
|
|
4008
|
+
reject(error)
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
})()
|
|
4013
|
+
};
|
|
4014
|
+
|
|
4015
|
+
// Create the concurrent runners in a detached (non-awaited)
|
|
4016
|
+
// promise. We need this so we can await the `next()` calls
|
|
4017
|
+
// to stop creating runners before hitting the concurrency limit
|
|
4018
|
+
// if the iterable has already been marked as done.
|
|
4019
|
+
// NOTE: We *must* do this for async iterators otherwise we'll spin up
|
|
4020
|
+
// infinite `next()` calls by default and never start the event loop.
|
|
4021
|
+
(async () => {
|
|
4022
|
+
for (let index = 0; index < concurrency; index++) {
|
|
4023
|
+
try {
|
|
4024
|
+
await next()
|
|
4025
|
+
} catch (error) {
|
|
4026
|
+
reject(error)
|
|
4027
|
+
break
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
if (isIterableDone || isRejected) {
|
|
4031
|
+
break
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
})()
|
|
4035
|
+
})
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
function pMapIterable(
|
|
4039
|
+
iterable,
|
|
4040
|
+
mapper,
|
|
4041
|
+
{
|
|
4042
|
+
concurrency = Number.POSITIVE_INFINITY,
|
|
4043
|
+
backpressure = concurrency,
|
|
4044
|
+
} = {},
|
|
4045
|
+
) {
|
|
4046
|
+
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
|
|
4047
|
+
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`)
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
if (typeof mapper !== 'function') {
|
|
4051
|
+
throw new TypeError('Mapper function is required')
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
|
|
4055
|
+
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`)
|
|
4056
|
+
}
|
|
4057
|
+
|
|
4058
|
+
if (!((Number.isSafeInteger(backpressure) && backpressure >= concurrency) || backpressure === Number.POSITIVE_INFINITY)) {
|
|
4059
|
+
throw new TypeError(`Expected \`backpressure\` to be an integer from \`concurrency\` (${concurrency}) and up or \`Infinity\`, got \`${backpressure}\` (${typeof backpressure})`)
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
return {
|
|
4063
|
+
async* [Symbol.asyncIterator]() {
|
|
4064
|
+
const iterator = iterable[Symbol.asyncIterator] === undefined ? iterable[Symbol.iterator]() : iterable[Symbol.asyncIterator]()
|
|
4065
|
+
|
|
4066
|
+
const promises = []
|
|
4067
|
+
let pendingPromisesCount = 0
|
|
4068
|
+
let isDone = false
|
|
4069
|
+
let index = 0
|
|
4070
|
+
|
|
4071
|
+
function trySpawn() {
|
|
4072
|
+
if (isDone || !(pendingPromisesCount < concurrency && promises.length < backpressure)) {
|
|
4073
|
+
return
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
pendingPromisesCount++
|
|
4077
|
+
|
|
4078
|
+
const promise = (async () => {
|
|
4079
|
+
const { done, value } = await iterator.next()
|
|
4080
|
+
|
|
4081
|
+
if (done) {
|
|
4082
|
+
pendingPromisesCount--
|
|
4083
|
+
return { done: true }
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
// Spawn if still below concurrency and backpressure limit
|
|
4087
|
+
trySpawn()
|
|
4088
|
+
|
|
4089
|
+
try {
|
|
4090
|
+
const returnValue = await mapper(await value, index++)
|
|
4091
|
+
|
|
4092
|
+
pendingPromisesCount--
|
|
4093
|
+
|
|
4094
|
+
if (returnValue === pMapSkip) {
|
|
4095
|
+
const index = promises.indexOf(promise)
|
|
4096
|
+
|
|
4097
|
+
if (index > 0) {
|
|
4098
|
+
promises.splice(index, 1)
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// Spawn if still below backpressure limit and just dropped below concurrency limit
|
|
4103
|
+
trySpawn()
|
|
4104
|
+
|
|
4105
|
+
return { done: false, value: returnValue }
|
|
4106
|
+
} catch (error) {
|
|
4107
|
+
pendingPromisesCount--
|
|
4108
|
+
isDone = true
|
|
4109
|
+
return { error }
|
|
4110
|
+
}
|
|
4111
|
+
})()
|
|
4112
|
+
|
|
4113
|
+
promises.push(promise)
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
trySpawn()
|
|
4117
|
+
|
|
4118
|
+
while (promises.length > 0) {
|
|
4119
|
+
const { error, done, value } = await promises[0]
|
|
4120
|
+
|
|
4121
|
+
promises.shift()
|
|
4122
|
+
|
|
4123
|
+
if (error) {
|
|
4124
|
+
throw error
|
|
4125
|
+
}
|
|
4126
|
+
|
|
4127
|
+
if (done) {
|
|
4128
|
+
return
|
|
4129
|
+
}
|
|
4130
|
+
|
|
4131
|
+
// Spawn if just dropped below backpressure limit and below the concurrency limit
|
|
4132
|
+
trySpawn()
|
|
4133
|
+
|
|
4134
|
+
if (value === pMapSkip) {
|
|
4135
|
+
continue
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
yield value
|
|
4139
|
+
}
|
|
4140
|
+
},
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
const pMapSkip = Symbol('skip')
|
|
4145
|
+
|
|
4146
|
+
;// ./lib/helpers/pMap/index.js
|
|
4147
|
+
|
|
4148
|
+
|
|
3827
4149
|
;// ./lib/helpers/pReduce/index.js
|
|
3828
4150
|
|
|
3829
4151
|
|
|
@@ -4180,11 +4502,19 @@ function toLowerCase(str) {
|
|
|
4180
4502
|
.toLowerCase()
|
|
4181
4503
|
}
|
|
4182
4504
|
|
|
4505
|
+
function toScreamingSnakeCase(str) {
|
|
4506
|
+
return str
|
|
4507
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
4508
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
|
4509
|
+
.toUpperCase()
|
|
4510
|
+
}
|
|
4511
|
+
|
|
4183
4512
|
const stringHelper = {
|
|
4184
4513
|
isSame,
|
|
4185
4514
|
setCode,
|
|
4186
4515
|
toCamelCase,
|
|
4187
4516
|
toLowerCase,
|
|
4517
|
+
toScreamingSnakeCase,
|
|
4188
4518
|
}
|
|
4189
4519
|
|
|
4190
4520
|
|
|
@@ -4320,6 +4650,7 @@ function tenantPlugin(schema, options) {
|
|
|
4320
4650
|
|
|
4321
4651
|
|
|
4322
4652
|
|
|
4653
|
+
|
|
4323
4654
|
|
|
4324
4655
|
|
|
4325
4656
|
;// ./lib/index.js
|
|
@@ -4329,4 +4660,4 @@ function tenantPlugin(schema, options) {
|
|
|
4329
4660
|
;// ./index.js
|
|
4330
4661
|
|
|
4331
4662
|
|
|
4332
|
-
export { ActionRecord, ApiResponse, AwsStsS3Client, keyValueObject_KeyValueObject as KeyValueObject, Metadata, PushEnvelope, QMeta, Repo, Service, Status, StatusDocument, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, bwipJsHelper, calculateAge, changeCreatorOwner, concatStringByArray, convertString, detectControlCharacters, downloadFileByUrl, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getObjectByArr, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, groupArrayByKey, init, initFromArray, initOnlyValidFromArray, isConvertibleToNumber, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pReduce, padZeros, printControlCharReport, replacePlaceholders, sanitizeText, shuffleArray, stringFormatter, stringHelper, tenantPlugin, trackingPlugin };
|
|
4663
|
+
export { ActionRecord, ApiResponse, AwsStsS3Client, keyValueObject_KeyValueObject as KeyValueObject, Metadata, PushEnvelope, QMeta, Repo, Service, Status, StatusDocument, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, bwipJsHelper, calculateAge, changeCreatorOwner, concatStringByArray, convertString, detectControlCharacters, downloadFileByUrl, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getObjectByArr, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, groupArrayByKey, init, initFromArray, initOnlyValidFromArray, isConvertibleToNumber, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pMap, pMapIterable, pMapSkip, pReduce, padZeros, printControlCharReport, replacePlaceholders, sanitizeText, shuffleArray, stringFormatter, stringHelper, tenantPlugin, trackingPlugin };
|
package/dist/q-utilities.min.js
CHANGED
|
@@ -120,6 +120,9 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
120
120
|
makeService: () => (/* reexport */ makeService),
|
|
121
121
|
mergeArraysByKey: () => (/* reexport */ mergeArraysByKey),
|
|
122
122
|
objectHelper: () => (/* reexport */ objectHelper),
|
|
123
|
+
pMap: () => (/* reexport */ pMap),
|
|
124
|
+
pMapIterable: () => (/* reexport */ pMapIterable),
|
|
125
|
+
pMapSkip: () => (/* reexport */ pMapSkip),
|
|
123
126
|
pReduce: () => (/* reexport */ pReduce),
|
|
124
127
|
padZeros: () => (/* reexport */ padZeros),
|
|
125
128
|
printControlCharReport: () => (/* reexport */ printControlCharReport),
|
|
@@ -1380,15 +1383,19 @@ class Repo {
|
|
|
1380
1383
|
}
|
|
1381
1384
|
|
|
1382
1385
|
// systemLog is optional
|
|
1383
|
-
findAll({ query, systemLog }) {
|
|
1386
|
+
findAll({ config = {}, query, systemLog }) {
|
|
1384
1387
|
const log = _makeLog({
|
|
1385
1388
|
systemLog,
|
|
1386
1389
|
label: 'REPO_READ',
|
|
1387
1390
|
message: `fn ${this._classname}.prototype.findAll`,
|
|
1388
1391
|
input: [{ query: { ...query }, systemLog: { ...systemLog } }]
|
|
1389
1392
|
})
|
|
1393
|
+
const queryOptions = {
|
|
1394
|
+
...this.queryOptions,
|
|
1395
|
+
...config,
|
|
1396
|
+
}
|
|
1390
1397
|
return new Promise((resolve, reject) => {
|
|
1391
|
-
this.model.findAll(query,
|
|
1398
|
+
this.model.findAll(query, queryOptions, (err, data, total) => {
|
|
1392
1399
|
if (err) {
|
|
1393
1400
|
log({ level: 'warn', output: err.toString() })
|
|
1394
1401
|
reject(err)
|
|
@@ -1405,15 +1412,19 @@ class Repo {
|
|
|
1405
1412
|
})
|
|
1406
1413
|
}
|
|
1407
1414
|
|
|
1408
|
-
findOne({ query, systemLog }) {
|
|
1415
|
+
findOne({ config = {}, query, systemLog }) {
|
|
1409
1416
|
const log = _makeLog({
|
|
1410
1417
|
systemLog,
|
|
1411
1418
|
label: 'REPO_READ',
|
|
1412
1419
|
message: `fn ${this._classname}.prototype.findOne`,
|
|
1413
1420
|
input: [{ query: { ...query }, systemLog: { ...systemLog } }]
|
|
1414
1421
|
})
|
|
1422
|
+
const queryOptions = {
|
|
1423
|
+
...this.queryOptions,
|
|
1424
|
+
...config,
|
|
1425
|
+
}
|
|
1415
1426
|
return new Promise((resolve, reject) => {
|
|
1416
|
-
this.model.findAll(query,
|
|
1427
|
+
this.model.findAll(query, queryOptions, (err, data) => {
|
|
1417
1428
|
if (err) {
|
|
1418
1429
|
reject(err)
|
|
1419
1430
|
} else if (data.length === 1) {
|
|
@@ -1447,15 +1458,16 @@ class Repo {
|
|
|
1447
1458
|
})
|
|
1448
1459
|
const promise = typeof this.model.saveAll === 'function'
|
|
1449
1460
|
? this.model.saveAll({ config, docs })
|
|
1450
|
-
:
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1461
|
+
: _saveAll({ config, docs, service: this })
|
|
1462
|
+
// : Promise.all(docs.map(async (doc) => {
|
|
1463
|
+
// if (doc) {
|
|
1464
|
+
// const result = await this.saveOne({ config, doc })
|
|
1465
|
+
// isNew = result.isNew
|
|
1466
|
+
// const _data = result._data || result.data
|
|
1467
|
+
// return _data[0]
|
|
1468
|
+
// }
|
|
1469
|
+
// return null
|
|
1470
|
+
// }))
|
|
1459
1471
|
return promise.then((savedData) => {
|
|
1460
1472
|
if (savedData.length !== 1)
|
|
1461
1473
|
isNew = null
|
|
@@ -1516,6 +1528,32 @@ function _makeLog({ systemLog, label, message: message1, input } = {}) {
|
|
|
1516
1528
|
}
|
|
1517
1529
|
}
|
|
1518
1530
|
|
|
1531
|
+
async function _saveAll({ config = {}, docs, service }) {
|
|
1532
|
+
let _result = null
|
|
1533
|
+
if (config.session || service.saveOptions.session) {
|
|
1534
|
+
_result = await pMap(docs, async (doc) => {
|
|
1535
|
+
if (doc) {
|
|
1536
|
+
const result = await service.saveOne({ config, doc })
|
|
1537
|
+
// isNew = result.isNew
|
|
1538
|
+
const _data = result._data || result.data
|
|
1539
|
+
return _data[0]
|
|
1540
|
+
}
|
|
1541
|
+
return null
|
|
1542
|
+
}, { concurrency: 1 })
|
|
1543
|
+
} else {
|
|
1544
|
+
_result = await Promise.all(docs.map(async (doc) => {
|
|
1545
|
+
if (doc) {
|
|
1546
|
+
const result = await service.saveOne({ config, doc })
|
|
1547
|
+
// isNew = result.isNew
|
|
1548
|
+
const _data = result._data || result.data
|
|
1549
|
+
return _data[0]
|
|
1550
|
+
}
|
|
1551
|
+
return null
|
|
1552
|
+
}))
|
|
1553
|
+
}
|
|
1554
|
+
return _result
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1519
1557
|
|
|
1520
1558
|
|
|
1521
1559
|
;// ./lib/models/repo/index.js
|
|
@@ -1551,16 +1589,16 @@ class Service {
|
|
|
1551
1589
|
// })
|
|
1552
1590
|
}
|
|
1553
1591
|
|
|
1554
|
-
async findAll({ query = {}, systemLog } = {}) {
|
|
1555
|
-
const result = await this.repo.findAll({ query, systemLog })
|
|
1592
|
+
async findAll({ config = {}, query = {}, systemLog } = {}) {
|
|
1593
|
+
const result = await this.repo.findAll({ config, query, systemLog })
|
|
1556
1594
|
return makeApiResponse({
|
|
1557
1595
|
repo: this.repo,
|
|
1558
1596
|
result
|
|
1559
1597
|
})
|
|
1560
1598
|
}
|
|
1561
1599
|
|
|
1562
|
-
async findOne({ query = {}, systemLog } = {}) {
|
|
1563
|
-
const result = await this.repo.findOne({ query, systemLog })
|
|
1600
|
+
async findOne({ config = {}, query = {}, systemLog } = {}) {
|
|
1601
|
+
const result = await this.repo.findOne({ config, query, systemLog })
|
|
1564
1602
|
return makeApiResponse({
|
|
1565
1603
|
repo: this.repo,
|
|
1566
1604
|
result
|
|
@@ -3930,6 +3968,293 @@ function padZeros(num, minLength = 6) {
|
|
|
3930
3968
|
|
|
3931
3969
|
|
|
3932
3970
|
|
|
3971
|
+
;// ./lib/helpers/pMap/pMap.js
|
|
3972
|
+
async function pMap(
|
|
3973
|
+
iterable,
|
|
3974
|
+
mapper,
|
|
3975
|
+
{
|
|
3976
|
+
concurrency = Number.POSITIVE_INFINITY,
|
|
3977
|
+
stopOnError = true,
|
|
3978
|
+
signal,
|
|
3979
|
+
} = {},
|
|
3980
|
+
) {
|
|
3981
|
+
return new Promise((resolve_, reject_) => {
|
|
3982
|
+
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
|
|
3983
|
+
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`)
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
if (typeof mapper !== 'function') {
|
|
3987
|
+
throw new TypeError('Mapper function is required')
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3990
|
+
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
|
|
3991
|
+
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`)
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
const result = []
|
|
3995
|
+
const errors = []
|
|
3996
|
+
const skippedIndexesMap = new Map()
|
|
3997
|
+
let isRejected = false
|
|
3998
|
+
let isResolved = false
|
|
3999
|
+
let isIterableDone = false
|
|
4000
|
+
let resolvingCount = 0
|
|
4001
|
+
let currentIndex = 0
|
|
4002
|
+
const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]()
|
|
4003
|
+
|
|
4004
|
+
const signalListener = () => {
|
|
4005
|
+
reject(signal.reason)
|
|
4006
|
+
}
|
|
4007
|
+
|
|
4008
|
+
const cleanup = () => {
|
|
4009
|
+
signal?.removeEventListener('abort', signalListener)
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
const resolve = (value) => {
|
|
4013
|
+
resolve_(value)
|
|
4014
|
+
cleanup()
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
const reject = (reason) => {
|
|
4018
|
+
isRejected = true
|
|
4019
|
+
isResolved = true
|
|
4020
|
+
reject_(reason)
|
|
4021
|
+
cleanup()
|
|
4022
|
+
}
|
|
4023
|
+
|
|
4024
|
+
if (signal) {
|
|
4025
|
+
if (signal.aborted) {
|
|
4026
|
+
reject(signal.reason)
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
signal.addEventListener('abort', signalListener, { once: true })
|
|
4030
|
+
}
|
|
4031
|
+
|
|
4032
|
+
const next = async () => {
|
|
4033
|
+
if (isResolved) {
|
|
4034
|
+
return
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
const nextItem = await iterator.next()
|
|
4038
|
+
|
|
4039
|
+
const index = currentIndex
|
|
4040
|
+
currentIndex++
|
|
4041
|
+
|
|
4042
|
+
// Note: `iterator.next()` can be called many times in parallel.
|
|
4043
|
+
// This can cause multiple calls to this `next()` function to
|
|
4044
|
+
// receive a `nextItem` with `done === true`.
|
|
4045
|
+
// The shutdown logic that rejects/resolves must be protected
|
|
4046
|
+
// so it runs only one time as the `skippedIndex` logic is
|
|
4047
|
+
// non-idempotent.
|
|
4048
|
+
if (nextItem.done) {
|
|
4049
|
+
isIterableDone = true
|
|
4050
|
+
|
|
4051
|
+
if (resolvingCount === 0 && !isResolved) {
|
|
4052
|
+
if (!stopOnError && errors.length > 0) {
|
|
4053
|
+
reject(new AggregateError(errors)) // eslint-disable-line unicorn/error-message
|
|
4054
|
+
return
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
isResolved = true
|
|
4058
|
+
|
|
4059
|
+
if (skippedIndexesMap.size === 0) {
|
|
4060
|
+
resolve(result)
|
|
4061
|
+
return
|
|
4062
|
+
}
|
|
4063
|
+
|
|
4064
|
+
const pureResult = []
|
|
4065
|
+
|
|
4066
|
+
// Support multiple `pMapSkip`'s.
|
|
4067
|
+
for (const [index, value] of result.entries()) {
|
|
4068
|
+
if (skippedIndexesMap.get(index) === pMapSkip) {
|
|
4069
|
+
continue
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
pureResult.push(value)
|
|
4073
|
+
}
|
|
4074
|
+
|
|
4075
|
+
resolve(pureResult)
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
return
|
|
4079
|
+
}
|
|
4080
|
+
|
|
4081
|
+
resolvingCount++;
|
|
4082
|
+
|
|
4083
|
+
// Intentionally detached
|
|
4084
|
+
(async () => {
|
|
4085
|
+
try {
|
|
4086
|
+
const element = await nextItem.value
|
|
4087
|
+
|
|
4088
|
+
if (isResolved) {
|
|
4089
|
+
return
|
|
4090
|
+
}
|
|
4091
|
+
|
|
4092
|
+
const value = await mapper(element, index)
|
|
4093
|
+
|
|
4094
|
+
// Use Map to stage the index of the element.
|
|
4095
|
+
if (value === pMapSkip) {
|
|
4096
|
+
skippedIndexesMap.set(index, value)
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
result[index] = value
|
|
4100
|
+
|
|
4101
|
+
resolvingCount--
|
|
4102
|
+
await next()
|
|
4103
|
+
} catch (error) {
|
|
4104
|
+
if (stopOnError) {
|
|
4105
|
+
reject(error)
|
|
4106
|
+
} else {
|
|
4107
|
+
errors.push(error)
|
|
4108
|
+
resolvingCount--
|
|
4109
|
+
|
|
4110
|
+
// In that case we can't really continue regardless of `stopOnError` state
|
|
4111
|
+
// since an iterable is likely to continue throwing after it throws once.
|
|
4112
|
+
// If we continue calling `next()` indefinitely we will likely end up
|
|
4113
|
+
// in an infinite loop of failed iteration.
|
|
4114
|
+
try {
|
|
4115
|
+
await next()
|
|
4116
|
+
} catch (error) {
|
|
4117
|
+
reject(error)
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
})()
|
|
4122
|
+
};
|
|
4123
|
+
|
|
4124
|
+
// Create the concurrent runners in a detached (non-awaited)
|
|
4125
|
+
// promise. We need this so we can await the `next()` calls
|
|
4126
|
+
// to stop creating runners before hitting the concurrency limit
|
|
4127
|
+
// if the iterable has already been marked as done.
|
|
4128
|
+
// NOTE: We *must* do this for async iterators otherwise we'll spin up
|
|
4129
|
+
// infinite `next()` calls by default and never start the event loop.
|
|
4130
|
+
(async () => {
|
|
4131
|
+
for (let index = 0; index < concurrency; index++) {
|
|
4132
|
+
try {
|
|
4133
|
+
await next()
|
|
4134
|
+
} catch (error) {
|
|
4135
|
+
reject(error)
|
|
4136
|
+
break
|
|
4137
|
+
}
|
|
4138
|
+
|
|
4139
|
+
if (isIterableDone || isRejected) {
|
|
4140
|
+
break
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
})()
|
|
4144
|
+
})
|
|
4145
|
+
}
|
|
4146
|
+
|
|
4147
|
+
function pMapIterable(
|
|
4148
|
+
iterable,
|
|
4149
|
+
mapper,
|
|
4150
|
+
{
|
|
4151
|
+
concurrency = Number.POSITIVE_INFINITY,
|
|
4152
|
+
backpressure = concurrency,
|
|
4153
|
+
} = {},
|
|
4154
|
+
) {
|
|
4155
|
+
if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
|
|
4156
|
+
throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`)
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4159
|
+
if (typeof mapper !== 'function') {
|
|
4160
|
+
throw new TypeError('Mapper function is required')
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4163
|
+
if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
|
|
4164
|
+
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`)
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
if (!((Number.isSafeInteger(backpressure) && backpressure >= concurrency) || backpressure === Number.POSITIVE_INFINITY)) {
|
|
4168
|
+
throw new TypeError(`Expected \`backpressure\` to be an integer from \`concurrency\` (${concurrency}) and up or \`Infinity\`, got \`${backpressure}\` (${typeof backpressure})`)
|
|
4169
|
+
}
|
|
4170
|
+
|
|
4171
|
+
return {
|
|
4172
|
+
async* [Symbol.asyncIterator]() {
|
|
4173
|
+
const iterator = iterable[Symbol.asyncIterator] === undefined ? iterable[Symbol.iterator]() : iterable[Symbol.asyncIterator]()
|
|
4174
|
+
|
|
4175
|
+
const promises = []
|
|
4176
|
+
let pendingPromisesCount = 0
|
|
4177
|
+
let isDone = false
|
|
4178
|
+
let index = 0
|
|
4179
|
+
|
|
4180
|
+
function trySpawn() {
|
|
4181
|
+
if (isDone || !(pendingPromisesCount < concurrency && promises.length < backpressure)) {
|
|
4182
|
+
return
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
pendingPromisesCount++
|
|
4186
|
+
|
|
4187
|
+
const promise = (async () => {
|
|
4188
|
+
const { done, value } = await iterator.next()
|
|
4189
|
+
|
|
4190
|
+
if (done) {
|
|
4191
|
+
pendingPromisesCount--
|
|
4192
|
+
return { done: true }
|
|
4193
|
+
}
|
|
4194
|
+
|
|
4195
|
+
// Spawn if still below concurrency and backpressure limit
|
|
4196
|
+
trySpawn()
|
|
4197
|
+
|
|
4198
|
+
try {
|
|
4199
|
+
const returnValue = await mapper(await value, index++)
|
|
4200
|
+
|
|
4201
|
+
pendingPromisesCount--
|
|
4202
|
+
|
|
4203
|
+
if (returnValue === pMapSkip) {
|
|
4204
|
+
const index = promises.indexOf(promise)
|
|
4205
|
+
|
|
4206
|
+
if (index > 0) {
|
|
4207
|
+
promises.splice(index, 1)
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
// Spawn if still below backpressure limit and just dropped below concurrency limit
|
|
4212
|
+
trySpawn()
|
|
4213
|
+
|
|
4214
|
+
return { done: false, value: returnValue }
|
|
4215
|
+
} catch (error) {
|
|
4216
|
+
pendingPromisesCount--
|
|
4217
|
+
isDone = true
|
|
4218
|
+
return { error }
|
|
4219
|
+
}
|
|
4220
|
+
})()
|
|
4221
|
+
|
|
4222
|
+
promises.push(promise)
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
trySpawn()
|
|
4226
|
+
|
|
4227
|
+
while (promises.length > 0) {
|
|
4228
|
+
const { error, done, value } = await promises[0]
|
|
4229
|
+
|
|
4230
|
+
promises.shift()
|
|
4231
|
+
|
|
4232
|
+
if (error) {
|
|
4233
|
+
throw error
|
|
4234
|
+
}
|
|
4235
|
+
|
|
4236
|
+
if (done) {
|
|
4237
|
+
return
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4240
|
+
// Spawn if just dropped below backpressure limit and below the concurrency limit
|
|
4241
|
+
trySpawn()
|
|
4242
|
+
|
|
4243
|
+
if (value === pMapSkip) {
|
|
4244
|
+
continue
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
yield value
|
|
4248
|
+
}
|
|
4249
|
+
},
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
const pMapSkip = Symbol('skip')
|
|
4254
|
+
|
|
4255
|
+
;// ./lib/helpers/pMap/index.js
|
|
4256
|
+
|
|
4257
|
+
|
|
3933
4258
|
;// ./lib/helpers/pReduce/index.js
|
|
3934
4259
|
|
|
3935
4260
|
|
|
@@ -4286,11 +4611,19 @@ function toLowerCase(str) {
|
|
|
4286
4611
|
.toLowerCase()
|
|
4287
4612
|
}
|
|
4288
4613
|
|
|
4614
|
+
function toScreamingSnakeCase(str) {
|
|
4615
|
+
return str
|
|
4616
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
4617
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
|
4618
|
+
.toUpperCase()
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4289
4621
|
const stringHelper = {
|
|
4290
4622
|
isSame,
|
|
4291
4623
|
setCode,
|
|
4292
4624
|
toCamelCase,
|
|
4293
4625
|
toLowerCase,
|
|
4626
|
+
toScreamingSnakeCase,
|
|
4294
4627
|
}
|
|
4295
4628
|
|
|
4296
4629
|
|
|
@@ -4426,6 +4759,7 @@ function tenantPlugin(schema, options) {
|
|
|
4426
4759
|
|
|
4427
4760
|
|
|
4428
4761
|
|
|
4762
|
+
|
|
4429
4763
|
|
|
4430
4764
|
|
|
4431
4765
|
;// ./lib/index.js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@questwork/q-utilities",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "Questwork QUtilities",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Questwork Consulting Limited",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@aws-sdk/client-sts": "^3.812.0",
|
|
32
32
|
"@babel/core": "^7.27.1",
|
|
33
33
|
"@babel/eslint-parser": "^7.27.1",
|
|
34
|
+
"@jmespath-community/jmespath": "^1.3.0",
|
|
34
35
|
"@questwork/q-eslint-config": "^0.1.8",
|
|
35
36
|
"babel-loader": "^8.2.4",
|
|
36
37
|
"babel-plugin-component": "^1.1.1",
|