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
|
@@ -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 = '
|
|
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
|
+
}
|