webext-messenger 0.25.0-0 → 0.25.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ export declare const events: EventTarget;
@@ -0,0 +1 @@
1
+ export const events = new EventTarget();
@@ -1,5 +1,6 @@
1
1
  export * from "./receiver.js";
2
2
  export * from "./sender.js";
3
3
  export * from "./types.js";
4
+ export * from "./events.js";
4
5
  export { getThisFrame, getTopLevelFrame } from "./thisTarget.js";
5
6
  export { toggleLogging } from "./logging.js";
@@ -2,6 +2,7 @@
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";
@@ -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;
@@ -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 (arguments.length === 1) {
169
+ if (!target) {
145
170
  return messenger.bind(undefined, type, {});
146
171
  }
147
- // @ts-expect-error `bind` types are junk
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 (arguments.length === 1) {
176
+ if (!target) {
153
177
  // @ts-expect-error `bind` types are junk
154
178
  return messenger.bind(undefined, type, options);
155
179
  }
156
- // @ts-expect-error `bind` types are junk
157
- return messenger.bind(undefined, type, options, target);
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" };
@@ -37,6 +37,7 @@ export interface Options {
37
37
  */
38
38
  isNotification?: boolean;
39
39
  trace?: Sender[];
40
+ retry?: boolean;
40
41
  /** Automatically generated internally */
41
42
  seq?: number;
42
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webext-messenger",
3
- "version": "0.25.0-0",
3
+ "version": "0.25.1",
4
4
  "description": "Browser Extension component messaging framework",
5
5
  "keywords": [],
6
6
  "repository": "pixiebrix/webext-messenger",
@@ -17,7 +17,7 @@
17
17
  "scripts": {
18
18
  "build": "tsc",
19
19
  "demo:watch": "parcel watch --no-cache --no-hmr",
20
- "demo:build": "parcel build --no-cache",
20
+ "demo:build": "parcel build --no-cache --no-scope-hoist",
21
21
  "prepack": "tsc --sourceMap false",
22
22
  "test": "eslint . && tsc --noEmit",
23
23
  "lint": "eslint .",
@@ -25,38 +25,40 @@
25
25
  "watch": "tsc --watch"
26
26
  },
27
27
  "dependencies": {
28
- "p-retry": "^6.0.0",
28
+ "p-retry": "^6.2.0",
29
29
  "serialize-error": "^11.0.2",
30
- "type-fest": "^4.6.0",
31
- "webext-detect-page": "^4.1.1"
30
+ "type-fest": "^4.10.2",
31
+ "webext-detect-page": "^5.0.0"
32
+ },
33
+ "@parcel/resolver-default": {
34
+ "packageExports": true
32
35
  },
33
36
  "devDependencies": {
34
- "@parcel/config-webextension": "^2.6.2",
37
+ "@parcel/config-webextension": "^2.11.0",
35
38
  "@sindresorhus/tsconfig": "^5.0.0",
36
- "@types/chrome": "^0.0.248",
37
- "@types/tape": "^5.6.3",
38
- "@types/webextension-polyfill": "^0.10.5",
39
+ "@types/chrome": "^0.0.259",
40
+ "@types/tape": "^5.6.4",
41
+ "@types/webextension-polyfill": "^0.10.7",
39
42
  "buffer": "^6.0.3",
40
- "eslint": "^8.52.0",
41
- "eslint-config-pixiebrix": "^0.29.1",
43
+ "eslint": "^8.56.0",
44
+ "eslint-config-pixiebrix": "^0.34.1",
42
45
  "events": "^3.3.0",
43
46
  "npm-run-all": "^4.1.5",
44
- "parcel": "^2.6.2",
47
+ "parcel": "^2.11.0",
45
48
  "path-browserify": "^1.0.1",
46
49
  "process": "^0.11.10",
47
50
  "stream-browserify": "^3.0.0",
48
- "tape": "^5.7.2",
49
- "typescript": "^5.2.2",
50
- "webext-content-scripts": "^2.5.5",
51
+ "tape": "^5.7.4",
52
+ "typescript": "^5.3.3",
53
+ "webext-content-scripts": "^2.6.1",
51
54
  "webextension-polyfill": "^0.10.0"
52
55
  },
53
- "alias": {
54
- "./this-stuff-is-just-for-local-parcel-tests": "https://github.com/parcel-bundler/parcel/issues/4936",
55
- "./source/**/*.js": "./source/$1/$2.ts"
56
- },
57
56
  "targets": {
58
57
  "main": false,
59
58
  "default": {
59
+ "engines": {
60
+ "browsers": "Chrome 110"
61
+ },
60
62
  "source": "source/test/manifest.json",
61
63
  "sourceMap": {
62
64
  "inline": true