
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
A TypeScript IMAP client with strict typing, automatic resource cleanup, and zero memory leaks.
The original ImapFlow library has persistent memory leak issues stemming from:
destroy().once() / .on() patternsThis rewrite eliminates these issues through a "functional core, imperative shell" architecture with explicit resource tracking.
AsyncDisposable and await using for guaranteed cleanupnpm install imap-sdk
# or
bun add imap-sdk
import { IMAPClient } from 'imap-sdk'
const client = new IMAPClient({
host: 'imap.gmail.com',
port: 993,
secure: true,
auth: {
user: 'user@gmail.com',
accessToken: 'your-access-token',
},
})
await client.connect()
const folders = await client.run('LIST', '', '*')
console.log('Folders:', folders.map(f => f.path))
const inbox = await client.run('SELECT', 'INBOX')
console.log('Messages:', inbox.exists)
await client.close()
await usingThe client implements AsyncDisposable, enabling automatic cleanup when the scope exits:
import { IMAPClient } from 'imap-sdk'
async function checkMail() {
await using client = new IMAPClient({
host: 'imap.gmail.com',
port: 993,
secure: true,
auth: { user: 'user@gmail.com', pass: 'password' },
})
await client.connect()
const status = await client.run('STATUS', 'INBOX', { messages: true })
console.log('Unread:', status.messages)
// client.close() called automatically when scope exits
// All sockets, timers, and listeners cleaned up
}
This pattern guarantees cleanup even if an error is thrown:
async function riskyOperation() {
await using client = new IMAPClient(options)
await client.connect()
// Even if this throws, client is still cleaned up
await client.run('SELECT', 'NonExistentFolder')
}
All event listeners are tracked and automatically removed on dispose:
// Internal implementation - listeners never leak
this.listenerTracker.on(socket, 'data', handler)
this.listenerTracker.once(socket, 'error', errorHandler)
// On dispose, ALL tracked listeners are removed
this.listenerTracker.dispose()
All timers are tracked and cleared on dispose:
// Internal implementation - timers never leak
this.timerManager.set('greeting', callback, 30000)
this.timerManager.set('idle', idleCallback, 300000)
// On dispose, ALL tracked timers are cleared
this.timerManager.dispose()
All IMAP commands are available via client.run():
// Mailbox operations
await client.run('LIST', '', '*')
await client.run('SELECT', 'INBOX')
await client.run('EXAMINE', 'INBOX')
await client.run('CREATE', 'NewFolder')
await client.run('DELETE', 'OldFolder')
await client.run('RENAME', 'Old', 'New')
await client.run('SUBSCRIBE', 'Folder')
await client.run('UNSUBSCRIBE', 'Folder')
await client.run('STATUS', 'INBOX', { messages: true, unseen: true })
// Message operations
await client.run('SEARCH', { unseen: true })
await client.run('FETCH', '1:10', { envelope: true, flags: true })
await client.run('STORE', '1:5', '+FLAGS', ['\\Seen'])
await client.run('COPY', '1:10', 'Archive')
await client.run('MOVE', '1:10', 'Trash')
await client.run('APPEND', 'INBOX', messageBuffer, { flags: ['\\Seen'] })
await client.run('EXPUNGE')
// Connection operations
await client.run('NOOP')
await client.run('IDLE')
await client.run('CAPABILITY')
await client.run('ENABLE', ['CONDSTORE'])
await client.run('COMPRESS')
await client.run('ID', { name: 'MyApp', version: '1.0' })
await client.run('NAMESPACE')
await client.run('QUOTA', '')
await client.run('LOGOUT')
Type-safe identifiers prevent mixing up UIDs, sequence numbers, and paths:
import { UID, MailboxPath, ModSeq } from 'imap-sdk'
const uid: UID = UID(12345)
const path: MailboxPath = MailboxPath('INBOX')
const modseq: ModSeq = ModSeq(BigInt(98765))
// Type error: can't pass UID where SequenceNumber expected
await client.run('FETCH', uid, { flags: true }) // Error!
Errors are typed and include server response codes:
import { IMAPSDKError, IMAPSDKErrorCode } from 'imap-sdk'
try {
await client.run('SELECT', 'NonExistent')
} catch (error) {
if (IMAPSDKError.isIMAPSDKError(error)) {
console.log('Code:', error.code)
console.log('Server code:', error.serverResponseCode)
console.log('Message:', error.message)
}
}
For code that prefers explicit error handling:
import { tryAsync, isOk, isErr } from 'imap-sdk'
const result = await tryAsync(() => client.run('SELECT', 'INBOX'))
if (isOk(result)) {
console.log('Messages:', result.value.exists)
} else {
console.log('Error:', result.error.message)
}
client.on('exists', ({ path, count, prevCount }) => {
console.log(`New messages in ${path}: ${count - prevCount}`)
})
client.on('expunge', ({ path, seq }) => {
console.log(`Message ${seq} expunged from ${path}`)
})
client.on('flags', ({ path, seq, flags }) => {
console.log(`Flags changed for ${seq} in ${path}:`, [...flags])
})
client.on('mailboxOpen', (mailbox) => {
console.log('Opened:', mailbox.path)
})
client.on('mailboxClose', (mailbox) => {
console.log('Closed:', mailbox.path)
})
client.on('close', () => {
console.log('Connection closed')
})
client.on('error', (error) => {
console.error('Error:', error)
})
See API.md for the full API reference.
MIT
FAQs
IMAP SDK for Node
We found that imap-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
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.