├── .eslintrc.json ├── .gitignore ├── .nvmrc ├── .prettierrc.json ├── .vscode └── settings.json ├── README.md ├── next-sitemap.config.js ├── next.config.js ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico └── site.webmanifest ├── src └── app │ ├── (common) │ ├── components │ │ ├── AnalyticsWrapper.tsx │ │ ├── AreaChart.tsx │ │ ├── BarsChart.tsx │ │ ├── DownloadsChart.tsx │ │ ├── OgBarsChart.tsx │ │ └── PackageAutocomplete.tsx │ └── utils │ │ ├── consts.ts │ │ ├── http.ts │ │ └── npm-api │ │ ├── index.ts │ │ └── types.ts │ ├── api │ └── package │ │ ├── og-image │ │ └── [...name] │ │ │ └── route.tsx │ │ └── readme-image │ │ └── [...name] │ │ └── route.tsx │ ├── globals.css │ ├── layout.tsx │ ├── package │ └── [...name] │ │ ├── PackageInsights.tsx │ │ └── page.tsx │ └── page.tsx ├── tailwind.config.js └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": ["eslint:recommended", "prettier", "next/core-web-vitals"], 4 | "overrides": [ 5 | { 6 | "files": ["**/*.ts?(x)"], 7 | "extends": ["plugin:@typescript-eslint/recommended", "plugin:import/typescript"], 8 | "plugins": ["@typescript-eslint", "import"], 9 | "rules": { 10 | "@typescript-eslint/no-explicit-any": "off", 11 | "import/no-extraneous-dependencies": "error", 12 | "import/order": [ 13 | "error", 14 | { 15 | "alphabetize": { 16 | "order": "asc" 17 | }, 18 | "groups": ["builtin", "external", "internal", ["parent", "sibling", "index"]], 19 | "newlines-between": "never", 20 | "pathGroups": [ 21 | { 22 | "group": "internal", 23 | "pattern": "#src/**" 24 | } 25 | ], 26 | "pathGroupsExcludedImportTypes": ["builtin"] 27 | } 28 | ], 29 | "no-console": ["error", { "allow": ["warn", "error"] }] 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | /public/robots.txt 39 | /public/sitemap* -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true, 4 | "tabWidth": 2, 5 | "bracketSpacing": true, 6 | "trailingComma": "es5", 7 | "bracketSameLine": true, 8 | "useTabs": false, 9 | "endOfLine": "lf", 10 | "printWidth": 100, 11 | "overrides": [ 12 | { 13 | "files": "*.md", 14 | "options": { 15 | "singleQuote": false, 16 | "quoteProps": "preserve" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "[json]": { 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll": "never" 6 | } 7 | }, 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll": "explicit" 10 | }, 11 | "typescript.tsdk": "node_modules/typescript/lib", 12 | "typescript.enablePromptUseWorkspaceTsdk": true, 13 | "cSpell.ignoreWords": ["rawdata"], 14 | "cSpell.words": ["colocation", "Mjml", "Synchronisation"] 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # npminsights 2 | 3 | The application is made using Next.js 13 with the new `app` directory architecture. 4 | 5 | The original idea behind this website was to generate images containing download metrics in order to display them on Github Readmes. 6 | In order to have something to link to when clicking the image, a website was created as well. 7 | 8 | ## Images 9 | 10 | The OG image for each package is made using the [@vercel/og](https://vercel.com/docs/concepts/functions/edge-functions/og-image-generation) library. 11 | 12 | We also provide an image made for being displayed on libraries READMEs like this one: 13 | [![react-easy-crop npminsights](https://npminsights.vercel.app/api/package/readme-image/react-easy-crop?v=2023-02-22)](https://npminsights.vercel.app/package/react-easy-crop) 14 | 15 | To add it to your README, just use the following markdown snippet (replace "YOUR_PACKAGE" by your package name): 16 | 17 | ```markdown 18 | [![YOUR_PACKAGE npminsights](https://npminsights.vercel.app/api/package/readme-image/YOUR_PACKAGE)](https://npminsights.vercel.app/package/YOUR_PACKAGE) 19 | ``` 20 | 21 | ## Development 22 | 23 | To install the dependencies: 24 | 25 | ```sh 26 | pnpm i 27 | ``` 28 | 29 | To run the app: 30 | 31 | ```sh 32 | pnpm dev 33 | ``` 34 | -------------------------------------------------------------------------------- /next-sitemap.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next-sitemap').IConfig} */ 2 | module.exports = { 3 | siteUrl: `https://npminsights.vercel.app`, 4 | generateRobotsTxt: true, 5 | robotsTxtOptions: { 6 | policies: [ 7 | { 8 | userAgent: '*', 9 | [process.env.VERCEL_ENV === 'production' ? 'allow' : 'disallow']: '/', 10 | }, 11 | ], 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | experimental: { 4 | appDir: true, 5 | typedRoutes: true, 6 | }, 7 | }; 8 | 9 | module.exports = nextConfig; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npminsights", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "postbuild": "next-sitemap", 9 | "start": "next start", 10 | "lint": "next lint", 11 | "preinstall": "npx only-allow pnpm" 12 | }, 13 | "engines": { 14 | "node": ">=18.0.0" 15 | }, 16 | "dependencies": { 17 | "@next/env": "^13.2.3", 18 | "@tailwindcss/line-clamp": "^0.4.2", 19 | "@vercel/analytics": "^0.1.10", 20 | "@vercel/og": "^0.1.0", 21 | "@visx/axis": "^2.0.0", 22 | "@visx/curve": "^2.0.0", 23 | "@visx/event": "^2.0.1", 24 | "@visx/gradient": "^2.0.0", 25 | "@visx/grid": "^2.0.1", 26 | "@visx/group": "^2.0.0", 27 | "@visx/mock-data": "^2.0.0", 28 | "@visx/responsive": "^2.0.0", 29 | "@visx/scale": "^2.0.0", 30 | "@visx/shape": "^2.0.0", 31 | "@visx/tooltip": "^2.0.0", 32 | "clsx": "^1.2.1", 33 | "d3-array": "^3.2.2", 34 | "d3-time-format": "^4.1.0", 35 | "date-fns": "^2.29.3", 36 | "downshift": "^7.2.0", 37 | "millify": "^5.0.1", 38 | "next": "^13.2.3", 39 | "next-sitemap": "^4.0.4", 40 | "react": "18.2.0", 41 | "react-dom": "18.2.0", 42 | "tailwindcss": "^3.2.6", 43 | "use-debounce": "^9.0.3" 44 | }, 45 | "devDependencies": { 46 | "@types/d3-array": "^3.0.4", 47 | "@types/d3-time-format": "^4.0.0", 48 | "@types/node": "18.13.0", 49 | "@types/react": "18.0.28", 50 | "@types/react-dom": "18.0.10", 51 | "@typescript-eslint/eslint-plugin": "^5.51.0", 52 | "@typescript-eslint/parser": "^5.51.0", 53 | "autoprefixer": "^10.4.13", 54 | "eslint": "8.34.0", 55 | "eslint-config-next": "13.1.6", 56 | "eslint-config-prettier": "^8.6.0", 57 | "eslint-plugin-import": "^2.27.5", 58 | "postcss": "^8.4.21", 59 | "prettier": "^2.8.4", 60 | "prettier-plugin-tailwindcss": "^0.2.2", 61 | "typescript": "4.9.5" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentinH/npminsights/f8790cd6693e6f99a43a1aeccedd3d331acf9e95/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentinH/npminsights/f8790cd6693e6f99a43a1aeccedd3d331acf9e95/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentinH/npminsights/f8790cd6693e6f99a43a1aeccedd3d331acf9e95/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentinH/npminsights/f8790cd6693e6f99a43a1aeccedd3d331acf9e95/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentinH/npminsights/f8790cd6693e6f99a43a1aeccedd3d331acf9e95/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentinH/npminsights/f8790cd6693e6f99a43a1aeccedd3d331acf9e95/public/favicon.ico -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npminsights.vercel.app", 3 | "short_name": "npminsights.vercel.app", 4 | "icons": [ 5 | { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, 6 | { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } 7 | ], 8 | "theme_color": "#0f162b", 9 | "background_color": "#0f162b", 10 | "display": "standalone" 11 | } 12 | -------------------------------------------------------------------------------- /src/app/(common)/components/AnalyticsWrapper.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { Analytics } from '@vercel/analytics/react'; 3 | 4 | export function AnalyticsWrapper() { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/(common)/components/AreaChart.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { curveLinear } from '@visx/curve'; 3 | import { localPoint } from '@visx/event'; 4 | import { LinearGradient } from '@visx/gradient'; 5 | import { GridRows, GridColumns } from '@visx/grid'; 6 | import { ParentSize } from '@visx/responsive'; 7 | import { scaleTime, scaleLinear } from '@visx/scale'; 8 | import { AreaClosed, Line, Bar } from '@visx/shape'; 9 | import { TooltipWithBounds, defaultStyles, useTooltip } from '@visx/tooltip'; 10 | import { max, extent, bisector } from 'd3-array'; 11 | import React, { useMemo, useCallback } from 'react'; 12 | import tailwindColors from 'tailwindcss/colors'; 13 | 14 | export type Point = { 15 | date: string; 16 | value: number; 17 | }; 18 | 19 | export const background = tailwindColors.slate[800]; 20 | export const background2 = tailwindColors.gray[800]; 21 | export const accentColor = tailwindColors.slate[50]; 22 | export const accentColorDark = tailwindColors.amber[500]; 23 | const tooltipStyles = { 24 | ...defaultStyles, 25 | background, 26 | border: '1px solid white', 27 | color: 'white', 28 | }; 29 | 30 | // accessors 31 | const getDate = (d: Point) => new Date(d.date); 32 | const getValue = (d: Point) => d.value; 33 | const bisectDate = bisector((d) => new Date(d.date)).left; 34 | 35 | export type AreaProps = { 36 | points: Point[]; 37 | formatDate: (date: Date) => string; 38 | formatValue: (value: number) => string; 39 | width: number; 40 | height: number; 41 | margin?: { top: number; right: number; bottom: number; left: number }; 42 | }; 43 | 44 | export const AreaChart = ({ 45 | points, 46 | formatDate, 47 | formatValue, 48 | width, 49 | height, 50 | margin = { top: 0, right: 0, bottom: 0, left: 0 }, 51 | }: AreaProps) => { 52 | const { 53 | tooltipLeft = 0, 54 | tooltipTop = 0, 55 | tooltipData, 56 | hideTooltip, 57 | showTooltip, 58 | } = useTooltip(); 59 | 60 | // bounds 61 | const innerWidth = width - margin.left - margin.right; 62 | const innerHeight = height - margin.top - margin.bottom; 63 | 64 | // scales 65 | const dateScale = useMemo( 66 | () => 67 | scaleTime({ 68 | range: [margin.left, innerWidth + margin.left], 69 | domain: extent(points, getDate) as [Date, Date], 70 | }), 71 | [innerWidth, margin.left, points] 72 | ); 73 | const valueScale = useMemo( 74 | () => 75 | scaleLinear({ 76 | range: [innerHeight + margin.top, margin.top], 77 | domain: [0, (max(points, getValue) || 0) + innerHeight / 3], 78 | nice: true, 79 | }), 80 | [innerHeight, margin.top, points] 81 | ); 82 | 83 | // tooltip handler 84 | const handleTooltip = useCallback( 85 | (event: React.TouchEvent | React.MouseEvent) => { 86 | const { x } = localPoint(event) || { x: 0 }; 87 | const x0 = dateScale.invert(x); 88 | const index = bisectDate(points, x0, 1); 89 | const d0 = points[index - 1]; 90 | const d1 = points[index]; 91 | let d = d0; 92 | if (d1 && getDate(d1)) { 93 | d = x0.valueOf() - getDate(d0).valueOf() > getDate(d1).valueOf() - x0.valueOf() ? d1 : d0; 94 | } 95 | showTooltip({ 96 | tooltipData: d, 97 | tooltipLeft: x, 98 | tooltipTop: valueScale(getValue(d)), 99 | }); 100 | }, 101 | [dateScale, points, showTooltip, valueScale] 102 | ); 103 | 104 | return ( 105 |
106 | 107 | 108 | 109 | 118 | 127 | 128 | data={points} 129 | x={(d) => dateScale(getDate(d)) ?? 0} 130 | y={(d) => valueScale(getValue(d)) ?? 0} 131 | yScale={valueScale} 132 | strokeWidth={1} 133 | stroke="url(#area-gradient)" 134 | fill="url(#area-gradient)" 135 | curve={curveLinear} 136 | /> 137 | hideTooltip()} 147 | /> 148 | {tooltipData && ( 149 | 150 | 158 | 169 | 178 | 179 | )} 180 | 181 | {tooltipData && ( 182 |
183 | 188 | {formatValue(getValue(tooltipData))} 189 | 190 | 199 | {formatDate(getDate(tooltipData))} 200 | 201 |
202 | )} 203 |
204 | ); 205 | }; 206 | 207 | type ResponsiveAreaProps = Omit; 208 | 209 | export const ResponsiveAreaChart = (props: ResponsiveAreaProps) => { 210 | return ( 211 | 212 | {({ width: visWidth, height: visHeight }) => ( 213 | 214 | )} 215 | 216 | ); 217 | }; 218 | -------------------------------------------------------------------------------- /src/app/(common)/components/BarsChart.tsx: -------------------------------------------------------------------------------- 1 | import { AxisBottom, AxisLeft } from '@visx/axis'; 2 | import { LinearGradient } from '@visx/gradient'; 3 | import { Group } from '@visx/group'; 4 | import ParentSize from '@visx/responsive/lib/components/ParentSizeModern'; 5 | import { scaleBand, scaleLinear } from '@visx/scale'; 6 | import { Bar } from '@visx/shape'; 7 | import { defaultStyles, Tooltip, useTooltip } from '@visx/tooltip'; 8 | import millify from 'millify'; 9 | import React, { useMemo } from 'react'; 10 | import tailwindColors from 'tailwindcss/colors'; 11 | 12 | export type Point = { 13 | date: string; 14 | value: number; 15 | }; 16 | 17 | const leftMargin = 40; 18 | const verticalMargin = 70; 19 | const background = tailwindColors.slate[800]; 20 | const background2 = tailwindColors.gray[800]; 21 | const accentColor = tailwindColors.slate[50]; 22 | const accentColorDark = tailwindColors.slate[100]; 23 | const axisColor = tailwindColors.amber[500]; 24 | 25 | const tooltipStyles = { 26 | ...defaultStyles, 27 | background, 28 | border: '1px solid white', 29 | color: 'white', 30 | }; 31 | 32 | const axisLeftTickLabelProps = { 33 | dx: '-0.25em', 34 | dy: '0.25em', 35 | fontFamily: 'Arial', 36 | fontSize: 10, 37 | textAnchor: 'end' as const, 38 | fill: axisColor, 39 | }; 40 | 41 | // access 42 | 43 | // accessors 44 | const getDate = (d: Point) => d.date; 45 | const getValue = (d: Point) => d.value; 46 | 47 | export type BarsProps = { 48 | points: Point[]; 49 | width: number; 50 | height: number; 51 | formatDate: (date: Date) => string; 52 | formatValue: (value: number) => string; 53 | }; 54 | 55 | function BarsChart({ points, width, height, formatDate, formatValue }: BarsProps) { 56 | const { 57 | tooltipLeft = 0, 58 | tooltipTop = 0, 59 | tooltipData, 60 | hideTooltip, 61 | showTooltip, 62 | } = useTooltip(); 63 | const tooltipTimeout = React.useRef(); 64 | 65 | // bounds 66 | const xMax = width - leftMargin; 67 | const yMax = height - verticalMargin; 68 | const showBottomLegend = width / points.length > 15; 69 | 70 | // scales, memoize for performance 71 | const dateScale = useMemo( 72 | () => 73 | scaleBand({ 74 | range: [0, xMax], 75 | round: true, 76 | domain: points.map(getDate), 77 | padding: 0.1, 78 | }), 79 | [xMax, points] 80 | ); 81 | const yScale = useMemo( 82 | () => 83 | scaleLinear({ 84 | range: [yMax, 0], 85 | round: true, 86 | domain: [0, Math.max(...points.map(getValue))], 87 | }), 88 | [yMax, points] 89 | ); 90 | 91 | const handleTooltip = React.useCallback( 92 | (point: Point, left: number, top: number) => () => { 93 | if (tooltipTimeout.current) clearTimeout(tooltipTimeout.current); 94 | showTooltip({ 95 | tooltipData: point, 96 | tooltipTop: top, 97 | tooltipLeft: left, 98 | }); 99 | }, 100 | [showTooltip] 101 | ); 102 | 103 | return width < 10 ? null : ( 104 | <> 105 | 106 | 107 | 113 | 114 | {points.map((p) => { 115 | const date = getDate(p); 116 | const barWidth = dateScale.bandwidth(); 117 | const barHeight = yMax - (yScale(getValue(p)) ?? 0); 118 | const barX = dateScale(date) || 0; 119 | const barY = yMax - barHeight; 120 | const tooltipX = barX + barWidth / 2 + leftMargin / 2; 121 | return ( 122 | { 131 | tooltipTimeout.current = window.setTimeout(hideTooltip, 300); 132 | }} 133 | onTouchStart={handleTooltip(p, tooltipX, barY)} 134 | onTouchMove={handleTooltip(p, tooltipX, barY)} 135 | onMouseMove={handleTooltip(p, tooltipX, barY)} 136 | /> 137 | ); 138 | })} 139 | {showBottomLegend && ( 140 | formatDate(new Date(d))} 144 | stroke={axisColor} 145 | tickStroke={axisColor} 146 | tickLabelProps={() => ({ 147 | fill: axisColor, 148 | fontSize: 14, 149 | textAnchor: 'middle', 150 | })} 151 | /> 152 | )} 153 | millify(Number(v))} 156 | numTicks={5} 157 | stroke={axisColor} 158 | tickStroke={axisColor} 159 | tickLabelProps={() => axisLeftTickLabelProps} 160 | /> 161 | 162 | 163 | {tooltipData && ( 164 |
165 | 175 |

{formatDate(new Date(getDate(tooltipData)))}

176 |

{formatValue(getValue(tooltipData))}

