
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@timesheet/sdk
Advanced tools
The official TypeScript SDK for the Timesheet API, providing a comprehensive solution for time tracking, project management, and team collaboration.
npm install @timesheet/sdk
# or
yarn add @timesheet/sdk
# or
pnpm add @timesheet/sdk
import { TimesheetClient } from '@timesheet/sdk';
// Initialize with API key
const client = new TimesheetClient({
apiKey: 'your-api-key'
});
// Create a project
const project = await client.projects.create({
title: 'My Project',
description: 'Created with TypeScript SDK'
});
// Start a timer
const timer = await client.timer.start({
projectId: project.id,
startDateTime: new Date().toISOString()
});
// List recent tasks
const tasks = await client.tasks.list({
limit: 10,
sort: 'created',
order: 'desc'
});
console.log(`Found ${tasks.params.count} tasks`);
const client = new TimesheetClient({
apiKey: 'your-api-key'
});
const client = new TimesheetClient({
oauth2Token: 'your-access-token'
});
// With automatic token refresh
const client = new TimesheetClient({
oauth2: {
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
refreshToken: 'your-refresh-token'
}
});
OAuth 2.1 is the recommended authentication method for new applications. It requires PKCE (Proof Key for Code Exchange) for enhanced security.
import { OAuth21Auth, generatePkceCodePair } from '@timesheet/sdk';
// Step 1: Generate PKCE code pair
const pkce = generatePkceCodePair();
// Store pkce.codeVerifier securely (e.g., in session storage)
// Step 2: Build authorization URL and redirect user
const authUrl = OAuth21Auth.buildAuthorizationUrl({
clientId: 'your-client-id',
redirectUri: 'https://your-app.com/callback',
codeChallenge: pkce.codeChallenge,
codeChallengeMethod: pkce.codeChallengeMethod,
state: 'random-csrf-state', // Optional but recommended
scope: 'read write', // Optional
});
// Redirect user to authUrl...
// Step 3: After user authorizes, exchange code for tokens
const auth = await OAuth21Auth.fromAuthorizationCode({
clientId: 'your-client-id',
clientSecret: 'your-client-secret', // Optional for public clients
authorizationCode: codeFromCallback,
redirectUri: 'https://your-app.com/callback',
codeVerifier: pkce.codeVerifier,
});
// Step 4: Use with TimesheetClient
const client = new TimesheetClient({
authentication: auth
});
The SDK supports automatic endpoint discovery from /.well-known/oauth-authorization-server, allowing you to dynamically configure OAuth endpoints:
import { OAuth21Auth, OAuthDiscovery, generatePkceCodePair } from '@timesheet/sdk';
// Step 1: Discover OAuth endpoints
const discovery = new OAuthDiscovery();
const metadata = await discovery.discover('https://api.timesheet.io');
console.log('Authorization endpoint:', metadata.authorizationServer.authorization_endpoint);
console.log('Token endpoint:', metadata.authorizationServer.token_endpoint);
console.log('Supported scopes:', metadata.authorizationServer.scopes_supported);
// Step 2: Generate PKCE and build authorization URL using discovered endpoints
const pkce = generatePkceCodePair();
const authUrl = OAuth21Auth.buildAuthorizationUrl({
clientId: 'your-client-id',
redirectUri: 'https://your-app.com/callback',
codeChallenge: pkce.codeChallenge,
codeChallengeMethod: pkce.codeChallengeMethod,
scope: 'read write',
state: 'random-csrf-state',
authorizationEndpoint: metadata.authorizationServer.authorization_endpoint,
});
// Store pkce.codeVerifier and token endpoint securely, then redirect user
sessionStorage.setItem('pkce_verifier', pkce.codeVerifier);
sessionStorage.setItem('token_endpoint', metadata.authorizationServer.token_endpoint);
// Step 3: After callback, exchange code using discovered token endpoint
const auth = await OAuth21Auth.fromAuthorizationCode({
clientId: 'your-client-id',
authorizationCode: codeFromCallback,
redirectUri: 'https://your-app.com/callback',
codeVerifier: sessionStorage.getItem('pkce_verifier'),
tokenEndpoint: sessionStorage.getItem('token_endpoint'),
});
const client = new TimesheetClient({
authentication: auth
});
The OAuthDiscovery class provides caching and additional discovery options:
import { OAuthDiscovery, discoverOAuth } from '@timesheet/sdk';
// Using convenience function (uses shared cached instance)
const result = await discoverOAuth('https://api.timesheet.io');
// Using class with custom options
const discovery = new OAuthDiscovery({
cacheTtl: 3600000, // Cache for 1 hour (default)
timeout: 10000, // Request timeout in ms
fetchOpenIdConfig: true, // Also fetch OpenID Connect config
fetchProtectedResource: true, // Also fetch protected resource metadata (RFC 9728)
});
const metadata = await discovery.discover('https://api.timesheet.io');
// Check cache status
if (discovery.isCached('https://api.timesheet.io')) {
console.log('Using cached metadata');
}
// Clear cache when needed
discovery.clearCache();
import { OAuth21Auth } from '@timesheet/sdk';
// With just an access token
const auth = new OAuth21Auth('your-access-token');
// With refresh token for automatic token refresh
const auth = new OAuth21Auth({
clientId: 'your-client-id',
clientSecret: 'your-client-secret', // Optional for public clients
refreshToken: 'your-refresh-token',
});
const client = new TimesheetClient({
authentication: auth
});
All API resources are available through the client:
client.tasks // Task management
client.projects // Project management
client.tags // Tag management
client.teams // Team management
client.organizations // Organization settings
client.timer // Real-time time tracking
client.rates // Billing rates
client.expenses // Expense tracking
client.notes // Note attachments
client.pauses // Break time tracking
client.documents // Document generation
client.webhooks // Event notifications
client.automations // Time tracking automation
client.todos // Task management
client.profile // User profile
client.settings // User settings
// Create a task
const task = await client.tasks.create({
projectId: 'project-id',
description: 'Implement new feature',
startDateTime: new Date(Date.now() - 3600000).toISOString(), // 1 hour ago
endDateTime: new Date().toISOString(),
tagIds: ['tag-1', 'tag-2'],
billable: true
});
// Update task
await client.tasks.update(task.id, {
description: 'Implement new feature - completed'
});
// Search tasks
const results = await client.tasks.searchAdvanced({
projectIds: ['project-id'],
search: 'feature',
startDate: '2024-01-01',
endDate: '2024-01-31'
});
// Create a project
const project = await client.projects.create({
title: 'New Website',
teamId: 'team-id',
color: 3,
taskDefaultBillable: true
});
// List projects with filters
const projects = await client.projects.list({
status: 'active',
limit: 20
});
// Start a timer
const timer = await client.timer.start({
projectId: 'project-id',
startDateTime: new Date().toISOString()
});
// Pause the timer
await client.timer.pause({
startDateTime: new Date().toISOString()
});
// Resume the timer
await client.timer.resume({
endDateTime: new Date().toISOString()
});
// Stop and create task
const stoppedTimer = await client.timer.stop({
endDateTime: new Date().toISOString()
});
// Manual pagination
const firstPage = await client.tasks.list({ limit: 50 });
console.log(`Page ${firstPage.params.page} of ${firstPage.totalPages}`);
console.log(`Total items: ${firstPage.params.count}`);
// Get next page
if (firstPage.hasNextPage) {
const nextPage = await firstPage.nextPage();
}
// Auto-pagination with async iterator
const allTasks = await client.tasks.list({ projectId: 'project-id' });
for await (const task of allTasks) {
console.log(task.description);
}
// Collect all results across all pages
const allTasksArray = await client.tasks.list({
projectId: 'project-id'
}).then(page => page.toArray());
import {
TimesheetApiError,
TimesheetAuthError,
TimesheetRateLimitError
} from '@timesheet/sdk';
try {
const task = await client.tasks.get('task-id');
} catch (error) {
if (error instanceof TimesheetAuthError) {
console.error('Authentication failed');
} else if (error instanceof TimesheetRateLimitError) {
console.error(`Rate limited. Retry after: ${error.retryAfter}s`);
} else if (error instanceof TimesheetApiError) {
console.error(`API error: ${error.statusCode} - ${error.message}`);
}
}
The SDK is written in TypeScript and provides comprehensive type definitions:
import type {
Task,
Project,
TaskCreateRequest,
TaskListQueryParams,
NavigablePage
} from '@timesheet/sdk';
// All methods are fully typed
const task: Task = await client.tasks.get('task-id');
const projects: NavigablePage<Project> = await client.projects.list();
// Type-safe parameters
const params: TaskCreateRequest = {
projectId: 'project-id',
description: 'Task description',
billable: true,
startDateTime: new Date().toISOString()
};
We welcome contributions! Please see our Contributing Guidelines for details.
# Clone the repository
git clone https://github.com/timesheetIO/timesheet-typescript.git
cd timesheet-typescript
# Install dependencies
npm install
# Run tests
npm test
# Build the project
npm run build
# Generate documentation
npm run docs
This SDK is distributed under the Apache License 2.0.
See CHANGELOG.md for a list of changes.
Made with ❤️ by the timesheet.io team
FAQs
Official TypeScript SDK for the Timesheet API
We found that @timesheet/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.