s3db.js 11.0.4 → 11.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "s3db.js",
3
- "version": "11.0.4",
3
+ "version": "11.0.5",
4
4
  "description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
5
5
  "main": "dist/s3db.cjs.js",
6
6
  "module": "dist/s3db.es.js",
@@ -851,18 +851,18 @@ export async function getWeekByDay(resourceName, field, week, options, fieldHand
851
851
  const year = parseInt(week.substring(0, 4));
852
852
  const weekNum = parseInt(week.substring(6, 8));
853
853
 
854
- // Calculate the first day of the week (Monday)
855
- const jan4 = new Date(year, 0, 4);
856
- const jan4Day = jan4.getDay() || 7; // Sunday = 7
857
- const firstMonday = new Date(year, 0, 4 - jan4Day + 1);
854
+ // Calculate the first day of the week (Monday) using ISO 8601 - use UTC
855
+ const jan4 = new Date(Date.UTC(year, 0, 4));
856
+ const jan4Day = jan4.getUTCDay() || 7; // Sunday = 7
857
+ const firstMonday = new Date(Date.UTC(year, 0, 4 - jan4Day + 1));
858
858
  const weekStart = new Date(firstMonday);
859
- weekStart.setDate(weekStart.getDate() + (weekNum - 1) * 7);
859
+ weekStart.setUTCDate(weekStart.getUTCDate() + (weekNum - 1) * 7);
860
860
 
861
861
  // Get all 7 days of the week
862
862
  const days = [];
863
863
  for (let i = 0; i < 7; i++) {
864
864
  const day = new Date(weekStart);
865
- day.setDate(weekStart.getDate() + i);
865
+ day.setUTCDate(weekStart.getUTCDate() + i);
866
866
  days.push(day.toISOString().substring(0, 10));
867
867
  }
868
868
 
@@ -897,16 +897,16 @@ export async function getWeekByHour(resourceName, field, week, options, fieldHan
897
897
  const year = parseInt(week.substring(0, 4));
898
898
  const weekNum = parseInt(week.substring(6, 8));
899
899
 
900
- // Calculate the first day of the week (Monday)
901
- const jan4 = new Date(year, 0, 4);
902
- const jan4Day = jan4.getDay() || 7; // Sunday = 7
903
- const firstMonday = new Date(year, 0, 4 - jan4Day + 1);
900
+ // Calculate the first day of the week (Monday) using ISO 8601 - use UTC
901
+ const jan4 = new Date(Date.UTC(year, 0, 4));
902
+ const jan4Day = jan4.getUTCDay() || 7; // Sunday = 7
903
+ const firstMonday = new Date(Date.UTC(year, 0, 4 - jan4Day + 1));
904
904
  const weekStart = new Date(firstMonday);
905
- weekStart.setDate(weekStart.getDate() + (weekNum - 1) * 7);
905
+ weekStart.setUTCDate(weekStart.getUTCDate() + (weekNum - 1) * 7);
906
906
 
907
907
  // Get first and last day of week
908
908
  const weekEnd = new Date(weekStart);
909
- weekEnd.setDate(weekEnd.getDate() + 6);
909
+ weekEnd.setUTCDate(weekEnd.getUTCDate() + 6);
910
910
 
911
911
  const startDate = weekStart.toISOString().substring(0, 10);
912
912
  const endDate = weekEnd.toISOString().substring(0, 10);
@@ -937,21 +937,31 @@ export async function getWeekByHour(resourceName, field, week, options, fieldHan
937
937
  export async function getLastNHours(resourceName, field, hours = 24, options, fieldHandlers) {
938
938
  const now = new Date();
939
939
  const hoursAgo = new Date(now);
940
- hoursAgo.setHours(hoursAgo.getHours() - hours);
940
+ hoursAgo.setHours(hoursAgo.getHours() - hours + 1); // +1 to include current hour
941
941
 
942
- const startDate = hoursAgo.toISOString().substring(0, 13); // YYYY-MM-DDTHH
943
- const endDate = now.toISOString().substring(0, 13);
942
+ const startHour = hoursAgo.toISOString().substring(0, 13); // YYYY-MM-DDTHH
943
+ const endHour = now.toISOString().substring(0, 13);
944
944
 
945
945
  const data = await getAnalytics(resourceName, field, {
946
946
  period: 'hour',
947
- startDate,
948
- endDate
947
+ startDate: startHour,
948
+ endDate: endHour
949
949
  }, fieldHandlers);
950
950
 
951
951
  if (options.fillGaps) {
952
- const startDay = hoursAgo.toISOString().substring(0, 10);
953
- const endDay = now.toISOString().substring(0, 10);
954
- return fillGaps(data, 'hour', startDay, endDay);
952
+ // For hour-level gaps, we need to manually generate the exact hours requested
953
+ const result = [];
954
+ const emptyRecord = { count: 0, sum: 0, avg: 0, min: 0, max: 0, recordCount: 0 };
955
+ const dataMap = new Map(data.map(d => [d.cohort, d]));
956
+
957
+ const current = new Date(hoursAgo);
958
+ for (let i = 0; i < hours; i++) {
959
+ const cohort = current.toISOString().substring(0, 13);
960
+ result.push(dataMap.get(cohort) || { cohort, ...emptyRecord });
961
+ current.setHours(current.getHours() + 1);
962
+ }
963
+
964
+ return result;
955
965
  }
956
966
 
957
967
  return data;
@@ -1008,7 +1018,7 @@ export async function getLastNWeeks(resourceName, field, weeks = 4, options, fie
1008
1018
  export async function getLastNMonths(resourceName, field, months = 12, options, fieldHandlers) {
1009
1019
  const now = new Date();
1010
1020
  const monthsAgo = new Date(now);
1011
- monthsAgo.setMonth(monthsAgo.getMonth() - months);
1021
+ monthsAgo.setMonth(monthsAgo.getMonth() - months + 1); // +1 to include current month
1012
1022
 
1013
1023
  const startDate = monthsAgo.toISOString().substring(0, 7); // YYYY-MM
1014
1024
  const endDate = now.toISOString().substring(0, 7);
@@ -1020,7 +1030,19 @@ export async function getLastNMonths(resourceName, field, months = 12, options,
1020
1030
  }, fieldHandlers);
1021
1031
 
1022
1032
  if (options.fillGaps) {
1023
- return fillGaps(data, 'month', startDate, endDate);
1033
+ // Generate exact months requested
1034
+ const result = [];
1035
+ const emptyRecord = { count: 0, sum: 0, avg: 0, min: 0, max: 0, recordCount: 0 };
1036
+ const dataMap = new Map(data.map(d => [d.cohort, d]));
1037
+
1038
+ const current = new Date(monthsAgo);
1039
+ for (let i = 0; i < months; i++) {
1040
+ const cohort = current.toISOString().substring(0, 7);
1041
+ result.push(dataMap.get(cohort) || { cohort, ...emptyRecord });
1042
+ current.setMonth(current.getMonth() + 1);
1043
+ }
1044
+
1045
+ return result;
1024
1046
  }
1025
1047
 
1026
1048
  return data;
@@ -17,7 +17,7 @@ import {
17
17
  runConsolidation
18
18
  } from "./consolidation.js";
19
19
  import { runGarbageCollection } from "./garbage-collection.js";
20
- import { updateAnalytics, getAnalytics, getMonthByDay, getDayByHour, getLastNDays, getYearByMonth, getYearByWeek, getMonthByWeek, getMonthByHour, getTopRecords } from "./analytics.js";
20
+ import { updateAnalytics, getAnalytics, getMonthByDay, getDayByHour, getLastNDays, getYearByMonth, getYearByWeek, getMonthByWeek, getMonthByHour, getTopRecords, getYearByDay, getWeekByDay, getWeekByHour, getLastNHours, getLastNWeeks, getLastNMonths } from "./analytics.js";
21
21
  import { onInstall, onStart, onStop, watchForResource, completeFieldSetup } from "./install.js";
22
22
 
23
23
  export class EventualConsistencyPlugin extends Plugin {
@@ -459,6 +459,78 @@ export class EventualConsistencyPlugin extends Plugin {
459
459
  async getTopRecords(resourceName, field, options = {}) {
460
460
  return await getTopRecords(resourceName, field, options, this.fieldHandlers);
461
461
  }
462
+
463
+ /**
464
+ * Get analytics for entire year, broken down by days
465
+ * @param {string} resourceName - Resource name
466
+ * @param {string} field - Field name
467
+ * @param {number} year - Year (e.g., 2025)
468
+ * @param {Object} options - Options
469
+ * @returns {Promise<Array>} Daily analytics for the year (up to 365/366 records)
470
+ */
471
+ async getYearByDay(resourceName, field, year, options = {}) {
472
+ return await getYearByDay(resourceName, field, year, options, this.fieldHandlers);
473
+ }
474
+
475
+ /**
476
+ * Get analytics for entire week, broken down by days
477
+ * @param {string} resourceName - Resource name
478
+ * @param {string} field - Field name
479
+ * @param {string} week - Week in YYYY-Www format (e.g., '2025-W42')
480
+ * @param {Object} options - Options
481
+ * @returns {Promise<Array>} Daily analytics for the week (7 records)
482
+ */
483
+ async getWeekByDay(resourceName, field, week, options = {}) {
484
+ return await getWeekByDay(resourceName, field, week, options, this.fieldHandlers);
485
+ }
486
+
487
+ /**
488
+ * Get analytics for entire week, broken down by hours
489
+ * @param {string} resourceName - Resource name
490
+ * @param {string} field - Field name
491
+ * @param {string} week - Week in YYYY-Www format (e.g., '2025-W42')
492
+ * @param {Object} options - Options
493
+ * @returns {Promise<Array>} Hourly analytics for the week (168 records)
494
+ */
495
+ async getWeekByHour(resourceName, field, week, options = {}) {
496
+ return await getWeekByHour(resourceName, field, week, options, this.fieldHandlers);
497
+ }
498
+
499
+ /**
500
+ * Get analytics for last N hours
501
+ * @param {string} resourceName - Resource name
502
+ * @param {string} field - Field name
503
+ * @param {number} hours - Number of hours to look back (default: 24)
504
+ * @param {Object} options - Options
505
+ * @returns {Promise<Array>} Hourly analytics
506
+ */
507
+ async getLastNHours(resourceName, field, hours = 24, options = {}) {
508
+ return await getLastNHours(resourceName, field, hours, options, this.fieldHandlers);
509
+ }
510
+
511
+ /**
512
+ * Get analytics for last N weeks
513
+ * @param {string} resourceName - Resource name
514
+ * @param {string} field - Field name
515
+ * @param {number} weeks - Number of weeks to look back (default: 4)
516
+ * @param {Object} options - Options
517
+ * @returns {Promise<Array>} Weekly analytics
518
+ */
519
+ async getLastNWeeks(resourceName, field, weeks = 4, options = {}) {
520
+ return await getLastNWeeks(resourceName, field, weeks, options, this.fieldHandlers);
521
+ }
522
+
523
+ /**
524
+ * Get analytics for last N months
525
+ * @param {string} resourceName - Resource name
526
+ * @param {string} field - Field name
527
+ * @param {number} months - Number of months to look back (default: 12)
528
+ * @param {Object} options - Options
529
+ * @returns {Promise<Array>} Monthly analytics
530
+ */
531
+ async getLastNMonths(resourceName, field, months = 12, options = {}) {
532
+ return await getLastNMonths(resourceName, field, months, options, this.fieldHandlers);
533
+ }
462
534
  }
463
535
 
464
536
  export default EventualConsistencyPlugin;