177 |
178 |
179 | )} 180 | 181 | ); 182 | } 183 | 184 | type ResponsiveBarsChartProps = Omit; 185 | 186 | export const ResponsiveBarsChart = (props: ResponsiveBarsChartProps) => { 187 | return ( 188 | 189 | {({ width: visWidth, height: visHeight }) => ( 190 | 191 | )} 192 | 193 | ); 194 | }; 195 | -------------------------------------------------------------------------------- /src/app/(common)/components/DownloadsChart.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import clsx from 'clsx'; 3 | import { format } from 'date-fns'; 4 | import React from 'react'; 5 | import { ResponsiveAreaChart } from './AreaChart'; 6 | import { ResponsiveBarsChart } from './BarsChart'; 7 | import { NpmDailyDownloads } from '../utils/npm-api/types'; 8 | 9 | type DownloadsChartProps = { 10 | title: string; 11 | downloads: NpmDailyDownloads[]; 12 | dateFormat: string; 13 | className?: string; 14 | chartType?: 'area' | 'bars'; 15 | }; 16 | 17 | export const DownloadsChart = ({ 18 | title, 19 | downloads, 20 | dateFormat, 21 | className, 22 | chartType = 'area', 23 | }: DownloadsChartProps) => { 24 | const downloadsPoints = React.useMemo( 25 | () => 26 | downloads.map((d) => ({ 27 | date: d.day, 28 | value: d.downloads, 29 | })), 30 | [downloads] 31 | ); 32 | return ( 33 |
34 |

{title}

35 | {chartType === 'area' ? ( 36 | value.toLocaleString('en')} 39 | formatDate={(date) => format(date, dateFormat)} 40 | /> 41 | ) : ( 42 | format(date, dateFormat)} 45 | formatValue={(value) => value.toLocaleString('en')} 46 | /> 47 | )} 48 |
49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /src/app/(common)/components/OgBarsChart.tsx: -------------------------------------------------------------------------------- 1 | import { scaleBand, scaleLinear } from '@visx/scale'; 2 | import tailwindColors from 'tailwindcss/colors'; 3 | 4 | type Point = { 5 | date: string; 6 | value: number; 7 | }; 8 | 9 | // accessors 10 | const getDate = (d: Point) => d.date; 11 | const getValue = (d: Point) => d.value; 12 | 13 | type OgBarsChartProps = { 14 | points: Point[]; 15 | width: number; 16 | height: number; 17 | }; 18 | 19 | /* This component is made to be used inside an ImageResponse from @vercel/og where we cannot use default @visx components for some reasons */ 20 | export function OgBarsChart({ points, width, height }: OgBarsChartProps) { 21 | const leftMargin = 40; 22 | const verticalMargin = 70; 23 | // bounds 24 | const xMax = width - leftMargin; 25 | const yMax = height - verticalMargin; 26 | 27 | // scales, memoize for performance 28 | const dateScale = scaleBand({ 29 | range: [0, xMax], 30 | round: true, 31 | domain: points.map(getDate), 32 | padding: 0.1, 33 | }); 34 | const yScale = scaleLinear({ 35 | range: [yMax, 0], 36 | round: true, 37 | domain: [0, Math.max(...points.map(getValue))], 38 | }); 39 | 40 | return ( 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {points.map((p) => { 50 | const date = getDate(p); 51 | const barWidth = dateScale.bandwidth(); 52 | const barHeight = yMax - (yScale(getValue(p)) ?? 0); 53 | const barX = dateScale(date) || 0; 54 | const barY = yMax - barHeight; 55 | return ( 56 | 65 | ); 66 | })} 67 | 68 | 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /src/app/(common)/components/PackageAutocomplete.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import clsx from 'clsx'; 3 | import { useCombobox } from 'downshift'; 4 | import { useRouter } from 'next/navigation'; 5 | import React from 'react'; 6 | import { useDebounce } from 'use-debounce'; 7 | import http from '../utils/http'; 8 | 9 | type PackageAutocompleteProps = { 10 | placeholder?: string; 11 | className?: string; 12 | }; 13 | 14 | export const PackageAutocomplete = ({ 15 | placeholder = 'find a package', 16 | className, 17 | }: PackageAutocompleteProps) => { 18 | const router = useRouter(); 19 | const [inputValue, setInputValue] = React.useState(''); 20 | const [suggestions, setSuggestions] = React.useState([]); 21 | const [isSearching, setIsSearching] = React.useState(false); 22 | const [hasNoResult, setHasNoResult] = React.useState(false); 23 | 24 | const onInputValueChange = (value: string) => { 25 | setInputValue(value); 26 | setHasNoResult(false); 27 | if (!value) { 28 | setSuggestions([]); 29 | } 30 | }; 31 | 32 | const { isOpen, getMenuProps, getInputProps, highlightedIndex, getItemProps } = useCombobox({ 33 | onInputValueChange({ inputValue }) { 34 | onInputValueChange(inputValue || ''); 35 | }, 36 | items: suggestions, 37 | itemToString(suggestion) { 38 | return suggestion ? suggestion.package.name : ''; 39 | }, 40 | onSelectedItemChange({ selectedItem }) { 41 | if (selectedItem) { 42 | router.push(`/package/${selectedItem.package.name}`); 43 | } 44 | }, 45 | }); 46 | 47 | const [debouncedValue] = useDebounce(inputValue, 300); 48 | 49 | // fetch the suggestions when the user has stopped typing her input 50 | React.useEffect(() => { 51 | let active = true; 52 | async function call() { 53 | setIsSearching(true); 54 | const suggestions = await getPackagesSuggestions(debouncedValue); 55 | if (active) { 56 | setHasNoResult(suggestions.length === 0); 57 | setSuggestions(suggestions); 58 | setIsSearching(false); 59 | } 60 | } 61 | if (debouncedValue) { 62 | call(); 63 | } 64 | return () => { 65 | active = false; 66 | }; 67 | }, [debouncedValue]); 68 | 69 | const showNoResult = hasNoResult && debouncedValue && !isSearching; 70 | 71 | return ( 72 |
73 | 78 |
    84 | {isOpen && ( 85 | <> 86 | {showNoResult && ( 87 |
  • 88 | No package found. Click{' '} 89 | 94 | here 95 | {' '} 96 | to create it 🙂 97 |
  • 98 | )} 99 | {suggestions.map((suggestion, index) => ( 100 |
  • 107 | {suggestion.package.name} 108 | 109 | {suggestion.package.description} 110 | 111 |
  • 112 | ))} 113 | 114 | )} 115 |
