webext-messenger 0.7.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- package/distribution/index.d.ts +22 -6
- package/distribution/index.js +45 -32
- package/package.json +25 -12
package/distribution/index.d.ts
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
/// <reference types="firefox-webext-browser" />
|
2
|
-
import { Asyncify } from "type-fest";
|
2
|
+
import { Asyncify, SetReturnType, ValueOf } from "type-fest";
|
3
3
|
declare global {
|
4
4
|
interface MessengerMethods {
|
5
5
|
_: Method;
|
6
6
|
}
|
7
7
|
}
|
8
|
+
declare type WithTarget<TMethod> = TMethod extends (...args: infer PreviousArguments) => infer TReturnValue ? (target: Target, ...args: PreviousArguments) => TReturnValue : never;
|
8
9
|
declare type ActuallyOmitThisParameter<T> = T extends (...args: infer A) => infer R ? (...args: A) => R : T;
|
10
|
+
/** Removes the `this` type and ensure it's always Promised */
|
11
|
+
declare type PublicMethod<TMethod extends ValueOf<MessengerMethods>> = Asyncify<ActuallyOmitThisParameter<TMethod>>;
|
12
|
+
declare type PublicMethodWithTarget<TMethod extends ValueOf<MessengerMethods>> = WithTarget<PublicMethod<TMethod>>;
|
9
13
|
export declare type MessengerMeta = browser.runtime.MessageSender;
|
10
14
|
declare type Arguments = any[];
|
11
15
|
declare type Method = (this: MessengerMeta, ...args: Arguments) => Promise<unknown>;
|
@@ -13,16 +17,28 @@ export interface Target {
|
|
13
17
|
tabId: number;
|
14
18
|
frameId?: number;
|
15
19
|
}
|
16
|
-
|
20
|
+
interface Options {
|
21
|
+
/**
|
22
|
+
* "Notifications" won't await the response, return values, attempt retries, nor throw errors
|
23
|
+
* @default false
|
24
|
+
*/
|
25
|
+
isNotification?: boolean;
|
26
|
+
}
|
17
27
|
/**
|
18
28
|
* Replicates the original method, including its types.
|
19
29
|
* To be called in the sender’s end.
|
20
30
|
*/
|
21
|
-
|
31
|
+
declare function getContentScriptMethod<TType extends keyof MessengerMethods, TMethod extends MessengerMethods[TType], TPublicMethod extends PublicMethodWithTarget<TMethod>>(type: TType, options: {
|
32
|
+
isNotification: true;
|
33
|
+
}): SetReturnType<TPublicMethod, void>;
|
34
|
+
declare function getContentScriptMethod<TType extends keyof MessengerMethods, TMethod extends MessengerMethods[TType], TPublicMethod extends PublicMethodWithTarget<TMethod>>(type: TType, options?: Options): TPublicMethod;
|
22
35
|
/**
|
23
36
|
* Replicates the original method, including its types.
|
24
37
|
* To be called in the sender’s end.
|
25
38
|
*/
|
26
|
-
|
27
|
-
|
28
|
-
|
39
|
+
declare function getMethod<TType extends keyof MessengerMethods, TMethod extends MessengerMethods[TType], TPublicMethod extends PublicMethod<TMethod>>(type: TType, options: {
|
40
|
+
isNotification: true;
|
41
|
+
}): SetReturnType<TPublicMethod, void>;
|
42
|
+
declare function getMethod<TType extends keyof MessengerMethods, TMethod extends MessengerMethods[TType], TPublicMethod extends PublicMethod<TMethod>>(type: TType, options?: Options): TPublicMethod;
|
43
|
+
declare function registerMethods(methods: Partial<MessengerMethods>): void;
|
44
|
+
export { getMethod, getContentScriptMethod, registerMethods };
|
package/distribution/index.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import pRetry from "p-retry";
|
1
2
|
import { deserializeError, serializeError } from "serialize-error";
|
2
3
|
const __webext_messenger__ = true;
|
3
4
|
function isObject(value) {
|
@@ -28,7 +29,28 @@ async function handleMessage(message, sender) {
|
|
28
29
|
console.debug(`Messenger:`, message.type, "responds", response);
|
29
30
|
return { ...response, __webext_messenger__ };
|
30
31
|
}
|
31
|
-
async function
|
32
|
+
// Do not turn this into an `async` function; Notifications must turn `void`
|
33
|
+
function manageConnection(type, options, sendMessage) {
|
34
|
+
if (!options.isNotification) {
|
35
|
+
return manageMessage(type, sendMessage);
|
36
|
+
}
|
37
|
+
void sendMessage().catch((error) => {
|
38
|
+
console.debug("Messenger:", type, "notification failed", { error });
|
39
|
+
});
|
40
|
+
}
|
41
|
+
async function manageMessage(type, sendMessage) {
|
42
|
+
const response = await pRetry(sendMessage, {
|
43
|
+
minTimeout: 100,
|
44
|
+
factor: 1.3,
|
45
|
+
maxRetryTime: 4000,
|
46
|
+
onFailedAttempt(error) {
|
47
|
+
if ((error === null || error === void 0 ? void 0 : error.message) !==
|
48
|
+
"Could not establish connection. Receiving end does not exist.") {
|
49
|
+
throw error;
|
50
|
+
}
|
51
|
+
console.debug("Messenger:", type, "will retry");
|
52
|
+
},
|
53
|
+
});
|
32
54
|
if (!isMessengerResponse(response)) {
|
33
55
|
// If the response is `undefined`, `registerMethod` was never called
|
34
56
|
throw new Error("No handlers registered in receiving end");
|
@@ -45,43 +67,33 @@ function onMessageListener(message, sender) {
|
|
45
67
|
}
|
46
68
|
// TODO: Add test for this eventuality: ignore unrelated messages
|
47
69
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
args,
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
return handleResponse(response);
|
70
|
+
function makeMessage(type, args) {
|
71
|
+
return {
|
72
|
+
__webext_messenger__,
|
73
|
+
type,
|
74
|
+
args,
|
75
|
+
};
|
76
|
+
}
|
77
|
+
function getContentScriptMethod(type, options = {}) {
|
78
|
+
const publicMethod = (target, ...args) => {
|
79
|
+
const sendMessage = async () => {
|
80
|
+
var _a;
|
81
|
+
return browser.tabs.sendMessage(target.tabId, makeMessage(type, args),
|
82
|
+
// `frameId` must be specified. If missing, the message would be sent to every frame
|
83
|
+
{ frameId: (_a = target.frameId) !== null && _a !== void 0 ? _a : 0 });
|
84
|
+
};
|
85
|
+
return manageConnection(type, options, sendMessage);
|
65
86
|
};
|
66
87
|
return publicMethod;
|
67
88
|
}
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
export function getMethod(type) {
|
73
|
-
const publicMethod = async (...args) => {
|
74
|
-
const response = await browser.runtime.sendMessage({
|
75
|
-
// Guarantees that a message is meant to be handled by this library
|
76
|
-
__webext_messenger__,
|
77
|
-
type,
|
78
|
-
args,
|
79
|
-
});
|
80
|
-
return handleResponse(response);
|
89
|
+
function getMethod(type, options = {}) {
|
90
|
+
const publicMethod = (...args) => {
|
91
|
+
const sendMessage = async () => browser.runtime.sendMessage(makeMessage(type, args));
|
92
|
+
return manageConnection(type, options, sendMessage);
|
81
93
|
};
|
82
94
|
return publicMethod;
|
83
95
|
}
|
84
|
-
|
96
|
+
function registerMethods(methods) {
|
85
97
|
for (const [type, method] of Object.entries(methods)) {
|
86
98
|
if (handlers.has(type)) {
|
87
99
|
throw new Error(`Handler already set for ${type}`);
|
@@ -91,3 +103,4 @@ export function registerMethods(methods) {
|
|
91
103
|
}
|
92
104
|
browser.runtime.onMessage.addListener(onMessageListener);
|
93
105
|
}
|
106
|
+
export { getMethod, getContentScriptMethod, registerMethods };
|
package/package.json
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
{
|
2
2
|
"name": "webext-messenger",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.9.2",
|
4
4
|
"description": "Browser Extension component messaging framework",
|
5
5
|
"keywords": [],
|
6
6
|
"repository": "pixiebrix/extension-messaging",
|
7
7
|
"license": "MIT",
|
8
8
|
"author": "Federico Brigante for PixieBrix <federico@pixiebrix.com> (https://www.pixiebrix.com)",
|
9
9
|
"type": "module",
|
10
|
-
"
|
10
|
+
"exports": "./distribution/index.js",
|
11
|
+
"types": "distribution/index.d.ts",
|
11
12
|
"files": [
|
12
13
|
"distribution/index.js",
|
13
14
|
"distribution/index.d.ts"
|
@@ -42,18 +43,19 @@
|
|
42
43
|
"plugin:unicorn/recommended"
|
43
44
|
],
|
44
45
|
"rules": {
|
45
|
-
"@typescript-eslint/no-require-imports": "off",
|
46
46
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
47
|
-
"import/extensions": "off",
|
48
|
-
"import/no-unassigned-import": "off",
|
49
|
-
"node/file-extension-in-import": "off",
|
50
47
|
"unicorn/filename-case": [
|
51
48
|
"error",
|
52
49
|
{
|
53
50
|
"case": "camelCase"
|
54
51
|
}
|
55
52
|
],
|
56
|
-
"unicorn/
|
53
|
+
"unicorn/no-useless-undefined": [
|
54
|
+
"error",
|
55
|
+
{
|
56
|
+
"checkArguments": false
|
57
|
+
}
|
58
|
+
],
|
57
59
|
"unicorn/prevent-abbreviations": [
|
58
60
|
"error",
|
59
61
|
{
|
@@ -63,11 +65,22 @@
|
|
63
65
|
}
|
64
66
|
]
|
65
67
|
},
|
68
|
+
"overrides": [
|
69
|
+
{
|
70
|
+
"files": [
|
71
|
+
"*.test.ts"
|
72
|
+
],
|
73
|
+
"rules": {
|
74
|
+
"@typescript-eslint/no-non-null-assertion": "off"
|
75
|
+
}
|
76
|
+
}
|
77
|
+
],
|
66
78
|
"globals": {
|
67
79
|
"chrome": true
|
68
80
|
}
|
69
81
|
},
|
70
82
|
"dependencies": {
|
83
|
+
"p-retry": "^4.6.1",
|
71
84
|
"serialize-error": "^8.1.0",
|
72
85
|
"type-fest": "^2.3.4",
|
73
86
|
"webext-detect-page": "^3.0.2",
|
@@ -76,20 +89,20 @@
|
|
76
89
|
"devDependencies": {
|
77
90
|
"@parcel/config-webextension": "^2.0.0-rc.0",
|
78
91
|
"@sindresorhus/tsconfig": "^2.0.0",
|
79
|
-
"@types/chrome": "^0.0.
|
92
|
+
"@types/chrome": "^0.0.158",
|
80
93
|
"@types/firefox-webext-browser": "^82.0.1",
|
81
|
-
"@typescript-eslint/eslint-plugin": "^4.
|
82
|
-
"@typescript-eslint/parser": "^4.
|
94
|
+
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
95
|
+
"@typescript-eslint/parser": "^4.31.2",
|
83
96
|
"eslint": "^7.32.0",
|
84
97
|
"eslint-config-prettier": "^8.3.0",
|
85
98
|
"eslint-config-xo": "^0.38.0",
|
86
99
|
"eslint-config-xo-typescript": "^0.44.0",
|
87
100
|
"eslint-plugin-import": "^2.24.2",
|
88
|
-
"eslint-plugin-unicorn": "^
|
101
|
+
"eslint-plugin-unicorn": "^36.0.0",
|
89
102
|
"fresh-tape": "^5.3.1",
|
90
103
|
"npm-run-all": "^4.1.5",
|
91
104
|
"parcel": "^2.0.0-rc.0",
|
92
|
-
"typescript": "^4.4.
|
105
|
+
"typescript": "^4.4.3",
|
93
106
|
"xo": "^0.44.0"
|
94
107
|
},
|
95
108
|
"targets": {
|