Files
rondevu-client/src/api/batcher.ts
Bas van den Aakster 3e55166cea Reorganize src/ directory into feature/domain-based structure
Restructure flat src/ directory (17 files) into organized folders:

Structure:
- src/core/ - Main API (rondevu.ts, offer-pool.ts, types.ts, index.ts)
- src/connections/ - WebRTC connections (base.ts, offerer.ts, answerer.ts, config.ts, events.ts)
- src/api/ - HTTP layer (client.ts, batcher.ts)
- src/crypto/ - Crypto adapters (adapter.ts, node.ts, web.ts)
- src/utils/ - Utilities (async-lock.ts, exponential-backoff.ts, message-buffer.ts)

Changes:
- Move all 17 files to appropriate feature folders
- Update all import paths to reflect new structure
- Update package.json main/types to point to dist/core/index.js
- Preserve git history with git mv

Benefits:
- Clear separation of concerns
- Easier navigation and maintenance
- Better scalability for future features
- Logical grouping of related files

🤖 Generated with Claude Code
2025-12-16 22:51:23 +01:00

158 lines
4.2 KiB
TypeScript

/**
* RPC Batcher - Throttles and batches RPC requests to reduce HTTP overhead
*/
export interface BatcherOptions {
/**
* Maximum number of requests to batch together
* Default: 10
*/
maxBatchSize?: number
/**
* Maximum time to wait before sending a batch (ms)
* Default: 50ms
*/
maxWaitTime?: number
/**
* Minimum time between batches (ms)
* Default: 10ms
*/
throttleInterval?: number
}
interface QueuedRequest {
request: any
resolve: (value: any) => void
reject: (error: Error) => void
}
/**
* Batches and throttles RPC requests to optimize network usage
*
* @example
* ```typescript
* const batcher = new RpcBatcher(
* (requests) => api.rpcBatch(requests),
* { maxBatchSize: 10, maxWaitTime: 50 }
* )
*
* // These will be batched together if called within maxWaitTime
* const result1 = await batcher.add(request1)
* const result2 = await batcher.add(request2)
* const result3 = await batcher.add(request3)
* ```
*/
export class RpcBatcher {
private queue: QueuedRequest[] = []
private batchTimeout: ReturnType<typeof setTimeout> | null = null
private lastBatchTime: number = 0
private options: Required<BatcherOptions>
private sendBatch: (requests: any[]) => Promise<any[]>
constructor(
sendBatch: (requests: any[]) => Promise<any[]>,
options?: BatcherOptions
) {
this.sendBatch = sendBatch
this.options = {
maxBatchSize: options?.maxBatchSize ?? 10,
maxWaitTime: options?.maxWaitTime ?? 50,
throttleInterval: options?.throttleInterval ?? 10,
}
}
/**
* Add an RPC request to the batch queue
* Returns a promise that resolves when the request completes
*/
async add(request: any): Promise<any> {
return new Promise((resolve, reject) => {
this.queue.push({ request, resolve, reject })
// Send immediately if batch is full
if (this.queue.length >= this.options.maxBatchSize) {
this.flush()
return
}
// Schedule batch if not already scheduled
if (!this.batchTimeout) {
this.batchTimeout = setTimeout(() => {
this.flush()
}, this.options.maxWaitTime)
}
})
}
/**
* Flush the queue immediately
*/
async flush(): Promise<void> {
// Clear timeout if set
if (this.batchTimeout) {
clearTimeout(this.batchTimeout)
this.batchTimeout = null
}
// Nothing to flush
if (this.queue.length === 0) {
return
}
// Throttle: wait if we sent a batch too recently
const now = Date.now()
const timeSinceLastBatch = now - this.lastBatchTime
if (timeSinceLastBatch < this.options.throttleInterval) {
const waitTime = this.options.throttleInterval - timeSinceLastBatch
await new Promise(resolve => setTimeout(resolve, waitTime))
}
// Extract requests from queue
const batch = this.queue.splice(0, this.options.maxBatchSize)
const requests = batch.map(item => item.request)
this.lastBatchTime = Date.now()
try {
// Send batch request
const results = await this.sendBatch(requests)
// Resolve individual promises
for (let i = 0; i < batch.length; i++) {
batch[i].resolve(results[i])
}
} catch (error) {
// Reject all promises in batch
for (const item of batch) {
item.reject(error as Error)
}
}
}
/**
* Get current queue size
*/
getQueueSize(): number {
return this.queue.length
}
/**
* Clear the queue without sending
*/
clear(): void {
if (this.batchTimeout) {
clearTimeout(this.batchTimeout)
this.batchTimeout = null
}
// Reject all pending requests
for (const item of this.queue) {
item.reject(new Error('Batch queue cleared'))
}
this.queue = []
}
}