webext-messenger 0.35.0 → 0.36.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/distribution/sender.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { isExtensionContext } from "webext-detect";
|
|
1
|
+
import { isBackground, isExtensionContext } from "webext-detect";
|
|
2
2
|
import { deserializeError } from "serialize-error";
|
|
3
3
|
import { isObject, MessengerError, ExtensionNotFoundError, __webextMessenger, } from "./shared.js";
|
|
4
4
|
import { log } from "./logging.js";
|
|
5
5
|
import { handlers } from "./handlers.js";
|
|
6
6
|
import { events } from "./events.js";
|
|
7
|
-
import { compareTargets } from "./targetLogic.js";
|
|
8
|
-
import { thisTarget } from "./thisTarget.js";
|
|
9
7
|
const _errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
|
|
10
8
|
const _errorTargetClosedEarly = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received";
|
|
11
9
|
export const errorTargetClosedEarly = "The target was closed before receiving a response";
|
|
@@ -186,17 +184,16 @@ function messenger(type, options, target, ...args) {
|
|
|
186
184
|
};
|
|
187
185
|
return manageConnection(type, options, target, sendMessage);
|
|
188
186
|
}
|
|
189
|
-
// Use local methods if the target matches the current context
|
|
190
|
-
if (compareTargets(target, thisTarget)) {
|
|
191
|
-
const handler = handlers.get(type);
|
|
192
|
-
if (handler) {
|
|
193
|
-
log.warn(type, seq, "is being handled locally");
|
|
194
|
-
return handler.apply({ trace: [] }, args);
|
|
195
|
-
}
|
|
196
|
-
throw new MessengerError("No handler registered locally for " + type);
|
|
197
|
-
}
|
|
198
187
|
// Message goes to extension page
|
|
199
188
|
if ("page" in target) {
|
|
189
|
+
if (target.page === "background" && isBackground()) {
|
|
190
|
+
const handler = handlers.get(type);
|
|
191
|
+
if (handler) {
|
|
192
|
+
log.warn(type, seq, "is being handled locally");
|
|
193
|
+
return handler.apply({ trace: [] }, args);
|
|
194
|
+
}
|
|
195
|
+
throw new MessengerError("No handler registered locally for " + type);
|
|
196
|
+
}
|
|
200
197
|
const sendMessage = async (attemptCount) => {
|
|
201
198
|
log.debug(type, seq, "↗️ sending message to runtime", attemptLog(attemptCount));
|
|
202
199
|
return chrome.runtime.sendMessage(makeMessage(type, args, target, options));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webext-messenger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "Browser Extension component messaging framework",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": "pixiebrix/webext-messenger",
|
|
@@ -27,18 +27,18 @@
|
|
|
27
27
|
"watch": "tsc --watch"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"one-event": "^4.
|
|
31
|
-
"serialize-error": "^
|
|
32
|
-
"type-fest": "^5.0
|
|
30
|
+
"one-event": "^4.4.0",
|
|
31
|
+
"serialize-error": "^13.0.1",
|
|
32
|
+
"type-fest": "^5.7.0",
|
|
33
33
|
"webext-detect": "^5.3.2"
|
|
34
34
|
},
|
|
35
35
|
"@parcel/resolver-default": {
|
|
36
36
|
"packageExports": true
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@parcel/config-webextension": "^2.16.
|
|
40
|
-
"@sindresorhus/tsconfig": "^8.0
|
|
41
|
-
"@types/chrome": "^0.1
|
|
39
|
+
"@parcel/config-webextension": "^2.16.3",
|
|
40
|
+
"@sindresorhus/tsconfig": "^8.1.0",
|
|
41
|
+
"@types/chrome": "^0.2.1",
|
|
42
42
|
"@types/tape": "^5.8.1",
|
|
43
43
|
"buffer": "^6.0.3",
|
|
44
44
|
"eslint": "^8.57.0",
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
"path-browserify": "^1.0.1",
|
|
50
50
|
"process": "^0.11.10",
|
|
51
51
|
"stream-browserify": "^3.0.0",
|
|
52
|
-
"tape": "^5.
|
|
52
|
+
"tape": "^5.10.2",
|
|
53
53
|
"typescript": "^5.9.3",
|
|
54
|
-
"vitest": "^
|
|
54
|
+
"vitest": "^4.1.9"
|
|
55
55
|
},
|
|
56
56
|
"targets": {
|
|
57
57
|
"main": false,
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument, @typescript-eslint/naming-convention */
|
|
2
|
-
import { describe, test, expect, vi, beforeEach } from "vitest";
|
|
3
|
-
import { handlers } from "./handlers.js";
|
|
4
|
-
import * as thisTargetModule from "./thisTarget.js";
|
|
5
|
-
// Mock dependencies
|
|
6
|
-
vi.mock("webext-detect", () => ({
|
|
7
|
-
isBackground: vi.fn(() => false),
|
|
8
|
-
isExtensionContext: vi.fn(() => true),
|
|
9
|
-
isOffscreenDocument: vi.fn(() => false),
|
|
10
|
-
isContentScript: vi.fn(() => true),
|
|
11
|
-
}));
|
|
12
|
-
vi.mock("./logging.js", () => ({
|
|
13
|
-
log: {
|
|
14
|
-
debug: vi.fn(),
|
|
15
|
-
warn: vi.fn(),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
vi.mock("./events.js", () => ({
|
|
19
|
-
events: {
|
|
20
|
-
dispatchEvent: vi.fn(),
|
|
21
|
-
},
|
|
22
|
-
}));
|
|
23
|
-
// Mock chrome APIs - simulate content script environment (no chrome.tabs)
|
|
24
|
-
globalThis.chrome = {
|
|
25
|
-
runtime: {
|
|
26
|
-
id: "test-extension-id",
|
|
27
|
-
sendMessage: vi.fn(),
|
|
28
|
-
},
|
|
29
|
-
// Note: chrome.tabs is undefined in content scripts
|
|
30
|
-
};
|
|
31
|
-
describe("messenger with tab targets and local methods", () => {
|
|
32
|
-
beforeEach(() => {
|
|
33
|
-
vi.clearAllMocks();
|
|
34
|
-
handlers.clear();
|
|
35
|
-
});
|
|
36
|
-
test("should use local method when targeting same tab and frame", async () => {
|
|
37
|
-
// Setup: Set thisTarget to have tabId: 1, frameId: 0
|
|
38
|
-
const mockThisTarget = {
|
|
39
|
-
tabId: 1,
|
|
40
|
-
frameId: 0,
|
|
41
|
-
page: "test",
|
|
42
|
-
};
|
|
43
|
-
vi.spyOn(thisTargetModule, "thisTarget", "get").mockReturnValue(mockThisTarget);
|
|
44
|
-
// Register a local handler
|
|
45
|
-
const mockHandler = vi.fn(async () => "local result");
|
|
46
|
-
handlers.set("testMethod", mockHandler);
|
|
47
|
-
// Import messenger after mocks are set up
|
|
48
|
-
const { messenger } = await import("./sender.js");
|
|
49
|
-
// Test: Send message to same tab and frame
|
|
50
|
-
const result = await messenger("testMethod", {}, { tabId: 1, frameId: 0 });
|
|
51
|
-
// Verify: Handler was called locally
|
|
52
|
-
expect(mockHandler).toHaveBeenCalledOnce();
|
|
53
|
-
expect(mockHandler).toHaveBeenCalledWith();
|
|
54
|
-
expect(result).toBe("local result");
|
|
55
|
-
// Verify: No message was sent via chrome.runtime.sendMessage
|
|
56
|
-
expect(chrome.runtime.sendMessage).not.toHaveBeenCalled();
|
|
57
|
-
});
|
|
58
|
-
test("should send message when targeting different tab", async () => {
|
|
59
|
-
// Setup: Set thisTarget to have tabId: 1, frameId: 0
|
|
60
|
-
const mockThisTarget = {
|
|
61
|
-
tabId: 1,
|
|
62
|
-
frameId: 0,
|
|
63
|
-
page: "test",
|
|
64
|
-
};
|
|
65
|
-
vi.spyOn(thisTargetModule, "thisTarget", "get").mockReturnValue(mockThisTarget);
|
|
66
|
-
// Register a local handler (should not be used)
|
|
67
|
-
const mockHandler = vi.fn(async () => "local result");
|
|
68
|
-
handlers.set("testMethod", mockHandler);
|
|
69
|
-
// Mock chrome.runtime.sendMessage to return a messenger response
|
|
70
|
-
vi.mocked(chrome.runtime.sendMessage).mockResolvedValue({
|
|
71
|
-
__webextMessenger: true,
|
|
72
|
-
value: "remote result",
|
|
73
|
-
});
|
|
74
|
-
// Import messenger after mocks are set up
|
|
75
|
-
const { messenger } = await import("./sender.js");
|
|
76
|
-
// Test: Send message to different tab
|
|
77
|
-
const result = await messenger("testMethod", {}, { tabId: 2, frameId: 0 });
|
|
78
|
-
// Verify: Message was sent via chrome.runtime.sendMessage
|
|
79
|
-
expect(chrome.runtime.sendMessage).toHaveBeenCalledOnce();
|
|
80
|
-
expect(result).toBe("remote result");
|
|
81
|
-
// Verify: Local handler was not called
|
|
82
|
-
expect(mockHandler).not.toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
test("should send message when targeting different frame in same tab", async () => {
|
|
85
|
-
// Setup: Set thisTarget to have tabId: 1, frameId: 0
|
|
86
|
-
const mockThisTarget = {
|
|
87
|
-
tabId: 1,
|
|
88
|
-
frameId: 0,
|
|
89
|
-
page: "test",
|
|
90
|
-
};
|
|
91
|
-
vi.spyOn(thisTargetModule, "thisTarget", "get").mockReturnValue(mockThisTarget);
|
|
92
|
-
// Register a local handler (should not be used)
|
|
93
|
-
const mockHandler = vi.fn(async () => "local result");
|
|
94
|
-
handlers.set("testMethod", mockHandler);
|
|
95
|
-
// Mock chrome.runtime.sendMessage to return a messenger response
|
|
96
|
-
vi.mocked(chrome.runtime.sendMessage).mockResolvedValue({
|
|
97
|
-
__webextMessenger: true,
|
|
98
|
-
value: "remote result",
|
|
99
|
-
});
|
|
100
|
-
// Import messenger after mocks are set up
|
|
101
|
-
const { messenger } = await import("./sender.js");
|
|
102
|
-
// Test: Send message to different frame in same tab
|
|
103
|
-
const result = await messenger("testMethod", {}, { tabId: 1, frameId: 1 });
|
|
104
|
-
// Verify: Message was sent via chrome.runtime.sendMessage
|
|
105
|
-
expect(chrome.runtime.sendMessage).toHaveBeenCalledOnce();
|
|
106
|
-
expect(result).toBe("remote result");
|
|
107
|
-
// Verify: Local handler was not called
|
|
108
|
-
expect(mockHandler).not.toHaveBeenCalled();
|
|
109
|
-
});
|
|
110
|
-
test("should send message when targeting allFrames", async () => {
|
|
111
|
-
// Setup: Set thisTarget to have tabId: 1, frameId: 0
|
|
112
|
-
const mockThisTarget = {
|
|
113
|
-
tabId: 1,
|
|
114
|
-
frameId: 0,
|
|
115
|
-
page: "test",
|
|
116
|
-
};
|
|
117
|
-
vi.spyOn(thisTargetModule, "thisTarget", "get").mockReturnValue(mockThisTarget);
|
|
118
|
-
// Register a local handler (should not be used for allFrames)
|
|
119
|
-
const mockHandler = vi.fn(async () => "local result");
|
|
120
|
-
handlers.set("testMethod", mockHandler);
|
|
121
|
-
// Mock chrome.runtime.sendMessage to return a messenger response
|
|
122
|
-
vi.mocked(chrome.runtime.sendMessage).mockResolvedValue({
|
|
123
|
-
__webextMessenger: true,
|
|
124
|
-
value: "remote result",
|
|
125
|
-
});
|
|
126
|
-
// Import messenger after mocks are set up
|
|
127
|
-
const { messenger } = await import("./sender.js");
|
|
128
|
-
// Test: Send message to allFrames in same tab
|
|
129
|
-
const result = await messenger("testMethod", {}, { tabId: 1, frameId: "allFrames" });
|
|
130
|
-
// Verify: Message was sent via chrome.runtime.sendMessage
|
|
131
|
-
expect(chrome.runtime.sendMessage).toHaveBeenCalledOnce();
|
|
132
|
-
expect(result).toBe("remote result");
|
|
133
|
-
// Verify: Local handler was not called
|
|
134
|
-
expect(mockHandler).not.toHaveBeenCalled();
|
|
135
|
-
});
|
|
136
|
-
test("should throw error when no local handler registered for same tab/frame", async () => {
|
|
137
|
-
// Setup: Set thisTarget to have tabId: 1, frameId: 0
|
|
138
|
-
const mockThisTarget = {
|
|
139
|
-
tabId: 1,
|
|
140
|
-
frameId: 0,
|
|
141
|
-
page: "test",
|
|
142
|
-
};
|
|
143
|
-
vi.spyOn(thisTargetModule, "thisTarget", "get").mockReturnValue(mockThisTarget);
|
|
144
|
-
// No handler registered
|
|
145
|
-
// Import messenger and MessengerError after mocks are set up
|
|
146
|
-
const { messenger } = await import("./sender.js");
|
|
147
|
-
const { MessengerError } = await import("./shared.js");
|
|
148
|
-
// Test: Send message to same tab and frame without handler
|
|
149
|
-
try {
|
|
150
|
-
await messenger("testMethod", {}, { tabId: 1, frameId: 0 });
|
|
151
|
-
expect.fail("Should have thrown an error");
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
expect(error).toBeInstanceOf(MessengerError);
|
|
155
|
-
expect(error.message).toBe("No handler registered locally for testMethod");
|
|
156
|
-
}
|
|
157
|
-
// Verify: No message was sent via chrome.runtime.sendMessage
|
|
158
|
-
expect(chrome.runtime.sendMessage).not.toHaveBeenCalled();
|
|
159
|
-
});
|
|
160
|
-
});
|