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