@fairfox/polly 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/cli/polly.ts +564 -0
- package/dist/background/api-client.d.ts +7 -0
- package/dist/background/context-menu.d.ts +7 -0
- package/dist/background/index.d.ts +31 -0
- package/dist/background/index.js +1309 -0
- package/dist/background/index.js.map +25 -0
- package/dist/background/log-store.d.ts +22 -0
- package/dist/background/message-router.d.ts +30 -0
- package/dist/background/message-router.js +1300 -0
- package/dist/background/message-router.js.map +24 -0
- package/dist/background/offscreen-manager.d.ts +10 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1471 -0
- package/dist/index.js.map +27 -0
- package/dist/shared/adapters/chrome/context-menus.chrome.d.ts +8 -0
- package/dist/shared/adapters/chrome/offscreen.chrome.d.ts +6 -0
- package/dist/shared/adapters/chrome/runtime.chrome.d.ts +13 -0
- package/dist/shared/adapters/chrome/storage.chrome.d.ts +8 -0
- package/dist/shared/adapters/chrome/tabs.chrome.d.ts +16 -0
- package/dist/shared/adapters/chrome/window.chrome.d.ts +6 -0
- package/dist/shared/adapters/context-menus.adapter.d.ts +22 -0
- package/dist/shared/adapters/fetch.adapter.d.ts +6 -0
- package/dist/shared/adapters/index.d.ts +34 -0
- package/dist/shared/adapters/index.js +298 -0
- package/dist/shared/adapters/index.js.map +18 -0
- package/dist/shared/adapters/logger.adapter.d.ts +44 -0
- package/dist/shared/adapters/offscreen.adapter.d.ts +20 -0
- package/dist/shared/adapters/runtime.adapter.d.ts +66 -0
- package/dist/shared/adapters/storage.adapter.d.ts +29 -0
- package/dist/shared/adapters/tabs.adapter.d.ts +39 -0
- package/dist/shared/adapters/window.adapter.d.ts +14 -0
- package/dist/shared/lib/context-helpers.d.ts +64 -0
- package/dist/shared/lib/context-helpers.js +1086 -0
- package/dist/shared/lib/context-helpers.js.map +24 -0
- package/dist/shared/lib/context-specific-helpers.d.ts +160 -0
- package/dist/shared/lib/errors.d.ts +67 -0
- package/dist/shared/lib/errors.js +94 -0
- package/dist/shared/lib/errors.js.map +10 -0
- package/dist/shared/lib/handler-execution-tracker.d.ts +24 -0
- package/dist/shared/lib/message-bus.d.ts +233 -0
- package/dist/shared/lib/message-bus.js +1033 -0
- package/dist/shared/lib/message-bus.js.map +23 -0
- package/dist/shared/lib/state.d.ts +102 -0
- package/dist/shared/lib/state.js +1265 -0
- package/dist/shared/lib/state.js.map +24 -0
- package/dist/shared/lib/test-helpers.d.ts +133 -0
- package/dist/shared/lib/test-helpers.js +136 -0
- package/dist/shared/lib/test-helpers.js.map +10 -0
- package/dist/shared/state/app-state.d.ts +8 -0
- package/dist/shared/state/app-state.js +1272 -0
- package/dist/shared/state/app-state.js.map +25 -0
- package/dist/shared/types/messages.d.ts +341 -0
- package/dist/shared/types/messages.js +25 -0
- package/dist/shared/types/messages.js.map +10 -0
- package/package.json +110 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// src/shared/adapters/chrome/context-menus.chrome.ts
|
|
2
|
+
class ChromeContextMenusAdapter {
|
|
3
|
+
async create(createProperties) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
chrome.contextMenus.create(createProperties, () => {
|
|
6
|
+
if (chrome.runtime.lastError) {
|
|
7
|
+
reject(new Error(chrome.runtime.lastError.message));
|
|
8
|
+
} else {
|
|
9
|
+
resolve();
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
async update(id, updateProperties) {
|
|
15
|
+
await chrome.contextMenus.update(id, updateProperties);
|
|
16
|
+
}
|
|
17
|
+
async remove(id) {
|
|
18
|
+
await chrome.contextMenus.remove(id);
|
|
19
|
+
}
|
|
20
|
+
async removeAll() {
|
|
21
|
+
await chrome.contextMenus.removeAll();
|
|
22
|
+
}
|
|
23
|
+
onClicked(callback) {
|
|
24
|
+
chrome.contextMenus.onClicked.addListener(callback);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/shared/adapters/chrome/offscreen.chrome.ts
|
|
29
|
+
class ChromeOffscreenAdapter {
|
|
30
|
+
async createDocument(parameters) {
|
|
31
|
+
await chrome.offscreen.createDocument({
|
|
32
|
+
url: parameters.url,
|
|
33
|
+
reasons: parameters.reasons,
|
|
34
|
+
justification: parameters.justification
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async closeDocument() {
|
|
38
|
+
await chrome.offscreen.closeDocument();
|
|
39
|
+
}
|
|
40
|
+
async hasDocument() {
|
|
41
|
+
const existingContexts = await chrome.runtime.getContexts({
|
|
42
|
+
contextTypes: ["OFFSCREEN_DOCUMENT"]
|
|
43
|
+
});
|
|
44
|
+
return existingContexts.length > 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/shared/adapters/chrome/runtime.chrome.ts
|
|
49
|
+
class ChromeRuntimeAdapter {
|
|
50
|
+
messageListeners = new Map;
|
|
51
|
+
static listenerCount = 0;
|
|
52
|
+
sendMessage(message) {
|
|
53
|
+
return chrome.runtime.sendMessage(message);
|
|
54
|
+
}
|
|
55
|
+
onMessage(callback) {
|
|
56
|
+
const wrappedCallback = (message, sender, sendResponse) => {
|
|
57
|
+
const mappedSender = {
|
|
58
|
+
...sender.tab && {
|
|
59
|
+
tab: {
|
|
60
|
+
id: sender.tab.id ?? 0,
|
|
61
|
+
url: sender.tab.url ?? "",
|
|
62
|
+
title: sender.tab.title ?? ""
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
...sender.frameId !== undefined && { frameId: sender.frameId },
|
|
66
|
+
...sender.url && { url: sender.url }
|
|
67
|
+
};
|
|
68
|
+
return callback(message, mappedSender, sendResponse);
|
|
69
|
+
};
|
|
70
|
+
this.messageListeners.set(callback, wrappedCallback);
|
|
71
|
+
chrome.runtime.onMessage.addListener(wrappedCallback);
|
|
72
|
+
ChromeRuntimeAdapter.listenerCount++;
|
|
73
|
+
if (ChromeRuntimeAdapter.listenerCount > 1) {
|
|
74
|
+
console.warn(`⚠️ WARNING: ${ChromeRuntimeAdapter.listenerCount} chrome.runtime.onMessage listeners registered!
|
|
75
|
+
|
|
76
|
+
Multiple listeners will cause message handlers to execute multiple times.
|
|
77
|
+
This is usually caused by:
|
|
78
|
+
1. Creating both MessageBus and MessageRouter with separate listeners
|
|
79
|
+
2. Calling createBackground() multiple times
|
|
80
|
+
3. Calling getMessageBus('background') after createBackground()
|
|
81
|
+
|
|
82
|
+
Fix: In background scripts, use createBackground() ONCE at startup.
|
|
83
|
+
Do not call getMessageBus('background') separately.`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
removeMessageListener(callback) {
|
|
87
|
+
const wrappedCallback = this.messageListeners.get(callback);
|
|
88
|
+
if (wrappedCallback) {
|
|
89
|
+
chrome.runtime.onMessage.removeListener(wrappedCallback);
|
|
90
|
+
this.messageListeners.delete(callback);
|
|
91
|
+
ChromeRuntimeAdapter.listenerCount = Math.max(0, ChromeRuntimeAdapter.listenerCount - 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
connect(name) {
|
|
95
|
+
const port = chrome.runtime.connect({ name });
|
|
96
|
+
return new ChromePortAdapter(port);
|
|
97
|
+
}
|
|
98
|
+
onConnect(callback) {
|
|
99
|
+
chrome.runtime.onConnect.addListener((port) => {
|
|
100
|
+
callback(new ChromePortAdapter(port));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
getURL(path) {
|
|
104
|
+
return chrome.runtime.getURL(path);
|
|
105
|
+
}
|
|
106
|
+
getId() {
|
|
107
|
+
return chrome.runtime.id;
|
|
108
|
+
}
|
|
109
|
+
openOptionsPage() {
|
|
110
|
+
chrome.runtime.openOptionsPage();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class ChromePortAdapter {
|
|
115
|
+
port;
|
|
116
|
+
listeners = {
|
|
117
|
+
message: new Set,
|
|
118
|
+
disconnect: new Set
|
|
119
|
+
};
|
|
120
|
+
constructor(port) {
|
|
121
|
+
this.port = port;
|
|
122
|
+
this.port.onMessage.addListener((message) => {
|
|
123
|
+
for (const callback of this.listeners.message) {
|
|
124
|
+
callback(message);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
this.port.onDisconnect.addListener(() => {
|
|
128
|
+
for (const callback of this.listeners.disconnect) {
|
|
129
|
+
callback();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
get name() {
|
|
134
|
+
return this.port.name;
|
|
135
|
+
}
|
|
136
|
+
postMessage(message) {
|
|
137
|
+
this.port.postMessage(message);
|
|
138
|
+
}
|
|
139
|
+
onMessage(callback) {
|
|
140
|
+
this.listeners.message.add(callback);
|
|
141
|
+
}
|
|
142
|
+
onDisconnect(callback) {
|
|
143
|
+
this.listeners.disconnect.add(callback);
|
|
144
|
+
}
|
|
145
|
+
disconnect() {
|
|
146
|
+
this.port.disconnect();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/shared/adapters/chrome/storage.chrome.ts
|
|
151
|
+
class ChromeStorageAdapter {
|
|
152
|
+
async get(keys) {
|
|
153
|
+
return await chrome.storage.local.get(keys);
|
|
154
|
+
}
|
|
155
|
+
async set(items) {
|
|
156
|
+
await chrome.storage.local.set(items);
|
|
157
|
+
}
|
|
158
|
+
async remove(keys) {
|
|
159
|
+
await chrome.storage.local.remove(keys);
|
|
160
|
+
}
|
|
161
|
+
async clear() {
|
|
162
|
+
await chrome.storage.local.clear();
|
|
163
|
+
}
|
|
164
|
+
onChanged(callback) {
|
|
165
|
+
chrome.storage.onChanged.addListener((changes, areaName) => {
|
|
166
|
+
const mappedChanges = {};
|
|
167
|
+
for (const [key, change] of Object.entries(changes)) {
|
|
168
|
+
mappedChanges[key] = {
|
|
169
|
+
oldValue: change.oldValue,
|
|
170
|
+
newValue: change.newValue
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
callback(mappedChanges, areaName);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/shared/adapters/chrome/tabs.chrome.ts
|
|
179
|
+
class ChromeTabsAdapter {
|
|
180
|
+
async query(queryInfo) {
|
|
181
|
+
return chrome.tabs.query(queryInfo);
|
|
182
|
+
}
|
|
183
|
+
async get(tabId) {
|
|
184
|
+
return chrome.tabs.get(tabId);
|
|
185
|
+
}
|
|
186
|
+
async sendMessage(tabId, message) {
|
|
187
|
+
return chrome.tabs.sendMessage(tabId, message);
|
|
188
|
+
}
|
|
189
|
+
async reload(tabId, reloadProperties) {
|
|
190
|
+
await chrome.tabs.reload(tabId, reloadProperties);
|
|
191
|
+
}
|
|
192
|
+
onRemoved(callback) {
|
|
193
|
+
chrome.tabs.onRemoved.addListener(callback);
|
|
194
|
+
}
|
|
195
|
+
onUpdated(callback) {
|
|
196
|
+
chrome.tabs.onUpdated.addListener(callback);
|
|
197
|
+
}
|
|
198
|
+
onActivated(callback) {
|
|
199
|
+
chrome.tabs.onActivated.addListener(callback);
|
|
200
|
+
}
|
|
201
|
+
async create(createProperties) {
|
|
202
|
+
return chrome.tabs.create(createProperties);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/shared/adapters/chrome/window.chrome.ts
|
|
207
|
+
class ChromeWindowAdapter {
|
|
208
|
+
postMessage(message, targetOrigin) {
|
|
209
|
+
window.postMessage(message, targetOrigin);
|
|
210
|
+
}
|
|
211
|
+
addEventListener(type, listener) {
|
|
212
|
+
window.addEventListener(type, listener);
|
|
213
|
+
}
|
|
214
|
+
removeEventListener(type, listener) {
|
|
215
|
+
window.removeEventListener(type, listener);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/shared/adapters/fetch.adapter.ts
|
|
220
|
+
class BrowserFetchAdapter {
|
|
221
|
+
fetch(input, init) {
|
|
222
|
+
return fetch(input, init);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/shared/adapters/logger.adapter.ts
|
|
227
|
+
class MessageLoggerAdapter {
|
|
228
|
+
runtime;
|
|
229
|
+
sourceContext;
|
|
230
|
+
options;
|
|
231
|
+
constructor(runtime, sourceContext, options) {
|
|
232
|
+
this.runtime = runtime;
|
|
233
|
+
this.sourceContext = sourceContext;
|
|
234
|
+
this.options = options;
|
|
235
|
+
}
|
|
236
|
+
debug(message, context) {
|
|
237
|
+
this.sendLog("debug", message, context);
|
|
238
|
+
}
|
|
239
|
+
info(message, context) {
|
|
240
|
+
this.sendLog("info", message, context);
|
|
241
|
+
}
|
|
242
|
+
warn(message, context) {
|
|
243
|
+
this.sendLog("warn", message, context);
|
|
244
|
+
}
|
|
245
|
+
error(message, error, context) {
|
|
246
|
+
this.sendLog("error", message, context, error);
|
|
247
|
+
}
|
|
248
|
+
log(level, message, context) {
|
|
249
|
+
this.sendLog(level, message, context);
|
|
250
|
+
}
|
|
251
|
+
sendLog(level, message, context, error) {
|
|
252
|
+
if (this.options?.consoleMirror) {
|
|
253
|
+
const consoleMethod = console[level] || console.log;
|
|
254
|
+
consoleMethod(`[${this.sourceContext}]`, message, context || "", error || "");
|
|
255
|
+
}
|
|
256
|
+
const logMessage = {
|
|
257
|
+
type: "LOG",
|
|
258
|
+
level,
|
|
259
|
+
message,
|
|
260
|
+
context,
|
|
261
|
+
error: error?.message,
|
|
262
|
+
stack: error?.stack,
|
|
263
|
+
source: this.sourceContext,
|
|
264
|
+
timestamp: Date.now()
|
|
265
|
+
};
|
|
266
|
+
this.runtime.sendMessage(logMessage).catch((sendError) => {
|
|
267
|
+
if (this.options?.fallbackToConsole !== false) {
|
|
268
|
+
console[level](`[${this.sourceContext}] ${message}`, context || "", error || "");
|
|
269
|
+
console.warn("Failed to send log message:", sendError);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/shared/adapters/index.ts
|
|
276
|
+
function createChromeAdapters(context, options) {
|
|
277
|
+
const runtime2 = new ChromeRuntimeAdapter;
|
|
278
|
+
return {
|
|
279
|
+
runtime: runtime2,
|
|
280
|
+
storage: new ChromeStorageAdapter,
|
|
281
|
+
tabs: new ChromeTabsAdapter,
|
|
282
|
+
window: new ChromeWindowAdapter,
|
|
283
|
+
offscreen: new ChromeOffscreenAdapter,
|
|
284
|
+
contextMenus: new ChromeContextMenusAdapter,
|
|
285
|
+
fetch: new BrowserFetchAdapter,
|
|
286
|
+
logger: new MessageLoggerAdapter(runtime2, context, {
|
|
287
|
+
...options?.consoleMirror !== undefined && { consoleMirror: options.consoleMirror },
|
|
288
|
+
fallbackToConsole: true
|
|
289
|
+
})
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
export {
|
|
293
|
+
createChromeAdapters,
|
|
294
|
+
MessageLoggerAdapter,
|
|
295
|
+
BrowserFetchAdapter
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
//# debugId=88CAE80DB6C0B30564756E2164756E21
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/shared/adapters/chrome/context-menus.chrome.ts", "../src/shared/adapters/chrome/offscreen.chrome.ts", "../src/shared/adapters/chrome/runtime.chrome.ts", "../src/shared/adapters/chrome/storage.chrome.ts", "../src/shared/adapters/chrome/tabs.chrome.ts", "../src/shared/adapters/chrome/window.chrome.ts", "../src/shared/adapters/fetch.adapter.ts", "../src/shared/adapters/logger.adapter.ts", "../src/shared/adapters/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"// Chrome context menus adapter implementation\n\nimport type { ContextMenusAdapter } from \"../context-menus.adapter\";\n\nexport class ChromeContextMenusAdapter implements ContextMenusAdapter {\n async create(createProperties: chrome.contextMenus.CreateProperties): Promise<void> {\n return new Promise((resolve, reject) => {\n chrome.contextMenus.create(createProperties, () => {\n if (chrome.runtime.lastError) {\n reject(new Error(chrome.runtime.lastError.message));\n } else {\n resolve();\n }\n });\n });\n }\n\n async update(id: string, updateProperties: chrome.contextMenus.UpdateProperties): Promise<void> {\n await chrome.contextMenus.update(id, updateProperties);\n }\n\n async remove(id: string): Promise<void> {\n await chrome.contextMenus.remove(id);\n }\n\n async removeAll(): Promise<void> {\n await chrome.contextMenus.removeAll();\n }\n\n onClicked(\n callback: (info: chrome.contextMenus.OnClickData, tab?: chrome.tabs.Tab) => void\n ): void {\n chrome.contextMenus.onClicked.addListener(callback);\n }\n}\n",
|
|
6
|
+
"// Chrome offscreen adapter implementation\n\nimport type { CreateOffscreenDocumentParameters, OffscreenAdapter } from \"../offscreen.adapter\";\n\nexport class ChromeOffscreenAdapter implements OffscreenAdapter {\n async createDocument(parameters: CreateOffscreenDocumentParameters): Promise<void> {\n await chrome.offscreen.createDocument({\n url: parameters.url,\n reasons: parameters.reasons as chrome.offscreen.Reason[],\n justification: parameters.justification,\n });\n }\n\n async closeDocument(): Promise<void> {\n await chrome.offscreen.closeDocument();\n }\n\n async hasDocument(): Promise<boolean> {\n // Chrome doesn't provide a direct API, so we query for offscreen contexts\n const existingContexts = await chrome.runtime.getContexts({\n contextTypes: [\"OFFSCREEN_DOCUMENT\" as chrome.runtime.ContextType],\n });\n return existingContexts.length > 0;\n }\n}\n",
|
|
7
|
+
"// Chrome runtime adapter implementation\n\nimport type { MessageSender, PortAdapter, RuntimeAdapter } from \"../runtime.adapter\";\n\ntype MessageListener = (\n message: unknown,\n sender: MessageSender,\n sendResponse: (response: unknown) => void\n) => undefined | boolean;\n\ntype ChromeMessageListener = (\n message: unknown,\n sender: chrome.runtime.MessageSender,\n sendResponse: (response?: unknown) => void\n) => undefined | boolean;\n\nexport class ChromeRuntimeAdapter implements RuntimeAdapter {\n private messageListeners = new Map<MessageListener, ChromeMessageListener>();\n private static listenerCount = 0;\n\n sendMessage<T>(message: T): Promise<unknown> {\n return chrome.runtime.sendMessage(message);\n }\n\n onMessage(\n callback: (\n message: unknown,\n sender: MessageSender,\n sendResponse: (response: unknown) => void\n ) => undefined | boolean\n ): void {\n const wrappedCallback = (\n message: unknown,\n sender: chrome.runtime.MessageSender,\n sendResponse: (response?: unknown) => void\n ) => {\n const mappedSender: MessageSender = {\n ...(sender.tab && {\n tab: {\n id: sender.tab.id ?? 0,\n url: sender.tab.url ?? \"\",\n title: sender.tab.title ?? \"\",\n },\n }),\n ...(sender.frameId !== undefined && { frameId: sender.frameId }),\n ...(sender.url && { url: sender.url }),\n };\n return callback(message, mappedSender, sendResponse);\n };\n\n this.messageListeners.set(callback, wrappedCallback);\n // Chrome's listener signature uses void | boolean, ours uses undefined | boolean\n // These are compatible - undefined is assignable to void for return types\n chrome.runtime.onMessage.addListener(\n wrappedCallback as (\n message: unknown,\n sender: chrome.runtime.MessageSender,\n sendResponse: (response?: unknown) => void\n ) => undefined | boolean\n );\n\n // Track listener count and warn if multiple listeners registered\n ChromeRuntimeAdapter.listenerCount++;\n\n if (ChromeRuntimeAdapter.listenerCount > 1) {\n console.warn(\n `⚠️ WARNING: ${ChromeRuntimeAdapter.listenerCount} chrome.runtime.onMessage listeners registered!\\n\\nMultiple listeners will cause message handlers to execute multiple times.\\nThis is usually caused by:\\n 1. Creating both MessageBus and MessageRouter with separate listeners\\n 2. Calling createBackground() multiple times\\n 3. Calling getMessageBus('background') after createBackground()\\n\\nFix: In background scripts, use createBackground() ONCE at startup.\\nDo not call getMessageBus('background') separately.`\n );\n }\n }\n\n removeMessageListener(\n callback: (\n message: unknown,\n sender: MessageSender,\n sendResponse: (response: unknown) => void\n ) => undefined | boolean\n ): void {\n const wrappedCallback = this.messageListeners.get(callback);\n if (wrappedCallback) {\n // Type-safe cast: wrappedCallback is stored with compatible signature\n chrome.runtime.onMessage.removeListener(\n wrappedCallback as (\n message: unknown,\n sender: chrome.runtime.MessageSender,\n sendResponse: (response?: unknown) => void\n ) => undefined | boolean\n );\n this.messageListeners.delete(callback);\n\n // Decrement listener count\n ChromeRuntimeAdapter.listenerCount = Math.max(0, ChromeRuntimeAdapter.listenerCount - 1);\n }\n }\n\n connect(name: string): PortAdapter {\n const port = chrome.runtime.connect({ name });\n return new ChromePortAdapter(port);\n }\n\n onConnect(callback: (port: PortAdapter) => void): void {\n chrome.runtime.onConnect.addListener((port) => {\n callback(new ChromePortAdapter(port));\n });\n }\n\n getURL(path: string): string {\n return chrome.runtime.getURL(path);\n }\n\n getId(): string {\n return chrome.runtime.id;\n }\n\n openOptionsPage(): void {\n chrome.runtime.openOptionsPage();\n }\n}\n\nclass ChromePortAdapter implements PortAdapter {\n private listeners = {\n message: new Set<(message: unknown) => void>(),\n disconnect: new Set<() => void>(),\n };\n\n constructor(private port: chrome.runtime.Port) {\n // Set up Chrome port listeners\n this.port.onMessage.addListener((message) => {\n for (const callback of this.listeners.message) {\n callback(message);\n }\n });\n\n this.port.onDisconnect.addListener(() => {\n for (const callback of this.listeners.disconnect) {\n callback();\n }\n });\n }\n\n get name(): string {\n return this.port.name;\n }\n\n postMessage(message: unknown): void {\n this.port.postMessage(message);\n }\n\n onMessage(callback: (message: unknown) => void): void {\n this.listeners.message.add(callback);\n }\n\n onDisconnect(callback: () => void): void {\n this.listeners.disconnect.add(callback);\n }\n\n disconnect(): void {\n this.port.disconnect();\n }\n}\n",
|
|
8
|
+
"// Chrome storage adapter implementation\n\nimport type { StorageAdapter, StorageChanges } from \"../storage.adapter\";\n\nexport class ChromeStorageAdapter implements StorageAdapter {\n async get<T = Record<string, unknown>>(keys: string | string[] | null): Promise<T> {\n return (await chrome.storage.local.get(keys)) as T;\n }\n\n async set(items: Record<string, unknown>): Promise<void> {\n await chrome.storage.local.set(items);\n }\n\n async remove(keys: string | string[]): Promise<void> {\n await chrome.storage.local.remove(keys);\n }\n\n async clear(): Promise<void> {\n await chrome.storage.local.clear();\n }\n\n onChanged(callback: (changes: StorageChanges, areaName: string) => void): void {\n chrome.storage.onChanged.addListener((changes, areaName) => {\n const mappedChanges: StorageChanges = {};\n for (const [key, change] of Object.entries(changes)) {\n mappedChanges[key] = {\n oldValue: change.oldValue,\n newValue: change.newValue,\n };\n }\n callback(mappedChanges, areaName);\n });\n }\n}\n",
|
|
9
|
+
"// Chrome tabs adapter implementation\n\nimport type { TabsAdapter } from \"../tabs.adapter\";\n\nexport class ChromeTabsAdapter implements TabsAdapter {\n async query(queryInfo: chrome.tabs.QueryInfo): Promise<chrome.tabs.Tab[]> {\n return chrome.tabs.query(queryInfo);\n }\n\n async get(tabId: number): Promise<chrome.tabs.Tab> {\n return chrome.tabs.get(tabId);\n }\n\n async sendMessage(tabId: number, message: unknown): Promise<unknown> {\n return chrome.tabs.sendMessage(tabId, message);\n }\n\n async reload(tabId: number, reloadProperties?: { bypassCache?: boolean }): Promise<void> {\n await chrome.tabs.reload(tabId, reloadProperties);\n }\n\n onRemoved(callback: (tabId: number, removeInfo: chrome.tabs.TabRemoveInfo) => void): void {\n chrome.tabs.onRemoved.addListener(callback);\n }\n\n onUpdated(\n callback: (tabId: number, changeInfo: chrome.tabs.TabChangeInfo, tab: chrome.tabs.Tab) => void\n ): void {\n chrome.tabs.onUpdated.addListener(callback);\n }\n\n onActivated(callback: (activeInfo: { tabId: number; windowId: number }) => void): void {\n chrome.tabs.onActivated.addListener(callback);\n }\n\n async create(createProperties: chrome.tabs.CreateProperties): Promise<chrome.tabs.Tab> {\n return chrome.tabs.create(createProperties);\n }\n}\n",
|
|
10
|
+
"// Chrome window adapter implementation\n\nimport type { WindowAdapter } from \"../window.adapter\";\n\nexport class ChromeWindowAdapter implements WindowAdapter {\n postMessage(message: unknown, targetOrigin: string): void {\n window.postMessage(message, targetOrigin);\n }\n\n addEventListener(type: \"message\", listener: (event: MessageEvent) => void): void {\n window.addEventListener(type, listener as EventListener);\n }\n\n removeEventListener(type: \"message\", listener: (event: MessageEvent) => void): void {\n window.removeEventListener(type, listener as EventListener);\n }\n}\n",
|
|
11
|
+
"// Fetch adapter interface (wraps fetch API)\n\nexport interface FetchAdapter {\n fetch(input: string | URL, init?: RequestInit): Promise<Response>;\n}\n\nexport class BrowserFetchAdapter implements FetchAdapter {\n fetch(input: string | URL, init?: RequestInit): Promise<Response> {\n return fetch(input, init);\n }\n}\n",
|
|
12
|
+
"import type { Context, LogLevel } from \"../types/messages\";\n// Logger adapter interface (message-based centralized logging)\nimport type { RuntimeAdapter } from \"./runtime.adapter\";\n\nexport interface LoggerAdapter {\n /**\n * Debug-level logging (verbose, development info)\n */\n debug(message: string, context?: Record<string, unknown>): void;\n\n /**\n * Info-level logging (general information)\n */\n info(message: string, context?: Record<string, unknown>): void;\n\n /**\n * Warning-level logging (non-critical issues)\n */\n warn(message: string, context?: Record<string, unknown>): void;\n\n /**\n * Error-level logging (errors and exceptions)\n */\n error(message: string, error?: Error, context?: Record<string, unknown>): void;\n\n /**\n * Log with explicit level\n */\n log(level: LogLevel, message: string, context?: Record<string, unknown>): void;\n}\n\nexport interface MessageLoggerOptions {\n consoleMirror?: boolean; // Also log to console (for development)\n fallbackToConsole?: boolean; // Log to console if message send fails (default: true)\n}\n\n/**\n * Message-based logger that sends LOG messages to background LogStore\n * Uses RuntimeAdapter directly to avoid circular dependency with MessageBus\n */\nexport class MessageLoggerAdapter implements LoggerAdapter {\n constructor(\n private runtime: RuntimeAdapter,\n private sourceContext: Context,\n private options?: MessageLoggerOptions\n ) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.sendLog(\"debug\", message, context);\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.sendLog(\"info\", message, context);\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.sendLog(\"warn\", message, context);\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.sendLog(\"error\", message, context, error);\n }\n\n log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\n this.sendLog(level, message, context);\n }\n\n private sendLog(\n level: LogLevel,\n message: string,\n context?: Record<string, unknown>,\n error?: Error\n ): void {\n // Optional console mirror for development\n if (this.options?.consoleMirror) {\n const consoleMethod = console[level] || console.log;\n consoleMethod(`[${this.sourceContext}]`, message, context || \"\", error || \"\");\n }\n\n // Send LOG message to background (fire-and-forget)\n const logMessage = {\n type: \"LOG\" as const,\n level,\n message,\n context,\n error: error?.message,\n stack: error?.stack,\n source: this.sourceContext,\n timestamp: Date.now(),\n };\n\n // Use runtime.sendMessage for fire-and-forget messaging\n this.runtime.sendMessage(logMessage).catch((sendError) => {\n // Fallback to console if messaging fails\n if (this.options?.fallbackToConsole !== false) {\n console[level](`[${this.sourceContext}] ${message}`, context || \"\", error || \"\");\n console.warn(\"Failed to send log message:\", sendError);\n }\n });\n }\n}\n",
|
|
13
|
+
"// Adapter factory and exports\n\nimport { ChromeContextMenusAdapter } from \"./chrome/context-menus.chrome\";\nimport { ChromeOffscreenAdapter } from \"./chrome/offscreen.chrome\";\nimport { ChromeRuntimeAdapter } from \"./chrome/runtime.chrome\";\nimport { ChromeStorageAdapter } from \"./chrome/storage.chrome\";\nimport { ChromeTabsAdapter } from \"./chrome/tabs.chrome\";\nimport { ChromeWindowAdapter } from \"./chrome/window.chrome\";\nimport { BrowserFetchAdapter } from \"./fetch.adapter\";\nimport { MessageLoggerAdapter } from \"./logger.adapter\";\n\nimport type { Context } from \"../types/messages\";\nimport type { ContextMenusAdapter } from \"./context-menus.adapter\";\nimport type { FetchAdapter } from \"./fetch.adapter\";\nimport type { LoggerAdapter } from \"./logger.adapter\";\nimport type { OffscreenAdapter } from \"./offscreen.adapter\";\nimport type { RuntimeAdapter } from \"./runtime.adapter\";\nimport type { StorageAdapter } from \"./storage.adapter\";\nimport type { TabsAdapter } from \"./tabs.adapter\";\nimport type { WindowAdapter } from \"./window.adapter\";\n\nexport interface ExtensionAdapters {\n runtime: RuntimeAdapter;\n storage: StorageAdapter;\n tabs: TabsAdapter;\n window: WindowAdapter;\n offscreen: OffscreenAdapter;\n contextMenus: ContextMenusAdapter;\n fetch: FetchAdapter;\n logger: LoggerAdapter;\n}\n\nexport interface CreateChromeAdaptersOptions {\n consoleMirror?: boolean; // Mirror logs to console for development\n}\n\n/**\n * Create Chrome-specific adapters with context\n */\nexport function createChromeAdapters(\n context: Context,\n options?: CreateChromeAdaptersOptions\n): ExtensionAdapters {\n const runtime = new ChromeRuntimeAdapter();\n\n return {\n runtime,\n storage: new ChromeStorageAdapter(),\n tabs: new ChromeTabsAdapter(),\n window: new ChromeWindowAdapter(),\n offscreen: new ChromeOffscreenAdapter(),\n contextMenus: new ChromeContextMenusAdapter(),\n fetch: new BrowserFetchAdapter(),\n logger: new MessageLoggerAdapter(runtime, context, {\n ...(options?.consoleMirror !== undefined && { consoleMirror: options.consoleMirror }),\n fallbackToConsole: true,\n }),\n };\n}\n\n// Re-export types\nexport * from \"./runtime.adapter\";\nexport * from \"./storage.adapter\";\nexport * from \"./tabs.adapter\";\nexport * from \"./window.adapter\";\nexport * from \"./offscreen.adapter\";\nexport * from \"./context-menus.adapter\";\nexport * from \"./fetch.adapter\";\nexport * from \"./logger.adapter\";\n"
|
|
14
|
+
],
|
|
15
|
+
"mappings": "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",
|
|
16
|
+
"debugId": "88CAE80DB6C0B30564756E2164756E21",
|
|
17
|
+
"names": []
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Context, LogLevel } from "../types/messages";
|
|
2
|
+
import type { RuntimeAdapter } from "./runtime.adapter";
|
|
3
|
+
export interface LoggerAdapter {
|
|
4
|
+
/**
|
|
5
|
+
* Debug-level logging (verbose, development info)
|
|
6
|
+
*/
|
|
7
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
8
|
+
/**
|
|
9
|
+
* Info-level logging (general information)
|
|
10
|
+
*/
|
|
11
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
12
|
+
/**
|
|
13
|
+
* Warning-level logging (non-critical issues)
|
|
14
|
+
*/
|
|
15
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
16
|
+
/**
|
|
17
|
+
* Error-level logging (errors and exceptions)
|
|
18
|
+
*/
|
|
19
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
20
|
+
/**
|
|
21
|
+
* Log with explicit level
|
|
22
|
+
*/
|
|
23
|
+
log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
|
|
24
|
+
}
|
|
25
|
+
export interface MessageLoggerOptions {
|
|
26
|
+
consoleMirror?: boolean;
|
|
27
|
+
fallbackToConsole?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Message-based logger that sends LOG messages to background LogStore
|
|
31
|
+
* Uses RuntimeAdapter directly to avoid circular dependency with MessageBus
|
|
32
|
+
*/
|
|
33
|
+
export declare class MessageLoggerAdapter implements LoggerAdapter {
|
|
34
|
+
private runtime;
|
|
35
|
+
private sourceContext;
|
|
36
|
+
private options?;
|
|
37
|
+
constructor(runtime: RuntimeAdapter, sourceContext: Context, options?: MessageLoggerOptions | undefined);
|
|
38
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
39
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
40
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
41
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
42
|
+
log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
|
|
43
|
+
private sendLog;
|
|
44
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface OffscreenAdapter {
|
|
2
|
+
/**
|
|
3
|
+
* Create offscreen document
|
|
4
|
+
*/
|
|
5
|
+
createDocument(parameters: CreateOffscreenDocumentParameters): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Close offscreen document
|
|
8
|
+
*/
|
|
9
|
+
closeDocument(): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Check if offscreen document exists
|
|
12
|
+
*/
|
|
13
|
+
hasDocument(): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
export interface CreateOffscreenDocumentParameters {
|
|
16
|
+
url: string;
|
|
17
|
+
reasons: OffscreenReason[];
|
|
18
|
+
justification: string;
|
|
19
|
+
}
|
|
20
|
+
export type OffscreenReason = "AUDIO_PLAYBACK" | "BLOBS" | "CLIPBOARD" | "DOM_PARSER" | "DOM_SCRAPING" | "IFRAME_SCRIPTING" | "LOCAL_STORAGE" | "MATCH_MEDIA" | "TESTING" | "USER_MEDIA" | "WEB_RTC" | "WORKERS";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface RuntimeAdapter {
|
|
2
|
+
/**
|
|
3
|
+
* Send a one-off message to another context
|
|
4
|
+
*/
|
|
5
|
+
sendMessage<T>(message: T): Promise<unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* Listen for one-off messages
|
|
8
|
+
* Return true from callback to indicate async response
|
|
9
|
+
*/
|
|
10
|
+
onMessage(callback: (message: unknown, sender: MessageSender, sendResponse: (response: unknown) => void) => undefined | boolean): void;
|
|
11
|
+
/**
|
|
12
|
+
* Remove a previously registered message listener
|
|
13
|
+
*/
|
|
14
|
+
removeMessageListener(callback: (message: unknown, sender: MessageSender, sendResponse: (response: unknown) => void) => undefined | boolean): void;
|
|
15
|
+
/**
|
|
16
|
+
* Create a long-lived connection
|
|
17
|
+
*/
|
|
18
|
+
connect(name: string): PortAdapter;
|
|
19
|
+
/**
|
|
20
|
+
* Listen for incoming connections
|
|
21
|
+
*/
|
|
22
|
+
onConnect(callback: (port: PortAdapter) => void): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get URL for extension resource
|
|
25
|
+
*/
|
|
26
|
+
getURL(path: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Get extension ID
|
|
29
|
+
*/
|
|
30
|
+
getId(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Open the extension's options page
|
|
33
|
+
*/
|
|
34
|
+
openOptionsPage(): void;
|
|
35
|
+
}
|
|
36
|
+
export interface PortAdapter {
|
|
37
|
+
/**
|
|
38
|
+
* Port name
|
|
39
|
+
*/
|
|
40
|
+
readonly name: string;
|
|
41
|
+
/**
|
|
42
|
+
* Send message through port
|
|
43
|
+
*/
|
|
44
|
+
postMessage(message: unknown): void;
|
|
45
|
+
/**
|
|
46
|
+
* Listen for messages on port
|
|
47
|
+
*/
|
|
48
|
+
onMessage(callback: (message: unknown) => void): void;
|
|
49
|
+
/**
|
|
50
|
+
* Listen for disconnection
|
|
51
|
+
*/
|
|
52
|
+
onDisconnect(callback: () => void): void;
|
|
53
|
+
/**
|
|
54
|
+
* Disconnect port
|
|
55
|
+
*/
|
|
56
|
+
disconnect(): void;
|
|
57
|
+
}
|
|
58
|
+
export interface MessageSender {
|
|
59
|
+
tab?: {
|
|
60
|
+
id: number;
|
|
61
|
+
url: string;
|
|
62
|
+
title: string;
|
|
63
|
+
};
|
|
64
|
+
frameId?: number;
|
|
65
|
+
url?: string;
|
|
66
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface StorageAdapter {
|
|
2
|
+
/**
|
|
3
|
+
* Get items from storage
|
|
4
|
+
* @param keys - String, array of strings, object with defaults, or null for all items
|
|
5
|
+
*/
|
|
6
|
+
get<T = Record<string, unknown>>(keys?: string | string[] | Record<string, unknown> | null): Promise<T>;
|
|
7
|
+
/**
|
|
8
|
+
* Set items in storage
|
|
9
|
+
*/
|
|
10
|
+
set(items: Record<string, unknown>): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Remove items from storage
|
|
13
|
+
*/
|
|
14
|
+
remove(keys: string | string[]): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Clear all storage
|
|
17
|
+
*/
|
|
18
|
+
clear(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Listen for storage changes
|
|
21
|
+
*/
|
|
22
|
+
onChanged(callback: (changes: StorageChanges, areaName: string) => void): void;
|
|
23
|
+
}
|
|
24
|
+
export interface StorageChanges {
|
|
25
|
+
[key: string]: {
|
|
26
|
+
oldValue?: unknown;
|
|
27
|
+
newValue?: unknown;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface TabsAdapter {
|
|
2
|
+
/**
|
|
3
|
+
* Query tabs
|
|
4
|
+
*/
|
|
5
|
+
query(queryInfo: chrome.tabs.QueryInfo): Promise<chrome.tabs.Tab[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Get tab by ID
|
|
8
|
+
*/
|
|
9
|
+
get(tabId: number): Promise<chrome.tabs.Tab>;
|
|
10
|
+
/**
|
|
11
|
+
* Send message to specific tab
|
|
12
|
+
*/
|
|
13
|
+
sendMessage(tabId: number, message: unknown): Promise<unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* Reload tab
|
|
16
|
+
*/
|
|
17
|
+
reload(tabId: number, reloadProperties?: {
|
|
18
|
+
bypassCache?: boolean;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Listen for tab removal
|
|
22
|
+
*/
|
|
23
|
+
onRemoved(callback: (tabId: number, removeInfo: chrome.tabs.TabRemoveInfo) => void): void;
|
|
24
|
+
/**
|
|
25
|
+
* Listen for tab updates
|
|
26
|
+
*/
|
|
27
|
+
onUpdated(callback: (tabId: number, changeInfo: chrome.tabs.TabChangeInfo, tab: chrome.tabs.Tab) => void): void;
|
|
28
|
+
/**
|
|
29
|
+
* Listen for tab activation
|
|
30
|
+
*/
|
|
31
|
+
onActivated(callback: (activeInfo: {
|
|
32
|
+
tabId: number;
|
|
33
|
+
windowId: number;
|
|
34
|
+
}) => void): void;
|
|
35
|
+
/**
|
|
36
|
+
* Create a new tab
|
|
37
|
+
*/
|
|
38
|
+
create(createProperties: chrome.tabs.CreateProperties): Promise<chrome.tabs.Tab>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface WindowAdapter {
|
|
2
|
+
/**
|
|
3
|
+
* Post message to window
|
|
4
|
+
*/
|
|
5
|
+
postMessage(message: unknown, targetOrigin: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Listen for messages
|
|
8
|
+
*/
|
|
9
|
+
addEventListener(type: "message", listener: (event: MessageEvent) => void): void;
|
|
10
|
+
/**
|
|
11
|
+
* Remove message listener
|
|
12
|
+
*/
|
|
13
|
+
removeEventListener(type: "message", listener: (event: MessageEvent) => void): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Helpers - DX Improvements for Extension Context Initialization
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities to reduce boilerplate when initializing extension contexts.
|
|
5
|
+
*/
|
|
6
|
+
import type { BaseMessage, Context, ExtensionMessage } from "../types/messages";
|
|
7
|
+
import { type MessageBus } from "./message-bus";
|
|
8
|
+
export interface ContextConfig<TMessage extends BaseMessage = ExtensionMessage> {
|
|
9
|
+
/**
|
|
10
|
+
* Called when the context is initialized.
|
|
11
|
+
* Use this to register handlers, setup UI, etc.
|
|
12
|
+
*/
|
|
13
|
+
onInit?: (bus: MessageBus<TMessage>) => Promise<void> | void;
|
|
14
|
+
/**
|
|
15
|
+
* Called when an error occurs during initialization or runtime.
|
|
16
|
+
*/
|
|
17
|
+
onError?: (error: Error, bus: MessageBus<TMessage>) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to wait for DOM to be ready before initializing.
|
|
20
|
+
* Only applies to contexts with a window (popup, options, devtools, sidepanel).
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
waitForDOM?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Custom logger prefix.
|
|
26
|
+
* @default `[${context}]`
|
|
27
|
+
*/
|
|
28
|
+
logPrefix?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create and initialize an extension context with reduced boilerplate.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // src/popup/index.ts
|
|
36
|
+
* createContext<MyMessages>('popup', {
|
|
37
|
+
* async onInit(bus) {
|
|
38
|
+
* registerHandlers(bus)
|
|
39
|
+
* setupUI()
|
|
40
|
+
* },
|
|
41
|
+
* onError(err) {
|
|
42
|
+
* console.error('Popup failed:', err)
|
|
43
|
+
* }
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function createContext<TMessage extends BaseMessage = ExtensionMessage>(context: Context, config?: ContextConfig<TMessage>): MessageBus<TMessage>;
|
|
48
|
+
/**
|
|
49
|
+
* Helper to run code only in specific contexts.
|
|
50
|
+
* Useful for shared modules that need context-specific behavior.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // shared/features/analytics.ts
|
|
55
|
+
* const bus = createContext('popup', { ... })
|
|
56
|
+
*
|
|
57
|
+
* if (bus.context === 'popup' || bus.context === 'options') {
|
|
58
|
+
* setupUI()
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @deprecated Use bus.context directly instead. This function cannot reliably detect context.
|
|
63
|
+
*/
|
|
64
|
+
export declare function runInContext(context: Context, contexts: Context | Context[], fn: (bus: MessageBus) => void | Promise<void>): void;
|