webext-messenger 0.31.0 → 0.32.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.
@@ -1,4 +1,3 @@
1
- /* Warning: Do not use import browser-polyfill directly or indirectly */
2
1
  // .bind preserves the call location in the console
3
2
  const debug = console.debug.bind(console, "Messenger:");
4
3
  const warn = console.warn.bind(console, "Messenger:");
@@ -1,4 +1,3 @@
1
- import browser from "webextension-polyfill";
2
1
  import { serializeError } from "serialize-error";
3
2
  import { getContextName } from "webext-detect";
4
3
  import { messenger } from "./sender.js";
@@ -13,8 +12,14 @@ export function isMessengerMessage(message) {
13
12
  message["__webextMessenger"] === true &&
14
13
  Array.isArray(message["args"]));
15
14
  }
16
- // MUST NOT be `async` or Promise-returning-only
17
- function onMessageListener(message, sender) {
15
+ /**
16
+ * Decides what to do with a message and sends a response (value or error) back to the sender.
17
+ *
18
+ * @warn This function cannot return a Promise.
19
+ * @warn Limit the amount of logic here because errors won't make it to `sendResponse`
20
+ */
21
+ //
22
+ function onMessageListener(message, sender, sendResponse) {
18
23
  if (!isMessengerMessage(message)) {
19
24
  // TODO: Add test for this eventuality: ignore unrelated messages
20
25
  return;
@@ -29,21 +34,10 @@ function onMessageListener(message, sender) {
29
34
  });
30
35
  return;
31
36
  }
32
- return handleMessage(message, sender, action);
33
- }
34
- // This function can only be called when the message *will* be handled locally.
35
- // Returning "undefined" or throwing an error will still handle it.
36
- async function handleMessage(message, sender,
37
- // Once messages reach this function they cannot be "ignored", they're already being handled
38
- action) {
39
37
  const { type, target, args, options = {} } = message;
40
38
  const { trace = [], seq } = options;
41
- trace.push(sender);
42
- const meta = { trace };
43
- let handleMessage;
44
39
  if (action === "forward") {
45
40
  log.debug(type, seq, "🔀 forwarded", { sender, target });
46
- handleMessage = async () => messenger(type, meta, target, ...args);
47
41
  }
48
42
  else {
49
43
  log.debug(type, seq, "↘️ received in", getContextName(), {
@@ -51,24 +45,40 @@ action) {
51
45
  args,
52
46
  wasForwarded: trace.length > 1,
53
47
  });
54
- const localHandler = handlers.get(type);
55
- if (!localHandler) {
56
- if (!didUserRegisterMethods()) {
57
- // TODO: Test the handling of __getTabData in contexts that have no registered methods
58
- // https://github.com/pixiebrix/webext-messenger/pull/82
59
- throw new MessengerError(`No handlers registered in ${getContextName()}`);
60
- }
61
- throw new MessengerError(`No handler registered for ${type} in ${getContextName()}`);
48
+ }
49
+ // Prepare the response asynchronously because the listener must return `true` synchronously
50
+ (async () => {
51
+ try {
52
+ trace.push(sender);
53
+ const value = await prepareResponse(message, action, { trace });
54
+ log.debug(type, seq, "↗️ responding", { value });
55
+ sendResponse({ __webextMessenger, value });
62
56
  }
63
- handleMessage = async () => localHandler.apply(meta, args);
57
+ catch (error) {
58
+ log.debug(type, seq, "↗️ responding", { error });
59
+ sendResponse({ __webextMessenger, error: serializeError(error) });
60
+ }
61
+ })();
62
+ // This indicates that the message is being handled and a response will be sent asynchronously
63
+ // TODO: Just return a promise if this is ever implemented https://issues.chromium.org/issues/40753031
64
+ return true;
65
+ }
66
+ /** Generates the value or error to return to the sender; does not include further messaging logic */
67
+ async function prepareResponse(message, action, meta) {
68
+ const { type, target, args } = message;
69
+ if (action === "forward") {
70
+ return messenger(type, meta, target, ...args);
71
+ }
72
+ const localHandler = handlers.get(type);
73
+ if (localHandler) {
74
+ return localHandler.apply(meta, args);
75
+ }
76
+ if (didUserRegisterMethods()) {
77
+ throw new MessengerError(`No handler registered for ${type} in ${getContextName()}`);
64
78
  }
65
- const response = await handleMessage().then((value) => ({ value }), (error) => ({
66
- // Errors must be serialized because the stack traces are currently lost on Chrome
67
- // and https://github.com/mozilla/webextension-polyfill/issues/210
68
- error: serializeError(error),
69
- }));
70
- log.debug(type, seq, "↗️ responding", response);
71
- return { ...response, __webextMessenger };
79
+ // TODO: Test the handling of __getTabData in contexts that have no registered methods
80
+ // https://github.com/pixiebrix/webext-messenger/pull/82
81
+ throw new MessengerError(`No handlers registered in ${getContextName()}`);
72
82
  }
73
83
  export function registerMethods(methods) {
74
84
  for (const [type, method] of Object.entries(methods)) {
@@ -78,7 +88,7 @@ export function registerMethods(methods) {
78
88
  log.debug("Registered", type);
79
89
  handlers.set(type, method);
80
90
  }
81
- browser.runtime.onMessage.addListener(onMessageListener);
91
+ chrome.runtime.onMessage.addListener(onMessageListener);
82
92
  }
83
93
  /** Ensure/document that the current function was called via Messenger */
84
94
  export function assertMessengerCall(_this) { }
@@ -1,4 +1,3 @@
1
- import browser from "webextension-polyfill";
2
1
  import pRetry from "p-retry";
3
2
  import { isBackground } from "webext-detect";
4
3
  import { deserializeError } from "serialize-error";
@@ -7,7 +6,6 @@ import { log } from "./logging.js";
7
6
  import { handlers } from "./handlers.js";
8
7
  import { events } from "./events.js";
9
8
  const _errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
10
- // https://github.com/mozilla/webextension-polyfill/issues/384
11
9
  const _errorTargetClosedEarly = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received";
12
10
  export const errorTargetClosedEarly = "The target was closed before receiving a response";
13
11
  export const errorTabDoesntExist = "The tab doesn't exist";
@@ -100,9 +98,9 @@ async function manageMessage(type, target, seq, retry, sendMessage) {
100
98
  String(error.message).startsWith("No handlers registered in ")))) {
101
99
  throw error;
102
100
  }
