Files
webrequest/ts/utils/deduplicator.ts

106 lines
2.4 KiB
TypeScript
Raw Normal View History

/**
* Request deduplication system
* Prevents multiple simultaneous identical requests
*/
import * as plugins from '../webrequest.plugins.js';
export class RequestDeduplicator {
private inFlightRequests: Map<
string,
plugins.smartpromise.Deferred<Response>
> = new Map();
/**
* Generate a deduplication key from a request
*/
public generateKey(request: Request): string {
// Use URL + method as the base key
const url = request.url;
const method = request.method;
// For GET/HEAD requests, just use URL + method
if (method === 'GET' || method === 'HEAD') {
return `${method}:${url}`;
}
// For other methods, we can't deduplicate as easily
// (body might be different)
// Use a timestamp to make it unique
return `${method}:${url}:${Date.now()}`;
}
/**
* Execute a request with deduplication
*/
public async execute(
key: string,
executeFn: () => Promise<Response>,
): Promise<{ response: Response; wasDeduplicated: boolean }> {
// Check if request is already in flight
const existingDeferred = this.inFlightRequests.get(key);
if (existingDeferred) {
// Wait for the existing request to complete
const response = await existingDeferred.promise;
// Clone the response so it can be used multiple times
return {
response: response.clone(),
wasDeduplicated: true,
};
}
// Create a new deferred for this request
const deferred = plugins.smartpromise.defer<Response>();
this.inFlightRequests.set(key, deferred);
try {
// Execute the request
const response = await executeFn();
// Resolve the deferred
deferred.resolve(response);
// Clean up
this.inFlightRequests.delete(key);
// Return the original response
return {
response,
wasDeduplicated: false,
};
} catch (error) {
// Reject the deferred
deferred.reject(error);
// Clean up
this.inFlightRequests.delete(key);
// Re-throw the error
throw error;
}
}
/**
* Check if a request is currently in flight
*/
public isInFlight(key: string): boolean {
return this.inFlightRequests.has(key);
}
/**
* Get the number of in-flight requests
*/
public getInFlightCount(): number {
return this.inFlightRequests.size;
}
/**
* Clear all in-flight requests
*/
public clear(): void {
this.inFlightRequests.clear();
}
}