s3db.js 11.0.3 → 11.0.4

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.3",
3
+ "version": "11.0.4",
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",
@@ -265,9 +265,9 @@ async function rollupPeriod(period, cohort, sourcePrefix, analyticsResource, con
265
265
  if (period === 'day') {
266
266
  sourcePeriod = 'hour';
267
267
  } else if (period === 'week') {
268
- sourcePeriod = 'day';
268
+ sourcePeriod = 'day'; // Week aggregates from days
269
269
  } else if (period === 'month') {
270
- sourcePeriod = 'week'; // Aggregate weeks to month
270
+ sourcePeriod = 'day'; // Month aggregates from days AND hours (like week)
271
271
  } else {
272
272
  sourcePeriod = 'day'; // Fallback
273
273
  }
@@ -291,6 +291,8 @@ async function rollupPeriod(period, cohort, sourcePrefix, analyticsResource, con
291
291
  });
292
292
  } else {
293
293
  // For day and month, simple prefix matching works
294
+ // day: aggregates from hours (cohort '2025-10-09' matches '2025-10-09T14', '2025-10-09T15', etc)
295
+ // month: aggregates from days (cohort '2025-10' matches '2025-10-01', '2025-10-02', etc)
294
296
  sourceAnalytics = allAnalytics.filter(a =>
295
297
  a.period === sourcePeriod && a.cohort.startsWith(sourcePrefix)
296
298
  );
@@ -806,3 +808,220 @@ export async function getTopRecords(resourceName, field, options, fieldHandlers)
806
808
  // Limit results
807
809
  return records.slice(0, limit);
808
810
  }
