s3db.js 10.0.4 → 10.0.6
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/s3db.cjs.js +206 -17
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.es.js +206 -17
- package/dist/s3db.es.js.map +1 -1
- package/package.json +18 -18
- package/src/plugins/eventual-consistency.plugin.js +279 -18
package/dist/s3db.es.js
CHANGED
|
@@ -4445,8 +4445,24 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4445
4445
|
this.addHelperMethods();
|
|
4446
4446
|
if (this.config.autoConsolidate) {
|
|
4447
4447
|
this.startConsolidationTimer();
|
|
4448
|
+
if (this.config.verbose) {
|
|
4449
|
+
console.log(
|
|
4450
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Auto-consolidation ENABLED (interval: ${this.config.consolidationInterval}s, window: ${this.config.consolidationWindow}h, mode: ${this.config.mode})`
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
} else {
|
|
4454
|
+
if (this.config.verbose) {
|
|
4455
|
+
console.log(
|
|
4456
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Auto-consolidation DISABLED (manual consolidation only)`
|
|
4457
|
+
);
|
|
4458
|
+
}
|
|
4448
4459
|
}
|
|
4449
4460
|
this.startGarbageCollectionTimer();
|
|
4461
|
+
if (this.config.verbose) {
|
|
4462
|
+
console.log(
|
|
4463
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Setup complete. Resources: ${this.config.resource}_transactions_${this.config.field}, ${this.config.resource}_consolidation_locks_${this.config.field}${this.config.enableAnalytics ? `, ${this.config.resource}_analytics_${this.config.field}` : ""}`
|
|
4464
|
+
);
|
|
4465
|
+
}
|
|
4450
4466
|
}
|
|
4451
4467
|
async onStart() {
|
|
4452
4468
|
if (this.deferredSetup) {
|
|
@@ -4707,11 +4723,21 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4707
4723
|
};
|
|
4708
4724
|
if (this.config.batchTransactions) {
|
|
4709
4725
|
this.pendingTransactions.set(transaction.id, transaction);
|
|
4726
|
+
if (this.config.verbose) {
|
|
4727
|
+
console.log(
|
|
4728
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Transaction batched: ${data.operation} ${data.value} for ${data.originalId} (batch: ${this.pendingTransactions.size}/${this.config.batchSize})`
|
|
4729
|
+
);
|
|
4730
|
+
}
|
|
4710
4731
|
if (this.pendingTransactions.size >= this.config.batchSize) {
|
|
4711
4732
|
await this.flushPendingTransactions();
|
|
4712
4733
|
}
|
|
4713
4734
|
} else {
|
|
4714
4735
|
await this.transactionResource.insert(transaction);
|
|
4736
|
+
if (this.config.verbose) {
|
|
4737
|
+
console.log(
|
|
4738
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Transaction created: ${data.operation} ${data.value} for ${data.originalId} (cohort: ${cohortInfo.hour}, applied: false)`
|
|
4739
|
+
);
|
|
4740
|
+
}
|
|
4715
4741
|
}
|
|
4716
4742
|
return transaction;
|
|
4717
4743
|
}
|
|
@@ -4776,11 +4802,23 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4776
4802
|
}
|
|
4777
4803
|
startConsolidationTimer() {
|
|
4778
4804
|
const intervalMs = this.config.consolidationInterval * 1e3;
|
|
4805
|
+
if (this.config.verbose) {
|
|
4806
|
+
const nextRun = new Date(Date.now() + intervalMs);
|
|
4807
|
+
console.log(
|
|
4808
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation timer started. Next run at ${nextRun.toISOString()} (every ${this.config.consolidationInterval}s)`
|
|
4809
|
+
);
|
|
4810
|
+
}
|
|
4779
4811
|
this.consolidationTimer = setInterval(async () => {
|
|
4780
4812
|
await this.runConsolidation();
|
|
4781
4813
|
}, intervalMs);
|
|
4782
4814
|
}
|
|
4783
4815
|
async runConsolidation() {
|
|
4816
|
+
const startTime = Date.now();
|
|
4817
|
+
if (this.config.verbose) {
|
|
4818
|
+
console.log(
|
|
4819
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Starting consolidation run at ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
4820
|
+
);
|
|
4821
|
+
}
|
|
4784
4822
|
try {
|
|
4785
4823
|
const now = /* @__PURE__ */ new Date();
|
|
4786
4824
|
const hoursToCheck = this.config.consolidationWindow || 24;
|
|
@@ -4790,6 +4828,11 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4790
4828
|
const cohortInfo = this.getCohortInfo(date);
|
|
4791
4829
|
cohortHours.push(cohortInfo.hour);
|
|
4792
4830
|
}
|
|
4831
|
+
if (this.config.verbose) {
|
|
4832
|
+
console.log(
|
|
4833
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Querying ${hoursToCheck} hour partitions for pending transactions...`
|
|
4834
|
+
);
|
|
4835
|
+
}
|
|
4793
4836
|
const transactionsByHour = await Promise.all(
|
|
4794
4837
|
cohortHours.map(async (cohortHour) => {
|
|
4795
4838
|
const [ok, err, txns] = await tryFn(
|
|
@@ -4804,26 +4847,47 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4804
4847
|
const transactions = transactionsByHour.flat();
|
|
4805
4848
|
if (transactions.length === 0) {
|
|
4806
4849
|
if (this.config.verbose) {
|
|
4807
|
-
console.log(
|
|
4850
|
+
console.log(
|
|
4851
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - No pending transactions found. Next run in ${this.config.consolidationInterval}s`
|
|
4852
|
+
);
|
|
4808
4853
|
}
|
|
4809
4854
|
return;
|
|
4810
4855
|
}
|
|
4811
4856
|
const uniqueIds = [...new Set(transactions.map((t) => t.originalId))];
|
|
4857
|
+
if (this.config.verbose) {
|
|
4858
|
+
console.log(
|
|
4859
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Found ${transactions.length} pending transactions for ${uniqueIds.length} records. Consolidating with concurrency=${this.config.consolidationConcurrency}...`
|
|
4860
|
+
);
|
|
4861
|
+
}
|
|
4812
4862
|
const { results, errors } = await PromisePool.for(uniqueIds).withConcurrency(this.config.consolidationConcurrency).process(async (id) => {
|
|
4813
4863
|
return await this.consolidateRecord(id);
|
|
4814
4864
|
});
|
|
4865
|
+
const duration = Date.now() - startTime;
|
|
4815
4866
|
if (errors && errors.length > 0) {
|
|
4816
|
-
console.error(
|
|
4867
|
+
console.error(
|
|
4868
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation completed with ${errors.length} errors in ${duration}ms:`,
|
|
4869
|
+
errors
|
|
4870
|
+
);
|
|
4871
|
+
}
|
|
4872
|
+
if (this.config.verbose) {
|
|
4873
|
+
console.log(
|
|
4874
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation complete: ${results.length} records consolidated in ${duration}ms (${errors.length} errors). Next run in ${this.config.consolidationInterval}s`
|
|
4875
|
+
);
|
|
4817
4876
|
}
|
|
4818
4877
|
this.emit("eventual-consistency.consolidated", {
|
|
4819
4878
|
resource: this.config.resource,
|
|
4820
4879
|
field: this.config.field,
|
|
4821
4880
|
recordCount: uniqueIds.length,
|
|
4822
4881
|
successCount: results.length,
|
|
4823
|
-
errorCount: errors.length
|
|
4882
|
+
errorCount: errors.length,
|
|
4883
|
+
duration
|
|
4824
4884
|
});
|
|
4825
4885
|
} catch (error) {
|
|
4826
|
-
|
|
4886
|
+
const duration = Date.now() - startTime;
|
|
4887
|
+
console.error(
|
|
4888
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation error after ${duration}ms:`,
|
|
4889
|
+
error
|
|
4890
|
+
);
|
|
4827
4891
|
this.emit("eventual-consistency.consolidation-error", error);
|
|
4828
4892
|
}
|
|
4829
4893
|
}
|
|
@@ -4858,8 +4922,18 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4858
4922
|
})
|
|
4859
4923
|
);
|
|
4860
4924
|
if (!ok || !transactions || transactions.length === 0) {
|
|
4925
|
+
if (this.config.verbose) {
|
|
4926
|
+
console.log(
|
|
4927
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - No pending transactions for ${originalId}, skipping`
|
|
4928
|
+
);
|
|
4929
|
+
}
|
|
4861
4930
|
return currentValue;
|
|
4862
4931
|
}
|
|
4932
|
+
if (this.config.verbose) {
|
|
4933
|
+
console.log(
|
|
4934
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidating ${originalId}: ${transactions.length} pending transactions (current: ${currentValue})`
|
|
4935
|
+
);
|
|
4936
|
+
}
|
|
4863
4937
|
transactions.sort(
|
|
4864
4938
|
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
4865
4939
|
);
|
|
@@ -4868,6 +4942,11 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
4868
4942
|
transactions.unshift(this._createSyntheticSetTransaction(currentValue));
|
|
4869
4943
|
}
|
|
4870
4944
|
const consolidatedValue = this.config.reducer(transactions);
|
|
4945
|
+
if (this.config.verbose) {
|
|
4946
|
+
console.log(
|
|
4947
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - ${originalId}: ${currentValue} \u2192 ${consolidatedValue} (${consolidatedValue > currentValue ? "+" : ""}${consolidatedValue - currentValue})`
|
|
4948
|
+
);
|
|
4949
|
+
}
|
|
4871
4950
|
const [updateOk, updateErr] = await tryFn(
|
|
4872
4951
|
() => this.targetResource.update(originalId, {
|
|
4873
4952
|
[this.config.field]: consolidatedValue
|
|
@@ -5104,21 +5183,43 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
5104
5183
|
*/
|
|
5105
5184
|
async updateAnalytics(transactions) {
|
|
5106
5185
|
if (!this.analyticsResource || transactions.length === 0) return;
|
|
5186
|
+
if (this.config.verbose) {
|
|
5187
|
+
console.log(
|
|
5188
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Updating analytics for ${transactions.length} transactions...`
|
|
5189
|
+
);
|
|
5190
|
+
}
|
|
5107
5191
|
try {
|
|
5108
5192
|
const byHour = this._groupByCohort(transactions, "cohortHour");
|
|
5193
|
+
const cohortCount = Object.keys(byHour).length;
|
|
5194
|
+
if (this.config.verbose) {
|
|
5195
|
+
console.log(
|
|
5196
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Updating ${cohortCount} hourly analytics cohorts...`
|
|
5197
|
+
);
|
|
5198
|
+
}
|
|
5109
5199
|
for (const [cohort, txns] of Object.entries(byHour)) {
|
|
5110
5200
|
await this._upsertAnalytics("hour", cohort, txns);
|
|
5111
5201
|
}
|
|
5112
5202
|
if (this.config.analyticsConfig.rollupStrategy === "incremental") {
|
|
5113
5203
|
const uniqueHours = Object.keys(byHour);
|
|
5204
|
+
if (this.config.verbose) {
|
|
5205
|
+
console.log(
|
|
5206
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Rolling up ${uniqueHours.length} hours to daily/monthly analytics...`
|
|
5207
|
+
);
|
|
5208
|
+
}
|
|
5114
5209
|
for (const cohortHour of uniqueHours) {
|
|
5115
5210
|
await this._rollupAnalytics(cohortHour);
|
|
5116
5211
|
}
|
|
5117
5212
|
}
|
|
5118
|
-
} catch (error) {
|
|
5119
5213
|
if (this.config.verbose) {
|
|
5120
|
-
console.
|
|
5214
|
+
console.log(
|
|
5215
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Analytics update complete for ${cohortCount} cohorts`
|
|
5216
|
+
);
|
|
5121
5217
|
}
|
|
5218
|
+
} catch (error) {
|
|
5219
|
+
console.warn(
|
|
5220
|
+
`[EventualConsistency] ${this.config.resource}.${this.config.field} - Analytics update error:`,
|
|
5221
|
+
error.message
|
|
5222
|
+
);
|
|
5122
5223
|
}
|
|
5123
5224
|
}
|
|
5124
5225
|
/**
|
|
@@ -5348,79 +5449,163 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
5348
5449
|
recordCount: a.recordCount
|
|
5349
5450
|
}));
|
|
5350
5451
|
}
|
|
5452
|
+
/**
|
|
5453
|
+
* Fill gaps in analytics data with zeros for continuous time series
|
|
5454
|
+
* @private
|
|
5455
|
+
* @param {Array} data - Sparse analytics data
|
|
5456
|
+
* @param {string} period - Period type ('hour', 'day', 'month')
|
|
5457
|
+
* @param {string} startDate - Start date (ISO format)
|
|
5458
|
+
* @param {string} endDate - End date (ISO format)
|
|
5459
|
+
* @returns {Array} Complete time series with gaps filled
|
|
5460
|
+
*/
|
|
5461
|
+
_fillGaps(data, period, startDate, endDate) {
|
|
5462
|
+
if (!data || data.length === 0) {
|
|
5463
|
+
data = [];
|
|
5464
|
+
}
|
|
5465
|
+
const dataMap = /* @__PURE__ */ new Map();
|
|
5466
|
+
data.forEach((item) => {
|
|
5467
|
+
dataMap.set(item.cohort, item);
|
|
5468
|
+
});
|
|
5469
|
+
const result = [];
|
|
5470
|
+
const emptyRecord = {
|
|
5471
|
+
count: 0,
|
|
5472
|
+
sum: 0,
|
|
5473
|
+
avg: 0,
|
|
5474
|
+
min: 0,
|
|
5475
|
+
max: 0,
|
|
5476
|
+
recordCount: 0
|
|
5477
|
+
};
|
|
5478
|
+
if (period === "hour") {
|
|
5479
|
+
const start = /* @__PURE__ */ new Date(startDate + "T00:00:00Z");
|
|
5480
|
+
const end = /* @__PURE__ */ new Date(endDate + "T23:59:59Z");
|
|
5481
|
+
for (let dt = new Date(start); dt <= end; dt.setHours(dt.getHours() + 1)) {
|
|
5482
|
+
const cohort = dt.toISOString().substring(0, 13);
|
|
5483
|
+
result.push(dataMap.get(cohort) || { cohort, ...emptyRecord });
|
|
5484
|
+
}
|
|
5485
|
+
} else if (period === "day") {
|
|
5486
|
+
const start = new Date(startDate);
|
|
5487
|
+
const end = new Date(endDate);
|
|
5488
|
+
for (let dt = new Date(start); dt <= end; dt.setDate(dt.getDate() + 1)) {
|
|
5489
|
+
const cohort = dt.toISOString().substring(0, 10);
|
|
5490
|
+
result.push(dataMap.get(cohort) || { cohort, ...emptyRecord });
|
|
5491
|
+
}
|
|
5492
|
+
} else if (period === "month") {
|
|
5493
|
+
const startYear = parseInt(startDate.substring(0, 4));
|
|
5494
|
+
const startMonth = parseInt(startDate.substring(5, 7));
|
|
5495
|
+
const endYear = parseInt(endDate.substring(0, 4));
|
|
5496
|
+
const endMonth = parseInt(endDate.substring(5, 7));
|
|
5497
|
+
for (let year = startYear; year <= endYear; year++) {
|
|
5498
|
+
const firstMonth = year === startYear ? startMonth : 1;
|
|
5499
|
+
const lastMonth = year === endYear ? endMonth : 12;
|
|
5500
|
+
for (let month = firstMonth; month <= lastMonth; month++) {
|
|
5501
|
+
const cohort = `${year}-${month.toString().padStart(2, "0")}`;
|
|
5502
|
+
result.push(dataMap.get(cohort) || { cohort, ...emptyRecord });
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
return result;
|
|
5507
|
+
}
|
|
5351
5508
|
/**
|
|
5352
5509
|
* Get analytics for entire month, broken down by days
|
|
5353
5510
|
* @param {string} resourceName - Resource name
|
|
5354
5511
|
* @param {string} field - Field name
|
|
5355
5512
|
* @param {string} month - Month in YYYY-MM format
|
|
5513
|
+
* @param {Object} options - Options
|
|
5514
|
+
* @param {boolean} options.fillGaps - Fill missing days with zeros (default: false)
|
|
5356
5515
|
* @returns {Promise<Array>} Daily analytics for the month
|
|
5357
5516
|
*/
|
|
5358
|
-
async getMonthByDay(resourceName, field, month) {
|
|
5517
|
+
async getMonthByDay(resourceName, field, month, options = {}) {
|
|
5359
5518
|
const year = parseInt(month.substring(0, 4));
|
|
5360
5519
|
const monthNum = parseInt(month.substring(5, 7));
|
|
5361
5520
|
const firstDay = new Date(year, monthNum - 1, 1);
|
|
5362
5521
|
const lastDay = new Date(year, monthNum, 0);
|
|
5363
5522
|
const startDate = firstDay.toISOString().substring(0, 10);
|
|
5364
5523
|
const endDate = lastDay.toISOString().substring(0, 10);
|
|
5365
|
-
|
|
5524
|
+
const data = await this.getAnalytics(resourceName, field, {
|
|
5366
5525
|
period: "day",
|
|
5367
5526
|
startDate,
|
|
5368
5527
|
endDate
|
|
5369
5528
|
});
|
|
5529
|
+
if (options.fillGaps) {
|
|
5530
|
+
return this._fillGaps(data, "day", startDate, endDate);
|
|
5531
|
+
}
|
|
5532
|
+
return data;
|
|
5370
5533
|
}
|
|
5371
5534
|
/**
|
|
5372
5535
|
* Get analytics for entire day, broken down by hours
|
|
5373
5536
|
* @param {string} resourceName - Resource name
|
|
5374
5537
|
* @param {string} field - Field name
|
|
5375
5538
|
* @param {string} date - Date in YYYY-MM-DD format
|
|
5539
|
+
* @param {Object} options - Options
|
|
5540
|
+
* @param {boolean} options.fillGaps - Fill missing hours with zeros (default: false)
|
|
5376
5541
|
* @returns {Promise<Array>} Hourly analytics for the day
|
|
5377
5542
|
*/
|
|
5378
|
-
async getDayByHour(resourceName, field, date) {
|
|
5379
|
-
|
|
5543
|
+
async getDayByHour(resourceName, field, date, options = {}) {
|
|
5544
|
+
const data = await this.getAnalytics(resourceName, field, {
|
|
5380
5545
|
period: "hour",
|
|
5381
5546
|
date
|
|
5382
5547
|
});
|
|
5548
|
+
if (options.fillGaps) {
|
|
5549
|
+
return this._fillGaps(data, "hour", date, date);
|
|
5550
|
+
}
|
|
5551
|
+
return data;
|
|
5383
5552
|
}
|
|
5384
5553
|
/**
|
|
5385
5554
|
* Get analytics for last N days, broken down by days
|
|
5386
5555
|
* @param {string} resourceName - Resource name
|
|
5387
5556
|
* @param {string} field - Field name
|
|
5388
5557
|
* @param {number} days - Number of days to look back (default: 7)
|
|
5558
|
+
* @param {Object} options - Options
|
|
5559
|
+
* @param {boolean} options.fillGaps - Fill missing days with zeros (default: false)
|
|
5389
5560
|
* @returns {Promise<Array>} Daily analytics
|
|
5390
5561
|
*/
|
|
5391
|
-
async getLastNDays(resourceName, field, days = 7) {
|
|
5562
|
+
async getLastNDays(resourceName, field, days = 7, options = {}) {
|
|
5392
5563
|
const dates = Array.from({ length: days }, (_, i) => {
|
|
5393
5564
|
const date = /* @__PURE__ */ new Date();
|
|
5394
5565
|
date.setDate(date.getDate() - i);
|
|
5395
5566
|
return date.toISOString().substring(0, 10);
|
|
5396
5567
|
}).reverse();
|
|
5397
|
-
|
|
5568
|
+
const data = await this.getAnalytics(resourceName, field, {
|
|
5398
5569
|
period: "day",
|
|
5399
5570
|
startDate: dates[0],
|
|
5400
5571
|
endDate: dates[dates.length - 1]
|
|
5401
5572
|
});
|
|
5573
|
+
if (options.fillGaps) {
|
|
5574
|
+
return this._fillGaps(data, "day", dates[0], dates[dates.length - 1]);
|
|
5575
|
+
}
|
|
5576
|
+
return data;
|
|
5402
5577
|
}
|
|
5403
5578
|
/**
|
|
5404
5579
|
* Get analytics for entire year, broken down by months
|
|
5405
5580
|
* @param {string} resourceName - Resource name
|
|
5406
5581
|
* @param {string} field - Field name
|
|
5407
5582
|
* @param {number} year - Year (e.g., 2025)
|
|
5583
|
+
* @param {Object} options - Options
|
|
5584
|
+
* @param {boolean} options.fillGaps - Fill missing months with zeros (default: false)
|
|
5408
5585
|
* @returns {Promise<Array>} Monthly analytics for the year
|
|
5409
5586
|
*/
|
|
5410
|
-
async getYearByMonth(resourceName, field, year) {
|
|
5411
|
-
|
|
5587
|
+
async getYearByMonth(resourceName, field, year, options = {}) {
|
|
5588
|
+
const data = await this.getAnalytics(resourceName, field, {
|
|
5412
5589
|
period: "month",
|
|
5413
5590
|
year
|
|
5414
5591
|
});
|
|
5592
|
+
if (options.fillGaps) {
|
|
5593
|
+
const startDate = `${year}-01`;
|
|
5594
|
+
const endDate = `${year}-12`;
|
|
5595
|
+
return this._fillGaps(data, "month", startDate, endDate);
|
|
5596
|
+
}
|
|
5597
|
+
return data;
|
|
5415
5598
|
}
|
|
5416
5599
|
/**
|
|
5417
5600
|
* Get analytics for entire month, broken down by hours
|
|
5418
5601
|
* @param {string} resourceName - Resource name
|
|
5419
5602
|
* @param {string} field - Field name
|
|
5420
5603
|
* @param {string} month - Month in YYYY-MM format (or 'last' for previous month)
|
|
5604
|
+
* @param {Object} options - Options
|
|
5605
|
+
* @param {boolean} options.fillGaps - Fill missing hours with zeros (default: false)
|
|
5421
5606
|
* @returns {Promise<Array>} Hourly analytics for the month (up to 24*31=744 records)
|
|
5422
5607
|
*/
|
|
5423
|
-
async getMonthByHour(resourceName, field, month) {
|
|
5608
|
+
async getMonthByHour(resourceName, field, month, options = {}) {
|
|
5424
5609
|
let year, monthNum;
|
|
5425
5610
|
if (month === "last") {
|
|
5426
5611
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -5435,11 +5620,15 @@ class EventualConsistencyPlugin extends Plugin {
|
|
|
5435
5620
|
const lastDay = new Date(year, monthNum, 0);
|
|
5436
5621
|
const startDate = firstDay.toISOString().substring(0, 10);
|
|
5437
5622
|
const endDate = lastDay.toISOString().substring(0, 10);
|
|
5438
|
-
|
|
5623
|
+
const data = await this.getAnalytics(resourceName, field, {
|
|
5439
5624
|
period: "hour",
|
|
5440
5625
|
startDate,
|
|
5441
5626
|
endDate
|
|
5442
5627
|
});
|
|
5628
|
+
if (options.fillGaps) {
|
|
5629
|
+
return this._fillGaps(data, "hour", startDate, endDate);
|
|
5630
|
+
}
|
|
5631
|
+
return data;
|
|
5443
5632
|
}
|
|
5444
5633
|
/**
|
|
5445
5634
|
* Get top records by volume
|
|
@@ -11560,7 +11749,7 @@ class Database extends EventEmitter {
|
|
|
11560
11749
|
this.id = idGenerator(7);
|
|
11561
11750
|
this.version = "1";
|
|
11562
11751
|
this.s3dbVersion = (() => {
|
|
11563
|
-
const [ok, err, version] = tryFn(() => true ? "10.0.
|
|
11752
|
+
const [ok, err, version] = tryFn(() => true ? "10.0.6" : "latest");
|
|
11564
11753
|
return ok ? version : "latest";
|
|
11565
11754
|
})();
|
|
11566
11755
|
this.resources = {};
|