webext-messenger 0.18.2 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ import { __getTabData } from "./thisTarget.js";
2
+ import { Method } from "./types.js";
3
+ declare global {
4
+ interface MessengerMethods {
5
+ __getTabData: typeof __getTabData;
6
+ }
7
+ }
8
+ export declare const privateMethods: (typeof __getTabData)[];
9
+ export declare const handlers: Map<string, Method>;
10
+ export declare function didUserRegisterMethods(): boolean;
@@ -0,0 +1,6 @@
1
+ import { __getTabData } from "./thisTarget.js";
2
+ export const privateMethods = [__getTabData];
3
+ export const handlers = new Map();
4
+ export function didUserRegisterMethods() {
5
+ return handlers.size > privateMethods.length;
6
+ }
@@ -1,8 +1,9 @@
1
1
  import { serializeError } from "serialize-error";
2
- import { messenger } from "./sender.js";
3
- import { handlers, isObject, MessengerError, debug, __webextMessenger, } from "./shared.js";
4
2
  import { getContextName, isBackground } from "webext-detect-page";
3
+ import { messenger } from "./sender.js";
4
+ import { isObject, MessengerError, debug, __webextMessenger, } from "./shared.js";
5
5
  import { getActionForMessage, nameThisTarget } from "./thisTarget.js";
6
+ import { didUserRegisterMethods, handlers } from "./handlers.js";
6
7
  export function isMessengerMessage(message) {
7
8
  return (isObject(message) &&
8
9
  typeof message["type"] === "string" &&
@@ -42,6 +43,9 @@ action) {
42
43
  args,
43
44
  wasForwarded: trace.length > 1,
44
45
  });
46
+ if (!didUserRegisterMethods()) {
47
+ throw new MessengerError(`No handlers registered in ${getContextName()}`);
48
+ }
45
49
  const localHandler = handlers.get(type);
