Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions src/http-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ const TARGET_BASE = 'https://www.reservauto.net';
// The extension injects CORS headers + cookies via declarativeNetRequest,
// so we can call the API directly without a proxy.
let extensionDetected = false;
let detectionDone = false;

async function detectExtension(): Promise<boolean> {
try {
const resp = await fetch(
`${TARGET_BASE}/WCF/LSI/LSIBookingServiceV3.svc/GetAvailableVehicles?BranchID=1&LanguageID=2`,
{ method: 'HEAD', mode: 'cors' },
);
return resp.ok;
} catch {
return false;
}
}
const detectionPromise = !import.meta.env.DEV
? (async () => {
try {
Comment on lines +11 to +13

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detectionPromise resolves to void and updates module-level state (extensionDetected/detectionDone). This makes it harder to reason about and requires the extra detectionDone flag. Consider making the promise resolve to the detected boolean (and/or always awaiting the same promise) to reduce shared mutable state and simplify the interceptor logic.

Copilot uses AI. Check for mistakes.
const resp = await fetch(
`${TARGET_BASE}/WCF/LSI/LSIBookingServiceV3.svc/GetAvailableVehicles?BranchID=1&LanguageID=2`,
{ method: 'HEAD', mode: 'cors' },
);
extensionDetected = resp.ok;
} catch {
extensionDetected = false;
}
detectionDone = true;
Comment on lines +13 to +22

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch() can hang for a long time on some networks; since all production requests now await detectionPromise, this can stall every API call indefinitely. Consider adding an explicit timeout/AbortController (and treating timeout as “no extension”) so requests can proceed via the proxy fallback even if detection never completes.

Suggested change
try {
const resp = await fetch(
`${TARGET_BASE}/WCF/LSI/LSIBookingServiceV3.svc/GetAvailableVehicles?BranchID=1&LanguageID=2`,
{ method: 'HEAD', mode: 'cors' },
);
extensionDetected = resp.ok;
} catch {
extensionDetected = false;
}
detectionDone = true;
// Ensure extension detection cannot hang indefinitely; treat timeout as "no extension".
if (typeof AbortController === 'undefined') {
// Fallback for environments without AbortController: behave as before (no explicit timeout).
try {
const resp = await fetch(
`${TARGET_BASE}/WCF/LSI/LSIBookingServiceV3.svc/GetAvailableVehicles?BranchID=1&LanguageID=2`,
{ method: 'HEAD', mode: 'cors' },
);
extensionDetected = resp.ok;
} catch {
extensionDetected = false;
}
detectionDone = true;
return;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const resp = await fetch(
`${TARGET_BASE}/WCF/LSI/LSIBookingServiceV3.svc/GetAvailableVehicles?BranchID=1&LanguageID=2`,
{ method: 'HEAD', mode: 'cors', signal: controller.signal },
);
extensionDetected = resp.ok;
} catch {
// Network errors or aborts (including timeout) are treated as "no extension".
extensionDetected = false;
} finally {
clearTimeout(timeoutId);
detectionDone = true;
}

Copilot uses AI. Check for mistakes.
})()
: Promise.resolve();

// Public API client — proxied through Vite in dev, direct with extension, corsproxy.io as fallback.
const apiClient = axios.create({
Expand All @@ -29,8 +33,10 @@ const apiClient = axios.create({
});

if (!import.meta.env.DEV) {
// In production, use corsproxy.io as default, but skip if extension is detected
apiClient.interceptors.request.use((config) => {
// In production, wait for extension detection then decide proxy vs direct
apiClient.interceptors.request.use(async (config) => {
if (!detectionDone) await detectionPromise;

if (extensionDetected) {
// Extension handles CORS + cookies — call API directly
return config;
Expand All @@ -47,11 +53,6 @@ if (!import.meta.env.DEV) {
config.url = `https://corsproxy.io/?url=${encodeURIComponent(targetUrl.toString())}`;
return config;
});

// Detect extension on startup
detectExtension().then((detected) => {
extensionDetected = detected;
});
}

// Authenticated REST API client — direct to restapifrontoffice
Expand Down
Loading