webext-messenger 0.24.0 → 0.25.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/events.d.ts +1 -0
- package/distribution/events.js +1 -0
- package/distribution/index.d.ts +1 -0
- package/distribution/index.js +3 -0
- package/distribution/sender.d.ts +3 -3
- package/distribution/sender.js +35 -9
- package/distribution/shared.js +1 -9
- package/distribution/thisTarget.js +11 -3
- package/distribution/types.d.ts +1 -0
- package/package.json +13 -13
@@ -0,0 +1 @@
|
|
1
|
+
export declare const events: EventTarget;
|
@@ -0,0 +1 @@
|
|
1
|
+
export const events = new EventTarget();
|
package/distribution/index.d.ts
CHANGED
package/distribution/index.js
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
export * from "./receiver.js";
|
3
3
|
export * from "./sender.js";
|
4
4
|
export * from "./types.js";
|
5
|
+
export * from "./events.js";
|
5
6
|
export { getThisFrame, getTopLevelFrame } from "./thisTarget.js";
|
6
7
|
export { toggleLogging } from "./logging.js";
|
7
8
|
import { initPrivateApi } from "./thisTarget.js";
|
9
|
+
// Required side effect to better track errors:
|
10
|
+
// https://github.com/pixiebrix/webext-messenger/pull/80
|
8
11
|
initPrivateApi();
|
package/distribution/sender.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { type PublicMethod, type PublicMethodWithTarget, type Options, type Target, type PageTarget } from "./types.js";
|
2
|
-
import { type SetReturnType } from "type-fest";
|
2
|
+
import { type Promisable, type SetReturnType } from "type-fest";
|
3
3
|
export declare const errorTargetClosedEarly = "The target was closed before receiving a response";
|
4
4
|
export declare const errorTabDoesntExist = "The tab doesn't exist";
|
5
5
|
export declare const errorTabWasDiscarded = "The tab was discarded";
|
@@ -7,9 +7,9 @@ declare function messenger<Type extends keyof MessengerMethods, Method extends M
|
|
7
7
|
isNotification: true;
|
8
8
|
}, target: Target | PageTarget, ...args: Parameters<Method>): void;
|
9
9
|
declare function messenger<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], ReturnValue extends Promise<ReturnType<Method>>>(type: Type, options: Options, target: Target | PageTarget, ...args: Parameters<Method>): ReturnValue;
|
10
|
-
declare function getMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends PublicMethod<Method>>(type: Type, target: Target | PageTarget): PublicMethodType;
|
10
|
+
declare function getMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends PublicMethod<Method>>(type: Type, target: Promisable<Target | PageTarget>): PublicMethodType;
|
11
11
|
declare function getMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodWithDynamicTarget extends PublicMethodWithTarget<Method>>(type: Type): PublicMethodWithDynamicTarget;
|
12
|
-
declare function getNotifier<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends SetReturnType<PublicMethod<Method>, void>>(type: Type, target: Target | PageTarget): PublicMethodType;
|
12
|
+
declare function getNotifier<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends SetReturnType<PublicMethod<Method>, void>>(type: Type, target: Promisable<Target | PageTarget>): PublicMethodType;
|
13
13
|
declare function getNotifier<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodWithDynamicTarget extends SetReturnType<PublicMethodWithTarget<Method>, void>>(type: Type): PublicMethodWithDynamicTarget;
|
14
14
|
export { messenger, getMethod, getNotifier };
|
15
15
|
export declare const backgroundTarget: PageTarget;
|
package/distribution/sender.js
CHANGED
@@ -4,6 +4,7 @@ import { deserializeError } from "serialize-error";
|
|
4
4
|
import { isObject, MessengerError, __webextMessenger } from "./shared.js";
|
5
5
|
import { log } from "./logging.js";
|
6
6
|
import { handlers } from "./handlers.js";
|
7
|
+
import { events } from "./events.js";
|
7
8
|
const _errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
|
8
9
|
// https://github.com/mozilla/webextension-polyfill/issues/384
|
9
10
|
const _errorTargetClosedEarly = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received";
|
@@ -16,6 +17,9 @@ function isMessengerResponse(response) {
|
|
16
17
|
function attemptLog(attemptCount) {
|
17
18
|
return attemptCount > 1 ? `(try: ${attemptCount})` : "";
|
18
19
|
}
|
20
|
+
function wasContextInvalidated() {
|
21
|
+
return !chrome.runtime?.id;
|
22
|
+
}
|
19
23
|
function makeMessage(type, args, target, options) {
|
20
24
|
return {
|
21
25
|
__webextMessenger,
|
@@ -26,15 +30,16 @@ function makeMessage(type, args, target, options) {
|
|
26
30
|
};
|
27
31
|
}
|
28
32
|
// Do not turn this into an `async` function; Notifications must turn `void`
|
29
|
-
function manageConnection(type, { seq, isNotification }, target, sendMessage) {
|
33
|
+
function manageConnection(type, { seq, isNotification, retry }, target, sendMessage) {
|
30
34
|
if (!isNotification) {
|
31
|
-
return manageMessage(type, target, seq, sendMessage);
|
35
|
+
return manageMessage(type, target, seq, retry ?? true, sendMessage);
|
32
36
|
}
|
33
37
|
void sendMessage(1).catch((error) => {
|
34
38
|
log.debug(type, seq, "notification failed", { error });
|
35
39
|
});
|
36
40
|
}
|
37
|
-
async function manageMessage(type, target, seq, sendMessage) {
|
41
|
+
async function manageMessage(type, target, seq, retry, sendMessage) {
|
42
|
+
// TODO: Split this up a bit because it's too long. Probably drop p-retry
|
38
43
|
const response = await pRetry(async (attemptCount) => {
|
39
44
|
const response = await sendMessage(attemptCount);
|
40
45
|
if (isMessengerResponse(response)) {
|
@@ -61,8 +66,25 @@ async function manageMessage(type, target, seq, sendMessage) {
|
|
61
66
|
}, {
|
62
67
|
minTimeout: 100,
|
63
68
|
factor: 1.3,
|
69
|
+
// Do not set this to undefined or Infinity, it doesn't work the same way
|
70
|
+
...(retry ? {} : { retries: 0 }),
|
64
71
|
maxRetryTime: 4000,
|
65
72
|
async onFailedAttempt(error) {
|
73
|
+
events.dispatchEvent(new CustomEvent("failed-attempt", {
|
74
|
+
detail: {
|
75
|
+
type,
|
76
|
+
seq,
|
77
|
+
target,
|
78
|
+
error,
|
79
|
+
attemptCount: error.attemptNumber,
|
80
|
+
},
|
81
|
+
}));
|
82
|
+
if (wasContextInvalidated()) {
|
83
|
+
// The error matches the native context invalidated error
|
84
|
+
// *.sendMessage() might fail with a message-specific error that is less useful,
|
85
|
+
// like "Sender closed without responding"
|
86
|
+
throw new Error("Extension context invalidated.");
|
87
|
+
}
|
66
88
|
if (error.message === _errorTargetClosedEarly) {
|
67
89
|
throw new Error(errorTargetClosedEarly);
|
68
90
|
}
|
@@ -94,6 +116,9 @@ async function manageMessage(type, target, seq, sendMessage) {
|
|
94
116
|
if (error?.message === _errorNonExistingTarget) {
|
95
117
|
throw new MessengerError(`The target ${JSON.stringify(target)} for ${type} was not found`);
|
96
118
|
}
|
119
|
+
events.dispatchEvent(new CustomEvent("attempts-exhausted", {
|
120
|
+
detail: { type, seq, target, error },
|
121
|
+
}));
|
97
122
|
throw error;
|
98
123
|
});
|
99
124
|
if ("error" in response) {
|
@@ -141,20 +166,21 @@ function messenger(type, options, target, ...args) {
|
|
141
166
|
});
|
142
167
|
}
|
143
168
|
function getMethod(type, target) {
|
144
|
-
if (
|
169
|
+
if (!target) {
|
145
170
|
return messenger.bind(undefined, type, {});
|
146
171
|
}
|
147
|
-
|
148
|
-
return messenger.bind(undefined, type, {}, target);
|
172
|
+
return (async (...args) => messenger(type, {}, await target, ...args));
|
149
173
|
}
|
150
174
|
function getNotifier(type, target) {
|
151
175
|
const options = { isNotification: true };
|
152
|
-
if (
|
176
|
+
if (!target) {
|
153
177
|
// @ts-expect-error `bind` types are junk
|
154
178
|
return messenger.bind(undefined, type, options);
|
155
179
|
}
|
156
|
-
|
157
|
-
|
180
|
+
return ((...args) => {
|
181
|
+
// Async wrapper needed to use `await` while preserving a non-Promise return type
|
182
|
+
(async () => messenger(type, options, await target, ...args))();
|
183
|
+
});
|
158
184
|
}
|
159
185
|
export { messenger, getMethod, getNotifier };
|
160
186
|
export const backgroundTarget = { page: "background" };
|
package/distribution/shared.js
CHANGED
@@ -4,15 +4,7 @@ export function isObject(value) {
|
|
4
4
|
return typeof value === "object" && value !== null;
|
5
5
|
}
|
6
6
|
export class MessengerError extends Error {
|
7
|
-
|
8
|
-
super(...arguments);
|
9
|
-
Object.defineProperty(this, "name", {
|
10
|
-
enumerable: true,
|
11
|
-
configurable: true,
|
12
|
-
writable: true,
|
13
|
-
value: "MessengerError"
|
14
|
-
});
|
15
|
-
}
|
7
|
+
name = "MessengerError";
|
16
8
|
}
|
17
9
|
// @ts-expect-error Wrong `errorConstructors` types
|
18
10
|
errorConstructors.set("MessengerError", MessengerError);
|
@@ -106,9 +106,7 @@ const storeTabData = once(async () => {
|
|
106
106
|
}
|
107
107
|
catch (error) {
|
108
108
|
tabDataStatus = "error";
|
109
|
-
throw new MessengerError("Tab registration failed. This page won’t be able to receive messages that require tab information",
|
110
|
-
// @ts-expect-error TODO: update lib to accept Error#cause
|
111
|
-
{ cause: error });
|
109
|
+
throw new MessengerError("Tab registration failed. This page won’t be able to receive messages that require tab information", { cause: error });
|
112
110
|
}
|
113
111
|
});
|
114
112
|
export function __getTabData() {
|
@@ -131,6 +129,16 @@ export async function getTopLevelFrame() {
|
|
131
129
|
};
|
132
130
|
}
|
133
131
|
export function initPrivateApi() {
|
132
|
+
// Improve DX by informing the developer that it's being loaded the wrong way
|
133
|
+
// https://github.com/pixiebrix/webext-messenger/issues/88
|
134
|
+
if (globalThis.__webextMessenger) {
|
135
|
+
// TODO: Use Error#cause after https://bugs.chromium.org/p/chromium/issues/detail?id=1211260
|
136
|
+
console.log(globalThis.__webextMessenger.replace(/^Error/, "webext-messenger"));
|
137
|
+
console.error("webext-messenger: Duplicate execution. This is a fatal error.\nhttps://github.com/pixiebrix/webext-messenger/issues/88");
|
138
|
+
return;
|
139
|
+
}
|
140
|
+
// Use Error to capture the stack and make it easier to find the cause
|
141
|
+
globalThis.__webextMessenger = new Error("First execution").stack;
|
134
142
|
if (isExtensionContext()) {
|
135
143
|
// Only `runtime` pages can handle this message but I can't remove it because its listener
|
136
144
|
// also serves the purpose of throwing a specific error when no methods have been registered.
|
package/distribution/types.d.ts
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "webext-messenger",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.25.0",
|
4
4
|
"description": "Browser Extension component messaging framework",
|
5
5
|
"keywords": [],
|
6
6
|
"repository": "pixiebrix/webext-messenger",
|
@@ -25,29 +25,29 @@
|
|
25
25
|
"watch": "tsc --watch"
|
26
26
|
},
|
27
27
|
"dependencies": {
|
28
|
-
"p-retry": "^6.
|
28
|
+
"p-retry": "^6.2.0",
|
29
29
|
"serialize-error": "^11.0.2",
|
30
|
-
"type-fest": "^4.
|
31
|
-
"webext-detect-page": "^4.
|
30
|
+
"type-fest": "^4.9.0",
|
31
|
+
"webext-detect-page": "^4.2.1"
|
32
32
|
},
|
33
33
|
"devDependencies": {
|
34
34
|
"@parcel/config-webextension": "^2.6.2",
|
35
|
-
"@sindresorhus/tsconfig": "^
|
36
|
-
"@types/chrome": "^0.0.
|
37
|
-
"@types/tape": "^5.6.
|
38
|
-
"@types/webextension-polyfill": "^0.10.
|
35
|
+
"@sindresorhus/tsconfig": "^5.0.0",
|
36
|
+
"@types/chrome": "^0.0.254",
|
37
|
+
"@types/tape": "^5.6.4",
|
38
|
+
"@types/webextension-polyfill": "^0.10.7",
|
39
39
|
"buffer": "^6.0.3",
|
40
|
-
"eslint": "^8.
|
41
|
-
"eslint-config-pixiebrix": "^0.
|
40
|
+
"eslint": "^8.56.0",
|
41
|
+
"eslint-config-pixiebrix": "^0.32.0",
|
42
42
|
"events": "^3.3.0",
|
43
43
|
"npm-run-all": "^4.1.5",
|
44
44
|
"parcel": "^2.6.2",
|
45
45
|
"path-browserify": "^1.0.1",
|
46
46
|
"process": "^0.11.10",
|
47
47
|
"stream-browserify": "^3.0.0",
|
48
|
-
"tape": "^5.7.
|
49
|
-
"typescript": "^5.
|
50
|
-
"webext-content-scripts": "^2.
|
48
|
+
"tape": "^5.7.2",
|
49
|
+
"typescript": "^5.3.3",
|
50
|
+
"webext-content-scripts": "^2.6.0",
|
51
51
|
"webextension-polyfill": "^0.10.0"
|
52
52
|
},
|
53
53
|
"alias": {
|