Skip to content

Commit 37bf091

Browse files
committed
Refactor message types and improve type safety across background, content, and injected scripts
1 parent 9128b83 commit 37bf091

6 files changed

Lines changed: 274 additions & 264 deletions

File tree

src/background/background.ts

Lines changed: 176 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -2,171 +2,210 @@ import "webextension-polyfill";
22
import { StorageManager } from "../shared/storage-manager";
33
import { safeDeserialize } from "../utils/serialization";
44
import type { QueryState } from "../types/storage";
5+
import type { UpdateMessage, QueryActionResult } from "../types/messages";
6+
7+
// Define serialized payload type
8+
interface SerializedPayload {
9+
serialized: string;
10+
usedSuperjson: boolean;
11+
isSerializedPayload: boolean;
12+
}
13+
14+
// Define possible background messages
15+
type BackgroundMessage =
16+
| UpdateMessage
17+
| QueryActionResult
18+
| {
19+
type: "QUERY_ACTION";
20+
inspectedTabId?: number;
21+
[key: string]: unknown;
22+
}
23+
| {
24+
type: "REQUEST_IMMEDIATE_UPDATE";
25+
[key: string]: unknown;
26+
};
527

628
// Handle messages from both content scripts and DevTools
7-
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
8-
// Handle DevTools messages (have inspectedTabId but no sender.tab.id)
9-
if (message.inspectedTabId && !sender.tab?.id) {
10-
// Forward DevTools actions to content script of the specified tab
11-
if (message.type === "QUERY_ACTION") {
12-
chrome.tabs
13-
.sendMessage(message.inspectedTabId, message)
14-
.then(() => {
15-
sendResponse({ received: true });
16-
})
17-
.catch((error) => {
18-
console.warn("Failed to send message to content script:", error);
19-
sendResponse({ received: false, error: error.message });
20-
});
29+
chrome.runtime.onMessage.addListener(
30+
(message: BackgroundMessage, sender, sendResponse) => {
31+
// Handle DevTools messages (have inspectedTabId but no sender.tab.id)
32+
if (
33+
"inspectedTabId" in message &&
34+
message.inspectedTabId &&
35+
!sender.tab?.id
36+
) {
37+
// Forward DevTools actions to content script of the specified tab
38+
if (message.type === "QUERY_ACTION") {
39+
chrome.tabs
40+
.sendMessage(message.inspectedTabId, message)
41+
.then(() => {
42+
sendResponse({ received: true });
43+
})
44+
.catch((error) => {
45+
console.warn("Failed to send message to content script:", error);
46+
sendResponse({ received: false, error: error.message });
47+
});
48+
return true;
49+
}
50+
51+
// Handle other DevTools messages
52+
sendResponse({ received: true });
2153
return true;
2254
}
2355

24-
// Handle other DevTools messages
25-
sendResponse({ received: true });
26-
return true;
27-
}
56+
// Handle Content Script messages (have sender.tab.id)
57+
const tabId = sender.tab?.id;
58+
if (tabId) {
59+
if (message.type === "UPDATE_QUERY_STATE") {
60+
// Process and deserialize payload before storing
61+
const processedPayload = { ...message.payload };
2862

29-
// Handle Content Script messages (have sender.tab.id)
30-
const tabId = sender.tab?.id;
31-
if (tabId) {
32-
if (message.type === "UPDATE_QUERY_STATE") {
33-
// Process and deserialize payload before storing
34-
const processedPayload = { ...message.payload };
35-
36-
// Deserialize queries if they are serialized
37-
if (processedPayload.queries) {
38-
try {
39-
if (
40-
typeof processedPayload.queries === "object" &&
41-
processedPayload.queries.isSerializedPayload
42-
) {
43-
const deserializedQueries = safeDeserialize(
44-
processedPayload.queries.serialized,
45-
);
46-
if (Array.isArray(deserializedQueries)) {
47-
processedPayload.queries = deserializedQueries;
63+
// Deserialize queries if they are serialized
64+
if (processedPayload.queries) {
65+
try {
66+
if (
67+
typeof processedPayload.queries === "object" &&
68+
processedPayload.queries !== null &&
69+
"isSerializedPayload" in processedPayload.queries &&
70+
(processedPayload.queries as SerializedPayload)
71+
.isSerializedPayload
72+
) {
73+
const serializedData =
74+
processedPayload.queries as SerializedPayload;
75+
const deserializedQueries = safeDeserialize(
76+
serializedData.serialized,
77+
);
78+
if (Array.isArray(deserializedQueries)) {
79+
processedPayload.queries = deserializedQueries;
80+
}
4881
}
82+
} catch (error) {
83+
console.error(
84+
"Failed to deserialize queries in background script:",
85+
error,
86+
);
87+
// Keep original payload if deserialization fails
4988
}
50-
} catch (error) {
51-
console.error(
52-
"Failed to deserialize queries in background script:",
53-
error,
54-
);
55-
// Keep original payload if deserialization fails
5689
}
57-
}
5890

59-
// Deserialize mutations if they are serialized
60-
if (processedPayload.mutations) {
61-
try {
62-
if (
63-
typeof processedPayload.mutations === "object" &&
64-
processedPayload.mutations.isSerializedPayload
65-
) {
66-
const deserializedMutations = safeDeserialize(
67-
processedPayload.mutations.serialized,
68-
);
69-
if (Array.isArray(deserializedMutations)) {
70-
processedPayload.mutations = deserializedMutations;
91+
// Deserialize mutations if they are serialized
92+
if (processedPayload.mutations) {
93+
try {
94+
if (
95+
typeof processedPayload.mutations === "object" &&
96+
processedPayload.mutations !== null &&
97+
"isSerializedPayload" in processedPayload.mutations &&
98+
(processedPayload.mutations as SerializedPayload)
99+
.isSerializedPayload
100+
) {
101+
const serializedData =
102+
processedPayload.mutations as SerializedPayload;
103+
const deserializedMutations = safeDeserialize(
104+
serializedData.serialized,
105+
);
106+
if (Array.isArray(deserializedMutations)) {
107+
processedPayload.mutations = deserializedMutations;
108+
}
71109
}
110+
} catch (error) {
111+
console.error(
112+
"Failed to deserialize mutations in background script:",
113+
error,
114+
);
115+
// Keep original payload if deserialization fails
72116
}
73-
} catch (error) {
74-
console.error(
75-
"Failed to deserialize mutations in background script:",
76-
error,
77-
);
78-
// Keep original payload if deserialization fails
79117
}
80-
}
81118

82-
// Update storage with the processed (deserialized) state
83-
// Only include fields that are actually provided to avoid overwriting
84-
const updateData: Partial<QueryState> = { tabId: tabId };
119+
// Update storage with the processed (deserialized) state
120+
// Only include fields that are actually provided to avoid overwriting
121+
const updateData: Partial<QueryState> = { tabId: tabId };
85122

86-
if (processedPayload.queries !== undefined) {
87-
updateData.queries = processedPayload.queries as QueryState["queries"];
88-
}
89-
if (processedPayload.mutations !== undefined) {
90-
updateData.mutations =
91-
processedPayload.mutations as QueryState["mutations"];
92-
}
93-
if (processedPayload.tanStackQueryDetected !== undefined) {
94-
updateData.tanStackQueryDetected =
95-
processedPayload.tanStackQueryDetected;
123+
if (processedPayload.queries !== undefined) {
124+
updateData.queries =
125+
processedPayload.queries as QueryState["queries"];
126+
}
127+
if (processedPayload.mutations !== undefined) {
128+
updateData.mutations =
129+
processedPayload.mutations as QueryState["mutations"];
130+
}
131+
if (processedPayload.tanStackQueryDetected !== undefined) {
132+
updateData.tanStackQueryDetected =
133+
processedPayload.tanStackQueryDetected;
134+
}
135+
136+
StorageManager.updatePartialState(updateData)
137+
.then(() => {
138+
sendResponse({ received: true });
139+
})
140+
.catch((error) => {
141+
console.error("Failed to update storage:", error);
142+
sendResponse({ received: false, error: error.message });
143+
});
144+
return true;
96145
}
97146

98-
StorageManager.updatePartialState(updateData)
99-
.then(() => {
100-
sendResponse({ received: true });
101-
})
102-
.catch((error) => {
103-
console.error("Failed to update storage:", error);
104-
sendResponse({ received: false, error: error.message });
105-
});
106-
return true;
107-
}
147+
// Handle action results from content scripts and forward to DevTools
148+
if (message.type === "QUERY_ACTION_RESULT") {
149+
// Update artificial states in storage for TRIGGER_LOADING and TRIGGER_ERROR
150+
if (
151+
message.success &&
152+
(message.action === "TRIGGER_LOADING" ||
153+
message.action === "TRIGGER_ERROR") &&
154+
message.queryHash
155+
) {
156+
StorageManager.getState()
157+
.then((currentState) => {
158+
const artificialStates = {
159+
...(currentState.artificialStates || {}),
160+
};
161+
const queryHash = message.queryHash as string;
108162

109-
// Handle action results from content scripts and forward to DevTools
110-
if (message.type === "QUERY_ACTION_RESULT") {
111-
// Update artificial states in storage for TRIGGER_LOADING and TRIGGER_ERROR
112-
if (
113-
message.success &&
114-
(message.action === "TRIGGER_LOADING" ||
115-
message.action === "TRIGGER_ERROR") &&
116-
message.queryHash
117-
) {
118-
StorageManager.getState()
119-
.then((currentState) => {
120-
const artificialStates = {
121-
...(currentState.artificialStates || {}),
122-
};
123-
const queryHash = message.queryHash as string;
124-
125-
if (message.action === "TRIGGER_LOADING") {
126-
if (artificialStates[queryHash] === "loading") {
127-
// Cancel loading state
128-
delete artificialStates[queryHash];
129-
} else {
130-
// Start loading state
131-
artificialStates[queryHash] = "loading";
132-
}
133-
} else if (message.action === "TRIGGER_ERROR") {
134-
if (artificialStates[queryHash] === "error") {
135-
// Cancel error state
136-
delete artificialStates[queryHash];
137-
} else {
138-
// Start error state
139-
artificialStates[queryHash] = "error";
163+
if (message.action === "TRIGGER_LOADING") {
164+
if (artificialStates[queryHash] === "loading") {
165+
// Cancel loading state
166+
delete artificialStates[queryHash];
167+
} else {
168+
// Start loading state
169+
artificialStates[queryHash] = "loading";
170+
}
171+
} else if (message.action === "TRIGGER_ERROR") {
172+
if (artificialStates[queryHash] === "error") {
173+
// Cancel error state
174+
delete artificialStates[queryHash];
175+
} else {
176+
// Start error state
177+
artificialStates[queryHash] = "error";
178+
}
140179
}
141-
}
142180

143-
// Update storage with new artificial states
144-
return StorageManager.updatePartialState({
145-
artificialStates,
146-
lastUpdated: Date.now(),
181+
// Update storage with new artificial states
182+
return StorageManager.updatePartialState({
183+
artificialStates,
184+
lastUpdated: Date.now(),
185+
});
186+
})
187+
.catch((error) => {
188+
console.error("Failed to update artificial states:", error);
147189
});
148-
})
149-
.catch((error) => {
150-
console.error("Failed to update artificial states:", error);
151-
});
190+
}
191+
192+
// Forward to DevTools - they listen for these via chrome.runtime.onMessage
193+
chrome.runtime.sendMessage(message).catch(() => {
194+
// DevTools might not be open, that's fine
195+
});
196+
sendResponse({ received: true });
197+
return true;
152198
}
153199

154-
// Forward to DevTools - they listen for these via chrome.runtime.onMessage
155-
chrome.runtime.sendMessage(message).catch(() => {
156-
// DevTools might not be open, that's fine
157-
});
158200
sendResponse({ received: true });
159201
return true;
160202
}
161203

162-
sendResponse({ received: true });
204+
// Fallback for unhandled messages
205+
sendResponse({ received: false, error: "Invalid message source" });
163206
return true;
164-
}
165-
166-
// Fallback for unhandled messages
167-
sendResponse({ received: false, error: "Invalid message source" });
168-
return true;
169-
});
207+
},
208+
);
170209

171210
// Clean up storage when tabs are closed
172211
chrome.tabs.onRemoved.addListener(async (tabId) => {

src/content/content.ts

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,9 @@
11
// Content script - bridges between injected script and extension storage
2-
3-
// Query action message types
4-
interface QueryActionMessage {
5-
type: "QUERY_ACTION";
6-
action:
7-
| "INVALIDATE"
8-
| "REFETCH"
9-
| "REMOVE"
10-
| "RESET"
11-
| "TRIGGER_LOADING"
12-
| "TRIGGER_ERROR";
13-
queryKey?: readonly unknown[];
14-
queryHash?: string;
15-
}
16-
17-
// Action result message
18-
interface QueryActionResult {
19-
type: "QUERY_ACTION_RESULT";
20-
action:
21-
| "INVALIDATE"
22-
| "REFETCH"
23-
| "REMOVE"
24-
| "RESET"
25-
| "TRIGGER_LOADING"
26-
| "TRIGGER_ERROR";
27-
queryKey?: readonly unknown[];
28-
queryHash?: string;
29-
success: boolean;
30-
error?: string;
31-
}
32-
33-
// Update message for storage
34-
interface UpdateMessage {
35-
type: "UPDATE_QUERY_STATE";
36-
payload: {
37-
queries?: unknown[];
38-
mutations?: unknown[];
39-
tanStackQueryDetected?: boolean;
40-
};
41-
}
2+
import type {
3+
QueryActionMessage,
4+
QueryActionResult,
5+
UpdateMessage,
6+
} from "../types/messages";
427

438
// Send message to background script for storage update
449
function sendToBackground(message: UpdateMessage | QueryActionResult) {

0 commit comments

Comments
 (0)