116 |
117 | ); 118 | }; 119 | 120 | type Suggestion = { 121 | package: { 122 | name: string; 123 | scope: string; 124 | version: string; 125 | description: string; 126 | keywords: string[]; 127 | date: string; 128 | }; 129 | score: { 130 | final: number; 131 | detail: { 132 | quality: number; 133 | popularity: number; 134 | maintenance: number; 135 | }; 136 | }; 137 | searchScore: number; 138 | highlight: string; 139 | }; 140 | 141 | const getPackagesSuggestions = async (query: string) => { 142 | const suggestionSort = (packageA: Suggestion, packageB: Suggestion) => { 143 | // Rank closely matching packages followed by most popular ones 144 | // taken from bundlephobia source 145 | if (Math.abs(Math.log(packageB.searchScore) - Math.log(packageA.searchScore)) > 1) { 146 | return packageB.searchScore - packageA.searchScore; 147 | } else { 148 | return packageB.score.detail.popularity - packageA.score.detail.popularity; 149 | } 150 | }; 151 | try { 152 | const { data: suggestions } = await http.get( 153 | `https://api.npms.io/v2/search/suggestions?q=${query}` 154 | ); 155 | return suggestions.sort(suggestionSort); 156 | } catch (e) { 157 | return []; 158 | } 159 | }; 160 | -------------------------------------------------------------------------------- /src/app/(common)/utils/consts.ts: -------------------------------------------------------------------------------- 1 | export const SIX_HOURS_IN_SECONDS = 60 * 60 * 6; 2 | -------------------------------------------------------------------------------- /src/app/(common)/utils/http.ts: -------------------------------------------------------------------------------- 1 | const makeWrapper = 2 | (method: RequestInit['method']) => 3 | async ( 4 | input: RequestInfo | URL, 5 | init?: RequestInit | undefined 6 | ): Promise<{ 7 | data: Data; 8 | status: number; 9 | }> => { 10 | const res = await fetch(input, { 11 | ...init, 12 | method, 13 | }); 14 | 15 | if (!res.ok) { 16 | throw new Error(res.statusText); 17 | } 18 | 19 | return { 20 | data: await res.json(), 21 | status: res.status, 22 | }; 23 | }; 24 | 25 | const http = { 26 | get: makeWrapper('GET'), 27 | post: makeWrapper('POST'), 28 | put: makeWrapper('PUT'), 29 | delete: makeWrapper('DELETE'), 30 | }; 31 | 32 | export default http; 33 | -------------------------------------------------------------------------------- /src/app/(common)/utils/npm-api/index.ts: -------------------------------------------------------------------------------- 1 | import { addYears, endOfYear, format } from 'date-fns'; 2 | import { NpmDailyDownloads, NpmRangeData } from './types'; 3 | import { SIX_HOURS_IN_SECONDS } from '../consts'; 4 | import http from '../http'; 5 | 6 | const FIRST_AVAILABLE_DATE = '2015-01-01'; 7 | 8 | export const getPackageInsights = async (packageName = '', sinceDate = FIRST_AVAILABLE_DATE) => { 9 | if (process.env.NODE_ENV === 'development') { 10 | return MOCK_DATA; 11 | } 12 | const allDailyDownloads = await getAllDailyDownloads(packageName, sinceDate); 13 | 14 | return { 15 | total: getSumOfDownloads(allDailyDownloads), 16 | lastDay: allDailyDownloads[allDailyDownloads.length - 1].downloads, 17 | lastDayPreviousWeek: allDailyDownloads[allDailyDownloads.length - 8].downloads, 18 | lastWeek: getSumOfDownloads(allDailyDownloads.slice(-7)), 19 | previousWeek: getSumOfDownloads(allDailyDownloads.slice(-14, -7)), 20 | lastMonth: getSumOfDownloads(allDailyDownloads.slice(-30)), 21 | previousMonth: getSumOfDownloads(allDailyDownloads.slice(-60, -30)), 22 | lastYear: getSumOfDownloads(allDailyDownloads.slice(-365)), 23 | previousYear: getSumOfDownloads(allDailyDownloads.slice(-730, -365)), 24 | allDailyDownloads, 25 | }; 26 | }; 27 | 28 | const getAllDailyDownloads = async (packageName: string, sinceDate: string) => { 29 | const ranges = getListOfRangesSinceStart(sinceDate); 30 | const rangesResponses = await Promise.all( 31 | ranges.map(({ start, end }) => { 32 | return http.get( 33 | `https://api.npmjs.org/downloads/range/${start}:${end}/${packageName}`, 34 | { 35 | next: { 36 | revalidate: SIX_HOURS_IN_SECONDS, 37 | }, 38 | } 39 | ); 40 | }) 41 | ); 42 | const allDownloads = rangesResponses.reduce((acc, { data }) => { 43 | return [...acc, ...data.downloads]; 44 | }, []); 45 | 46 | const lastDayWithData = allDownloads.findLastIndex(({ downloads }) => downloads > 0); 47 | const allDownloadsUntilLastDayWithData = allDownloads.slice(0, lastDayWithData + 1); 48 | 49 | // remove all leading zeros 50 | const firstNonZeroIndex = allDownloadsUntilLastDayWithData.findIndex((p) => p.downloads !== 0); 51 | if (firstNonZeroIndex > 0) { 52 | return allDownloadsUntilLastDayWithData.slice(firstNonZeroIndex); 53 | } 54 | 55 | return allDownloadsUntilLastDayWithData; 56 | }; 57 | 58 | const getSumOfDownloads = (downloads: NpmDailyDownloads[]) => { 59 | return downloads.reduce((acc, { downloads }) => { 60 | return acc + downloads; 61 | }, 0); 62 | }; 63 | 64 | const getListOfRangesSinceStart = (sinceDate: string) => { 65 | const startDate = new Date(sinceDate); 66 | const today = new Date(); 67 | const ranges = []; 68 | let current = startDate; 69 | while (current <= today) { 70 | ranges.push({ 71 | start: format(current, 'yyyy-MM-dd'), 72 | end: format(endOfYear(current), 'yyyy-MM-dd'), 73 | }); 74 | current = addYears(current, 1); 75 | } 76 | return ranges; 77 | }; 78 | 79 | declare global { 80 | interface Array { 81 | findLastIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number; 82 | } 83 | } 84 | 85 | const MOCK_DATA = { 86 | total: 16442488, 87 | lastDay: 6966, 88 | lastDayPreviousWeek: 7888, 89 | lastWeek: 222199, 90 | previousWeek: 219398, 91 | lastMonth: 884518, 92 | previousMonth: 729721, 93 | lastYear: 8786605, 94 | previousYear: 5352532, 95 | allDailyDownloads: [ 96 | { downloads: 49, day: '2018-06-19' }, 97 | { downloads: 11, day: '2018-06-20' }, 98 | { downloads: 2, day: '2018-06-21' }, 99 | { downloads: 10, day: '2018-06-22' }, 100 | { downloads: 63, day: '2018-06-23' }, 101 | { downloads: 6, day: '2018-06-24' }, 102 | { downloads: 61, day: '2018-06-25' }, 103 | { downloads: 47, day: '2018-06-26' }, 104 | { downloads: 17, day: '2018-06-27' }, 105 | { downloads: 11, day: '2018-06-28' }, 106 | { downloads: 21, day: '2018-06-29' }, 107 | { downloads: 5, day: '2018-06-30' }, 108 | { downloads: 2, day: '2018-07-01' }, 109 | { downloads: 1, day: '2018-07-02' }, 110 | { downloads: 1, day: '2018-07-03' }, 111 | { downloads: 5, day: '2018-07-04' }, 112 | { downloads: 6, day: '2018-07-05' }, 113 | { downloads: 3, day: '2018-07-06' }, 114 | { downloads: 0, day: '2018-07-07' }, 115 | { downloads: 66, day: '2018-07-08' }, 116 | { downloads: 12, day: '2018-07-09' }, 117 | { downloads: 7, day: '2018-07-10' }, 118 | { downloads: 20, day: '2018-07-11' }, 119 | { downloads: 66, day: '2018-07-12' }, 120 | { downloads: 5, day: '2018-07-13' }, 121 | { downloads: 0, day: '2018-07-14' }, 122 | { downloads: 0, day: '2018-07-15' }, 123 | { downloads: 2, day: '2018-07-16' }, 124 | { downloads: 0, day: '2018-07-17' }, 125 | { downloads: 7, day: '2018-07-18' }, 126 | { downloads: 2, day: '2018-07-19' }, 127 | { downloads: 56, day: '2018-07-20' }, 128 | { downloads: 2, day: '2018-07-21' }, 129 | { downloads: 1, day: '2018-07-22' }, 130 | { downloads: 17, day: '2018-07-23' }, 131 | { downloads: 60, day: '2018-07-24' }, 132 | { downloads: 7, day: '2018-07-25' }, 133 | { downloads: 2, day: '2018-07-26' }, 134 | { downloads: 3, day: '2018-07-27' }, 135 | { downloads: 2, day: '2018-07-28' }, 136 | { downloads: 1, day: '2018-07-29' }, 137 | { downloads: 9, day: '2018-07-30' }, 138 | { downloads: 12, day: '2018-07-31' }, 139 | { downloads: 7, day: '2018-08-01' }, 140 | { downloads: 5, day: '2018-08-02' }, 141 | { downloads: 51, day: '2018-08-03' }, 142 | { downloads: 8, day: '2018-08-04' }, 143 | { downloads: 2, day: '2018-08-05' }, 144 | { downloads: 8, day: '2018-08-06' }, 145 | { downloads: 11, day: '2018-08-07' }, 146 | { downloads: 21, day: '2018-08-08' }, 147 | { downloads: 11, day: '2018-08-09' }, 148 | { downloads: 16, day: '2018-08-10' }, 149 | { downloads: 8, day: '2018-08-11' }, 150 | { downloads: 7, day: '2018-08-12' }, 151 | { downloads: 4, day: '2018-08-13' }, 152 | { downloads: 7, day: '2018-08-14' }, 153 | { downloads: 14, day: '2018-08-15' }, 154 | { downloads: 11, day: '2018-08-16' }, 155 | { downloads: 14, day: '2018-08-17' }, 156 | { downloads: 2, day: '2018-08-18' }, 157 | { downloads: 11, day: '2018-08-19' }, 158 | { downloads: 14, day: '2018-08-20' }, 159 | { downloads: 21, day: '2018-08-21' }, 160 | { downloads: 31, day: '2018-08-22' }, 161 | { downloads: 24, day: '2018-08-23' }, 162 | { downloads: 22, day: '2018-08-24' }, 163 | { downloads: 3, day: '2018-08-25' }, 164 | { downloads: 2, day: '2018-08-26' }, 165 | { downloads: 27, day: '2018-08-27' }, 166 | { downloads: 23, day: '2018-08-28' }, 167 | { downloads: 2, day: '2018-08-29' }, 168 | { downloads: 4, day: '2018-08-30' }, 169 | { downloads: 20, day: '2018-08-31' }, 170 | { downloads: 0, day: '2018-09-01' }, 171 | { downloads: 1, day: '2018-09-02' }, 172 | { downloads: 2, day: '2018-09-03' }, 173 | { downloads: 9, day: '2018-09-04' }, 174 | { downloads: 22, day: '2018-09-05' }, 175 | { downloads: 13, day: '2018-09-06' }, 176 | { downloads: 9, day: '2018-09-07' }, 177 | { downloads: 1, day: '2018-09-08' }, 178 | { downloads: 0, day: '2018-09-09' }, 179 | { downloads: 10, day: '2018-09-10' }, 180 | { downloads: 12, day: '2018-09-11' }, 181 | { downloads: 10, day: '2018-09-12' }, 182 | { downloads: 11, day: '2018-09-13' }, 183 | { downloads: 5, day: '2018-09-14' }, 184 | { downloads: 0, day: '2018-09-15' }, 185 | { downloads: 0, day: '2018-09-16' }, 186 | { downloads: 10, day: '2018-09-17' }, 187 | { downloads: 18, day: '2018-09-18' }, 188 | { downloads: 26, day: '2018-09-19' }, 189 | { downloads: 44, day: '2018-09-20' }, 190 | { downloads: 32, day: '2018-09-21' }, 191 | { downloads: 3, day: '2018-09-22' }, 192 | { downloads: 1, day: '2018-09-23' }, 193 | { downloads: 13, day: '2018-09-24' }, 194 | { downloads: 30, day: '2018-09-25' }, 195 | { downloads: 31, day: '2018-09-26' }, 196 | { downloads: 47, day: '2018-09-27' }, 197 | { downloads: 15, day: '2018-09-28' }, 198 | { downloads: 3, day: '2018-09-29' }, 199 | { downloads: 1, day: '2018-09-30' }, 200 | { downloads: 41, day: '2018-10-01' }, 201 | { downloads: 62, day: '2018-10-02' }, 202 | { downloads: 42, day: '2018-10-03' }, 203 | { downloads: 47, day: '2018-10-04' }, 204 | { downloads: 20, day: '2018-10-05' }, 205 | { downloads: 3, day: '2018-10-06' }, 206 | { downloads: 1, day: '2018-10-07' }, 207 | { downloads: 35, day: '2018-10-08' }, 208 | { downloads: 38, day: '2018-10-09' }, 209 | { downloads: 31, day: '2018-10-10' }, 210 | { downloads: 10, day: '2018-10-11' }, 211 | { downloads: 13, day: '2018-10-12' }, 212 | { downloads: 0, day: '2018-10-13' }, 213 | { downloads: 8, day: '2018-10-14' }, 214 | { downloads: 27, day: '2018-10-15' }, 215 | { downloads: 35, day: '2018-10-16' }, 216 | { downloads: 25, day: '2018-10-17' }, 217 | { downloads: 69, day: '2018-10-18' }, 218 | { downloads: 47, day: '2018-10-19' }, 219 | { downloads: 28, day: '2018-10-20' }, 220 | { downloads: 5, day: '2018-10-21' }, 221 | { downloads: 12, day: '2018-10-22' }, 222 | { downloads: 48, day: '2018-10-23' }, 223 | { downloads: 38, day: '2018-10-24' }, 224 | { downloads: 54, day: '2018-10-25' }, 225 | { downloads: 40, day: '2018-10-26' }, 226 | { downloads: 25, day: '2018-10-27' }, 227 | { downloads: 6, day: '2018-10-28' }, 228 | { downloads: 14, day: '2018-10-29' }, 229 | { downloads: 38, day: '2018-10-30' }, 230 | { downloads: 23, day: '2018-10-31' }, 231 | { downloads: 18, day: '2018-11-01' }, 232 | { downloads: 27, day: '2018-11-02' }, 233 | { downloads: 2, day: '2018-11-03' }, 234 | { downloads: 23, day: '2018-11-04' }, 235 | { downloads: 28, day: '2018-11-05' }, 236 | { downloads: 33, day: '2018-11-06' }, 237 | { downloads: 108, day: '2018-11-07' }, 238 | { downloads: 41, day: '2018-11-08' }, 239 | { downloads: 42, day: '2018-11-09' }, 240 | { downloads: 11, day: '2018-11-10' }, 241 | { downloads: 16, day: '2018-11-11' }, 242 | { downloads: 43, day: '2018-11-12' }, 243 | { downloads: 39, day: '2018-11-13' }, 244 | { downloads: 25, day: '2018-11-14' }, 245 | { downloads: 40, day: '2018-11-15' }, 246 | { downloads: 25, day: '2018-11-16' }, 247 | { downloads: 22, day: '2018-11-17' }, 248 | { downloads: 16, day: '2018-11-18' }, 249 | { downloads: 22, day: '2018-11-19' }, 250 | { downloads: 67, day: '2018-11-20' }, 251 | { downloads: 42, day: '2018-11-21' }, 252 | { downloads: 39, day: '2018-11-22' }, 253 | { downloads: 48, day: '2018-11-23' }, 254 | { downloads: 22, day: '2018-11-24' }, 255 | { downloads: 15, day: '2018-11-25' }, 256 | { downloads: 29, day: '2018-11-26' }, 257 | { downloads: 31, day: '2018-11-27' }, 258 | { downloads: 27, day: '2018-11-28' }, 259 | { downloads: 31, day: '2018-11-29' }, 260 | { downloads: 27, day: '2018-11-30' }, 261 | { downloads: 27, day: '2018-12-01' }, 262 | { downloads: 18, day: '2018-12-02' }, 263 | { downloads: 28, day: '2018-12-03' }, 264 | { downloads: 44, day: '2018-12-04' }, 265 | { downloads: 50, day: '2018-12-05' }, 266 | { downloads: 66, day: '2018-12-06' }, 267 | { downloads: 30, day: '2018-12-07' }, 268 | { downloads: 28, day: '2018-12-08' }, 269 | { downloads: 14, day: '2018-12-09' }, 270 | { downloads: 54, day: '2018-12-10' }, 271 | { downloads: 37, day: '2018-12-11' }, 272 | { downloads: 24, day: '2018-12-12' }, 273 | { downloads: 47, day: '2018-12-13' }, 274 | { downloads: 25, day: '2018-12-14' }, 275 | { downloads: 12, day: '2018-12-15' }, 276 | { downloads: 2, day: '2018-12-16' }, 277 | { downloads: 29, day: '2018-12-17' }, 278 | { downloads: 50, day: '2018-12-18' }, 279 | { downloads: 37, day: '2018-12-19' }, 280 | { downloads: 40, day: '2018-12-20' }, 281 | { downloads: 43, day: '2018-12-21' }, 282 | { downloads: 22, day: '2018-12-22' }, 283 | { downloads: 29, day: '2018-12-23' }, 284 | { downloads: 27, day: '2018-12-24' }, 285 | { downloads: 24, day: '2018-12-25' }, 286 | { downloads: 24, day: '2018-12-26' }, 287 | { downloads: 105, day: '2018-12-27' }, 288 | { downloads: 52, day: '2018-12-28' }, 289 | { downloads: 29, day: '2018-12-29' }, 290 | { downloads: 20, day: '2018-12-30' }, 291 | { downloads: 14, day: '2018-12-31' }, 292 | { downloads: 3, day: '2019-01-01' }, 293 | { downloads: 50, day: '2019-01-02' }, 294 | { downloads: 27, day: '2019-01-03' }, 295 | { downloads: 37, day: '2019-01-04' }, 296 | { downloads: 18, day: '2019-01-05' }, 297 | { downloads: 40, day: '2019-01-06' }, 298 | { downloads: 28, day: '2019-01-07' }, 299 | { downloads: 85, day: '2019-01-08' }, 300 | { downloads: 92, day: '2019-01-09' }, 301 | { downloads: 97, day: '2019-01-10' }, 302 | { downloads: 69, day: '2019-01-11' }, 303 | { downloads: 21, day: '2019-01-12' }, 304 | { downloads: 35, day: '2019-01-13' }, 305 | { downloads: 90, day: '2019-01-14' }, 306 | { downloads: 73, day: '2019-01-15' }, 307 | { downloads: 44, day: '2019-01-16' }, 308 | { downloads: 53, day: '2019-01-17' }, 309 | { downloads: 50, day: '2019-01-18' }, 310 | { downloads: 16, day: '2019-01-19' }, 311 | { downloads: 50, day: '2019-01-20' }, 312 | { downloads: 78, day: '2019-01-21' }, 313 | { downloads: 107, day: '2019-01-22' }, 314 | { downloads: 63, day: '2019-01-23' }, 315 | { downloads: 80, day: '2019-01-24' }, 316 | { downloads: 104, day: '2019-01-25' }, 317 | { downloads: 38, day: '2019-01-26' }, 318 | { downloads: 38, day: '2019-01-27' }, 319 | { downloads: 116, day: '2019-01-28' }, 320 | { downloads: 159, day: '2019-01-29' }, 321 | { downloads: 137, day: '2019-01-30' }, 322 | { downloads: 171, day: '2019-01-31' }, 323 | { downloads: 145, day: '2019-02-01' }, 324 | { downloads: 48, day: '2019-02-02' }, 325 | { downloads: 27, day: '2019-02-03' }, 326 | { downloads: 161, day: '2019-02-04' }, 327 | { downloads: 129, day: '2019-02-05' }, 328 | { downloads: 164, day: '2019-02-06' }, 329 | { downloads: 199, day: '2019-02-07' }, 330 | { downloads: 212, day: '2019-02-08' }, 331 | { downloads: 42, day: '2019-02-09' }, 332 | { downloads: 69, day: '2019-02-10' }, 333 | { downloads: 188, day: '2019-02-11' }, 334 | { downloads: 190, day: '2019-02-12' }, 335 | { downloads: 150, day: '2019-02-13' }, 336 | { downloads: 135, day: '2019-02-14' }, 337 | { downloads: 150, day: '2019-02-15' }, 338 | { downloads: 30, day: '2019-02-16' }, 339 | { downloads: 36, day: '2019-02-17' }, 340 | { downloads: 191, day: '2019-02-18' }, 341 | { downloads: 192, day: '2019-02-19' }, 342 | { downloads: 188, day: '2019-02-20' }, 343 | { downloads: 191, day: '2019-02-21' }, 344 | { downloads: 185, day: '2019-02-22' }, 345 | { downloads: 79, day: '2019-02-23' }, 346 | { downloads: 125, day: '2019-02-24' }, 347 | { downloads: 279, day: '2019-02-25' }, 348 | { downloads: 236, day: '2019-02-26' }, 349 | { downloads: 284, day: '2019-02-27' }, 350 | { downloads: 249, day: '2019-02-28' }, 351 | { downloads: 157, day: '2019-03-01' }, 352 | { downloads: 60, day: '2019-03-02' }, 353 | { downloads: 23, day: '2019-03-03' }, 354 | { downloads: 160, day: '2019-03-04' }, 355 | { downloads: 237, day: '2019-03-05' }, 356 | { downloads: 203, day: '2019-03-06' }, 357 | { downloads: 291, day: '2019-03-07' }, 358 | { downloads: 216, day: '2019-03-08' }, 359 | { downloads: 116, day: '2019-03-09' }, 360 | { downloads: 81, day: '2019-03-10' }, 361 | { downloads: 285, day: '2019-03-11' }, 362 | { downloads: 302, day: '2019-03-12' }, 363 | { downloads: 258, day: '2019-03-13' }, 364 | { downloads: 349, day: '2019-03-14' }, 365 | { downloads: 367, day: '2019-03-15' }, 366 | { downloads: 125, day: '2019-03-16' }, 367 | { downloads: 119, day: '2019-03-17' }, 368 | { downloads: 182, day: '2019-03-18' }, 369 | { downloads: 416, day: '2019-03-19' }, 370 | { downloads: 302, day: '2019-03-20' }, 371 | { downloads: 326, day: '2019-03-21' }, 372 | { downloads: 331, day: '2019-03-22' }, 373 | { downloads: 91, day: '2019-03-23' }, 374 | { downloads: 89, day: '2019-03-24' }, 375 | { downloads: 512, day: '2019-03-25' }, 376 | { downloads: 361, day: '2019-03-26' }, 377 | { downloads: 377, day: '2019-03-27' }, 378 | { downloads: 342, day: '2019-03-28' }, 379 | { downloads: 226, day: '2019-03-29' }, 380 | { downloads: 40, day: '2019-03-30' }, 381 | { downloads: 26, day: '2019-03-31' }, 382 | { downloads: 583, day: '2019-04-01' }, 383 | { downloads: 526, day: '2019-04-02' }, 384 | { downloads: 437, day: '2019-04-03' }, 385 | { downloads: 307, day: '2019-04-04' }, 386 | { downloads: 369, day: '2019-04-05' }, 387 | { downloads: 64, day: '2019-04-06' }, 388 | { downloads: 89, day: '2019-04-07' }, 389 | { downloads: 357, day: '2019-04-08' }, 390 | { downloads: 383, day: '2019-04-09' }, 391 | { downloads: 496, day: '2019-04-10' }, 392 | { downloads: 408, day: '2019-04-11' }, 393 | { downloads: 428, day: '2019-04-12' }, 394 | { downloads: 68, day: '2019-04-13' }, 395 | { downloads: 57, day: '2019-04-14' }, 396 | { downloads: 285, day: '2019-04-15' }, 397 | { downloads: 562, day: '2019-04-16' }, 398 | { downloads: 561, day: '2019-04-17' }, 399 | { downloads: 449, day: '2019-04-18' }, 400 | { downloads: 316, day: '2019-04-19' }, 401 | { downloads: 35, day: '2019-04-20' }, 402 | { downloads: 95, day: '2019-04-21' }, 403 | { downloads: 275, day: '2019-04-22' }, 404 | { downloads: 427, day: '2019-04-23' }, 405 | { downloads: 465, day: '2019-04-24' }, 406 | { downloads: 487, day: '2019-04-25' }, 407 | { downloads: 376, day: '2019-04-26' }, 408 | { downloads: 120, day: '2019-04-27' }, 409 | { downloads: 100, day: '2019-04-28' }, 410 | { downloads: 283, day: '2019-04-29' }, 411 | { downloads: 359, day: '2019-04-30' }, 412 | { downloads: 248, day: '2019-05-01' }, 413 | { downloads: 401, day: '2019-05-02' }, 414 | { downloads: 381, day: '2019-05-03' }, 415 | { downloads: 63, day: '2019-05-04' }, 416 | { downloads: 76, day: '2019-05-05' }, 417 | { downloads: 491, day: '2019-05-06' }, 418 | { downloads: 662, day: '2019-05-07' }, 419 | { downloads: 720, day: '2019-05-08' }, 420 | { downloads: 572, day: '2019-05-09' }, 421 | { downloads: 429, day: '2019-05-10' }, 422 | { downloads: 111, day: '2019-05-11' }, 423 | { downloads: 79, day: '2019-05-12' }, 424 | { downloads: 632, day: '2019-05-13' }, 425 | { downloads: 662, day: '2019-05-14' }, 426 | { downloads: 482, day: '2019-05-15' }, 427 | { downloads: 574, day: '2019-05-16' }, 428 | { downloads: 506, day: '2019-05-17' }, 429 | { downloads: 137, day: '2019-05-18' }, 430 | { downloads: 140, day: '2019-05-19' }, 431 | { downloads: 563, day: '2019-05-20' }, 432 | { downloads: 596, day: '2019-05-21' }, 433 | { downloads: 687, day: '2019-05-22' }, 434 | { downloads: 865, day: '2019-05-23' }, 435 | { downloads: 436, day: '2019-05-24' }, 436 | { downloads: 316, day: '2019-05-25' }, 437 | { downloads: 85, day: '2019-05-26' }, 438 | { downloads: 639, day: '2019-05-27' }, 439 | { downloads: 681, day: '2019-05-28' }, 440 | { downloads: 1211, day: '2019-05-29' }, 441 | { downloads: 516, day: '2019-05-30' }, 442 | { downloads: 496, day: '2019-05-31' }, 443 | { downloads: 88, day: '2019-06-01' }, 444 | { downloads: 77, day: '2019-06-02' }, 445 | { downloads: 1013, day: '2019-06-03' }, 446 | { downloads: 629, day: '2019-06-04' }, 447 | { downloads: 551, day: '2019-06-05' }, 448 | { downloads: 782, day: '2019-06-06' }, 449 | { downloads: 792, day: '2019-06-07' }, 450 | { downloads: 397, day: '2019-06-08' }, 451 | { downloads: 140, day: '2019-06-09' }, 452 | { downloads: 1072, day: '2019-06-10' }, 453 | { downloads: 884, day: '2019-06-11' }, 454 | { downloads: 1225, day: '2019-06-12' }, 455 | { downloads: 960, day: '2019-06-13' }, 456 | { downloads: 729, day: '2019-06-14' }, 457 | { downloads: 120, day: '2019-06-15' }, 458 | { downloads: 142, day: '2019-06-16' }, 459 | { downloads: 1603, day: '2019-06-17' }, 460 | { downloads: 1303, day: '2019-06-18' }, 461 | { downloads: 938, day: '2019-06-19' }, 462 | { downloads: 699, day: '2019-06-20' }, 463 | { downloads: 1033, day: '2019-06-21' }, 464 | { downloads: 120, day: '2019-06-22' }, 465 | { downloads: 127, day: '2019-06-23' }, 466 | { downloads: 869, day: '2019-06-24' }, 467 | { downloads: 904, day: '2019-06-25' }, 468 | { downloads: 903, day: '2019-06-26' }, 469 | { downloads: 1275, day: '2019-06-27' }, 470 | { downloads: 1130, day: '2019-06-28' }, 471 | { downloads: 120, day: '2019-06-29' }, 472 | { downloads: 134, day: '2019-06-30' }, 473 | { downloads: 674, day: '2019-07-01' }, 474 | { downloads: 963, day: '2019-07-02' }, 475 | { downloads: 742, day: '2019-07-03' }, 476 | { downloads: 764, day: '2019-07-04' }, 477 | { downloads: 694, day: '2019-07-05' }, 478 | { downloads: 133, day: '2019-07-06' }, 479 | { downloads: 108, day: '2019-07-07' }, 480 | { downloads: 1285, day: '2019-07-08' }, 481 | { downloads: 1469, day: '2019-07-09' }, 482 | { downloads: 875, day: '2019-07-10' }, 483 | { downloads: 1286, day: '2019-07-11' }, 484 | { downloads: 1225, day: '2019-07-12' }, 485 | { downloads: 130, day: '2019-07-13' }, 486 | { downloads: 163, day: '2019-07-14' }, 487 | { downloads: 1069, day: '2019-07-15' }, 488 | { downloads: 1709, day: '2019-07-16' }, 489 | { downloads: 1490, day: '2019-07-17' }, 490 | { downloads: 1824, day: '2019-07-18' }, 491 | { downloads: 1336, day: '2019-07-19' }, 492 | { downloads: 225, day: '2019-07-20' }, 493 | { downloads: 225, day: '2019-07-21' }, 494 | { downloads: 1043, day: '2019-07-22' }, 495 | { downloads: 1056, day: '2019-07-23' }, 496 | { downloads: 1169, day: '2019-07-24' }, 497 | { downloads: 982, day: '2019-07-25' }, 498 | { downloads: 822, day: '2019-07-26' }, 499 | { downloads: 137, day: '2019-07-27' }, 500 | { downloads: 136, day: '2019-07-28' }, 501 | { downloads: 1054, day: '2019-07-29' }, 502 | { downloads: 929, day: '2019-07-30' }, 503 | { downloads: 999, day: '2019-07-31' }, 504 | { downloads: 1089, day: '2019-08-01' }, 505 | { downloads: 911, day: '2019-08-02' }, 506 | { downloads: 115, day: '2019-08-03' }, 507 | { downloads: 133, day: '2019-08-04' }, 508 | { downloads: 1120, day: '2019-08-05' }, 509 | { downloads: 1227, day: '2019-08-06' }, 510 | { downloads: 1256, day: '2019-08-07' }, 511 | { downloads: 1218, day: '2019-08-08' }, 512 | { downloads: 1052, day: '2019-08-09' }, 513 | { downloads: 113, day: '2019-08-10' }, 514 | { downloads: 132, day: '2019-08-11' }, 515 | { downloads: 1264, day: '2019-08-12' }, 516 | { downloads: 1282, day: '2019-08-13' }, 517 | { downloads: 1408, day: '2019-08-14' }, 518 | { downloads: 1393, day: '2019-08-15' }, 519 | { downloads: 1166, day: '2019-08-16' }, 520 | { downloads: 147, day: '2019-08-17' }, 521 | { downloads: 221, day: '2019-08-18' }, 522 | { downloads: 1658, day: '2019-08-19' }, 523 | { downloads: 1830, day: '2019-08-20' }, 524 | { downloads: 1764, day: '2019-08-21' }, 525 | { downloads: 1565, day: '2019-08-22' }, 526 | { downloads: 1101, day: '2019-08-23' }, 527 | { downloads: 149, day: '2019-08-24' }, 528 | { downloads: 221, day: '2019-08-25' }, 529 | { downloads: 1499, day: '2019-08-26' }, 530 | { downloads: 1880, day: '2019-08-27' }, 531 | { downloads: 1747, day: '2019-08-28' }, 532 | { downloads: 1591, day: '2019-08-29' }, 533 | { downloads: 1642, day: '2019-08-30' }, 534 | { downloads: 671, day: '2019-08-31' }, 535 | { downloads: 234, day: '2019-09-01' }, 536 | { downloads: 1773, day: '2019-09-02' }, 537 | { downloads: 1979, day: '2019-09-03' }, 538 | { downloads: 2919, day: '2019-09-04' }, 539 | { downloads: 1457, day: '2019-09-05' }, 540 | { downloads: 1106, day: '2019-09-06' }, 541 | { downloads: 252, day: '2019-09-07' }, 542 | { downloads: 416, day: '2019-09-08' }, 543 | { downloads: 2277, day: '2019-09-09' }, 544 | { downloads: 2559, day: '2019-09-10' }, 545 | { downloads: 2190, day: '2019-09-11' }, 546 | { downloads: 1702, day: '2019-09-12' }, 547 | { downloads: 1346, day: '2019-09-13' }, 548 | { downloads: 154, day: '2019-09-14' }, 549 | { downloads: 318, day: '2019-09-15' }, 550 | { downloads: 1494, day: '2019-09-16' }, 551 | { downloads: 1583, day: '2019-09-17' }, 552 | { downloads: 3051, day: '2019-09-18' }, 553 | { downloads: 1764, day: '2019-09-19' }, 554 | { downloads: 1545, day: '2019-09-20' }, 555 | { downloads: 287, day: '2019-09-21' }, 556 | { downloads: 378, day: '2019-09-22' }, 557 | { downloads: 2213, day: '2019-09-23' }, 558 | { downloads: 1974, day: '2019-09-24' }, 559 | { downloads: 3392, day: '2019-09-25' }, 560 | { downloads: 2164, day: '2019-09-26' }, 561 | { downloads: 1639, day: '2019-09-27' }, 562 | { downloads: 205, day: '2019-09-28' }, 563 | { downloads: 379, day: '2019-09-29' }, 564 | { downloads: 2991, day: '2019-09-30' }, 565 | { downloads: 2334, day: '2019-10-01' }, 566 | { downloads: 2324, day: '2019-10-02' }, 567 | { downloads: 1900, day: '2019-10-03' }, 568 | { downloads: 2275, day: '2019-10-04' }, 569 | { downloads: 417, day: '2019-10-05' }, 570 | { downloads: 547, day: '2019-10-06' }, 571 | { downloads: 2960, day: '2019-10-07' }, 572 | { downloads: 4101, day: '2019-10-08' }, 573 | { downloads: 2289, day: '2019-10-09' }, 574 | { downloads: 2377, day: '2019-10-10' }, 575 | { downloads: 1931, day: '2019-10-11' }, 576 | { downloads: 275, day: '2019-10-12' }, 577 | { downloads: 234, day: '2019-10-13' }, 578 | { downloads: 2267, day: '2019-10-14' }, 579 | { downloads: 2636, day: '2019-10-15' }, 580 | { downloads: 2707, day: '2019-10-16' }, 581 | { downloads: 2412, day: '2019-10-17' }, 582 | { downloads: 2874, day: '2019-10-18' }, 583 | { downloads: 1128, day: '2019-10-19' }, 584 | { downloads: 353, day: '2019-10-20' }, 585 | { downloads: 2869, day: '2019-10-21' }, 586 | { downloads: 2916, day: '2019-10-22' }, 587 | { downloads: 3302, day: '2019-10-23' }, 588 | { downloads: 2626, day: '2019-10-24' }, 589 | { downloads: 2398, day: '2019-10-25' }, 590 | { downloads: 398, day: '2019-10-26' }, 591 | { downloads: 743, day: '2019-10-27' }, 592 | { downloads: 3469, day: '2019-10-28' }, 593 | { downloads: 2892, day: '2019-10-29' }, 594 | { downloads: 2469, day: '2019-10-30' }, 595 | { downloads: 2059, day: '2019-10-31' }, 596 | { downloads: 1764, day: '2019-11-01' }, 597 | { downloads: 282, day: '2019-11-02' }, 598 | { downloads: 491, day: '2019-11-03' }, 599 | { downloads: 2536, day: '2019-11-04' }, 600 | { downloads: 2644, day: '2019-11-05' }, 601 | { downloads: 3058, day: '2019-11-06' }, 602 | { downloads: 2623, day: '2019-11-07' }, 603 | { downloads: 2323, day: '2019-11-08' }, 604 | { downloads: 363, day: '2019-11-09' }, 605 | { downloads: 478, day: '2019-11-10' }, 606 | { downloads: 2866, day: '2019-11-11' }, 607 | { downloads: 3000, day: '2019-11-12' }, 608 | { downloads: 2512, day: '2019-11-13' }, 609 | { downloads: 2582, day: '2019-11-14' }, 610 | { downloads: 1966, day: '2019-11-15' }, 611 | { downloads: 298, day: '2019-11-16' }, 612 | { downloads: 390, day: '2019-11-17' }, 613 | { downloads: 2659, day: '2019-11-18' }, 614 | { downloads: 2355, day: '2019-11-19' }, 615 | { downloads: 2421, day: '2019-11-20' }, 616 | { downloads: 2666, day: '2019-11-21' }, 617 | { downloads: 2338, day: '2019-11-22' }, 618 | { downloads: 446, day: '2019-11-23' }, 619 | { downloads: 655, day: '2019-11-24' }, 620 | { downloads: 3337, day: '2019-11-25' }, 621 | { downloads: 2914, day: '2019-11-26' }, 622 | { downloads: 2544, day: '2019-11-27' }, 623 | { downloads: 2156, day: '2019-11-28' }, 624 | { downloads: 1946, day: '2019-11-29' }, 625 | { downloads: 330, day: '2019-11-30' }, 626 | { downloads: 410, day: '2019-12-01' }, 627 | { downloads: 3136, day: '2019-12-02' }, 628 | { downloads: 2493, day: '2019-12-03' }, 629 | { downloads: 2088, day: '2019-12-04' }, 630 | { downloads: 2455, day: '2019-12-05' }, 631 | { downloads: 1920, day: '2019-12-06' }, 632 | { downloads: 364, day: '2019-12-07' }, 633 | { downloads: 510, day: '2019-12-08' }, 634 | { downloads: 2705, day: '2019-12-09' }, 635 | { downloads: 2905, day: '2019-12-10' }, 636 | { downloads: 2624, day: '2019-12-11' }, 637 | { downloads: 2617, day: '2019-12-12' }, 638 | { downloads: 2059, day: '2019-12-13' }, 639 | { downloads: 313, day: '2019-12-14' }, 640 | { downloads: 423, day: '2019-12-15' }, 641 | { downloads: 2559, day: '2019-12-16' }, 642 | { downloads: 2727, day: '2019-12-17' }, 643 | { downloads: 2831, day: '2019-12-18' }, 644 | { downloads: 4387, day: '2019-12-19' }, 645 | { downloads: 1845, day: '2019-12-20' }, 646 | { downloads: 282, day: '2019-12-21' }, 647 | { downloads: 507, day: '2019-12-22' }, 648 | { downloads: 1771, day: '2019-12-23' }, 649 | { downloads: 1348, day: '2019-12-24' }, 650 | { downloads: 560, day: '2019-12-25' }, 651 | { downloads: 1117, day: '2019-12-26' }, 652 | { downloads: 1162, day: '2019-12-27' }, 653 | { downloads: 374, day: '2019-12-28' }, 654 | { downloads: 259, day: '2019-12-29' }, 655 | { downloads: 1510, day: '2019-12-30' }, 656 | { downloads: 836, day: '2019-12-31' }, 657 | { downloads: 359, day: '2020-01-01' }, 658 | { downloads: 1709, day: '2020-01-02' }, 659 | { downloads: 1577, day: '2020-01-03' }, 660 | { downloads: 329, day: '2020-01-04' }, 661 | { downloads: 658, day: '2020-01-05' }, 662 | { downloads: 2837, day: '2020-01-06' }, 663 | { downloads: 2849, day: '2020-01-07' }, 664 | { downloads: 2752, day: '2020-01-08' }, 665 | { downloads: 2765, day: '2020-01-09' }, 666 | { downloads: 2135, day: '2020-01-10' }, 667 | { downloads: 341, day: '2020-01-11' }, 668 | { downloads: 397, day: '2020-01-12' }, 669 | { downloads: 2763, day: '2020-01-13' }, 670 | { downloads: 2593, day: '2020-01-14' }, 671 | { downloads: 2199, day: '2020-01-15' }, 672 | { downloads: 2599, day: '2020-01-16' }, 673 | { downloads: 2378, day: '2020-01-17' }, 674 | { downloads: 294, day: '2020-01-18' }, 675 | { downloads: 412, day: '2020-01-19' }, 676 | { downloads: 3003, day: '2020-01-20' }, 677 | { downloads: 2687, day: '2020-01-21' }, 678 | { downloads: 2838, day: '2020-01-22' }, 679 | { downloads: 2513, day: '2020-01-23' }, 680 | { downloads: 2091, day: '2020-01-24' }, 681 | { downloads: 204, day: '2020-01-25' }, 682 | { downloads: 666, day: '2020-01-26' }, 683 | { downloads: 3051, day: '2020-01-27' }, 684 | { downloads: 2688, day: '2020-01-28' }, 685 | { downloads: 2564, day: '2020-01-29' }, 686 | { downloads: 2316, day: '2020-01-30' }, 687 | { downloads: 2040, day: '2020-01-31' }, 688 | { downloads: 293, day: '2020-02-01' }, 689 | { downloads: 632, day: '2020-02-02' }, 690 | { downloads: 2847, day: '2020-02-03' }, 691 | { downloads: 3024, day: '2020-02-04' }, 692 | { downloads: 3148, day: '2020-02-05' }, 693 | { downloads: 3173, day: '2020-02-06' }, 694 | { downloads: 2414, day: '2020-02-07' }, 695 | { downloads: 522, day: '2020-02-08' }, 696 | { downloads: 564, day: '2020-02-09' }, 697 | { downloads: 3399, day: '2020-02-10' }, 698 | { downloads: 2796, day: '2020-02-11' }, 699 | { downloads: 2828, day: '2020-02-12' }, 700 | { downloads: 2775, day: '2020-02-13' }, 701 | { downloads: 2198, day: '2020-02-14' }, 702 | { downloads: 407, day: '2020-02-15' }, 703 | { downloads: 458, day: '2020-02-16' }, 704 | { downloads: 3009, day: '2020-02-17' }, 705 | { downloads: 3091, day: '2020-02-18' }, 706 | { downloads: 3166, day: '2020-02-19' }, 707 | { downloads: 3087, day: '2020-02-20' }, 708 | { downloads: 2519, day: '2020-02-21' }, 709 | { downloads: 472, day: '2020-02-22' }, 710 | { downloads: 511, day: '2020-02-23' }, 711 | { downloads: 2798, day: '2020-02-24' }, 712 | { downloads: 2981, day: '2020-02-25' }, 713 | { downloads: 2798, day: '2020-02-26' }, 714 | { downloads: 2887, day: '2020-02-27' }, 715 | { downloads: 2414, day: '2020-02-28' }, 716 | { downloads: 422, day: '2020-02-29' }, 717 | { downloads: 532, day: '2020-03-01' }, 718 | { downloads: 2962, day: '2020-03-02' }, 719 | { downloads: 2684, day: '2020-03-03' }, 720 | { downloads: 3097, day: '2020-03-04' }, 721 | { downloads: 3701, day: '2020-03-05' }, 722 | { downloads: 2461, day: '2020-03-06' }, 723 | { downloads: 472, day: '2020-03-07' }, 724 | { downloads: 693, day: '2020-03-08' }, 725 | { downloads: 3127, day: '2020-03-09' }, 726 | { downloads: 3081, day: '2020-03-10' }, 727 | { downloads: 2674, day: '2020-03-11' }, 728 | { downloads: 2642, day: '2020-03-12' }, 729 | { downloads: 2457, day: '2020-03-13' }, 730 | { downloads: 420, day: '2020-03-14' }, 731 | { downloads: 725, day: '2020-03-15' }, 732 | { downloads: 2827, day: '2020-03-16' }, 733 | { downloads: 2804, day: '2020-03-17' }, 734 | { downloads: 2801, day: '2020-03-18' }, 735 | { downloads: 3073, day: '2020-03-19' }, 736 | { downloads: 2536, day: '2020-03-20' }, 737 | { downloads: 484, day: '2020-03-21' }, 738 | { downloads: 709, day: '2020-03-22' }, 739 | { downloads: 3477, day: '2020-03-23' }, 740 | { downloads: 3363, day: '2020-03-24' }, 741 | { downloads: 3454, day: '2020-03-25' }, 742 | { downloads: 3184, day: '2020-03-26' }, 743 | { downloads: 2825, day: '2020-03-27' }, 744 | { downloads: 708, day: '2020-03-28' }, 745 | { downloads: 896, day: '2020-03-29' }, 746 | { downloads: 3686, day: '2020-03-30' }, 747 | { downloads: 3517, day: '2020-03-31' }, 748 | { downloads: 3429, day: '2020-04-01' }, 749 | { downloads: 3464, day: '2020-04-02' }, 750 | { downloads: 2866, day: '2020-04-03' }, 751 | { downloads: 652, day: '2020-04-04' }, 752 | { downloads: 943, day: '2020-04-05' }, 753 | { downloads: 3456, day: '2020-04-06' }, 754 | { downloads: 3754, day: '2020-04-07' }, 755 | { downloads: 3578, day: '2020-04-08' }, 756 | { downloads: 3224, day: '2020-04-09' }, 757 | { downloads: 2196, day: '2020-04-10' }, 758 | { downloads: 1007, day: '2020-04-11' }, 759 | { downloads: 735, day: '2020-04-12' }, 760 | { downloads: 2545, day: '2020-04-13' }, 761 | { downloads: 3289, day: '2020-04-14' }, 762 | { downloads: 2824, day: '2020-04-15' }, 763 | { downloads: 3264, day: '2020-04-16' }, 764 | { downloads: 3568, day: '2020-04-17' }, 765 | { downloads: 869, day: '2020-04-18' }, 766 | { downloads: 844, day: '2020-04-19' }, 767 | { downloads: 3775, day: '2020-04-20' }, 768 | { downloads: 3622, day: '2020-04-21' }, 769 | { downloads: 3659, day: '2020-04-22' }, 770 | { downloads: 3633, day: '2020-04-23' }, 771 | { downloads: 3442, day: '2020-04-24' }, 772 | { downloads: 714, day: '2020-04-25' }, 773 | { downloads: 877, day: '2020-04-26' }, 774 | { downloads: 3727, day: '2020-04-27' }, 775 | { downloads: 3926, day: '2020-04-28' }, 776 | { downloads: 4238, day: '2020-04-29' }, 777 | { downloads: 4037, day: '2020-04-30' }, 778 | { downloads: 1963, day: '2020-05-01' }, 779 | { downloads: 982, day: '2020-05-02' }, 780 | { downloads: 1121, day: '2020-05-03' }, 781 | { downloads: 4246, day: '2020-05-04' }, 782 | { downloads: 4040, day: '2020-05-05' }, 783 | { downloads: 4025, day: '2020-05-06' }, 784 | { downloads: 3730, day: '2020-05-07' }, 785 | { downloads: 2851, day: '2020-05-08' }, 786 | { downloads: 666, day: '2020-05-09' }, 787 | { downloads: 911, day: '2020-05-10' }, 788 | { downloads: 3638, day: '2020-05-11' }, 789 | { downloads: 3515, day: '2020-05-12' }, 790 | { downloads: 4475, day: '2020-05-13' }, 791 | { downloads: 4013, day: '2020-05-14' }, 792 | { downloads: 5328, day: '2020-05-15' }, 793 | { downloads: 548, day: '2020-05-16' }, 794 | { downloads: 857, day: '2020-05-17' }, 795 | { downloads: 4706, day: '2020-05-18' }, 796 | { downloads: 4875, day: '2020-05-19' }, 797 | { downloads: 4330, day: '2020-05-20' }, 798 | { downloads: 3774, day: '2020-05-21' }, 799 | { downloads: 3173, day: '2020-05-22' }, 800 | { downloads: 815, day: '2020-05-23' }, 801 | { downloads: 1214, day: '2020-05-24' }, 802 | { downloads: 4052, day: '2020-05-25' }, 803 | { downloads: 4291, day: '2020-05-26' }, 804 | { downloads: 4747, day: '2020-05-27' }, 805 | { downloads: 4660, day: '2020-05-28' }, 806 | { downloads: 3874, day: '2020-05-29' }, 807 | { downloads: 862, day: '2020-05-30' }, 808 | { downloads: 1175, day: '2020-05-31' }, 809 | { downloads: 3924, day: '2020-06-01' }, 810 | { downloads: 6377, day: '2020-06-02' }, 811 | { downloads: 5049, day: '2020-06-03' }, 812 | { downloads: 4817, day: '2020-06-04' }, 813 | { downloads: 4423, day: '2020-06-05' }, 814 | { downloads: 1153, day: '2020-06-06' }, 815 | { downloads: 1139, day: '2020-06-07' }, 816 | { downloads: 4455, day: '2020-06-08' }, 817 | { downloads: 4774, day: '2020-06-09' }, 818 | { downloads: 4522, day: '2020-06-10' }, 819 | { downloads: 5446, day: '2020-06-11' }, 820 | { downloads: 3922, day: '2020-06-12' }, 821 | { downloads: 791, day: '2020-06-13' }, 822 | { downloads: 1264, day: '2020-06-14' }, 823 | { downloads: 4682, day: '2020-06-15' }, 824 | { downloads: 5214, day: '2020-06-16' }, 825 | { downloads: 5030, day: '2020-06-17' }, 826 | { downloads: 5165, day: '2020-06-18' }, 827 | { downloads: 4285, day: '2020-06-19' }, 828 | { downloads: 934, day: '2020-06-20' }, 829 | { downloads: 910, day: '2020-06-21' }, 830 | { downloads: 0, day: '2020-06-22' }, 831 | { downloads: 5522, day: '2020-06-23' }, 832 | { downloads: 5310, day: '2020-06-24' }, 833 | { downloads: 4741, day: '2020-06-25' }, 834 | { downloads: 4136, day: '2020-06-26' }, 835 | { downloads: 813, day: '2020-06-27' }, 836 | { downloads: 1375, day: '2020-06-28' }, 837 | { downloads: 5145, day: '2020-06-29' }, 838 | { downloads: 7971, day: '2020-06-30' }, 839 | { downloads: 6060, day: '2020-07-01' }, 840 | { downloads: 5330, day: '2020-07-02' }, 841 | { downloads: 4195, day: '2020-07-03' }, 842 | { downloads: 916, day: '2020-07-04' }, 843 | { downloads: 1011, day: '2020-07-05' }, 844 | { downloads: 5331, day: '2020-07-06' }, 845 | { downloads: 5409, day: '2020-07-07' }, 846 | { downloads: 5322, day: '2020-07-08' }, 847 | { downloads: 5388, day: '2020-07-09' }, 848 | { downloads: 4167, day: '2020-07-10' }, 849 | { downloads: 1117, day: '2020-07-11' }, 850 | { downloads: 1161, day: '2020-07-12' }, 851 | { downloads: 4963, day: '2020-07-13' }, 852 | { downloads: 5499, day: '2020-07-14' }, 853 | { downloads: 5425, day: '2020-07-15' }, 854 | { downloads: 5779, day: '2020-07-16' }, 855 | { downloads: 4913, day: '2020-07-17' }, 856 | { downloads: 1346, day: '2020-07-18' }, 857 | { downloads: 1359, day: '2020-07-19' }, 858 | { downloads: 5643, day: '2020-07-20' }, 859 | { downloads: 5816, day: '2020-07-21' }, 860 | { downloads: 5428, day: '2020-07-22' }, 861 | { downloads: 4951, day: '2020-07-23' }, 862 | { downloads: 4685, day: '2020-07-24' }, 863 | { downloads: 1166, day: '2020-07-25' }, 864 | { downloads: 1057, day: '2020-07-26' }, 865 | { downloads: 5644, day: '2020-07-27' }, 866 | { downloads: 6512, day: '2020-07-28' }, 867 | { downloads: 7614, day: '2020-07-29' }, 868 | { downloads: 6124, day: '2020-07-30' }, 869 | { downloads: 5396, day: '2020-07-31' }, 870 | { downloads: 1321, day: '2020-08-01' }, 871 | { downloads: 1298, day: '2020-08-02' }, 872 | { downloads: 5991, day: '2020-08-03' }, 873 | { downloads: 6336, day: '2020-08-04' }, 874 | { downloads: 6207, day: '2020-08-05' }, 875 | { downloads: 5785, day: '2020-08-06' }, 876 | { downloads: 5127, day: '2020-08-07' }, 877 | { downloads: 1101, day: '2020-08-08' }, 878 | { downloads: 1011, day: '2020-08-09' }, 879 | { downloads: 5722, day: '2020-08-10' }, 880 | { downloads: 6166, day: '2020-08-11' }, 881 | { downloads: 6130, day: '2020-08-12' }, 882 | { downloads: 6561, day: '2020-08-13' }, 883 | { downloads: 5746, day: '2020-08-14' }, 884 | { downloads: 1224, day: '2020-08-15' }, 885 | { downloads: 1110, day: '2020-08-16' }, 886 | { downloads: 6313, day: '2020-08-17' }, 887 | { downloads: 6047, day: '2020-08-18' }, 888 | { downloads: 6077, day: '2020-08-19' }, 889 | { downloads: 6197, day: '2020-08-20' }, 890 | { downloads: 6006, day: '2020-08-21' }, 891 | { downloads: 1321, day: '2020-08-22' }, 892 | { downloads: 1119, day: '2020-08-23' }, 893 | { downloads: 7193, day: '2020-08-24' }, 894 | { downloads: 7333, day: '2020-08-25' }, 895 | { downloads: 7801, day: '2020-08-26' }, 896 | { downloads: 6911, day: '2020-08-27' }, 897 | { downloads: 6366, day: '2020-08-28' }, 898 | { downloads: 1556, day: '2020-08-29' }, 899 | { downloads: 1175, day: '2020-08-30' }, 900 | { downloads: 6501, day: '2020-08-31' }, 901 | { downloads: 6968, day: '2020-09-01' }, 902 | { downloads: 7071, day: '2020-09-02' }, 903 | { downloads: 7372, day: '2020-09-03' }, 904 | { downloads: 6463, day: '2020-09-04' }, 905 | { downloads: 1688, day: '2020-09-05' }, 906 | { downloads: 1426, day: '2020-09-06' }, 907 | { downloads: 6957, day: '2020-09-07' }, 908 | { downloads: 5815, day: '2020-09-08' }, 909 | { downloads: 7117, day: '2020-09-09' }, 910 | { downloads: 7402, day: '2020-09-10' }, 911 | { downloads: 6800, day: '2020-09-11' }, 912 | { downloads: 1248, day: '2020-09-12' }, 913 | { downloads: 1620, day: '2020-09-13' }, 914 | { downloads: 8133, day: '2020-09-14' }, 915 | { downloads: 7766, day: '2020-09-15' }, 916 | { downloads: 8533, day: '2020-09-16' }, 917 | { downloads: 8259, day: '2020-09-17' }, 918 | { downloads: 8075, day: '2020-09-18' }, 919 | { downloads: 1374, day: '2020-09-19' }, 920 | { downloads: 1491, day: '2020-09-20' }, 921 | { downloads: 8193, day: '2020-09-21' }, 922 | { downloads: 8509, day: '2020-09-22' }, 923 | { downloads: 8446, day: '2020-09-23' }, 924 | { downloads: 8401, day: '2020-09-24' }, 925 | { downloads: 7156, day: '2020-09-25' }, 926 | { downloads: 1378, day: '2020-09-26' }, 927 | { downloads: 1742, day: '2020-09-27' }, 928 | { downloads: 8163, day: '2020-09-28' }, 929 | { downloads: 8778, day: '2020-09-29' }, 930 | { downloads: 8819, day: '2020-09-30' }, 931 | { downloads: 8625, day: '2020-10-01' }, 932 | { downloads: 7338, day: '2020-10-02' }, 933 | { downloads: 1723, day: '2020-10-03' }, 934 | { downloads: 1473, day: '2020-10-04' }, 935 | { downloads: 8439, day: '2020-10-05' }, 936 | { downloads: 8866, day: '2020-10-06' }, 937 | { downloads: 8638, day: '2020-10-07' }, 938 | { downloads: 8553, day: '2020-10-08' }, 939 | { downloads: 7695, day: '2020-10-09' }, 940 | { downloads: 1833, day: '2020-10-10' }, 941 | { downloads: 1500, day: '2020-10-11' }, 942 | { downloads: 8405, day: '2020-10-12' }, 943 | { downloads: 8663, day: '2020-10-13' }, 944 | { downloads: 8201, day: '2020-10-14' }, 945 | { downloads: 8725, day: '2020-10-15' }, 946 | { downloads: 7587, day: '2020-10-16' }, 947 | { downloads: 1869, day: '2020-10-17' }, 948 | { downloads: 1656, day: '2020-10-18' }, 949 | { downloads: 9255, day: '2020-10-19' }, 950 | { downloads: 9689, day: '2020-10-20' }, 951 | { downloads: 10055, day: '2020-10-21' }, 952 | { downloads: 9441, day: '2020-10-22' }, 953 | { downloads: 8283, day: '2020-10-23' }, 954 | { downloads: 1875, day: '2020-10-24' }, 955 | { downloads: 1714, day: '2020-10-25' }, 956 | { downloads: 9356, day: '2020-10-26' }, 957 | { downloads: 9939, day: '2020-10-27' }, 958 | { downloads: 9474, day: '2020-10-28' }, 959 | { downloads: 8920, day: '2020-10-29' }, 960 | { downloads: 8336, day: '2020-10-30' }, 961 | { downloads: 1663, day: '2020-10-31' }, 962 | { downloads: 1637, day: '2020-11-01' }, 963 | { downloads: 9399, day: '2020-11-02' }, 964 | { downloads: 9599, day: '2020-11-03' }, 965 | { downloads: 10174, day: '2020-11-04' }, 966 | { downloads: 10094, day: '2020-11-05' }, 967 | { downloads: 9012, day: '2020-11-06' }, 968 | { downloads: 1491, day: '2020-11-07' }, 969 | { downloads: 2002, day: '2020-11-08' }, 970 | { downloads: 10683, day: '2020-11-09' }, 971 | { downloads: 10686, day: '2020-11-10' }, 972 | { downloads: 9316, day: '2020-11-11' }, 973 | { downloads: 10968, day: '2020-11-12' }, 974 | { downloads: 9145, day: '2020-11-13' }, 975 | { downloads: 1972, day: '2020-11-14' }, 976 | { downloads: 2082, day: '2020-11-15' }, 977 | { downloads: 10143, day: '2020-11-16' }, 978 | { downloads: 10779, day: '2020-11-17' }, 979 | { downloads: 10292, day: '2020-11-18' }, 980 | { downloads: 9920, day: '2020-11-19' }, 981 | { downloads: 9903, day: '2020-11-20' }, 982 | { downloads: 2053, day: '2020-11-21' }, 983 | { downloads: 2254, day: '2020-11-22' }, 984 | { downloads: 9560, day: '2020-11-23' }, 985 | { downloads: 10011, day: '2020-11-24' }, 986 | { downloads: 10549, day: '2020-11-25' }, 987 | { downloads: 8573, day: '2020-11-26' }, 988 | { downloads: 7715, day: '2020-11-27' }, 989 | { downloads: 2459, day: '2020-11-28' }, 990 | { downloads: 1869, day: '2020-11-29' }, 991 | { downloads: 10833, day: '2020-11-30' }, 992 | { downloads: 11431, day: '2020-12-01' }, 993 | { downloads: 10372, day: '2020-12-02' }, 994 | { downloads: 10828, day: '2020-12-03' }, 995 | { downloads: 9406, day: '2020-12-04' }, 996 | { downloads: 1985, day: '2020-12-05' }, 997 | { downloads: 2066, day: '2020-12-06' }, 998 | { downloads: 11573, day: '2020-12-07' }, 999 | { downloads: 10430, day: '2020-12-08' }, 1000 | { downloads: 10548, day: '2020-12-09' }, 1001 | { downloads: 10708, day: '2020-12-10' }, 1002 | { downloads: 9158, day: '2020-12-11' }, 1003 | { downloads: 1808, day: '2020-12-12' }, 1004 | { downloads: 2214, day: '2020-12-13' }, 1005 | { downloads: 10716, day: '2020-12-14' }, 1006 | { downloads: 10867, day: '2020-12-15' }, 1007 | { downloads: 10790, day: '2020-12-16' }, 1008 | { downloads: 11221, day: '2020-12-17' }, 1009 | { downloads: 9125, day: '2020-12-18' }, 1010 | { downloads: 2249, day: '2020-12-19' }, 1011 | { downloads: 2102, day: '2020-12-20' }, 1012 | { downloads: 9801, day: '2020-12-21' }, 1013 | { downloads: 8810, day: '2020-12-22' }, 1014 | { downloads: 8646, day: '2020-12-23' }, 1015 | { downloads: 5364, day: '2020-12-24' }, 1016 | { downloads: 2514, day: '2020-12-25' }, 1017 | { downloads: 1572, day: '2020-12-26' }, 1018 | { downloads: 1623, day: '2020-12-27' }, 1019 | { downloads: 6163, day: '2020-12-28' }, 1020 | { downloads: 6710, day: '2020-12-29' }, 1021 | { downloads: 5912, day: '2020-12-30' }, 1022 | { downloads: 3938, day: '2020-12-31' }, 1023 | { downloads: 1427, day: '2021-01-01' }, 1024 | { downloads: 1294, day: '2021-01-02' }, 1025 | { downloads: 1801, day: '2021-01-03' }, 1026 | { downloads: 10020, day: '2021-01-04' }, 1027 | { downloads: 9475, day: '2021-01-05' }, 1028 | { downloads: 9266, day: '2021-01-06' }, 1029 | { downloads: 10181, day: '2021-01-07' }, 1030 | { downloads: 9235, day: '2021-01-08' }, 1031 | { downloads: 2109, day: '2021-01-09' }, 1032 | { downloads: 2497, day: '2021-01-10' }, 1033 | { downloads: 10239, day: '2021-01-11' }, 1034 | { downloads: 11846, day: '2021-01-12' }, 1035 | { downloads: 11318, day: '2021-01-13' }, 1036 | { downloads: 11283, day: '2021-01-14' }, 1037 | { downloads: 10513, day: '2021-01-15' }, 1038 | { downloads: 2743, day: '2021-01-16' }, 1039 | { downloads: 2095, day: '2021-01-17' }, 1040 | { downloads: 10724, day: '2021-01-18' }, 1041 | { downloads: 10897, day: '2021-01-19' }, 1042 | { downloads: 11697, day: '2021-01-20' }, 1043 | { downloads: 11723, day: '2021-01-21' }, 1044 | { downloads: 8091, day: '2021-01-22' }, 1045 | { downloads: 2370, day: '2021-01-23' }, 1046 | { downloads: 3182, day: '2021-01-24' }, 1047 | { downloads: 11936, day: '2021-01-25' }, 1048 | { downloads: 11961, day: '2021-01-26' }, 1049 | { downloads: 14148, day: '2021-01-27' }, 1050 | { downloads: 12098, day: '2021-01-28' }, 1051 | { downloads: 11306, day: '2021-01-29' }, 1052 | { downloads: 2709, day: '2021-01-30' }, 1053 | { downloads: 2574, day: '2021-01-31' }, 1054 | { downloads: 13500, day: '2021-02-01' }, 1055 | { downloads: 14012, day: '2021-02-02' }, 1056 | { downloads: 13653, day: '2021-02-03' }, 1057 | { downloads: 14097, day: '2021-02-04' }, 1058 | { downloads: 12384, day: '2021-02-05' }, 1059 | { downloads: 2488, day: '2021-02-06' }, 1060 | { downloads: 2758, day: '2021-02-07' }, 1061 | { downloads: 12517, day: '2021-02-08' }, 1062 | { downloads: 13557, day: '2021-02-09' }, 1063 | { downloads: 13746, day: '2021-02-10' }, 1064 | { downloads: 12207, day: '2021-02-11' }, 1065 | { downloads: 10246, day: '2021-02-12' }, 1066 | { downloads: 2376, day: '2021-02-13' }, 1067 | { downloads: 2268, day: '2021-02-14' }, 1068 | { downloads: 11205, day: '2021-02-15' }, 1069 | { downloads: 13315, day: '2021-02-16' }, 1070 | { downloads: 13321, day: '2021-02-17' }, 1071 | { downloads: 13522, day: '2021-02-18' }, 1072 | { downloads: 13547, day: '2021-02-19' }, 1073 | { downloads: 3921, day: '2021-02-20' }, 1074 | { downloads: 2682, day: '2021-02-21' }, 1075 | { downloads: 13575, day: '2021-02-22' }, 1076 | { downloads: 14629, day: '2021-02-23' }, 1077 | { downloads: 14808, day: '2021-02-24' }, 1078 | { downloads: 13795, day: '2021-02-25' }, 1079 | { downloads: 12563, day: '2021-02-26' }, 1080 | { downloads: 2749, day: '2021-02-27' }, 1081 | { downloads: 3439, day: '2021-02-28' }, 1082 | { downloads: 14130, day: '2021-03-01' }, 1083 | { downloads: 14379, day: '2021-03-02' }, 1084 | { downloads: 14398, day: '2021-03-03' }, 1085 | { downloads: 13125, day: '2021-03-04' }, 1086 | { downloads: 12657, day: '2021-03-05' }, 1087 | { downloads: 3430, day: '2021-03-06' }, 1088 | { downloads: 2411, day: '2021-03-07' }, 1089 | { downloads: 12864, day: '2021-03-08' }, 1090 | { downloads: 14089, day: '2021-03-09' }, 1091 | { downloads: 14194, day: '2021-03-10' }, 1092 | { downloads: 13202, day: '2021-03-11' }, 1093 | { downloads: 12324, day: '2021-03-12' }, 1094 | { downloads: 3335, day: '2021-03-13' }, 1095 | { downloads: 2957, day: '2021-03-14' }, 1096 | { downloads: 14420, day: '2021-03-15' }, 1097 | { downloads: 15477, day: '2021-03-16' }, 1098 | { downloads: 14104, day: '2021-03-17' }, 1099 | { downloads: 15266, day: '2021-03-18' }, 1100 | { downloads: 12605, day: '2021-03-19' }, 1101 | { downloads: 3216, day: '2021-03-20' }, 1102 | { downloads: 3331, day: '2021-03-21' }, 1103 | { downloads: 14022, day: '2021-03-22' }, 1104 | { downloads: 14545, day: '2021-03-23' }, 1105 | { downloads: 14477, day: '2021-03-24' }, 1106 | { downloads: 14110, day: '2021-03-25' }, 1107 | { downloads: 13062, day: '2021-03-26' }, 1108 | { downloads: 3174, day: '2021-03-27' }, 1109 | { downloads: 3216, day: '2021-03-28' }, 1110 | { downloads: 13280, day: '2021-03-29' }, 1111 | { downloads: 15469, day: '2021-03-30' }, 1112 | { downloads: 14700, day: '2021-03-31' }, 1113 | { downloads: 13964, day: '2021-04-01' }, 1114 | { downloads: 8189, day: '2021-04-02' }, 1115 | { downloads: 2513, day: '2021-04-03' }, 1116 | { downloads: 2159, day: '2021-04-04' }, 1117 | { downloads: 9163, day: '2021-04-05' }, 1118 | { downloads: 14229, day: '2021-04-06' }, 1119 | { downloads: 14784, day: '2021-04-07' }, 1120 | { downloads: 15348, day: '2021-04-08' }, 1121 | { downloads: 12845, day: '2021-04-09' }, 1122 | { downloads: 3092, day: '2021-04-10' }, 1123 | { downloads: 3114, day: '2021-04-11' }, 1124 | { downloads: 14398, day: '2021-04-12' }, 1125 | { downloads: 15289, day: '2021-04-13' }, 1126 | { downloads: 14062, day: '2021-04-14' }, 1127 | { downloads: 14967, day: '2021-04-15' }, 1128 | { downloads: 12873, day: '2021-04-16' }, 1129 | { downloads: 2701, day: '2021-04-17' }, 1130 | { downloads: 3061, day: '2021-04-18' }, 1131 | { downloads: 15368, day: '2021-04-19' }, 1132 | { downloads: 15509, day: '2021-04-20' }, 1133 | { downloads: 14965, day: '2021-04-21' }, 1134 | { downloads: 17099, day: '2021-04-22' }, 1135 | { downloads: 15196, day: '2021-04-23' }, 1136 | { downloads: 2833, day: '2021-04-24' }, 1137 | { downloads: 3083, day: '2021-04-25' }, 1138 | { downloads: 15879, day: '2021-04-26' }, 1139 | { downloads: 16184, day: '2021-04-27' }, 1140 | { downloads: 15640, day: '2021-04-28' }, 1141 | { downloads: 15984, day: '2021-04-29' }, 1142 | { downloads: 13973, day: '2021-04-30' }, 1143 | { downloads: 2967, day: '2021-05-01' }, 1144 | { downloads: 2678, day: '2021-05-02' }, 1145 | { downloads: 12260, day: '2021-05-03' }, 1146 | { downloads: 14772, day: '2021-05-04' }, 1147 | { downloads: 14483, day: '2021-05-05' }, 1148 | { downloads: 14915, day: '2021-05-06' }, 1149 | { downloads: 13918, day: '2021-05-07' }, 1150 | { downloads: 2998, day: '2021-05-08' }, 1151 | { downloads: 2484, day: '2021-05-09' }, 1152 | { downloads: 15499, day: '2021-05-10' }, 1153 | { downloads: 15886, day: '2021-05-11' }, 1154 | { downloads: 15599, day: '2021-05-12' }, 1155 | { downloads: 12282, day: '2021-05-13' }, 1156 | { downloads: 12010, day: '2021-05-14' }, 1157 | { downloads: 2789, day: '2021-05-15' }, 1158 | { downloads: 2226, day: '2021-05-16' }, 1159 | { downloads: 16002, day: '2021-05-17' }, 1160 | { downloads: 17478, day: '2021-05-18' }, 1161 | { downloads: 17373, day: '2021-05-19' }, 1162 | { downloads: 16097, day: '2021-05-20' }, 1163 | { downloads: 15067, day: '2021-05-21' }, 1164 | { downloads: 2910, day: '2021-05-22' }, 1165 | { downloads: 2394, day: '2021-05-23' }, 1166 | { downloads: 13375, day: '2021-05-24' }, 1167 | { downloads: 17253, day: '2021-05-25' }, 1168 | { downloads: 17067, day: '2021-05-26' }, 1169 | { downloads: 16519, day: '2021-05-27' }, 1170 | { downloads: 14606, day: '2021-05-28' }, 1171 | { downloads: 2924, day: '2021-05-29' }, 1172 | { downloads: 2691, day: '2021-05-30' }, 1173 | { downloads: 14008, day: '2021-05-31' }, 1174 | { downloads: 16105, day: '2021-06-01' }, 1175 | { downloads: 15855, day: '2021-06-02' }, 1176 | { downloads: 16038, day: '2021-06-03' }, 1177 | { downloads: 13029, day: '2021-06-04' }, 1178 | { downloads: 3082, day: '2021-06-05' }, 1179 | { downloads: 2519, day: '2021-06-06' }, 1180 | { downloads: 17397, day: '2021-06-07' }, 1181 | { downloads: 17110, day: '2021-06-08' }, 1182 | { downloads: 17443, day: '2021-06-09' }, 1183 | { downloads: 18055, day: '2021-06-10' }, 1184 | { downloads: 16318, day: '2021-06-11' }, 1185 | { downloads: 2090, day: '2021-06-12' }, 1186 | { downloads: 2493, day: '2021-06-13' }, 1187 | { downloads: 15910, day: '2021-06-14' }, 1188 | { downloads: 16561, day: '2021-06-15' }, 1189 | { downloads: 17149, day: '2021-06-16' }, 1190 | { downloads: 17291, day: '2021-06-17' }, 1191 | { downloads: 14909, day: '2021-06-18' }, 1192 | { downloads: 2557, day: '2021-06-19' }, 1193 | { downloads: 3024, day: '2021-06-20' }, 1194 | { downloads: 14962, day: '2021-06-21' }, 1195 | { downloads: 17421, day: '2021-06-22' }, 1196 | { downloads: 17590, day: '2021-06-23' }, 1197 | { downloads: 15768, day: '2021-06-24' }, 1198 | { downloads: 14512, day: '2021-06-25' }, 1199 | { downloads: 3191, day: '2021-06-26' }, 1200 | { downloads: 4146, day: '2021-06-27' }, 1201 | { downloads: 16997, day: '2021-06-28' }, 1202 | { downloads: 17675, day: '2021-06-29' }, 1203 | { downloads: 17149, day: '2021-06-30' }, 1204 | { downloads: 18237, day: '2021-07-01' }, 1205 | { downloads: 14677, day: '2021-07-02' }, 1206 | { downloads: 3293, day: '2021-07-03' }, 1207 | { downloads: 3116, day: '2021-07-04' }, 1208 | { downloads: 15002, day: '2021-07-05' }, 1209 | { downloads: 17869, day: '2021-07-06' }, 1210 | { downloads: 17786, day: '2021-07-07' }, 1211 | { downloads: 18635, day: '2021-07-08' }, 1212 | { downloads: 16273, day: '2021-07-09' }, 1213 | { downloads: 3008, day: '2021-07-10' }, 1214 | { downloads: 2889, day: '2021-07-11' }, 1215 | { downloads: 18389, day: '2021-07-12' }, 1216 | { downloads: 21028, day: '2021-07-13' }, 1217 | { downloads: 18301, day: '2021-07-14' }, 1218 | { downloads: 18114, day: '2021-07-15' }, 1219 | { downloads: 15539, day: '2021-07-16' }, 1220 | { downloads: 3189, day: '2021-07-17' }, 1221 | { downloads: 2913, day: '2021-07-18' }, 1222 | { downloads: 18148, day: '2021-07-19' }, 1223 | { downloads: 18340, day: '2021-07-20' }, 1224 | { downloads: 17457, day: '2021-07-21' }, 1225 | { downloads: 18051, day: '2021-07-22' }, 1226 | { downloads: 15482, day: '2021-07-23' }, 1227 | { downloads: 3313, day: '2021-07-24' }, 1228 | { downloads: 3098, day: '2021-07-25' }, 1229 | { downloads: 17327, day: '2021-07-26' }, 1230 | { downloads: 18032, day: '2021-07-27' }, 1231 | { downloads: 18094, day: '2021-07-28' }, 1232 | { downloads: 17640, day: '2021-07-29' }, 1233 | { downloads: 15443, day: '2021-07-30' }, 1234 | { downloads: 3327, day: '2021-07-31' }, 1235 | { downloads: 4188, day: '2021-08-01' }, 1236 | { downloads: 19168, day: '2021-08-02' }, 1237 | { downloads: 19134, day: '2021-08-03' }, 1238 | { downloads: 18939, day: '2021-08-04' }, 1239 | { downloads: 17883, day: '2021-08-05' }, 1240 | { downloads: 16251, day: '2021-08-06' }, 1241 | { downloads: 2908, day: '2021-08-07' }, 1242 | { downloads: 4130, day: '2021-08-08' }, 1243 | { downloads: 18302, day: '2021-08-09' }, 1244 | { downloads: 19210, day: '2021-08-10' }, 1245 | { downloads: 19337, day: '2021-08-11' }, 1246 | { downloads: 19711, day: '2021-08-12' }, 1247 | { downloads: 16786, day: '2021-08-13' }, 1248 | { downloads: 3329, day: '2021-08-14' }, 1249 | { downloads: 3496, day: '2021-08-15' }, 1250 | { downloads: 18235, day: '2021-08-16' }, 1251 | { downloads: 18126, day: '2021-08-17' }, 1252 | { downloads: 19149, day: '2021-08-18' }, 1253 | { downloads: 18057, day: '2021-08-19' }, 1254 | { downloads: 15817, day: '2021-08-20' }, 1255 | { downloads: 3485, day: '2021-08-21' }, 1256 | { downloads: 3454, day: '2021-08-22' }, 1257 | { downloads: 18415, day: '2021-08-23' }, 1258 | { downloads: 19289, day: '2021-08-24' }, 1259 | { downloads: 19838, day: '2021-08-25' }, 1260 | { downloads: 22179, day: '2021-08-26' }, 1261 | { downloads: 18509, day: '2021-08-27' }, 1262 | { downloads: 4271, day: '2021-08-28' }, 1263 | { downloads: 3712, day: '2021-08-29' }, 1264 | { downloads: 20219, day: '2021-08-30' }, 1265 | { downloads: 23797, day: '2021-08-31' }, 1266 | { downloads: 22070, day: '2021-09-01' }, 1267 | { downloads: 21820, day: '2021-09-02' }, 1268 | { downloads: 19190, day: '2021-09-03' }, 1269 | { downloads: 3376, day: '2021-09-04' }, 1270 | { downloads: 3340, day: '2021-09-05' }, 1271 | { downloads: 18596, day: '2021-09-06' }, 1272 | { downloads: 20935, day: '2021-09-07' }, 1273 | { downloads: 22178, day: '2021-09-08' }, 1274 | { downloads: 22613, day: '2021-09-09' }, 1275 | { downloads: 19655, day: '2021-09-10' }, 1276 | { downloads: 3810, day: '2021-09-11' }, 1277 | { downloads: 3326, day: '2021-09-12' }, 1278 | { downloads: 24508, day: '2021-09-13' }, 1279 | { downloads: 27615, day: '2021-09-14' }, 1280 | { downloads: 23890, day: '2021-09-15' }, 1281 | { downloads: 22986, day: '2021-09-16' }, 1282 | { downloads: 19889, day: '2021-09-17' }, 1283 | { downloads: 4533, day: '2021-09-18' }, 1284 | { downloads: 3717, day: '2021-09-19' }, 1285 | { downloads: 21422, day: '2021-09-20' }, 1286 | { downloads: 24495, day: '2021-09-21' }, 1287 | { downloads: 23190, day: '2021-09-22' }, 1288 | { downloads: 22621, day: '2021-09-23' }, 1289 | { downloads: 19920, day: '2021-09-24' }, 1290 | { downloads: 3830, day: '2021-09-25' }, 1291 | { downloads: 4399, day: '2021-09-26' }, 1292 | { downloads: 21918, day: '2021-09-27' }, 1293 | { downloads: 23411, day: '2021-09-28' }, 1294 | { downloads: 24833, day: '2021-09-29' }, 1295 | { downloads: 24037, day: '2021-09-30' }, 1296 | { downloads: 20407, day: '2021-10-01' }, 1297 | { downloads: 3687, day: '2021-10-02' }, 1298 | { downloads: 3856, day: '2021-10-03' }, 1299 | { downloads: 22178, day: '2021-10-04' }, 1300 | { downloads: 24268, day: '2021-10-05' }, 1301 | { downloads: 23067, day: '2021-10-06' }, 1302 | { downloads: 23608, day: '2021-10-07' }, 1303 | { downloads: 19178, day: '2021-10-08' }, 1304 | { downloads: 4502, day: '2021-10-09' }, 1305 | { downloads: 3611, day: '2021-10-10' }, 1306 | { downloads: 21473, day: '2021-10-11' }, 1307 | { downloads: 23326, day: '2021-10-12' }, 1308 | { downloads: 25214, day: '2021-10-13' }, 1309 | { downloads: 24399, day: '2021-10-14' }, 1310 | { downloads: 19409, day: '2021-10-15' }, 1311 | { downloads: 4148, day: '2021-10-16' }, 1312 | { downloads: 3788, day: '2021-10-17' }, 1313 | { downloads: 22531, day: '2021-10-18' }, 1314 | { downloads: 25295, day: '2021-10-19' }, 1315 | { downloads: 24707, day: '2021-10-20' }, 1316 | { downloads: 25515, day: '2021-10-21' }, 1317 | { downloads: 21869, day: '2021-10-22' }, 1318 | { downloads: 4571, day: '2021-10-23' }, 1319 | { downloads: 4742, day: '2021-10-24' }, 1320 | { downloads: 25229, day: '2021-10-25' }, 1321 | { downloads: 25415, day: '2021-10-26' }, 1322 | { downloads: 25684, day: '2021-10-27' }, 1323 | { downloads: 23915, day: '2021-10-28' }, 1324 | { downloads: 21822, day: '2021-10-29' }, 1325 | { downloads: 5250, day: '2021-10-30' }, 1326 | { downloads: 4635, day: '2021-10-31' }, 1327 | { downloads: 20201, day: '2021-11-01' }, 1328 | { downloads: 24734, day: '2021-11-02' }, 1329 | { downloads: 25293, day: '2021-11-03' }, 1330 | { downloads: 22824, day: '2021-11-04' }, 1331 | { downloads: 20082, day: '2021-11-05' }, 1332 | { downloads: 3632, day: '2021-11-06' }, 1333 | { downloads: 4235, day: '2021-11-07' }, 1334 | { downloads: 24611, day: '2021-11-08' }, 1335 | { downloads: 26413, day: '2021-11-09' }, 1336 | { downloads: 27268, day: '2021-11-10' }, 1337 | { downloads: 21000, day: '2021-11-11' }, 1338 | { downloads: 20030, day: '2021-11-12' }, 1339 | { downloads: 3941, day: '2021-11-13' }, 1340 | { downloads: 4049, day: '2021-11-14' }, 1341 | { downloads: 24220, day: '2021-11-15' }, 1342 | { downloads: 24851, day: '2021-11-16' }, 1343 | { downloads: 26268, day: '2021-11-17' }, 1344 | { downloads: 23605, day: '2021-11-18' }, 1345 | { downloads: 21361, day: '2021-11-19' }, 1346 | { downloads: 4007, day: '2021-11-20' }, 1347 | { downloads: 4003, day: '2021-11-21' }, 1348 | { downloads: 24576, day: '2021-11-22' }, 1349 | { downloads: 26079, day: '2021-11-23' }, 1350 | { downloads: 25256, day: '2021-11-24' }, 1351 | { downloads: 22621, day: '2021-11-25' }, 1352 | { downloads: 18511, day: '2021-11-26' }, 1353 | { downloads: 4149, day: '2021-11-27' }, 1354 | { downloads: 4099, day: '2021-11-28' }, 1355 | { downloads: 24582, day: '2021-11-29' }, 1356 | { downloads: 26649, day: '2021-11-30' }, 1357 | { downloads: 24646, day: '2021-12-01' }, 1358 | { downloads: 24766, day: '2021-12-02' }, 1359 | { downloads: 20932, day: '2021-12-03' }, 1360 | { downloads: 4443, day: '2021-12-04' }, 1361 | { downloads: 4011, day: '2021-12-05' }, 1362 | { downloads: 25458, day: '2021-12-06' }, 1363 | { downloads: 24968, day: '2021-12-07' }, 1364 | { downloads: 25894, day: '2021-12-08' }, 1365 | { downloads: 26756, day: '2021-12-09' }, 1366 | { downloads: 23560, day: '2021-12-10' }, 1367 | { downloads: 4922, day: '2021-12-11' }, 1368 | { downloads: 3708, day: '2021-12-12' }, 1369 | { downloads: 25771, day: '2021-12-13' }, 1370 | { downloads: 26577, day: '2021-12-14' }, 1371 | { downloads: 27421, day: '2021-12-15' }, 1372 | { downloads: 25676, day: '2021-12-16' }, 1373 | { downloads: 22096, day: '2021-12-17' }, 1374 | { downloads: 5879, day: '2021-12-18' }, 1375 | { downloads: 4095, day: '2021-12-19' }, 1376 | { downloads: 24699, day: '2021-12-20' }, 1377 | { downloads: 24770, day: '2021-12-21' }, 1378 | { downloads: 21117, day: '2021-12-22' }, 1379 | { downloads: 19277, day: '2021-12-23' }, 1380 | { downloads: 11016, day: '2021-12-24' }, 1381 | { downloads: 2668, day: '2021-12-25' }, 1382 | { downloads: 3033, day: '2021-12-26' }, 1383 | { downloads: 13819, day: '2021-12-27' }, 1384 | { downloads: 15214, day: '2021-12-28' }, 1385 | { downloads: 15638, day: '2021-12-29' }, 1386 | { downloads: 14448, day: '2021-12-30' }, 1387 | { downloads: 8706, day: '2021-12-31' }, 1388 | { downloads: 3137, day: '2022-01-01' }, 1389 | { downloads: 3301, day: '2022-01-02' }, 1390 | { downloads: 19172, day: '2022-01-03' }, 1391 | { downloads: 23747, day: '2022-01-04' }, 1392 | { downloads: 23173, day: '2022-01-05' }, 1393 | { downloads: 23276, day: '2022-01-06' }, 1394 | { downloads: 20123, day: '2022-01-07' }, 1395 | { downloads: 4769, day: '2022-01-08' }, 1396 | { downloads: 4905, day: '2022-01-09' }, 1397 | { downloads: 27235, day: '2022-01-10' }, 1398 | { downloads: 26015, day: '2022-01-11' }, 1399 | { downloads: 26248, day: '2022-01-12' }, 1400 | { downloads: 24192, day: '2022-01-13' }, 1401 | { downloads: 21248, day: '2022-01-14' }, 1402 | { downloads: 4429, day: '2022-01-15' }, 1403 | { downloads: 4572, day: '2022-01-16' }, 1404 | { downloads: 22816, day: '2022-01-17' }, 1405 | { downloads: 25118, day: '2022-01-18' }, 1406 | { downloads: 26303, day: '2022-01-19' }, 1407 | { downloads: 26641, day: '2022-01-20' }, 1408 | { downloads: 22070, day: '2022-01-21' }, 1409 | { downloads: 5595, day: '2022-01-22' }, 1410 | { downloads: 4315, day: '2022-01-23' }, 1411 | { downloads: 25502, day: '2022-01-24' }, 1412 | { downloads: 26344, day: '2022-01-25' }, 1413 | { downloads: 23669, day: '2022-01-26' }, 1414 | { downloads: 27169, day: '2022-01-27' }, 1415 | { downloads: 21687, day: '2022-01-28' }, 1416 | { downloads: 4751, day: '2022-01-29' }, 1417 | { downloads: 4458, day: '2022-01-30' }, 1418 | { downloads: 24724, day: '2022-01-31' }, 1419 | { downloads: 25014, day: '2022-02-01' }, 1420 | { downloads: 23714, day: '2022-02-02' }, 1421 | { downloads: 24696, day: '2022-02-03' }, 1422 | { downloads: 23412, day: '2022-02-04' }, 1423 | { downloads: 3756, day: '2022-02-05' }, 1424 | { downloads: 4581, day: '2022-02-06' }, 1425 | { downloads: 26062, day: '2022-02-07' }, 1426 | { downloads: 26729, day: '2022-02-08' }, 1427 | { downloads: 26712, day: '2022-02-09' }, 1428 | { downloads: 25821, day: '2022-02-10' }, 1429 | { downloads: 23171, day: '2022-02-11' }, 1430 | { downloads: 4471, day: '2022-02-12' }, 1431 | { downloads: 4284, day: '2022-02-13' }, 1432 | { downloads: 26273, day: '2022-02-14' }, 1433 | { downloads: 26237, day: '2022-02-15' }, 1434 | { downloads: 25788, day: '2022-02-16' }, 1435 | { downloads: 26366, day: '2022-02-17' }, 1436 | { downloads: 22849, day: '2022-02-18' }, 1437 | { downloads: 6604, day: '2022-02-19' }, 1438 | { downloads: 5814, day: '2022-02-20' }, 1439 | { downloads: 25854, day: '2022-02-21' }, 1440 | { downloads: 26270, day: '2022-02-22' }, 1441 | { downloads: 27448, day: '2022-02-23' }, 1442 | { downloads: 31904, day: '2022-02-24' }, 1443 | { downloads: 27934, day: '2022-02-25' }, 1444 | { downloads: 9438, day: '2022-02-26' }, 1445 | { downloads: 5411, day: '2022-02-27' }, 1446 | { downloads: 27084, day: '2022-02-28' }, 1447 | { downloads: 29002, day: '2022-03-01' }, 1448 | { downloads: 28817, day: '2022-03-02' }, 1449 | { downloads: 26228, day: '2022-03-03' }, 1450 | { downloads: 22278, day: '2022-03-04' }, 1451 | { downloads: 5117, day: '2022-03-05' }, 1452 | { downloads: 4969, day: '2022-03-06' }, 1453 | { downloads: 24715, day: '2022-03-07' }, 1454 | { downloads: 28971, day: '2022-03-08' }, 1455 | { downloads: 26958, day: '2022-03-09' }, 1456 | { downloads: 27303, day: '2022-03-10' }, 1457 | { downloads: 22885, day: '2022-03-11' }, 1458 | { downloads: 4911, day: '2022-03-12' }, 1459 | { downloads: 4532, day: '2022-03-13' }, 1460 | { downloads: 28165, day: '2022-03-14' }, 1461 | { downloads: 29901, day: '2022-03-15' }, 1462 | { downloads: 30626, day: '2022-03-16' }, 1463 | { downloads: 25614, day: '2022-03-17' }, 1464 | { downloads: 23235, day: '2022-03-18' }, 1465 | { downloads: 5534, day: '2022-03-19' }, 1466 | { downloads: 5481, day: '2022-03-20' }, 1467 | { downloads: 28275, day: '2022-03-21' }, 1468 | { downloads: 33409, day: '2022-03-22' }, 1469 | { downloads: 32456, day: '2022-03-23' }, 1470 | { downloads: 28524, day: '2022-03-24' }, 1471 | { downloads: 24744, day: '2022-03-25' }, 1472 | { downloads: 4857, day: '2022-03-26' }, 1473 | { downloads: 4094, day: '2022-03-27' }, 1474 | { downloads: 26896, day: '2022-03-28' }, 1475 | { downloads: 28467, day: '2022-03-29' }, 1476 | { downloads: 28534, day: '2022-03-30' }, 1477 | { downloads: 28613, day: '2022-03-31' }, 1478 | { downloads: 25779, day: '2022-04-01' }, 1479 | { downloads: 5205, day: '2022-04-02' }, 1480 | { downloads: 5320, day: '2022-04-03' }, 1481 | { downloads: 28246, day: '2022-04-04' }, 1482 | { downloads: 29402, day: '2022-04-05' }, 1483 | { downloads: 28430, day: '2022-04-06' }, 1484 | { downloads: 28574, day: '2022-04-07' }, 1485 | { downloads: 27081, day: '2022-04-08' }, 1486 | { downloads: 6195, day: '2022-04-09' }, 1487 | { downloads: 5024, day: '2022-04-10' }, 1488 | { downloads: 30936, day: '2022-04-11' }, 1489 | { downloads: 30621, day: '2022-04-12' }, 1490 | { downloads: 29339, day: '2022-04-13' }, 1491 | { downloads: 26861, day: '2022-04-14' }, 1492 | { downloads: 17977, day: '2022-04-15' }, 1493 | { downloads: 4523, day: '2022-04-16' }, 1494 | { downloads: 4167, day: '2022-04-17' }, 1495 | { downloads: 21539, day: '2022-04-18' }, 1496 | { downloads: 30929, day: '2022-04-19' }, 1497 | { downloads: 28602, day: '2022-04-20' }, 1498 | { downloads: 27895, day: '2022-04-21' }, 1499 | { downloads: 25713, day: '2022-04-22' }, 1500 | { downloads: 4633, day: '2022-04-23' }, 1501 | { downloads: 5476, day: '2022-04-24' }, 1502 | { downloads: 28085, day: '2022-04-25' }, 1503 | { downloads: 31156, day: '2022-04-26' }, 1504 | { downloads: 30001, day: '2022-04-27' }, 1505 | { downloads: 28740, day: '2022-04-28' }, 1506 | { downloads: 24246, day: '2022-04-29' }, 1507 | { downloads: 4429, day: '2022-04-30' }, 1508 | { downloads: 4645, day: '2022-05-01' }, 1509 | { downloads: 23394, day: '2022-05-02' }, 1510 | { downloads: 25268, day: '2022-05-03' }, 1511 | { downloads: 26268, day: '2022-05-04' }, 1512 | { downloads: 26215, day: '2022-05-05' }, 1513 | { downloads: 24627, day: '2022-05-06' }, 1514 | { downloads: 5653, day: '2022-05-07' }, 1515 | { downloads: 3509, day: '2022-05-08' }, 1516 | { downloads: 29735, day: '2022-05-09' }, 1517 | { downloads: 30191, day: '2022-05-10' }, 1518 | { downloads: 30939, day: '2022-05-11' }, 1519 | { downloads: 32058, day: '2022-05-12' }, 1520 | { downloads: 28173, day: '2022-05-13' }, 1521 | { downloads: 5005, day: '2022-05-14' }, 1522 | { downloads: 4988, day: '2022-05-15' }, 1523 | { downloads: 32776, day: '2022-05-16' }, 1524 | { downloads: 33497, day: '2022-05-17' }, 1525 | { downloads: 32289, day: '2022-05-18' }, 1526 | { downloads: 29797, day: '2022-05-19' }, 1527 | { downloads: 26221, day: '2022-05-20' }, 1528 | { downloads: 4508, day: '2022-05-21' }, 1529 | { downloads: 4966, day: '2022-05-22' }, 1530 | { downloads: 32711, day: '2022-05-23' }, 1531 | { downloads: 32588, day: '2022-05-24' }, 1532 | { downloads: 32101, day: '2022-05-25' }, 1533 | { downloads: 25048, day: '2022-05-26' }, 1534 | { downloads: 24802, day: '2022-05-27' }, 1535 | { downloads: 5849, day: '2022-05-28' }, 1536 | { downloads: 5202, day: '2022-05-29' }, 1537 | { downloads: 28520, day: '2022-05-30' }, 1538 | { downloads: 30475, day: '2022-05-31' }, 1539 | { downloads: 30353, day: '2022-06-01' }, 1540 | { downloads: 30926, day: '2022-06-02' }, 1541 | { downloads: 26175, day: '2022-06-03' }, 1542 | { downloads: 4535, day: '2022-06-04' }, 1543 | { downloads: 4255, day: '2022-06-05' }, 1544 | { downloads: 25802, day: '2022-06-06' }, 1545 | { downloads: 31346, day: '2022-06-07' }, 1546 | { downloads: 30334, day: '2022-06-08' }, 1547 | { downloads: 29086, day: '2022-06-09' }, 1548 | { downloads: 24124, day: '2022-06-10' }, 1549 | { downloads: 4822, day: '2022-06-11' }, 1550 | { downloads: 4425, day: '2022-06-12' }, 1551 | { downloads: 29069, day: '2022-06-13' }, 1552 | { downloads: 31053, day: '2022-06-14' }, 1553 | { downloads: 31716, day: '2022-06-15' }, 1554 | { downloads: 27208, day: '2022-06-16' }, 1555 | { downloads: 28744, day: '2022-06-17' }, 1556 | { downloads: 4882, day: '2022-06-18' }, 1557 | { downloads: 4421, day: '2022-06-19' }, 1558 | { downloads: 28605, day: '2022-06-20' }, 1559 | { downloads: 29576, day: '2022-06-21' }, 1560 | { downloads: 29542, day: '2022-06-22' }, 1561 | { downloads: 30136, day: '2022-06-23' }, 1562 | { downloads: 25527, day: '2022-06-24' }, 1563 | { downloads: 3832, day: '2022-06-25' }, 1564 | { downloads: 4131, day: '2022-06-26' }, 1565 | { downloads: 28584, day: '2022-06-27' }, 1566 | { downloads: 31124, day: '2022-06-28' }, 1567 | { downloads: 30564, day: '2022-06-29' }, 1568 | { downloads: 28658, day: '2022-06-30' }, 1569 | { downloads: 24830, day: '2022-07-01' }, 1570 | { downloads: 4217, day: '2022-07-02' }, 1571 | { downloads: 4203, day: '2022-07-03' }, 1572 | { downloads: 28486, day: '2022-07-04' }, 1573 | { downloads: 30650, day: '2022-07-05' }, 1574 | { downloads: 32060, day: '2022-07-06' }, 1575 | { downloads: 29658, day: '2022-07-07' }, 1576 | { downloads: 27030, day: '2022-07-08' }, 1577 | { downloads: 4736, day: '2022-07-09' }, 1578 | { downloads: 4755, day: '2022-07-10' }, 1579 | { downloads: 30104, day: '2022-07-11' }, 1580 | { downloads: 31549, day: '2022-07-12' }, 1581 | { downloads: 30665, day: '2022-07-13' }, 1582 | { downloads: 29965, day: '2022-07-14' }, 1583 | { downloads: 27043, day: '2022-07-15' }, 1584 | { downloads: 4847, day: '2022-07-16' }, 1585 | { downloads: 5770, day: '2022-07-17' }, 1586 | { downloads: 30356, day: '2022-07-18' }, 1587 | { downloads: 33346, day: '2022-07-19' }, 1588 | { downloads: 33039, day: '2022-07-20' }, 1589 | { downloads: 31887, day: '2022-07-21' }, 1590 | { downloads: 0, day: '2022-07-22' }, 1591 | { downloads: 4123, day: '2022-07-23' }, 1592 | { downloads: 4823, day: '2022-07-24' }, 1593 | { downloads: 30082, day: '2022-07-25' }, 1594 | { downloads: 33090, day: '2022-07-26' }, 1595 | { downloads: 32965, day: '2022-07-27' }, 1596 | { downloads: 32991, day: '2022-07-28' }, 1597 | { downloads: 28886, day: '2022-07-29' }, 1598 | { downloads: 6898, day: '2022-07-30' }, 1599 | { downloads: 6571, day: '2022-07-31' }, 1600 | { downloads: 32839, day: '2022-08-01' }, 1601 | { downloads: 33688, day: '2022-08-02' }, 1602 | { downloads: 34684, day: '2022-08-03' }, 1603 | { downloads: 32561, day: '2022-08-04' }, 1604 | { downloads: 26647, day: '2022-08-05' }, 1605 | { downloads: 4877, day: '2022-08-06' }, 1606 | { downloads: 5276, day: '2022-08-07' }, 1607 | { downloads: 32457, day: '2022-08-08' }, 1608 | { downloads: 31744, day: '2022-08-09' }, 1609 | { downloads: 31611, day: '2022-08-10' }, 1610 | { downloads: 31098, day: '2022-08-11' }, 1611 | { downloads: 26773, day: '2022-08-12' }, 1612 | { downloads: 5060, day: '2022-08-13' }, 1613 | { downloads: 5098, day: '2022-08-14' }, 1614 | { downloads: 27416, day: '2022-08-15' }, 1615 | { downloads: 34018, day: '2022-08-16' }, 1616 | { downloads: 32774, day: '2022-08-17' }, 1617 | { downloads: 32718, day: '2022-08-18' }, 1618 | { downloads: 28023, day: '2022-08-19' }, 1619 | { downloads: 4801, day: '2022-08-20' }, 1620 | { downloads: 5171, day: '2022-08-21' }, 1621 | { downloads: 32751, day: '2022-08-22' }, 1622 | { downloads: 33456, day: '2022-08-23' }, 1623 | { downloads: 31006, day: '2022-08-24' }, 1624 | { downloads: 31785, day: '2022-08-25' }, 1625 | { downloads: 26889, day: '2022-08-26' }, 1626 | { downloads: 5497, day: '2022-08-27' }, 1627 | { downloads: 5294, day: '2022-08-28' }, 1628 | { downloads: 33090, day: '2022-08-29' }, 1629 | { downloads: 36161, day: '2022-08-30' }, 1630 | { downloads: 33293, day: '2022-08-31' }, 1631 | { downloads: 32106, day: '2022-09-01' }, 1632 | { downloads: 27208, day: '2022-09-02' }, 1633 | { downloads: 5224, day: '2022-09-03' }, 1634 | { downloads: 6470, day: '2022-09-04' }, 1635 | { downloads: 29139, day: '2022-09-05' }, 1636 | { downloads: 33166, day: '2022-09-06' }, 1637 | { downloads: 31557, day: '2022-09-07' }, 1638 | { downloads: 34596, day: '2022-09-08' }, 1639 | { downloads: 28714, day: '2022-09-09' }, 1640 | { downloads: 5203, day: '2022-09-10' }, 1641 | { downloads: 5399, day: '2022-09-11' }, 1642 | { downloads: 33560, day: '2022-09-12' }, 1643 | { downloads: 36236, day: '2022-09-13' }, 1644 | { downloads: 36132, day: '2022-09-14' }, 1645 | { downloads: 34617, day: '2022-09-15' }, 1646 | { downloads: 29741, day: '2022-09-16' }, 1647 | { downloads: 5060, day: '2022-09-17' }, 1648 | { downloads: 5771, day: '2022-09-18' }, 1649 | { downloads: 31977, day: '2022-09-19' }, 1650 | { downloads: 35706, day: '2022-09-20' }, 1651 | { downloads: 34017, day: '2022-09-21' }, 1652 | { downloads: 32439, day: '2022-09-22' }, 1653 | { downloads: 29629, day: '2022-09-23' }, 1654 | { downloads: 5147, day: '2022-09-24' }, 1655 | { downloads: 4780, day: '2022-09-25' }, 1656 | { downloads: 33646, day: '2022-09-26' }, 1657 | { downloads: 35435, day: '2022-09-27' }, 1658 | { downloads: 35569, day: '2022-09-28' }, 1659 | { downloads: 35693, day: '2022-09-29' }, 1660 | { downloads: 32102, day: '2022-09-30' }, 1661 | { downloads: 5729, day: '2022-10-01' }, 1662 | { downloads: 4946, day: '2022-10-02' }, 1663 | { downloads: 34076, day: '2022-10-03' }, 1664 | { downloads: 38469, day: '2022-10-04' }, 1665 | { downloads: 34310, day: '2022-10-05' }, 1666 | { downloads: 36319, day: '2022-10-06' }, 1667 | { downloads: 30059, day: '2022-10-07' }, 1668 | { downloads: 5956, day: '2022-10-08' }, 1669 | { downloads: 5382, day: '2022-10-09' }, 1670 | { downloads: 32179, day: '2022-10-10' }, 1671 | { downloads: 36314, day: '2022-10-11' }, 1672 | { downloads: 35675, day: '2022-10-12' }, 1673 | { downloads: 35651, day: '2022-10-13' }, 1674 | { downloads: 34028, day: '2022-10-14' }, 1675 | { downloads: 5861, day: '2022-10-15' }, 1676 | { downloads: 5769, day: '2022-10-16' }, 1677 | { downloads: 39276, day: '2022-10-17' }, 1678 | { downloads: 38890, day: '2022-10-18' }, 1679 | { downloads: 36397, day: '2022-10-19' }, 1680 | { downloads: 35706, day: '2022-10-20' }, 1681 | { downloads: 32912, day: '2022-10-21' }, 1682 | { downloads: 6114, day: '2022-10-22' }, 1683 | { downloads: 5681, day: '2022-10-23' }, 1684 | { downloads: 35678, day: '2022-10-24' }, 1685 | { downloads: 37611, day: '2022-10-25' }, 1686 | { downloads: 35831, day: '2022-10-26' }, 1687 | { downloads: 36072, day: '2022-10-27' }, 1688 | { downloads: 30830, day: '2022-10-28' }, 1689 | { downloads: 5925, day: '2022-10-29' }, 1690 | { downloads: 5620, day: '2022-10-30' }, 1691 | { downloads: 35421, day: '2022-10-31' }, 1692 | { downloads: 31406, day: '2022-11-01' }, 1693 | { downloads: 33934, day: '2022-11-02' }, 1694 | { downloads: 34642, day: '2022-11-03' }, 1695 | { downloads: 31059, day: '2022-11-04' }, 1696 | { downloads: 6312, day: '2022-11-05' }, 1697 | { downloads: 5734, day: '2022-11-06' }, 1698 | { downloads: 40370, day: '2022-11-07' }, 1699 | { downloads: 41793, day: '2022-11-08' }, 1700 | { downloads: 36861, day: '2022-11-09' }, 1701 | { downloads: 37679, day: '2022-11-10' }, 1702 | { downloads: 29681, day: '2022-11-11' }, 1703 | { downloads: 6448, day: '2022-11-12' }, 1704 | { downloads: 5750, day: '2022-11-13' }, 1705 | { downloads: 38041, day: '2022-11-14' }, 1706 | { downloads: 36741, day: '2022-11-15' }, 1707 | { downloads: 37698, day: '2022-11-16' }, 1708 | { downloads: 38804, day: '2022-11-17' }, 1709 | { downloads: 33797, day: '2022-11-18' }, 1710 | { downloads: 6479, day: '2022-11-19' }, 1711 | { downloads: 5937, day: '2022-11-20' }, 1712 | { downloads: 40877, day: '2022-11-21' }, 1713 | { downloads: 38022, day: '2022-11-22' }, 1714 | { downloads: 36447, day: '2022-11-23' }, 1715 | { downloads: 31590, day: '2022-11-24' }, 1716 | { downloads: 27911, day: '2022-11-25' }, 1717 | { downloads: 5189, day: '2022-11-26' }, 1718 | { downloads: 5967, day: '2022-11-27' }, 1719 | { downloads: 36745, day: '2022-11-28' }, 1720 | { downloads: 40053, day: '2022-11-29' }, 1721 | { downloads: 37322, day: '2022-11-30' }, 1722 | { downloads: 38226, day: '2022-12-01' }, 1723 | { downloads: 32213, day: '2022-12-02' }, 1724 | { downloads: 6044, day: '2022-12-03' }, 1725 | { downloads: 5820, day: '2022-12-04' }, 1726 | { downloads: 37962, day: '2022-12-05' }, 1727 | { downloads: 38440, day: '2022-12-06' }, 1728 | { downloads: 39273, day: '2022-12-07' }, 1729 | { downloads: 35667, day: '2022-12-08' }, 1730 | { downloads: 31468, day: '2022-12-09' }, 1731 | { downloads: 6117, day: '2022-12-10' }, 1732 | { downloads: 6682, day: '2022-12-11' }, 1733 | { downloads: 38430, day: '2022-12-12' }, 1734 | { downloads: 38991, day: '2022-12-13' }, 1735 | { downloads: 37843, day: '2022-12-14' }, 1736 | { downloads: 37617, day: '2022-12-15' }, 1737 | { downloads: 31443, day: '2022-12-16' }, 1738 | { downloads: 5893, day: '2022-12-17' }, 1739 | { downloads: 5776, day: '2022-12-18' }, 1740 | { downloads: 35503, day: '2022-12-19' }, 1741 | { downloads: 34704, day: '2022-12-20' }, 1742 | { downloads: 34609, day: '2022-12-21' }, 1743 | { downloads: 33900, day: '2022-12-22' }, 1744 | { downloads: 22964, day: '2022-12-23' }, 1745 | { downloads: 5033, day: '2022-12-24' }, 1746 | { downloads: 4424, day: '2022-12-25' }, 1747 | { downloads: 18848, day: '2022-12-26' }, 1748 | { downloads: 21971, day: '2022-12-27' }, 1749 | { downloads: 23520, day: '2022-12-28' }, 1750 | { downloads: 20961, day: '2022-12-29' }, 1751 | { downloads: 16497, day: '2022-12-30' }, 1752 | { downloads: 4104, day: '2022-12-31' }, 1753 | { downloads: 4278, day: '2023-01-01' }, 1754 | { downloads: 19072, day: '2023-01-02' }, 1755 | { downloads: 34361, day: '2023-01-03' }, 1756 | { downloads: 35335, day: '2023-01-04' }, 1757 | { downloads: 37489, day: '2023-01-05' }, 1758 | { downloads: 30434, day: '2023-01-06' }, 1759 | { downloads: 7336, day: '2023-01-07' }, 1760 | { downloads: 6774, day: '2023-01-08' }, 1761 | { downloads: 41070, day: '2023-01-09' }, 1762 | { downloads: 38197, day: '2023-01-10' }, 1763 | { downloads: 37240, day: '2023-01-11' }, 1764 | { downloads: 42525, day: '2023-01-12' }, 1765 | { downloads: 33596, day: '2023-01-13' }, 1766 | { downloads: 6275, day: '2023-01-14' }, 1767 | { downloads: 6022, day: '2023-01-15' }, 1768 | { downloads: 34909, day: '2023-01-16' }, 1769 | { downloads: 40495, day: '2023-01-17' }, 1770 | { downloads: 39721, day: '2023-01-18' }, 1771 | { downloads: 38582, day: '2023-01-19' }, 1772 | { downloads: 33645, day: '2023-01-20' }, 1773 | { downloads: 7159, day: '2023-01-21' }, 1774 | { downloads: 6557, day: '2023-01-22' }, 1775 | { downloads: 38114, day: '2023-01-23' }, 1776 | { downloads: 39853, day: '2023-01-24' }, 1777 | { downloads: 38599, day: '2023-01-25' }, 1778 | { downloads: 35754, day: '2023-01-26' }, 1779 | { downloads: 36477, day: '2023-01-27' }, 1780 | { downloads: 7163, day: '2023-01-28' }, 1781 | { downloads: 6617, day: '2023-01-29' }, 1782 | { downloads: 41037, day: '2023-01-30' }, 1783 | { downloads: 43828, day: '2023-01-31' }, 1784 | { downloads: 41075, day: '2023-02-01' }, 1785 | { downloads: 41143, day: '2023-02-02' }, 1786 | { downloads: 37810, day: '2023-02-03' }, 1787 | { downloads: 7888, day: '2023-02-04' }, 1788 | { downloads: 7252, day: '2023-02-05' }, 1789 | { downloads: 39773, day: '2023-02-06' }, 1790 | { downloads: 44874, day: '2023-02-07' }, 1791 | { downloads: 42851, day: '2023-02-08' }, 1792 | { downloads: 43653, day: '2023-02-09' }, 1793 | { downloads: 36830, day: '2023-02-10' }, 1794 | { downloads: 6966, day: '2023-02-11' }, 1795 | ], 1796 | }; 1797 | -------------------------------------------------------------------------------- /src/app/(common)/utils/npm-api/types.ts: -------------------------------------------------------------------------------- 1 | export type NpmDailyDownloads = { 2 | downloads: number; 3 | day: string; 4 | }; 5 | 6 | export type NpmRangeData = { 7 | downloads: NpmDailyDownloads[]; 8 | start: string; 9 | end: string; 10 | package: string; 11 | }; 12 | -------------------------------------------------------------------------------- /src/app/api/package/og-image/[...name]/route.tsx: -------------------------------------------------------------------------------- 1 | import { ImageResponse } from '@vercel/og'; 2 | import clsx from 'clsx'; 3 | import { NextRequest } from 'next/server'; 4 | import React from 'react'; 5 | import { OgBarsChart } from '#src/app/(common)/components/OgBarsChart'; 6 | import { SIX_HOURS_IN_SECONDS } from '#src/app/(common)/utils/consts'; 7 | import { getPackageInsights } from '#src/app/(common)/utils/npm-api'; 8 | import { NpmDailyDownloads } from '#src/app/(common)/utils/npm-api/types'; 9 | 10 | export const runtime = 'experimental-edge'; 11 | 12 | export async function GET(req: NextRequest) { 13 | const { searchParams } = req.nextUrl; 14 | const packageName = searchParams.getAll('name').join('/'); 15 | if (!packageName) { 16 | return new Response('Missing package name', { status: 400 }); 17 | } 18 | 19 | const data = await getPackageInsights(packageName); 20 | 21 | const allMonthlyDownloads = data.allDailyDownloads.reduce((acc, curr) => { 22 | const last = acc[acc.length - 1]; 23 | const isNewMonth = !last || new Date(curr.day).getMonth() !== new Date(last.day).getMonth(); 24 | if (isNewMonth) { 25 | return [ 26 | ...acc, 27 | { 28 | day: curr.day, 29 | downloads: curr.downloads, 30 | }, 31 | ]; 32 | } 33 | 34 | last.downloads += curr.downloads; 35 | return acc; 36 | }, []); 37 | 38 | const points = allMonthlyDownloads.map((d) => ({ 39 | date: d.day, 40 | value: d.downloads, 41 | })); 42 | 43 | return new ImageResponse( 44 | ( 45 |
46 |

47 | {packageName} insights 48 |

49 |
50 |
51 | 52 | 53 | 54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 |

npminsights

62 |
63 |
64 | ), 65 | { 66 | width: 1200, 67 | height: 630, 68 | headers: { 69 | 'Cache-Control': `public, immutable, no-transform, max-age=${SIX_HOURS_IN_SECONDS}`, 70 | }, 71 | } 72 | ); 73 | } 74 | 75 | type CardProps = { 76 | title: string; 77 | value: number; 78 | previousValue: number; 79 | }; 80 | 81 | const Card = ({ title, value, previousValue }: CardProps) => { 82 | const percentageDiff = 83 | value !== 0 && previousValue !== 0 ? ((value - previousValue) / previousValue) * 100 : 0; 84 | return ( 85 |
86 |
87 |

{title}

88 |

0 ? 'text-green-500' : 'text-red-600')}> 89 | {percentageDiff > 0 && '+'} 90 | {percentageDiff.toLocaleString('en', { maximumFractionDigits: 1 }) || '-'}% 91 |

