webext-messenger 0.24.0 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- 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": {
|