
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
fs-box-sync
Advanced tools
TypeScript SDK for Box API with seamless Box Drive integration - manage files, folders, webhooks, and sync between cloud and local filesystem
Toolkit for Box REST API with automatic token management, OAuth automation support, and Box Drive integration.
C:\Users\{user}\AppData\Local\fs-box-sync\tokens.json~/.config/fs-box-sync/tokens.json┌─────────────────────────────────────┐
│ BoxFS (High-level API) │ ← Recommended
│ - readDir(id, ensureSync) │
│ - readFile(id, ensureSync) │
│ - uploadWithYearMonthFolders() │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ BoxDrive (Sync Bridge) │
│ - getLocalPath(id) │
│ - waitForSync(id, strategy) │
│ - Smart sync verification │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ BoxAPI (Pure API) │
│ - getFileInfo(id) │
│ - uploadFile(id, file) │
└─────────────────────────────────────┘
npm install fs-box-sync
fs-box-sync supports three usage patterns based on your needs:
Best for: Learning Box API, POC, quick experiments, one-off scripts
Get access token: Box Developer Console → My Apps → Your App → Configuration → Developer Token (Generate)
import { BoxAPI } from 'fs-box-sync';
// Just paste the developer token - works immediately!
const api = new BoxAPI({
accessToken: 'your-developer-token-from-console',
});
// Use right away - perfect for testing
const files = await api.listFolderItems('folder-id');
await api.uploadFile('folder-id', './test.pdf');
Characteristics:
Best for: Scheduled tasks, automation scripts, long-running services (up to 60 days)
Setup: Perform OAuth flow once to get refresh token, then use it directly
import box from 'fs-box-sync';
box.configure({
clientId: process.env.BOX_CLIENT_ID,
clientSecret: process.env.BOX_CLIENT_SECRET,
refreshToken: 'your-refresh-token', // From initial OAuth flow
});
// Auto-refreshes access tokens for ~60 days
await box.uploadFile('folder-id', './file.pdf');
await box.uploadWithYearMonthFolders('folder-id', './file.pdf', 'ja-JP');
Characteristics:
Storage locations:
C:\Users\{user}\AppData\Local\fs-box-sync\tokens.json~/.config/fs-box-sync\tokens.jsonBest for: Fully automated systems, CI/CD, unattended services, production deployments
Setup: Implement OAuth automation (e.g., Playwright) - works indefinitely
// boxClient.ts - Create a wrapper module
import credentials from '@constants/credentials';
import box from 'fs-box-sync';
import Playwright from './Playwright';
box.configure({
clientId: credentials.BOX_CLIENT_ID,
clientSecret: credentials.BOX_CLIENT_SECRET,
tokenProvider: async (authUrl) => {
// Fully automate OAuth - no manual intervention needed
return await Playwright.getBoxCode(authUrl);
},
});
export default box;
Then use anywhere in your app:
import box from './boxClient';
// Works indefinitely - auto re-authenticates when needed
await box.uploadFile('folder-id', './file.pdf');
await box.readDir('folder-id'); // Always reads from synced local filesystem
Characteristics:
| Feature | Tier 1: Testing | Tier 2: Production | Tier 3: Enterprise |
|---|---|---|---|
| Setup Complexity | Minimal (copy/paste) | Low | Medium |
| Duration | ~1 hour | ~60 days | Indefinite |
| Auto-Refresh | ❌ No | ✅ Yes | ✅ Yes |
| Manual Work | Regenerate hourly | Re-auth every 60 days | None |
| Best For | Testing, Learning | Automation Scripts | Production Services |
| OAuth Required | ❌ No | ✅ Initial only | ✅ Fully automated |
Choose Tier 1 if you want to:
Choose Tier 2 if you have:
Choose Tier 3 if you need:
For any production application, we recommend creating a wrapper module that exports a pre-configured singleton instance. This is the cleanest and most maintainable approach.
✅ Single source of truth - Configuration in one place ✅ No duplication - Import once, use everywhere ✅ Type safety - Full TypeScript support ✅ Easy testing - Simple to mock in tests ✅ DI friendly - Easy to swap implementations
Step 1: Create a wrapper module (e.g., boxClient.ts or lib/box.ts)
// src/lib/boxClient.ts
import credentials from '@constants/credentials';
import box from 'fs-box-sync';
import Playwright from './Playwright';
// Configure once
box.configure({
clientId: credentials.BOX_CLIENT_ID,
clientSecret: credentials.BOX_CLIENT_SECRET,
tokenProvider: async (authUrl) => {
return await Playwright.getBoxCode(authUrl);
},
});
// Export the pre-configured singleton
export default box;
Step 2: Use anywhere in your application
// In any file - just import and use!
import box from '@/lib/boxClient';
// Ready to use - no configuration needed
async function uploadReport() {
await box.uploadFile('folder-id', './report.pdf');
}
async function listFiles() {
const files = await box.readDir('folder-id');
return files;
}
This pattern works for all tiers:
Tier 1 (Testing):
// boxClient.ts
import { BoxAPI } from 'fs-box-sync';
const api = new BoxAPI({
accessToken: process.env.BOX_ACCESS_TOKEN,
});
export default api;
Tier 2 (Production):
// boxClient.ts
import box from 'fs-box-sync';
box.configure({
clientId: process.env.BOX_CLIENT_ID,
clientSecret: process.env.BOX_CLIENT_SECRET,
refreshToken: process.env.BOX_REFRESH_TOKEN,
});
export default box;
Tier 3 (Enterprise):
// boxClient.ts
import box from 'fs-box-sync';
import Playwright from './auth/Playwright';
box.configure({
clientId: process.env.BOX_CLIENT_ID,
clientSecret: process.env.BOX_CLIENT_SECRET,
tokenProvider: async (authUrl) => {
return await Playwright.getBoxCode(authUrl);
},
});
export default box;
The package exports a singleton by default (export default Box.getInstance()), which means:
The wrapper module pattern simply organizes the singleton configuration - it's the recommended way to use this package in production.
interface BoxConfig {
// === Authentication ===
accessToken?: string; // For quick testing (Tier 1)
tokenProvider?: (callback: string) => Promise<string> | string; // For automation (Tier 3)
refreshToken?: string; // For production (Tier 2)
clientId?: string;
clientSecret?: string;
redirectUri?: string; // Default: 'https://oauth.pstmn.io/v1/callback'
// === Box Drive ===
boxDriveRoot?: string; // Auto-detected if not provided
// Windows: C:/Users/{username}/Box
// Mac: ~/Library/CloudStorage/Box-Box
// Linux: ~/Box
// === Box Domain ===
domain?: string; // Default: 'app.box.com'
// === Sync Settings ===
syncTimeout?: number; // Default: 30000 (30 seconds)
syncInterval?: number; // Default: 1000 (1 second)
}
BoxDrive supports 3 sync strategies:
poll - Simple existence check// Just checks if file exists locally (fastest, least reliable)
await box.waitForSync('file-id', 'file', 'poll');
smart - Size & modification verification (default)// Verifies file size matches cloud (recommended)
await box.waitForSync('file-id', 'file', 'smart');
force - Try to trigger sync// Attempts to force Box Drive sync (limited capabilities)
await box.waitForSync('file-id', 'file', 'force');
readDir(folderId) - Read directory contents (always synced locally)listFolderItems(folderId) - Read with IDs and types (from cloud API)readFile(fileId) - Read file content (always synced locally)writeFile(folderId, filename, content) - Write filedeleteFile(fileId) - Delete filegetLocalPath(id, type) - Get Box Drive path (fast, may not exist)getLocalPathSynced(id, type, strategy?) - Get Box Drive path (guaranteed to exist)openLocally(id, type) - Open in Box DriveexistsAndSynced(id, type) - Check if ID exists and is syncedexistsByNameAndSynced(parentId, name, type) - Check if named item exists and is syncedfindByName(folderId, name) - Find by partial namesearch(folderId, query, type?) - Search in folderuploadFile(folderId, filePath) - Upload filedownloadFile(fileId, destPath) - Download fileuploadWithYearMonthFolders(folderId, filePath, locale?) - Upload with date structure (default locale: 'en-US')moveFile(fileId, toFolderId) - Move filecreateFolderIfNotExists(parentId, name) - Create if neededgetFileInfo(fileId) - Get metadatagetFolderInfo(folderId) - Get metadataisBoxDriveRunning() - Check if Box Drive is runningwaitForSync(id, type, strategy?) - Wait for syncgetBoxDriveRoot() - Get Box Drive root pathgetAllWebhooks() - List webhookscreateWebhook(folderId, address) - Create webhookdeleteWebhook(webhookId) - Delete webhookgetOfficeOnlineUrl(fileId) - Get Office Online URLgetOfficeOnlineUrlByName(folderId, fileName) - Get by searchAll pure Box REST API operations without Box Drive integration:
getFileInfo(fileId), getFolderInfo(folderId)listFolderItems(folderId)uploadFile(folderId, filePath) - With auto-chunking >20MBdownloadFile(fileId, destPath)createFolder(parentId, name)deleteFile(fileId), moveFile(fileId, toId)searchInFolder(folderId, query, type?)getLocalPath(id, type) - Convert ID to local pathwaitForSync(id, type, strategy) - Wait for syncisSynced(id, type) - Check sync statusisBoxDriveRunning() - Health checkopenLocally(localPath) - Open file/foldernpm run build
npm test
npm run lint
npm run format
npm run check:exports
Contributions are welcome! Please feel free to submit a Pull Request.
If you encounter any issues, please report them here.
MIT © oharu121
FAQs
TypeScript SDK for Box API with seamless Box Drive integration - manage files, folders, webhooks, and sync between cloud and local filesystem
We found that fs-box-sync 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
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.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.