92 |
93 |
94 |

95 | {value.toLocaleString('en') || '-'} 96 |

97 |

from {previousValue.toLocaleString('en') || '-'}

98 |
99 |
100 | ); 101 | }; 102 | -------------------------------------------------------------------------------- /src/app/api/package/readme-image/[...name]/route.tsx: -------------------------------------------------------------------------------- 1 | import { ImageResponse } from '@vercel/og'; 2 | import clsx from 'clsx'; 3 | import { NextRequest } from 'next/server'; 4 | import React from 'react'; 5 | import { OgBarsChart } from '#src/app/(common)/components/OgBarsChart'; 6 | import { SIX_HOURS_IN_SECONDS } from '#src/app/(common)/utils/consts'; 7 | import { getPackageInsights } from '#src/app/(common)/utils/npm-api'; 8 | import { NpmDailyDownloads } from '#src/app/(common)/utils/npm-api/types'; 9 | 10 | export const runtime = 'experimental-edge'; 11 | 12 | export async function GET(req: NextRequest) { 13 | const { searchParams } = req.nextUrl; 14 | const packageName = searchParams.getAll('name').join('/'); 15 | if (!packageName) { 16 | return new Response('Missing package name', { status: 400 }); 17 | } 18 | 19 | const data = await getPackageInsights(packageName); 20 | 21 | const allMonthlyDownloads = data.allDailyDownloads.reduce((acc, curr) => { 22 | const last = acc[acc.length - 1]; 23 | const isNewMonth = !last || new Date(curr.day).getMonth() !== new Date(last.day).getMonth(); 24 | if (isNewMonth) { 25 | return [ 26 | ...acc, 27 | { 28 | day: curr.day, 29 | downloads: curr.downloads, 30 | }, 31 | ]; 32 | } 33 | 34 | last.downloads += curr.downloads; 35 | return acc; 36 | }, []); 37 | 38 | const points = allMonthlyDownloads.map((d) => ({ 39 | date: d.day, 40 | value: d.downloads, 41 | })); 42 | 43 | return new ImageResponse( 44 | ( 45 |
46 |

47 | {packageName} insights 48 |

49 |

Downloads

50 |
51 | 52 | 53 | 54 | 55 |
56 |

Monthly downloads history

57 | 58 |

npminsights

59 |
60 | ), 61 | { 62 | width: 1200, 63 | height: 630, 64 | headers: { 65 | 'cache-control': `public, immutable, no-transform, max-age=${SIX_HOURS_IN_SECONDS}`, 66 | }, 67 | } 68 | ); 69 | } 70 | 71 | type CardProps = { 72 | title: string; 73 | value: number; 74 | previousValue: number; 75 | }; 76 | 77 | const Card = ({ title, value, previousValue }: CardProps) => { 78 | const percentageDiff = 79 | value !== 0 && previousValue !== 0 ? ((value - previousValue) / previousValue) * 100 : 0; 80 | return ( 81 |
82 |
83 |

{title}

84 |

0 ? 'text-green-500' : 'text-red-600')}> 85 | {percentageDiff > 0 && '+'} 86 | {percentageDiff.toLocaleString('en', { maximumFractionDigits: 1 }) || '-'}% 87 |

