A production-grade TypeScript SDK for extracting and normalizing GitHub developer signals.
- Type-Safe: Full TypeScript support with exported types
- Tree-Shakeable: Zero side effects, optimized bundle size
- Normalized Data: UI-ready JSON responses, no raw GitHub API responses
- Error Handling: Typed errors with rate limit information
- Zero Dependencies: Uses native fetch only
- Dual Format: ESM and CommonJS support
npm install devtrackr- Node.js: >=18.0.0
- GitHub Personal Access Token: Required for API authentication
npm install devtrackrimport { createDevTrackr } from 'devtrackr';
// Initialize with your GitHub token
const devtrackr = createDevTrackr({
token: process.env.GITHUB_TOKEN! // Load from environment variable
});
// Fetch user profile
const profile = await devtrackr.getProfile('octocat');
console.log(`${profile.name} has ${profile.followers} followers`);Create a .env file in your project root:
GITHUB_TOKEN=your_github_personal_access_token_hereThen use it in your code:
import { createDevTrackr } from 'devtrackr';
import 'dotenv/config'; // If using dotenv package
const devtrackr = createDevTrackr({
token: process.env.GITHUB_TOKEN!
});Windows (PowerShell):
$env:GITHUB_TOKEN="your_token_here"Linux/macOS:
export GITHUB_TOKEN="your_token_here"Creates a DevTrackr instance.
Parameters:
config.token(string, required): GitHub Personal Access Token
Returns: DevTrackrInstance
All methods are async and return normalized data ready for UI rendering.
Returns normalized profile data.
Returns: Profile
Returns array of normalized repositories.
Options:
sort?: 'created' | 'updated' | 'pushed' | 'full_name'direction?: 'asc' | 'desc'perPage?: numberpage?: number
Returns: Repository[]
Returns array of recent commits.
Options:
perPage?: numberpage?: numbersince?: string(ISO 8601 date)until?: string(ISO 8601 date)
Returns: RecentCommit[]
Returns language statistics with percentages and colors.
Returns: LanguageStats
Returns contribution statistics (commits, streaks, etc.).
Returns: ContributionStats
Returns activity timeline with daily commit counts.
Options:
days?: number(default: 365)
Returns: ActivityTimeline
DevTrackr throws typed errors:
DevTrackrAuthError: Invalid or expired token (401)DevTrackrRateLimitError: Rate limit exceeded (403)- Exposes
limit,remaining, andresetAtproperties
- Exposes
DevTrackrNetworkError: Network or API errors
import { DevTrackrRateLimitError } from 'devtrackr';
try {
const profile = await devtrackr.getProfile('octocat');
} catch (error) {
if (error instanceof DevTrackrRateLimitError) {
console.log(`Rate limit resets at: ${error.resetAt}`);
}
}All data is normalized to UI-ready schemas. See TypeScript types for full definitions.
{
username: string;
name: string | null;
avatarUrl: string;
bio: string | null;
followers: number;
following: number;
publicRepos: number;
profileUrl: string;
}{
name: string;
description: string | null;
stars: number;
forks: number;
primaryLanguage: string | null;
updatedAt: string;
repoUrl: string;
}{
repo: string;
message: string;
additions: number;
deletions: number;
committedAt: string;
commitUrl: string;
}{
totalBytes: number;
languages: {
name: string;
percentage: number; // 0-100
color: string; // Hex color
}[];
}{
totalCommits: number;
activeDays: number;
avgCommitsPerWeek: number;
longestStreak: number;
currentStreak: number;
}{
date: string; // YYYY-MM-DD
commits: number;
}[]DevTrackr requires a GitHub Personal Access Token (PAT) to authenticate with the GitHub API.
- Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click "Generate new token (classic)"
- Give it a descriptive name (e.g., "DevTrackr SDK")
- Select the following scopes:
public_repo- Access public repositoriesread:user- Read user profile informationread:org- Read org and team membership (if needed)
- Click "Generate token"
- Copy the token immediately (you won't be able to see it again)
Warning: Never commit tokens to version control! Use environment variables:
import { createDevTrackr } from 'devtrackr';
const devtrackr = createDevTrackr({
token: process.env.GITHUB_TOKEN! // Load from environment
});GitHub API has rate limits:
- Authenticated requests: 5,000 requests/hour
- Unauthenticated requests: 60 requests/hour
DevTrackr automatically tracks rate limit headers and throws DevTrackrRateLimitError when limits are exceeded.
DevTrackr throws typed errors that extend DevTrackrError:
Thrown when authentication fails (401) or access is forbidden (403 without rate limit).
import { DevTrackrAuthError } from 'devtrackr';
try {
await devtrackr.getProfile('username');
} catch (error) {
if (error instanceof DevTrackrAuthError) {
console.error('Authentication failed:', error.message);
// Check your token is valid and has correct permissions
}
}Common causes:
- Invalid or expired token
- Token missing required scopes
- Token revoked
Thrown when GitHub API rate limit is exceeded (403 with rate limit headers).
import { DevTrackrRateLimitError } from 'devtrackr';
try {
await devtrackr.getProfile('username');
} catch (error) {
if (error instanceof DevTrackrRateLimitError) {
console.error('Rate limit exceeded');
console.log(`Limit: ${error.limit}`);
console.log(`Remaining: ${error.remaining}`);
console.log(`Resets at: ${error.resetAt.toISOString()}`);
// Calculate wait time
const waitMs = error.resetAt.getTime() - Date.now();
console.log(`Wait ${Math.ceil(waitMs / 1000)} seconds`);
}
}Properties:
limit: number- Total rate limitremaining: number- Remaining requests (0 when error thrown)resetAt: Date- When the rate limit resets
Thrown for network failures, API errors (5xx), or other HTTP errors.
import { DevTrackrNetworkError } from 'devtrackr';
try {
await devtrackr.getProfile('username');
} catch (error) {
if (error instanceof DevTrackrNetworkError) {
console.error('Network error:', error.message);
// Retry logic or fallback behavior
}
}Common causes:
- Network connectivity issues
- GitHub API downtime (5xx errors)
- Invalid endpoint or malformed request
import {
DevTrackrError,
DevTrackrAuthError,
DevTrackrRateLimitError,
DevTrackrNetworkError,
} from 'devtrackr';
async function fetchUserData(username: string) {
try {
const profile = await devtrackr.getProfile(username);
return profile;
} catch (error) {
if (error instanceof DevTrackrRateLimitError) {
// Implement exponential backoff or wait until reset
const waitTime = error.resetAt.getTime() - Date.now();
console.warn(`Rate limited. Waiting ${waitTime}ms`);
// Consider implementing retry logic here
throw error;
} else if (error instanceof DevTrackrAuthError) {
// Token issue - don't retry
console.error('Authentication failed. Check your token.');
throw error;
} else if (error instanceof DevTrackrNetworkError) {
// Network issue - might be retryable
console.error('Network error:', error.message);
throw error;
} else {
// Unknown error
console.error('Unexpected error:', error);
throw error;
}
}
}import { createDevTrackr } from 'devtrackr';
const devtrackr = createDevTrackr({
token: process.env.GITHUB_TOKEN!
});
// Fetch user profile
const profile = await devtrackr.getProfile('octocat');
console.log(`${profile.name} has ${profile.followers} followers`);// Get first page of repositories
const repos = await devtrackr.getRepositories('octocat', {
sort: 'updated',
direction: 'desc',
perPage: 30,
page: 1
});
// Get most starred repositories
const starred = await devtrackr.getRepositories('octocat', {
sort: 'created',
direction: 'desc',
perPage: 10
});// Get recent commits from last 30 days
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const commits = await devtrackr.getRecentCommits('octocat', {
perPage: 50,
since: thirtyDaysAgo.toISOString()
});
// Calculate total changes
const totalAdditions = commits.reduce((sum, commit) => sum + commit.additions, 0);
const totalDeletions = commits.reduce((sum, commit) => sum + commit.deletions, 0);
console.log(`Total: +${totalAdditions} -${totalDeletions}`);const stats = await devtrackr.getLanguageStats('octocat');
console.log(`Total code: ${stats.totalBytes} bytes`);
stats.languages.forEach(lang => {
console.log(`${lang.name}: ${lang.percentage.toFixed(1)}% (${lang.color})`);
});const contributionStats = await devtrackr.getContributionStats('octocat');
console.log(`Total commits: ${contributionStats.totalCommits}`);
console.log(`Active days: ${contributionStats.activeDays}`);
console.log(`Avg commits/week: ${contributionStats.avgCommitsPerWeek.toFixed(1)}`);
console.log(`Longest streak: ${contributionStats.longestStreak} days`);
console.log(`Current streak: ${contributionStats.currentStreak} days`);const timeline = await devtrackr.getActivityTimeline('octocat', { days: 90 });
// Group by week for visualization
const weeklyData = [];
for (let i = 0; i < timeline.length; i += 7) {
const week = timeline.slice(i, i + 7);
const weekCommits = week.reduce((sum, day) => sum + day.commits, 0);
weeklyData.push({
week: week[0].date,
commits: weekCommits
});
}- Node.js: 18.0.0 or higher
- GitHub Personal Access Token: Required for all API calls
- Minimum scopes:
public_repo,read:user - Classic token or fine-grained token with repository read access
- Minimum scopes:
DevTrackr is written in TypeScript and includes full type definitions. All types are exported for your convenience:
import type {
Profile,
Repository,
RecentCommit,
LanguageStats,
ContributionStats,
ActivityTimeline,
DevTrackrInstance,
DevTrackrConfig,
} from 'devtrackr';Contributions are welcome! Please see our Contributing Guide for details.
MIT © DevTrackr Contributors