@@ -13,21 +13,18 @@ import { parseRepoUrl } from '#shared/utils/git-providers'
1313import type { PackageMetaResponse } from '#shared/types'
1414import { encodePackageName } from '#shared/utils/npm'
1515import { fetchNpmDownloadsRange } from '~/utils/npm/api'
16+ import { parseIsoDate , toIsoDate , addDays } from '~/utils/date'
17+ import {
18+ buildDailyEvolution ,
19+ buildWeeklyEvolution ,
20+ buildMonthlyEvolution ,
21+ buildYearlyEvolution ,
22+ } from '~/utils/chart-data-buckets'
1623
1724export type PackumentLikeForTime = {
1825 time ?: Record < string , string >
1926}
2027
21- function toIsoDateString ( date : Date ) : string {
22- return date . toISOString ( ) . slice ( 0 , 10 )
23- }
24-
25- function addDays ( date : Date , days : number ) : Date {
26- const updatedDate = new Date ( date )
27- updatedDate . setUTCDate ( updatedDate . getUTCDate ( ) + days )
28- return updatedDate
29- }
30-
3128function startOfUtcMonth ( date : Date ) : Date {
3229 return new Date ( Date . UTC ( date . getUTCFullYear ( ) , date . getUTCMonth ( ) , 1 ) )
3330}
@@ -36,17 +33,9 @@ function startOfUtcYear(date: Date): Date {
3633 return new Date ( Date . UTC ( date . getUTCFullYear ( ) , 0 , 1 ) )
3734}
3835
39- function parseIsoDateOnly ( value : string ) : Date {
40- return new Date ( `${ value } T00:00:00.000Z` )
41- }
42-
43- function formatIsoDateOnly ( date : Date ) : string {
44- return date . toISOString ( ) . slice ( 0 , 10 )
45- }
46-
4736function differenceInUtcDaysInclusive ( startIso : string , endIso : string ) : number {
48- const start = parseIsoDateOnly ( startIso )
49- const end = parseIsoDateOnly ( endIso )
37+ const start = parseIsoDate ( startIso )
38+ const end = parseIsoDate ( endIso )
5039 return Math . floor ( ( end . getTime ( ) - start . getTime ( ) ) / 86400000 ) + 1
5140}
5241
@@ -59,16 +48,16 @@ function splitIsoRangeIntoChunksInclusive(
5948 if ( totalDays <= maximumDaysPerRequest ) return [ { startIso, endIso } ]
6049
6150 const chunks : Array < { startIso : string ; endIso : string } > = [ ]
62- let cursorStart = parseIsoDateOnly ( startIso )
63- const finalEnd = parseIsoDateOnly ( endIso )
51+ let cursorStart = parseIsoDate ( startIso )
52+ const finalEnd = parseIsoDate ( endIso )
6453
6554 while ( cursorStart . getTime ( ) <= finalEnd . getTime ( ) ) {
6655 const cursorEnd = addDays ( cursorStart , maximumDaysPerRequest - 1 )
6756 const actualEnd = cursorEnd . getTime ( ) < finalEnd . getTime ( ) ? cursorEnd : finalEnd
6857
6958 chunks . push ( {
70- startIso : formatIsoDateOnly ( cursorStart ) ,
71- endIso : formatIsoDateOnly ( actualEnd ) ,
59+ startIso : toIsoDate ( cursorStart ) ,
60+ endIso : toIsoDate ( actualEnd ) ,
7261 } )
7362
7463 cursorStart = addDays ( actualEnd , 1 )
@@ -89,101 +78,6 @@ function mergeDailyPoints(points: DailyRawPoint[]): DailyRawPoint[] {
8978 . map ( ( [ day , value ] ) => ( { day, value } ) )
9079}
9180
92- export function buildDailyEvolutionFromDaily ( daily : DailyRawPoint [ ] ) : DailyDataPoint [ ] {
93- return daily
94- . slice ( )
95- . sort ( ( a , b ) => a . day . localeCompare ( b . day ) )
96- . map ( item => {
97- const dayDate = parseIsoDateOnly ( item . day )
98- const timestamp = dayDate . getTime ( )
99-
100- return { day : item . day , value : item . value , timestamp }
101- } )
102- }
103-
104- export function buildRollingWeeklyEvolutionFromDaily (
105- daily : DailyRawPoint [ ] ,
106- rangeStartIso : string ,
107- rangeEndIso : string ,
108- ) : WeeklyDataPoint [ ] {
109- const sorted = daily . slice ( ) . sort ( ( a , b ) => a . day . localeCompare ( b . day ) )
110- const rangeStartDate = parseIsoDateOnly ( rangeStartIso )
111- const rangeEndDate = parseIsoDateOnly ( rangeEndIso )
112-
113- const groupedByIndex = new Map < number , number > ( )
114-
115- for ( const item of sorted ) {
116- const itemDate = parseIsoDateOnly ( item . day )
117- const dayOffset = Math . floor ( ( itemDate . getTime ( ) - rangeStartDate . getTime ( ) ) / 86400000 )
118- if ( dayOffset < 0 ) continue
119-
120- const weekIndex = Math . floor ( dayOffset / 7 )
121- groupedByIndex . set ( weekIndex , ( groupedByIndex . get ( weekIndex ) ?? 0 ) + item . value )
122- }
123-
124- return Array . from ( groupedByIndex . entries ( ) )
125- . sort ( ( [ a ] , [ b ] ) => a - b )
126- . map ( ( [ weekIndex , value ] ) => {
127- const weekStartDate = addDays ( rangeStartDate , weekIndex * 7 )
128- const weekEndDate = addDays ( weekStartDate , 6 )
129-
130- // Clamp weekEnd to the actual data range end date
131- const clampedWeekEndDate =
132- weekEndDate . getTime ( ) > rangeEndDate . getTime ( ) ? rangeEndDate : weekEndDate
133-
134- const weekStartIso = toIsoDateString ( weekStartDate )
135- const weekEndIso = toIsoDateString ( clampedWeekEndDate )
136-
137- const timestampStart = weekStartDate . getTime ( )
138- const timestampEnd = clampedWeekEndDate . getTime ( )
139-
140- return {
141- value,
142- weekKey : `${ weekStartIso } _${ weekEndIso } ` ,
143- weekStart : weekStartIso ,
144- weekEnd : weekEndIso ,
145- timestampStart,
146- timestampEnd,
147- }
148- } )
149- }
150-
151- export function buildMonthlyEvolutionFromDaily ( daily : DailyRawPoint [ ] ) : MonthlyDataPoint [ ] {
152- const sorted = daily . slice ( ) . sort ( ( a , b ) => a . day . localeCompare ( b . day ) )
153- const valuesByMonth = new Map < string , number > ( )
154-
155- for ( const item of sorted ) {
156- const month = item . day . slice ( 0 , 7 )
157- valuesByMonth . set ( month , ( valuesByMonth . get ( month ) ?? 0 ) + item . value )
158- }
159-
160- return Array . from ( valuesByMonth . entries ( ) )
161- . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
162- . map ( ( [ month , value ] ) => {
163- const monthStartDate = parseIsoDateOnly ( `${ month } -01` )
164- const timestamp = monthStartDate . getTime ( )
165- return { month, value, timestamp }
166- } )
167- }
168-
169- export function buildYearlyEvolutionFromDaily ( daily : DailyRawPoint [ ] ) : YearlyDataPoint [ ] {
170- const sorted = daily . slice ( ) . sort ( ( a , b ) => a . day . localeCompare ( b . day ) )
171- const valuesByYear = new Map < string , number > ( )
172-
173- for ( const item of sorted ) {
174- const year = item . day . slice ( 0 , 4 )
175- valuesByYear . set ( year , ( valuesByYear . get ( year ) ?? 0 ) + item . value )
176- }
177-
178- return Array . from ( valuesByYear . entries ( ) )
179- . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
180- . map ( ( [ year , value ] ) => {
181- const yearStartDate = parseIsoDateOnly ( `${ year } -01-01` )
182- const timestamp = yearStartDate . getTime ( )
183- return { year, value, timestamp }
184- } )
185- }
186-
18781const npmDailyRangeCache = import . meta. client ? new Map < string , Promise < DailyRawPoint [ ] > > ( ) : null
18882const likesEvolutionCache = import . meta. client ? new Map < string , Promise < DailyRawPoint [ ] > > ( ) : null
18983const contributorsEvolutionCache = import . meta. client
@@ -238,8 +132,8 @@ function buildWeeklyEvolutionFromContributorCounts(
238132
239133 const clampedWeekEndDate = weekEndDate . getTime ( ) > rangeEnd . getTime ( ) ? rangeEnd : weekEndDate
240134
241- const weekStartIso = toIsoDateString ( weekStartDate )
242- const weekEndIso = toIsoDateString ( clampedWeekEndDate )
135+ const weekStartIso = toIsoDate ( weekStartDate )
136+ const weekEndIso = toIsoDate ( clampedWeekEndDate )
243137
244138 return {
245139 value,
@@ -415,11 +309,11 @@ export function useCharts() {
415309 )
416310
417311 const endDateOnly = toDateOnly ( evolutionOptions . endDate )
418- const end = endDateOnly ? parseIsoDateOnly ( endDateOnly ) : yesterday
312+ const end = endDateOnly ? parseIsoDate ( endDateOnly ) : yesterday
419313
420314 const startDateOnly = toDateOnly ( evolutionOptions . startDate )
421315 if ( startDateOnly ) {
422- const start = parseIsoDateOnly ( startDateOnly )
316+ const start = parseIsoDate ( startDateOnly )
423317 return { start, end }
424318 }
425319
@@ -465,16 +359,17 @@ export function useCharts() {
465359
466360 const { start, end } = resolveDateRange ( resolvedOptions , resolvedCreatedIso )
467361
468- const startIso = toIsoDateString ( start )
469- const endIso = toIsoDateString ( end )
362+ const startIso = toIsoDate ( start )
363+ const endIso = toIsoDate ( end )
470364
471365 const sortedDaily = await fetchDailyRangeChunked ( resolvedPackageName , startIso , endIso )
472366
473- if ( resolvedOptions . granularity === 'day' ) return buildDailyEvolutionFromDaily ( sortedDaily )
367+ if ( resolvedOptions . granularity === 'day' ) return buildDailyEvolution ( sortedDaily )
474368 if ( resolvedOptions . granularity === 'week' )
475- return buildRollingWeeklyEvolutionFromDaily ( sortedDaily , startIso , endIso )
476- if ( resolvedOptions . granularity === 'month' ) return buildMonthlyEvolutionFromDaily ( sortedDaily )
477- return buildYearlyEvolutionFromDaily ( sortedDaily )
369+ return buildWeeklyEvolution ( sortedDaily , startIso , endIso )
370+ if ( resolvedOptions . granularity === 'month' )
371+ return buildMonthlyEvolution ( sortedDaily , startIso , endIso )
372+ return buildYearlyEvolution ( sortedDaily , startIso , endIso )
478373 }
479374
480375 async function fetchPackageLikesEvolution (
@@ -508,17 +403,17 @@ export function useCharts() {
508403 const sortedDaily = await dailyLikesPromise
509404
510405 const { start, end } = resolveDateRange ( resolvedOptions , null )
511- const startIso = toIsoDateString ( start )
512- const endIso = toIsoDateString ( end )
406+ const startIso = toIsoDate ( start )
407+ const endIso = toIsoDate ( end )
513408
514409 const filteredDaily = sortedDaily . filter ( d => d . day >= startIso && d . day <= endIso )
515410
516- if ( resolvedOptions . granularity === 'day' ) return buildDailyEvolutionFromDaily ( filteredDaily )
411+ if ( resolvedOptions . granularity === 'day' ) return buildDailyEvolution ( filteredDaily )
517412 if ( resolvedOptions . granularity === 'week' )
518- return buildRollingWeeklyEvolutionFromDaily ( filteredDaily , startIso , endIso )
413+ return buildWeeklyEvolution ( filteredDaily , startIso , endIso )
519414 if ( resolvedOptions . granularity === 'month' )
520- return buildMonthlyEvolutionFromDaily ( filteredDaily )
521- return buildYearlyEvolutionFromDaily ( filteredDaily )
415+ return buildMonthlyEvolution ( filteredDaily , startIso , endIso )
416+ return buildYearlyEvolution ( filteredDaily , startIso , endIso )
522417 }
523418
524419 async function fetchRepoContributorsEvolution (
0 commit comments