811
+
812
+ /**
813
+ * Get analytics for entire year, broken down by days
814
+ *
815
+ * @param {string} resourceName - Resource name
816
+ * @param {string} field - Field name
817
+ * @param {number} year - Year (e.g., 2025)
818
+ * @param {Object} options - Options
819
+ * @param {Object} fieldHandlers - Field handlers map
820
+ * @returns {Promise<Array>} Daily analytics for the year (up to 365/366 records)
821
+ */
822
+ export async function getYearByDay(resourceName, field, year, options, fieldHandlers) {
823
+ const startDate = `${year}-01-01`;
824
+ const endDate = `${year}-12-31`;
825
+
826
+ const data = await getAnalytics(resourceName, field, {
827
+ period: 'day',
828
+ startDate,
829
+ endDate
830
+ }, fieldHandlers);
831
+
832
+ if (options.fillGaps) {
833
+ return fillGaps(data, 'day', startDate, endDate);
834
+ }
835
+
836
+ return data;
837
+ }
838
+
839
+ /**
840
+ * Get analytics for entire week, broken down by days
841
+ *
842
+ * @param {string} resourceName - Resource name
843
+ * @param {string} field - Field name
844
+ * @param {string} week - Week in YYYY-Www format (e.g., '2025-W42')
845
+ * @param {Object} options - Options
846
+ * @param {Object} fieldHandlers - Field handlers map
847
+ * @returns {Promise<Array>} Daily analytics for the week (7 records)
848
+ */
849
+ export async function getWeekByDay(resourceName, field, week, options, fieldHandlers) {
850
+ // week format: '2025-W42'
851
+ const year = parseInt(week.substring(0, 4));
852
+ const weekNum = parseInt(week.substring(6, 8));
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);
858
+ const weekStart = new Date(firstMonday);
859
+ weekStart.setDate(weekStart.getDate() + (weekNum - 1) * 7);
860
+
861
+ // Get all 7 days of the week
862
+ const days = [];
863
+ for (let i = 0; i < 7; i++) {
864
+ const day = new Date(weekStart);
865
+ day.setDate(weekStart.getDate() + i);
866
+ days.push(day.toISOString().substring(0, 10));
867
+ }
868
+
869
+ const startDate = days[0];
870
+ const endDate = days[6];
871
+
872
+ const data = await getAnalytics(resourceName, field, {
873
+ period: 'day',
874
+ startDate,
875
+ endDate
876
+ }, fieldHandlers);
877
+
878
+ if (options.fillGaps) {
879
+ return fillGaps(data, 'day', startDate, endDate);
880
+ }
881
+
882
+ return data;
883
+ }
884
+
885
+ /**
886
+ * Get analytics for entire week, broken down by hours
887
+ *
888
+ * @param {string} resourceName - Resource name
889
+ * @param {string} field - Field name
890
+ * @param {string} week - Week in YYYY-Www format (e.g., '2025-W42')
891
+ * @param {Object} options - Options
892
+ * @param {Object} fieldHandlers - Field handlers map
893
+ * @returns {Promise<Array>} Hourly analytics for the week (168 records)
894
+ */
895
+ export async function getWeekByHour(resourceName, field, week, options, fieldHandlers) {
896
+ // week format: '2025-W42'
897
+ const year = parseInt(week.substring(0, 4));
898
+ const weekNum = parseInt(week.substring(6, 8));
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);
904
+ const weekStart = new Date(firstMonday);
905
+ weekStart.setDate(weekStart.getDate() + (weekNum - 1) * 7);
906
+
907
+ // Get first and last day of week
908
+ const weekEnd = new Date(weekStart);
909
+ weekEnd.setDate(weekEnd.getDate() + 6);
910
+
911
+ const startDate = weekStart.toISOString().substring(0, 10);
912
+ const endDate = weekEnd.toISOString().substring(0, 10);
913
+
914
+ const data = await getAnalytics(resourceName, field, {
915
+ period: 'hour',
916
+ startDate,
917
+ endDate
918
+ }, fieldHandlers);
919
+
920
+ if (options.fillGaps) {
921
+ return fillGaps(data, 'hour', startDate, endDate);
922
+ }
923
+
924
+ return data;
925
+ }
926
+
927
+ /**
928
+ * Get analytics for last N hours
929
+ *
930
+ * @param {string} resourceName - Resource name
931
+ * @param {string} field - Field name
932
+ * @param {number} hours - Number of hours to look back (default: 24)
933
+ * @param {Object} options - Options
934
+ * @param {Object} fieldHandlers - Field handlers map
935
+ * @returns {Promise<Array>} Hourly analytics
936
+ */
937
+ export async function getLastNHours(resourceName, field, hours = 24, options, fieldHandlers) {
938
+ const now = new Date();
939
+ const hoursAgo = new Date(now);
940
+ hoursAgo.setHours(hoursAgo.getHours() - hours);
941
+
942
+ const startDate = hoursAgo.toISOString().substring(0, 13); // YYYY-MM-DDTHH
943
+ const endDate = now.toISOString().substring(0, 13);
944
+
945
+ const data = await getAnalytics(resourceName, field, {
946
+ period: 'hour',
947
+ startDate,
948
+ endDate
949
+ }, fieldHandlers);
950
+
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);
955
+ }
956
+
957
+ return data;
958
+ }
959
+
960
+ /**
961
+ * Get analytics for last N weeks
962
+ *
963
+ * @param {string} resourceName - Resource name
964
+ * @param {string} field - Field name
965
+ * @param {number} weeks - Number of weeks to look back (default: 4)
966
+ * @param {Object} options - Options
967
+ * @param {Object} fieldHandlers - Field handlers map
968
+ * @returns {Promise<Array>} Weekly analytics
969
+ */
970
+ export async function getLastNWeeks(resourceName, field, weeks = 4, options, fieldHandlers) {
971
+ const now = new Date();
972
+ const weeksAgo = new Date(now);
973
+ weeksAgo.setDate(weeksAgo.getDate() - (weeks * 7));
974
+
975
+ // Get week cohorts for the range
976
+ const weekCohorts = [];
977
+ const currentDate = new Date(weeksAgo);
978
+ while (currentDate <= now) {
979
+ const weekCohort = getCohortWeekFromDate(currentDate);
980
+ if (!weekCohorts.includes(weekCohort)) {
981
+ weekCohorts.push(weekCohort);
982
+ }
983
+ currentDate.setDate(currentDate.getDate() + 7);
984
+ }
985
+
986
+ const startWeek = weekCohorts[0];
987
+ const endWeek = weekCohorts[weekCohorts.length - 1];
988
+
989
+ const data = await getAnalytics(resourceName, field, {
990
+ period: 'week',
991
+ startDate: startWeek,
992
+ endDate: endWeek
993
+ }, fieldHandlers);
994
+
995
+ return data;
996
+ }
997
+
998
+ /**
999
+ * Get analytics for last N months
1000
+ *
1001
+ * @param {string} resourceName - Resource name
1002
+ * @param {string} field - Field name
1003
+ * @param {number} months - Number of months to look back (default: 12)
1004
+ * @param {Object} options - Options
1005
+ * @param {Object} fieldHandlers - Field handlers map
1006
+ * @returns {Promise<Array>} Monthly analytics
1007
+ */
1008
+ export async function getLastNMonths(resourceName, field, months = 12, options, fieldHandlers) {
1009
+ const now = new Date();
1010
+ const monthsAgo = new Date(now);
1011
+ monthsAgo.setMonth(monthsAgo.getMonth() - months);
1012
+
1013
+ const startDate = monthsAgo.toISOString().substring(0, 7); // YYYY-MM
1014
+ const endDate = now.toISOString().substring(0, 7);
1015
+
1016
+ const data = await getAnalytics(resourceName, field, {
1017
+ period: 'month',
1018
+ startDate,
1019
+ endDate
1020
+ }, fieldHandlers);
1021
+
1022
+ if (options.fillGaps) {
1023
+ return fillGaps(data, 'month', startDate, endDate);
1024
+ }
1025
+
1026
+ return data;
1027
+ }