46
50
  if (!localHandler) {
47
51
  throw new MessengerError(`No handler registered for ${type} in ${getContextName()}`);
@@ -1,6 +1,7 @@
1
1
  import { PublicMethod, PublicMethodWithTarget, Options, Target, PageTarget } from "./types.js";
2
2
  import { SetReturnType } from "type-fest";
3
- export declare const errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
3
+ export declare const errorTargetClosedEarly = "The target was closed before receiving a response";
4
+ export declare const errorTabDoesntExist = "The tab doesn't exist";
4
5
  declare function messenger<Type extends keyof MessengerMethods, Method extends MessengerMethods[Type]>(type: Type, options: {
5
6
  isNotification: true;
6
7
  }, target: Target | PageTarget, ...args: Parameters<Method>): void;
@@ -1,8 +1,14 @@
1
1
  import pRetry from "p-retry";
2
2
  import { isBackground } from "webext-detect-page";
3
+ import { doesTabExist } from "webext-tools";
3
4
  import { deserializeError } from "serialize-error";
4
- import { isObject, MessengerError, __webextMessenger, handlers, debug, warn, } from "./shared.js";
5
- export const errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
5
+ import { isObject, MessengerError, __webextMessenger, debug, warn, } from "./shared.js";
6
+ import { handlers } from "./handlers.js";
7
+ const _errorNonExistingTarget = "Could not establish connection. Receiving end does not exist.";
8
+ // https://github.com/mozilla/webextension-polyfill/issues/384
9
+ const _errorTargetClosedEarly = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received";
10
+ export const errorTargetClosedEarly = "The target was closed before receiving a response";
11
+ export const errorTabDoesntExist = "The tab doesn't exist";
6
12
  function isMessengerResponse(response) {
7
13
  return isObject(response) && response["__webextMessenger"] === true;
8
14
  }
@@ -35,11 +41,22 @@ async function manageMessage(type, target, sendMessage) {
35
41
  minTimeout: 100,
36
42
  factor: 1.3,
37
43
  maxRetryTime: 4000,
38
- onFailedAttempt(error) {
44
+ async onFailedAttempt(error) {
45
+ if (error.message === _errorTargetClosedEarly) {
46
+ throw new Error(errorTargetClosedEarly);
47
+ }
39
48
  if (
40
49
  // Don't retry sending to the background page unless it really hasn't loaded yet
41
50
  (target.page !== "background" && error instanceof MessengerError) ||
42
- String(error.message).startsWith(errorNonExistingTarget)) {
51
+ // Page or its content script not yet loaded
52
+ String(error.message).startsWith(_errorNonExistingTarget) ||
53
+ // `registerMethods` not yet loaded
54
+ String(error.message).startsWith("No handlers registered in ")) {
55
+ if (browser.tabs &&
56
+ typeof target.tabId === "number" &&
57
+ !(await doesTabExist(target.tabId))) {
58
+ throw new Error(errorTabDoesntExist);
59
+ }
43
60
  debug(type, "will retry. Attempt", error.attemptNumber);
44
61
  }
45
62
  else {
@@ -1,5 +1,4 @@
1
1
  import { JsonObject } from "type-fest";
2
- import { Method } from "./types.js";
3
2
  declare type ErrorObject = {
4
3
  name?: string;
5
4
  stack?: string;
@@ -11,7 +10,6 @@ export declare function isObject(value: unknown): value is Record<string, unknow
11
10
  export declare class MessengerError extends Error {
12
11
  name: string;
13
12
  }
14
- export declare const handlers: Map<string, Method>;
15
13
  export declare const debug: (...args: any[]) => void;
16
14
  export declare const warn: (...args: any[]) => void;
17
15
  export declare function isErrorObject(error: unknown): error is ErrorObject;
@@ -25,13 +25,12 @@ export class MessengerError extends Error {
25
25
  });
26
26
  }
27
27
  }
28
- export const handlers = new Map();
29
28
  // .bind preserves the call location in the console
30
29
  export const debug = logging ? console.debug.bind(console, "Messenger:") : noop;
31
30
  export const warn = logging ? console.warn.bind(console, "Messenger:") : noop;
32
31
  export function isErrorObject(error) {
33
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a type guard function and it uses ?.
34
- return typeof (error === null || error === void 0 ? void 0 : error.message) === "string";
33
+ return typeof error?.message === "string";
35
34
  }
36
35
  export async function delay(milliseconds) {
37
36
  return new Promise((resolve) => {
@@ -1,11 +1,5 @@
1
1
  import { AnyTarget, MessengerMeta, Sender } from "./types.js";
2
2
  export declare function getActionForMessage(from: Sender, { ...to }: AnyTarget): "respond" | "forward" | "ignore";
3
3
  export declare function nameThisTarget(): Promise<void>;
4
- declare function __getTabData(this: MessengerMeta): AnyTarget;
5
- declare global {
6
- interface MessengerMethods {
7
- __getTabData: typeof __getTabData;
8
- }
9
- }
4
+ export declare function __getTabData(this: MessengerMeta): AnyTarget;
10
5
  export declare function initPrivateApi(): void;
11
- export {};
@@ -29,7 +29,6 @@ function compareTargets(to, thisTarget) {
29
29
  }
30
30
  export function getActionForMessage(from, { ...to } // Clone object because we're editing it
31
31
  ) {
32
- var _a;
33
32
  if (to.page === "any") {
34
33
  return "respond";
35
34
  }
@@ -48,7 +47,7 @@ export function getActionForMessage(from, { ...to } // Clone object because we'r
48
47
  return "ignore";
49
48
  }
50
49
  // Set "this" tab to the current tabId
51
- if (to.tabId === "this" && thisTarget.tabId === ((_a = from.tab) === null || _a === void 0 ? void 0 : _a.id)) {
50
+ if (to.tabId === "this" && thisTarget.tabId === from.tab?.id) {
52
51
  to.tabId = thisTarget.tabId;
53
52
  }
54
53
  // Every `target` key must match `thisTarget`
@@ -67,9 +66,8 @@ export async function nameThisTarget() {
67
66
  thisTarget.page = location.pathname + location.search;
68
67
  }
69
68
  }
70
- function __getTabData() {
71
- var _a, _b, _c;
72
- 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 };
69
+ export function __getTabData() {
70
+ return { tabId: this.trace[0]?.tab?.id, frameId: this.trace[0]?.frameId };
73
71
  }
74
72
  export function initPrivateApi() {
75
73
  if (isBackground()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webext-messenger",
3
- "version": "0.18.2",
3
+ "version": "0.20.0",
4
4
  "description": "Browser Extension component messaging framework",
5
5
  "keywords": [],
6
6
  "repository": "pixiebrix/webext-messenger",
@@ -18,123 +18,42 @@
18
18
  "fix": "eslint . --fix",
19
19
  "watch": "tsc --watch"
20
20
  },
21
- "eslintConfig": {
22
- "env": {
23
- "browser": true
24
- },
25
- "parserOptions": {
26
- "project": "tsconfig.json"
27
- },
28
- "plugins": [
29
- "import"
30
- ],
31
- "extends": [
32
- "plugin:@typescript-eslint/recommended",
33
- "xo",
34
- "xo-typescript",
35
- "prettier",
36
- "plugin:import/recommended",
37
- "plugin:import/typescript",
38
- "plugin:unicorn/recommended"
39
- ],
40
- "rules": {
41
- "no-restricted-imports": [
42
- "error",
43
- {
44
- "paths": [
45
- {
46
- "name": "./index",
47
- "message": "The index file is only used to re-export internal files. Use direct imports instead."
48
- }
49
- ]
50
- }
51
- ],
52
- "import/extensions": [
53
- "error",
54
- "always"
55
- ],
56
- "import/no-unresolved": "off",
57
- "unicorn/filename-case": [
58
- "error",
59
- {
60
- "case": "camelCase"
61
- }
62
- ],
63
- "unicorn/no-useless-undefined": [
64
- "error",
65
- {
66
- "checkArguments": false
67
- }
68
- ],
69
- "unicorn/prevent-abbreviations": [
70
- "error",
71
- {
72
- "allowList": {
73
- "args": true
74
- }
75
- }
76
- ]
77
- },
78
- "overrides": [
79
- {
80
- "files": [
81
- "*.test.ts"
82
- ],
83
- "rules": {
84
- "@typescript-eslint/no-explicit-any": "off",
85
- "@typescript-eslint/no-non-null-assertion": "off",
86
- "@typescript-eslint/no-unsafe-member-access": "off"
87
- }
88
- },
89
- {
90
- "files": [
91
- "source/test/**/*"
92
- ],
93
- "rules": {
94
- "import/extensions": "off"
95
- }
96
- }
97
- ]
98
- },
99
21
  "dependencies": {
100
- "p-retry": "^5.1.0",
22
+ "p-retry": "^5.1.1",
101
23
  "serialize-error": "^11.0.0",
102
- "type-fest": "^2.12.1",
103
- "webext-detect-page": "^4.0.1"
24
+ "type-fest": "^2.13.0",
25
+ "webext-detect-page": "^4.0.1",
26
+ "webext-tools": "^1.1.0"
104
27
  },
105
28
  "devDependencies": {
106
- "@parcel/config-webextension": "^2.4.0",
107
- "@sindresorhus/tsconfig": "^2.0.0",
108
- "@types/chrome": "^0.0.180",
29
+ "@parcel/config-webextension": "^2.6.0",
30
+ "@sindresorhus/tsconfig": "^3.0.1",
31
+ "@types/chrome": "^0.0.188",
109
32
  "@types/tape": "^4.13.2",
110
- "@types/webextension-polyfill": "^0.8.3",
111
- "@typescript-eslint/eslint-plugin": "^5.17.0",
112
- "@typescript-eslint/parser": "^5.17.0",
33
+ "@types/webextension-polyfill": "^0.9.0",
34
+ "@typescript-eslint/eslint-plugin": "^5.27.0",
35
+ "@typescript-eslint/parser": "^5.27.0",
113
36
  "buffer": "^6.0.3",
114
- "eslint": "^8.12.0",
37
+ "eslint": "^8.17.0",
115
38
  "eslint-config-prettier": "^8.5.0",
116
- "eslint-config-xo": "^0.40.0",
117
- "eslint-config-xo-typescript": "^0.50.0",
118
- "eslint-plugin-import": "^2.25.4",
119
- "eslint-plugin-unicorn": "^41.0.1",
39
+ "eslint-config-xo": "^0.41.0",
40
+ "eslint-config-xo-typescript": "^0.51.1",
41
+ "eslint-plugin-import": "^2.26.0",
42
+ "eslint-plugin-unicorn": "^42.0.0",
120
43
  "events": "^3.3.0",
121
44
  "npm-run-all": "^4.1.5",
122
- "parcel": "^2.4.0",
45
+ "parcel": "^2.6.0",
123
46
  "path-browserify": "^1.0.1",
124
47
  "process": "^0.11.10",
125
48
  "stream-browserify": "^3.0.0",
126
- "tape": "^5.5.2",
127
- "typescript": "^4.6.3",
128
- "webext-content-scripts": "^1.0.1",
49
+ "tape": "^5.5.3",
50
+ "typescript": "^4.7.3",
51
+ "webext-content-scripts": "^1.0.2",
129
52
  "webextension-polyfill": "^0.9.0"
130
53
  },
131
54
  "alias": {
132
- "./this-stuff-is-just-for-local-parcel-tests": "./package.json",
133
- "./source/sender.js": "./source/sender.ts",
134
- "./source/receiver.js": "./source/receiver.ts",
135
- "./source/types.js": "./source/types.ts",
136
- "./source/shared.js": "./source/shared.ts",
137
- "./source/thisTarget.js": "./source/thisTarget.ts"
55
+ "./this-stuff-is-just-for-local-parcel-tests": "https://github.com/parcel-bundler/parcel/issues/4936",
56
+ "./source/**/*.js": "./source/$1/$2.ts"
138
57
  },
139
58
  "targets": {
140
59
  "main": false,
package/readme.md CHANGED
@@ -14,3 +14,7 @@ npm install webext-messenger
14
14
  ```js
15
15
  import messenger from "webext-messenger";
16
16
  ```
17
+
18
+ ## Context
19
+
20
+ - [Initial considerations for this library](https://github.com/pixiebrix/webext-messenger/issues/1)