103
- if (browser.tabs && typeof target.tabId === "number") {
101
+ if (chrome.tabs && typeof target.tabId === "number") {
104
102
  try {
105
- const tabInfo = await browser.tabs.get(target.tabId);
103
+ const tabInfo = await chrome.tabs.get(target.tabId);
106
104
  if (tabInfo.discarded) {
107
105
  throw new Error(errorTabWasDiscarded);
108
106
  }
@@ -155,15 +153,15 @@ function messenger(type, options, target, ...args) {
155
153
  }
156
154
  const sendMessage = async (attemptCount) => {
157
155
  log.debug(type, seq, "↗️ sending message to runtime", attemptLog(attemptCount));
158
- return browser.runtime.sendMessage(makeMessage(type, args, target, options));
156
+ return chrome.runtime.sendMessage(makeMessage(type, args, target, options));
159
157
  };
160
158
  return manageConnection(type, options, target, sendMessage);
161
159
  }
162
160
  // Contexts without direct Tab access must go through background
163
- if (!browser.tabs) {
161
+ if (!chrome.tabs) {
164
162
  return manageConnection(type, options, target, async (attemptCount) => {
165
163
  log.debug(type, seq, "↗️ sending message to runtime", attemptLog(attemptCount));
166
- return browser.runtime.sendMessage(makeMessage(type, args, target, options));
164
+ return chrome.runtime.sendMessage(makeMessage(type, args, target, options));
167
165
  });
168
166
  }
169
167
  // `frameId` must be specified. If missing, the message is sent to every frame
@@ -171,7 +169,7 @@ function messenger(type, options, target, ...args) {
171
169
  // Message tab directly
172
170
  return manageConnection(type, options, target, async (attemptCount) => {
173
171
  log.debug(type, seq, "↗️ sending message to tab", tabId, "frame", frameId, attemptLog(attemptCount));
174
- return browser.tabs.sendMessage(tabId, makeMessage(type, args, target, options), frameId === "allFrames"
172
+ return chrome.tabs.sendMessage(tabId, makeMessage(type, args, target, options), frameId === "allFrames"
175
173
  ? {}
176
174
  : {
177
175
  frameId,
@@ -11,6 +11,11 @@ const tab = {
11
11
  pinned: false,
12
12
  highlighted: true,
13
13
  incognito: false,
14
+ discarded: false,
15
+ frozen: false,
16
+ selected: true,
17
+ autoDiscardable: false,
18
+ groupId: -1,
14
19
  };
15
20
  const senders = {
16
21
  background: { page: "background" },
@@ -1,4 +1,3 @@
1
- import { type Runtime } from "webextension-polyfill";
2
1
  import { type Asyncify, type ValueOf } from "type-fest";
3
2
  import { type ErrorObject } from "serialize-error";
4
3
  /**
@@ -48,9 +47,7 @@ export type Message<LocalArguments extends Arguments = Arguments> = {
48
47
  /** If the message is being sent to an intermediary receiver, also set the options */
49
48
  options?: Options;
50
49
  };
51
- export type Sender = Runtime.MessageSender & {
52
- origin?: string;
53
- };
50
+ export type Sender = chrome.runtime.MessageSender;
54
51
  export type MessengerMessage = Message & {
55
52
  /** Guarantees that a message is meant to be handled by this library */
56
53
  __webextMessenger: true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webext-messenger",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "Browser Extension component messaging framework",
5
5
  "keywords": [],
6
6
  "repository": "pixiebrix/webext-messenger",
@@ -29,9 +29,8 @@
29
29
  "p-event": "^6.0.1",
30
30
  "p-retry": "^6.2.1",
31
31
  "serialize-error": "^12.0.0",
32
- "type-fest": "^4.33.0",
33
- "webext-detect": "^5.3.2",
34
- "webext-events": "^3.1.1"
32
+ "type-fest": "^4.36.0",
33
+ "webext-detect": "^5.3.2"
35
34
  },
36
35
  "@parcel/resolver-default": {
37
36
  "packageExports": true
@@ -39,9 +38,8 @@
39
38
  "devDependencies": {
40
39
  "@parcel/config-webextension": "^2.11.0",
41
40
  "@sindresorhus/tsconfig": "^7.0.0",
42
- "@types/chrome": "^0.0.301",
41
+ "@types/chrome": "^0.0.307",
43
42
  "@types/tape": "^5.8.1",
44
- "@types/webextension-polyfill": "^0.12.1",
45
43
  "buffer": "^6.0.3",
46
44
  "eslint": "^8.57.0",
47
45
  "eslint-config-pixiebrix": "^0.41.1",
@@ -52,10 +50,8 @@
52
50
  "process": "^0.11.10",
53
51
  "stream-browserify": "^3.0.0",
54
52
  "tape": "^5.9.0",
55
- "typescript": "^5.7.3",
56
- "vitest": "^3.0.5",
57
- "webext-content-scripts": "^2.7.2",
58
- "webextension-polyfill": "^0.12.0"
53
+ "typescript": "^5.8.2",
54
+ "vitest": "^3.0.7"
59
55
  },
60
56
  "targets": {
61
57
  "main": false,