Summary
In plugins/api-mock-interceptor/src/core/mocker.ts, the createMockXHRResponse method uses setTimeout(applyMock, 0) (line 128) without storing the timeout ID. This means pending mock responses cannot be cancelled when the interceptor is disabled.
Current Behavior
// mocker.ts:128
setTimeout(applyMock, 0); // timeout ID not stored
If disableXHRInterception() is called while applyMock is queued in the event loop, the callback will still fire and attempt to modify XHR state on an object that may no longer be intercepted.
Suggested Fix
Store timeout IDs in a Set and clear them when the interceptor is disabled:
private pendingTimeouts = new Set<ReturnType<typeof setTimeout>>();
// In createMockXHRResponse:
const timeoutId = setTimeout(applyMock, 0);
this.pendingTimeouts.add(timeoutId);
// In disable/cleanup:
this.pendingTimeouts.forEach(id => clearTimeout(id));
this.pendingTimeouts.clear();
Impact
Low in practice — the setTimeout(fn, 0) delay is minimal, so the window for this race condition is very small. However, it could cause unexpected behavior if interceptor state is toggled rapidly.
Related
The same pattern exists in error-boundary-visualizer/src/core/store.ts line 319 where setTimeout(triggerError, simulation.delay) doesn't store the timeout ID for the delayed simulation trigger.
Summary
In
plugins/api-mock-interceptor/src/core/mocker.ts, thecreateMockXHRResponsemethod usessetTimeout(applyMock, 0)(line 128) without storing the timeout ID. This means pending mock responses cannot be cancelled when the interceptor is disabled.Current Behavior
If
disableXHRInterception()is called whileapplyMockis queued in the event loop, the callback will still fire and attempt to modify XHR state on an object that may no longer be intercepted.Suggested Fix
Store timeout IDs in a Set and clear them when the interceptor is disabled:
Impact
Low in practice — the
setTimeout(fn, 0)delay is minimal, so the window for this race condition is very small. However, it could cause unexpected behavior if interceptor state is toggled rapidly.Related
The same pattern exists in
error-boundary-visualizer/src/core/store.tsline 319 wheresetTimeout(triggerError, simulation.delay)doesn't store the timeout ID for the delayed simulation trigger.