88 |
89 |
90 |

{value.toLocaleString('en') || '-'}

91 |

from {previousValue.toLocaleString('en') || '-'}

92 |
93 |
94 | ); 95 | }; 96 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from 'next'; 2 | import Link from 'next/link'; 3 | import { AnalyticsWrapper } from './(common)/components/AnalyticsWrapper'; 4 | import { PackageAutocomplete } from './(common)/components/PackageAutocomplete'; 5 | import './globals.css'; 6 | 7 | export const metadata: Metadata = { 8 | description: 'Get insights about NPM packages and authors', 9 | icons: { 10 | icon: '/favicon.ico', 11 | }, 12 | manifest: '/site.webmanifest', 13 | }; 14 | 15 | type RootLayoutProps = { 16 | children: React.ReactNode; 17 | }; 18 | 19 | export default function RootLayout({ children }: RootLayoutProps) { 20 | return ( 21 | 22 | 23 | 24 |
25 | 26 | npminsights 27 | 28 | 32 | 35 | 36 | contribute 37 | 38 |
39 |
{children}
40 | 48 | 49 | 50 | 51 | ); 52 | } 53 | 54 | type SvgProps = { 55 | size?: number; 56 | }; 57 | 58 | const GithubLogo = ({ size = 16 }: SvgProps) => { 59 | return ( 60 | 61 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/app/package/[...name]/PackageInsights.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import React from 'react'; 3 | import { DownloadsChart } from '#src/app/(common)/components/DownloadsChart'; 4 | import * as npmApi from '#src/app/(common)/utils/npm-api'; 5 | import { NpmDailyDownloads } from '#src/app/(common)/utils/npm-api/types'; 6 | 7 | type PackageProps = { 8 | name?: string; 9 | }; 10 | 11 | export default async function PackageInsights({ name }: PackageProps) { 12 | const data = await npmApi.getPackageInsights(name); 13 | 14 | const firstMonday = data.allDailyDownloads.findIndex((d) => new Date(d.day).getDay() === 1); 15 | const allWeeklyDownloads = data.allDailyDownloads 16 | .slice(firstMonday) 17 | .reduce((acc, curr) => { 18 | const isMonday = new Date(curr.day).getDay() === 1; 19 | if (isMonday) { 20 | return [ 21 | ...acc, 22 | { 23 | day: curr.day, 24 | downloads: curr.downloads, 25 | }, 26 | ]; 27 | } 28 | 29 | const last = acc[acc.length - 1]; 30 | last.downloads += curr.downloads; 31 | return acc; 32 | }, []); 33 | 34 | const allMonthlyDownloads = data.allDailyDownloads.reduce((acc, curr) => { 35 | const last = acc[acc.length - 1]; 36 | const isNewMonth = !last || new Date(curr.day).getMonth() !== new Date(last.day).getMonth(); 37 | if (isNewMonth) { 38 | return [ 39 | ...acc, 40 | { 41 | day: curr.day, 42 | downloads: curr.downloads, 43 | }, 44 | ]; 45 | } 46 | 47 | last.downloads += curr.downloads; 48 | return acc; 49 | }, []); 50 | 51 | const allYearlyDownloads = data.allDailyDownloads.reduce((acc, curr) => { 52 | const last = acc[acc.length - 1]; 53 | const isNewYear = 54 | !last || new Date(curr.day).getFullYear() !== new Date(last.day).getFullYear(); 55 | if (isNewYear) { 56 | return [ 57 | ...acc, 58 | { 59 | day: curr.day, 60 | downloads: curr.downloads, 61 | }, 62 | ]; 63 | } 64 | 65 | last.downloads += curr.downloads; 66 | return acc; 67 | }, []); 68 | 69 | return ( 70 | <> 71 |
72 |

Downloads

73 |
74 | 75 | 76 | 77 | 78 |
79 |
80 |
81 |

History

82 | 88 | 94 | 101 | 108 |
109 | 110 | ); 111 | } 112 | 113 | type CardProps = { 114 | title: string; 115 | value: number; 116 | previousValue: number; 117 | }; 118 | 119 | const Card = ({ title, value, previousValue }: CardProps) => { 120 | const percentageDiff = 121 | value !== 0 && previousValue !== 0 ? ((value - previousValue) / previousValue) * 100 : 0; 122 | return ( 123 |
124 |
125 |

{title}

126 |

0 ? 'text-green-500' : 'text-red-600')}> 127 | {percentageDiff > 0 && '+'} 128 | {percentageDiff.toLocaleString('en', { maximumFractionDigits: 1 }) || '-'}% 129 |

130 |
131 |
132 |

{value.toLocaleString('en') || '-'}

133 |

from {previousValue.toLocaleString('en') || '-'}

134 |
135 |
136 | ); 137 | }; 138 | -------------------------------------------------------------------------------- /src/app/package/[...name]/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next'; 2 | import React from 'react'; 3 | import PackageInsights from './PackageInsights'; 4 | 5 | // cannot use edge-runtime due to https://github.com/vercel/next.js/issues/43384 6 | // export const runtime = 'experimental-edge'; 7 | 8 | export async function generateStaticParams() { 9 | return [ 10 | { 11 | name: ['react-easy-crop'], 12 | }, 13 | ]; 14 | } 15 | 16 | export type PackageProps = { 17 | params: { name: string[] }; // we use an array of strings to support scoped packages 18 | }; 19 | 20 | export default async function Package({ params }: PackageProps) { 21 | const { name } = params; 22 | const packageName = name.map(decodeURIComponent).join('/'); 23 | 24 | return ( 25 | <> 26 |

