webext-messenger 0.13.0-8 → 0.14.1
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/api.d.ts +10 -0
- package/distribution/api.js +13 -0
- package/distribution/index.d.ts +3 -3
- package/distribution/index.js +8 -2
- package/distribution/receiver.js +5 -10
- package/distribution/sender.d.ts +10 -17
- package/distribution/sender.js +39 -27
- package/distribution/thisTarget.d.ts +12 -0
- package/distribution/thisTarget.js +53 -0
- package/distribution/types.d.ts +6 -2
- package/package.json +44 -24
@@ -0,0 +1,10 @@
|
|
1
|
+
import { Target, MessengerMeta } from "./types";
|
2
|
+
declare function __getTabData(this: MessengerMeta): Target;
|
3
|
+
declare global {
|
4
|
+
interface MessengerMethods {
|
5
|
+
__getTabData: typeof __getTabData;
|
6
|
+
}
|
7
|
+
}
|
8
|
+
export declare const getTabData: () => Promise<Target>;
|
9
|
+
export declare function initPrivateApi(): void;
|
10
|
+
export {};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { getMethod } from "./sender";
|
2
|
+
import { registerMethods } from "./receiver";
|
3
|
+
import { isBackgroundPage } from "webext-detect-page";
|
4
|
+
function __getTabData() {
|
5
|
+
return { tabId: this.trace[0].tab.id, frameId: this.trace[0].frameId };
|
6
|
+
}
|
7
|
+
// First page to respond wins. Any context has this piece of information.
|
8
|
+
export const getTabData = getMethod("__getTabData", { page: "any" });
|
9
|
+
export function initPrivateApi() {
|
10
|
+
if (isBackgroundPage()) {
|
11
|
+
registerMethods({ __getTabData });
|
12
|
+
}
|
13
|
+
}
|
package/distribution/index.d.ts
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
export
|
2
|
-
export
|
3
|
-
export
|
1
|
+
export * from "./receiver.js";
|
2
|
+
export * from "./sender.js";
|
3
|
+
export * from "./types.js";
|
package/distribution/index.js
CHANGED
@@ -1,2 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
// Imports must use the .js extension because of ESM requires it and TS refuses to rewrite .ts to .js
|
2
|
+
// This works in TS even if the .js doesn't exist, but it breaks Parcel (the tests builder)
|
3
|
+
// For this reason, there's an `alias` field in package.json to redirect these imports.
|
4
|
+
// If you see "@parcel/resolver-default: Cannot load file './yourNewFile.js'" you need to add it to the `alias` list
|
5
|
+
// 🥲
|
6
|
+
export * from "./receiver.js";
|
7
|
+
export * from "./sender.js";
|
8
|
+
export * from "./types.js";
|
package/distribution/receiver.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
import browser from "webextension-polyfill";
|
1
2
|
import { serializeError } from "serialize-error";
|
2
|
-
import {
|
3
|
+
import { messenger } from "./sender.js";
|
3
4
|
import { handlers, isObject, MessengerError, debug, warn, __webextMessenger, } from "./shared.js";
|
4
5
|
export function isMessengerMessage(message) {
|
5
6
|
return (isObject(message) &&
|
@@ -21,8 +22,7 @@ function onMessageListener(message, sender) {
|
|
21
22
|
throw new MessengerError(`Message ${type} sent to wrong context, it can't be forwarded to ${JSON.stringify(target)}`);
|
22
23
|
}
|
23
24
|
debug(type, "🔀 forwarded", { sender, target });
|
24
|
-
|
25
|
-
handleMessage = async () => publicMethod(target, ...args);
|
25
|
+
handleMessage = async () => messenger(type, {}, target, ...args);
|
26
26
|
}
|
27
27
|
else {
|
28
28
|
const localHandler = handlers.get(type);
|
@@ -50,13 +50,8 @@ export function registerMethods(methods) {
|
|
50
50
|
if (handlers.has(type)) {
|
51
51
|
throw new MessengerError(`Handler already set for ${type}`);
|
52
52
|
}
|
53
|
-
console.debug(
|
53
|
+
console.debug("Messenger: Registered", type);
|
54
54
|
handlers.set(type, method);
|
55
55
|
}
|
56
|
-
|
57
|
-
browser.runtime.onMessage.addListener(onMessageListener);
|
58
|
-
}
|
59
|
-
else {
|
60
|
-
throw new Error("`webext-messenger` requires `webextension");
|
61
|
-
}
|
56
|
+
browser.runtime.onMessage.addListener(onMessageListener);
|
62
57
|
}
|
package/distribution/sender.d.ts
CHANGED
@@ -1,20 +1,13 @@
|
|
1
|
+
import { PublicMethod, PublicMethodWithTarget, Options, Target, PageTarget } from "./types.js";
|
1
2
|
import { SetReturnType } from "type-fest";
|
2
|
-
import { PublicMethod, PublicMethodWithTarget, Options } from "./types.js";
|
3
3
|
export declare const errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
|
4
|
-
|
5
|
-
* Replicates the original method, including its types.
|
6
|
-
* To be called in the sender’s end.
|
7
|
-
*/
|
8
|
-
declare function getContentScriptMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethod extends PublicMethodWithTarget<Method>>(type: Type, options: {
|
4
|
+
declare function messenger<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type]>(type: Type, options: {
|
9
5
|
isNotification: true;
|
10
|
-
}
|
11
|
-
declare function
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
}): SetReturnType<PublicMethodType, void>;
|
19
|
-
declare function getMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends PublicMethod<Method>>(type: Type, options?: Options): PublicMethodType;
|
20
|
-
export { getContentScriptMethod, getMethod };
|
6
|
+
}, target: Target | PageTarget, ...args: Parameters<Method>): void;
|
7
|
+
declare function messenger<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], ReturnValue extends ReturnType<Method>>(type: Type, options: Options, target: Target | PageTarget, ...args: Parameters<Method>): ReturnValue;
|
8
|
+
declare function getMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends PublicMethod<Method>>(type: Type, target: Target | PageTarget): PublicMethodType;
|
9
|
+
declare function getMethod<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodWithDynamicTarget extends PublicMethodWithTarget<Method>>(type: Type): PublicMethodWithDynamicTarget;
|
10
|
+
declare function getNotifier<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodType extends SetReturnType<PublicMethod<Method>, void>>(type: Type, target: Target | PageTarget): PublicMethodType;
|
11
|
+
declare function getNotifier<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type], PublicMethodWithDynamicTarget extends SetReturnType<PublicMethodWithTarget<Method>, void>>(type: Type): PublicMethodWithDynamicTarget;
|
12
|
+
export { messenger, getMethod, getNotifier };
|
13
|
+
export declare const backgroundTarget: PageTarget;
|
package/distribution/sender.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import browser from "webextension-polyfill";
|
1
2
|
import pRetry from "p-retry";
|
2
3
|
import { isBackgroundPage } from "webext-detect-page";
|
3
4
|
import { deserializeError } from "serialize-error";
|
@@ -45,30 +46,9 @@ async function manageMessage(type, sendMessage) {
|
|
45
46
|
debug(type, "↘️ replied successfully", response.value);
|
46
47
|
return response.value;
|
47
48
|
}
|
48
|
-
function
|
49
|
-
|
50
|
-
|
51
|
-
if (!browser.tabs) {
|
52
|
-
return manageConnection(type, options, async () => {
|
53
|
-
debug(type, "↗️ sending message to runtime");
|
54
|
-
return browser.runtime.sendMessage(makeMessage(type, args, target));
|
55
|
-
});
|
56
|
-
}
|
57
|
-
// `frameId` must be specified. If missing, the message is sent to every frame
|
58
|
-
const { tabId, frameId = 0 } = target;
|
59
|
-
// Message tab directly
|
60
|
-
return manageConnection(type, options, async () => {
|
61
|
-
debug(type, "↗️ sending message to tab", tabId, "frame", frameId);
|
62
|
-
return browser.tabs.sendMessage(tabId, makeMessage(type, args), {
|
63
|
-
frameId,
|
64
|
-
});
|
65
|
-
});
|
66
|
-
};
|
67
|
-
return publicMethod;
|
68
|
-
}
|
69
|
-
function getMethod(type, options = {}) {
|
70
|
-
const publicMethod = (...args) => {
|
71
|
-
if (isBackgroundPage()) {
|
49
|
+
function messenger(type, options, target, ...args) {
|
50
|
+
if ("page" in target) {
|
51
|
+
if (target.page === "background" && isBackgroundPage()) {
|
72
52
|
const handler = handlers.get(type);
|
73
53
|
if (handler) {
|
74
54
|
warn(type, "is being handled locally");
|
@@ -81,7 +61,39 @@ function getMethod(type, options = {}) {
|
|
81
61
|
return browser.runtime.sendMessage(makeMessage(type, args));
|
82
62
|
};
|
83
63
|
return manageConnection(type, options, sendMessage);
|
84
|
-
}
|
85
|
-
|
64
|
+
}
|
65
|
+
// Contexts without direct Tab access must go through background
|
66
|
+
if (!browser.tabs) {
|
67
|
+
return manageConnection(type, options, async () => {
|
68
|
+
debug(type, "↗️ sending message to runtime");
|
69
|
+
return browser.runtime.sendMessage(makeMessage(type, args, target));
|
70
|
+
});
|
71
|
+
}
|
72
|
+
// `frameId` must be specified. If missing, the message is sent to every frame
|
73
|
+
const { tabId, frameId = 0 } = target;
|
74
|
+
// Message tab directly
|
75
|
+
return manageConnection(type, options, async () => {
|
76
|
+
debug(type, "↗️ sending message to tab", tabId, "frame", frameId);
|
77
|
+
return browser.tabs.sendMessage(tabId, makeMessage(type, args), {
|
78
|
+
frameId,
|
79
|
+
});
|
80
|
+
});
|
81
|
+
}
|
82
|
+
function getMethod(type, target) {
|
83
|
+
if (arguments.length === 1) {
|
84
|
+
return messenger.bind(undefined, type, {});
|
85
|
+
}
|
86
|
+
// @ts-expect-error `bind` types are junk
|
87
|
+
return messenger.bind(undefined, type, {}, target);
|
88
|
+
}
|
89
|
+
function getNotifier(type, target) {
|
90
|
+
const options = { isNotification: true };
|
91
|
+
if (arguments.length === 1) {
|
92
|
+
// @ts-expect-error `bind` types are junk
|
93
|
+
return messenger.bind(undefined, type, options);
|
94
|
+
}
|
95
|
+
// @ts-expect-error `bind` types are junk
|
96
|
+
return messenger.bind(undefined, type, options, target);
|
86
97
|
}
|
87
|
-
export {
|
98
|
+
export { messenger, getMethod, getNotifier };
|
99
|
+
export const backgroundTarget = { page: "background" };
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Target, PageTarget, MessengerMeta } from "./types.js";
|
2
|
+
declare type AnyTarget = Partial<Target & PageTarget>;
|
3
|
+
export declare function getActionForMessage(target: AnyTarget): "respond" | "forward" | "ignore";
|
4
|
+
export declare function nameThisTarget(): Promise<void>;
|
5
|
+
declare function __getTabData(this: MessengerMeta): AnyTarget;
|
6
|
+
declare global {
|
7
|
+
interface MessengerMethods {
|
8
|
+
__getTabData: typeof __getTabData;
|
9
|
+
}
|
10
|
+
}
|
11
|
+
export declare function initPrivateApi(): void;
|
12
|
+
export {};
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { isBackgroundPage, isContentScript } from "webext-detect-page";
|
2
|
+
import { messenger } from "./sender.js";
|
3
|
+
import { registerMethods } from "./receiver.js";
|
4
|
+
import { debug } from "./shared.js";
|
5
|
+
// Soft warning: Race conditions are possible.
|
6
|
+
// This CANNOT be awaited because waiting for it means "I will handle the message."
|
7
|
+
// If a message is received before this is ready, it will just have to be ignored.
|
8
|
+
let thisTarget;
|
9
|
+
//
|
10
|
+
export function getActionForMessage(target) {
|
11
|
+
if (target.page === "any") {
|
12
|
+
return "respond";
|
13
|
+
}
|
14
|
+
// Content scripts only receive messages that are meant for them. In the future
|
15
|
+
// they'll also forward them, but that still means they need to be handled here.
|
16
|
+
if (isContentScript()) {
|
17
|
+
return "respond";
|
18
|
+
}
|
19
|
+
// We're in an extension page, but the target is not one.
|
20
|
+
if (!("page" in target)) {
|
21
|
+
return "forward";
|
22
|
+
}
|
23
|
+
if (!thisTarget) {
|
24
|
+
console.warn("A message was received before this context was ready");
|
25
|
+
// If this *was* the target, then probably no one else answered
|
26
|
+
return "ignore";
|
27
|
+
}
|
28
|
+
// Every `target` key must match `thisTarget`
|
29
|
+
const isThisTarget = Object.entries(target).every(
|
30
|
+
// @ts-expect-error Optional properties
|
31
|
+
([key, value]) => thisTarget[key] === value);
|
32
|
+
if (!isThisTarget) {
|
33
|
+
debug("The message’s target is", target, "but this is", thisTarget);
|
34
|
+
}
|
35
|
+
return isThisTarget ? "respond" : "ignore";
|
36
|
+
}
|
37
|
+
export async function nameThisTarget() {
|
38
|
+
// Same as above: CS receives messages correctly
|
39
|
+
if (!thisTarget && !isContentScript()) {
|
40
|
+
thisTarget = await messenger("__getTabData", {}, { page: "any" });
|
41
|
+
thisTarget.page = location.pathname;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
function __getTabData() {
|
45
|
+
var _a, _b, _c;
|
46
|
+
return { tabId: (_b = (_a = this.trace[0]) === null || _a === void 0 ? void 0 : _a.tab) === null || _b === void 0 ? void 0 : _b.id, frameId: (_c = this.trace[0]) === null || _c === void 0 ? void 0 : _c.frameId };
|
47
|
+
}
|
48
|
+
export function initPrivateApi() {
|
49
|
+
if (isBackgroundPage()) {
|
50
|
+
thisTarget = { page: "background" };
|
51
|
+
registerMethods({ __getTabData });
|
52
|
+
}
|
53
|
+
}
|
package/distribution/types.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
import { Runtime } from "webextension-polyfill";
|
2
2
|
import { Asyncify, ValueOf } from "type-fest";
|
3
3
|
import { ErrorObject } from "serialize-error";
|
4
4
|
declare global {
|
@@ -12,7 +12,7 @@ declare type ActuallyOmitThisParameter<T> = T extends (...args: infer A) => infe
|
|
12
12
|
export declare type PublicMethod<Method extends ValueOf<MessengerMethods>> = Asyncify<ActuallyOmitThisParameter<Method>>;
|
13
13
|
export declare type PublicMethodWithTarget<Method extends ValueOf<MessengerMethods>> = WithTarget<PublicMethod<Method>>;
|
14
14
|
export interface MessengerMeta {
|
15
|
-
trace:
|
15
|
+
trace: Runtime.MessageSender[];
|
16
16
|
}
|
17
17
|
declare type RawMessengerResponse = {
|
18
18
|
value: unknown;
|
@@ -48,4 +48,8 @@ export interface Target {
|
|
48
48
|
tabId: number;
|
49
49
|
frameId?: number;
|
50
50
|
}
|
51
|
+
export interface PageTarget {
|
52
|
+
tabId?: number;
|
53
|
+
page: string;
|
54
|
+
}
|
51
55
|
export {};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "webext-messenger",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.14.1",
|
4
4
|
"description": "Browser Extension component messaging framework",
|
5
5
|
"keywords": [],
|
6
6
|
"repository": "pixiebrix/webext-messenger",
|
@@ -17,8 +17,8 @@
|
|
17
17
|
},
|
18
18
|
"scripts": {
|
19
19
|
"build": "tsc",
|
20
|
-
"demo:watch": "parcel watch --no-cache --no-hmr
|
21
|
-
"demo:build": "parcel build --no-cache
|
20
|
+
"demo:watch": "parcel watch --no-cache --no-hmr",
|
21
|
+
"demo:build": "parcel build --no-cache",
|
22
22
|
"prepack": "tsc --sourceMap false",
|
23
23
|
"test": "eslint . && tsc --noEmit",
|
24
24
|
"lint": "eslint .",
|
@@ -45,6 +45,21 @@
|
|
45
45
|
"plugin:unicorn/recommended"
|
46
46
|
],
|
47
47
|
"rules": {
|
48
|
+
"no-restricted-imports": [
|
49
|
+
"error",
|
50
|
+
{
|
51
|
+
"paths": [
|
52
|
+
{
|
53
|
+
"name": "./index",
|
54
|
+
"message": "The index file is only used to re-export internal files. Use direct imports instead."
|
55
|
+
}
|
56
|
+
]
|
57
|
+
}
|
58
|
+
],
|
59
|
+
"import/extensions": [
|
60
|
+
"error",
|
61
|
+
"always"
|
62
|
+
],
|
48
63
|
"import/no-unresolved": "off",
|
49
64
|
"unicorn/filename-case": [
|
50
65
|
"error",
|
@@ -77,37 +92,42 @@
|
|
77
92
|
"@typescript-eslint/no-explicit-any": "off",
|
78
93
|
"@typescript-eslint/no-unsafe-member-access": "off"
|
79
94
|
}
|
95
|
+
},
|
96
|
+
{
|
97
|
+
"files": [
|
98
|
+
"source/test/**/*"
|
99
|
+
],
|
100
|
+
"rules": {
|
101
|
+
"import/extensions": "off"
|
102
|
+
}
|
80
103
|
}
|
81
|
-
]
|
82
|
-
"globals": {
|
83
|
-
"chrome": true
|
84
|
-
}
|
104
|
+
]
|
85
105
|
},
|
86
106
|
"dependencies": {
|
87
|
-
"p-retry": "^
|
88
|
-
"serialize-error": "^
|
89
|
-
"type-fest": "^2.
|
90
|
-
"webext-detect-page": "^3.0
|
107
|
+
"p-retry": "^5.0.0",
|
108
|
+
"serialize-error": "^9.0.0",
|
109
|
+
"type-fest": "^2.6.0",
|
110
|
+
"webext-detect-page": "^3.1.0",
|
91
111
|
"webextension-polyfill": "^0.8.0"
|
92
112
|
},
|
93
113
|
"devDependencies": {
|
94
|
-
"@parcel/config-webextension": "^2.0.
|
114
|
+
"@parcel/config-webextension": "^2.0.1",
|
95
115
|
"@sindresorhus/tsconfig": "^2.0.0",
|
96
|
-
"@types/chrome": "^0.0.
|
97
|
-
"@types/
|
98
|
-
"@
|
99
|
-
"@typescript-eslint/
|
100
|
-
"eslint": "^
|
116
|
+
"@types/chrome": "^0.0.164",
|
117
|
+
"@types/tape": "^4.13.2",
|
118
|
+
"@types/webextension-polyfill": "^0.8.2",
|
119
|
+
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
120
|
+
"@typescript-eslint/parser": "^5.4.0",
|
121
|
+
"eslint": "^8.3.0",
|
101
122
|
"eslint-config-prettier": "^8.3.0",
|
102
123
|
"eslint-config-xo": "^0.39.0",
|
103
|
-
"eslint-config-xo-typescript": "^0.
|
104
|
-
"eslint-plugin-import": "^2.25.
|
105
|
-
"eslint-plugin-unicorn": "^
|
106
|
-
"fresh-tape": "^5.3.1",
|
124
|
+
"eslint-config-xo-typescript": "^0.47.1",
|
125
|
+
"eslint-plugin-import": "^2.25.3",
|
126
|
+
"eslint-plugin-unicorn": "^39.0.0",
|
107
127
|
"npm-run-all": "^4.1.5",
|
108
|
-
"parcel": "^2.0.
|
109
|
-
"
|
110
|
-
"
|
128
|
+
"parcel": "^2.0.1",
|
129
|
+
"tape": "^5.3.2",
|
130
|
+
"typescript": "^4.5.2"
|
111
131
|
},
|
112
132
|
"targets": {
|
113
133
|
"main": false,
|