27 | {packageName} insights 28 |

29 | {/* @ts-expect-error Server Component */} 30 | 31 | 32 | ); 33 | } 34 | 35 | export async function generateMetadata({ params }: PackageProps): Promise { 36 | const { name } = params; 37 | const packageName = name.map(decodeURIComponent).join('/'); 38 | 39 | const url = `https://npminsights.vercel.app/package/${packageName}`; 40 | const description = `Get insights about ${packageName} NPM package`; 41 | const image: NonNullable['images'] = { 42 | url: `https://npminsights.vercel.app/api/package/og-image/${packageName}`, 43 | width: 1200, 44 | height: 630, 45 | alt: `${packageName} insights preview`, 46 | }; 47 | return { 48 | title: `${packageName} - npminsights`, 49 | openGraph: { 50 | title: packageName, 51 | description, 52 | url, 53 | siteName: 'npminsights', 54 | type: 'website', 55 | locale: 'en-US', 56 | images: image, 57 | }, 58 | twitter: { 59 | card: 'summary_large_image', 60 | creator: '@ValentinHervieu', 61 | title: packageName, 62 | description, 63 | images: image, 64 | }, 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PackageInsights from './package/[...name]/PackageInsights'; 3 | 4 | export const runtime = 'experimental-edge'; 5 | 6 | export default async function Home() { 7 | return ( 8 | <> 9 |

10 | all NPM packages insights 11 |

12 | {/* @ts-expect-error Server Component */} 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const colors = require('tailwindcss/colors'); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: ['./src/**/*.tsx'], 6 | theme: { 7 | colors: { 8 | transparent: 'transparent', 9 | current: 'currentColor', 10 | black: colors.black, 11 | white: colors.white, 12 | gray: colors.slate, 13 | green: colors.emerald, 14 | purple: colors.violet, 15 | yellow: colors.amber, 16 | pink: colors.pink, 17 | red: colors.rose, 18 | }, 19 | extend: {}, 20 | }, 21 | plugins: [require('@tailwindcss/line-clamp')], 22 | }; 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "baseUrl": ".", 23 | "paths": { 24 | "#src/*": ["./src/*"] 25 | } 26 | }, 27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 28 | "exclude": ["node_modules"] 29 | } 30 | --------------------------------------------------------------------------------