webext-messenger
Advanced tools
Comparing version 0.22.0 to 0.23.0
@@ -5,1 +5,2 @@ export * from "./receiver.js"; | ||
export { getThisFrame, getTopLevelFrame } from "./thisTarget.js"; | ||
export { toggleLogging } from "./shared.js"; |
@@ -6,3 +6,4 @@ // Imports must use the .js extension because ESM requires it and TS refuses to rewrite .ts to .js | ||
export { getThisFrame, getTopLevelFrame } from "./thisTarget.js"; | ||
export { toggleLogging } from "./shared.js"; | ||
import { initPrivateApi } from "./thisTarget.js"; | ||
initPrivateApi(); |
@@ -1,3 +0,5 @@ | ||
import { type Message } from "./types.js"; | ||
import { type Message, type MessengerMeta } from "./types.js"; | ||
export declare function isMessengerMessage(message: unknown): message is Message; | ||
export declare function registerMethods(methods: Partial<MessengerMethods>): void; | ||
/** Ensure/document that the current function was called via Messenger */ | ||
export declare function assertMessengerCall(_this: MessengerMeta): asserts _this is MessengerMeta; |
import { serializeError } from "serialize-error"; | ||
import { getContextName } from "webext-detect-page"; | ||
import { messenger } from "./sender.js"; | ||
import { isObject, MessengerError, debug, __webextMessenger, } from "./shared.js"; | ||
import { isObject, MessengerError, log, __webextMessenger } from "./shared.js"; | ||
import { getActionForMessage } from "./thisTarget.js"; | ||
@@ -32,3 +32,3 @@ import { didUserRegisterMethods, handlers } from "./handlers.js"; | ||
const { type, target, args, options = {} } = message; | ||
const { trace = [] } = options; | ||
const { trace = [], seq } = options; | ||
trace.push(sender); | ||
@@ -38,7 +38,7 @@ const meta = { trace }; | ||
if (action === "forward") { | ||
debug(type, "🔀 forwarded", { sender, target }); | ||
log.debug(type, seq, "🔀 forwarded", { sender, target }); | ||
handleMessage = async () => messenger(type, meta, target, ...args); | ||
} | ||
else { | ||
debug(type, "↘️ received in", getContextName(), { | ||
log.debug(type, seq, "↘️ received in", getContextName(), { | ||
sender, | ||
@@ -64,3 +64,3 @@ args, | ||
})); | ||
debug(type, "↗️ responding", response); | ||
log.debug(type, seq, "↗️ responding", response); | ||
return { ...response, __webextMessenger }; | ||
@@ -73,3 +73,3 @@ } | ||
} | ||
debug("Registered", type); | ||
log.debug("Registered", type); | ||
handlers.set(type, method); | ||
@@ -79,1 +79,5 @@ } | ||
} | ||
/** Ensure/document that the current function was called via Messenger */ | ||
export function assertMessengerCall(_this | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function -- TypeScript already does this, it's a documentation-only call | ||
) { } |
@@ -5,3 +5,3 @@ import pRetry from "p-retry"; | ||
import { deserializeError } from "serialize-error"; | ||
import { isObject, MessengerError, __webextMessenger, debug, warn, } from "./shared.js"; | ||
import { isObject, MessengerError, __webextMessenger, log } from "./shared.js"; | ||
import { handlers } from "./handlers.js"; | ||
@@ -16,2 +16,5 @@ const _errorNonExistingTarget = "Could not establish connection. Receiving end does not exist."; | ||
} | ||
function attemptLog(attemptCount) { | ||
return attemptCount > 1 ? `(try: ${attemptCount})` : ""; | ||
} | ||
function makeMessage(type, args, target, options) { | ||
@@ -27,13 +30,13 @@ return { | ||
// Do not turn this into an `async` function; Notifications must turn `void` | ||
function manageConnection(type, options, target, sendMessage) { | ||
if (!options.isNotification) { | ||
return manageMessage(type, target, sendMessage); | ||
function manageConnection(type, { seq, isNotification }, target, sendMessage) { | ||
if (!isNotification) { | ||
return manageMessage(type, target, seq, sendMessage); | ||
} | ||
void sendMessage().catch((error) => { | ||
debug(type, "notification failed", { error }); | ||
void sendMessage(1).catch((error) => { | ||
log.debug(type, seq, "notification failed", { error }); | ||
}); | ||
} | ||
async function manageMessage(type, target, sendMessage) { | ||
const response = await pRetry(async () => { | ||
const response = await sendMessage(); | ||
async function manageMessage(type, target, seq, sendMessage) { | ||
const response = await pRetry(async (attemptCount) => { | ||
const response = await sendMessage(attemptCount); | ||
if (isMessengerResponse(response)) { | ||
@@ -80,3 +83,3 @@ return response; | ||
} | ||
debug(type, "will retry. Attempt", error.attemptNumber); | ||
log.debug(type, seq, "will retry. Attempt", error.attemptNumber); | ||
} | ||
@@ -94,9 +97,12 @@ else { | ||
if ("error" in response) { | ||
debug(type, "↘️ replied with error", response.error); | ||
log.debug(type, seq, "↘️ replied with error", response.error); | ||
throw deserializeError(response.error); | ||
} | ||
debug(type, "↘️ replied successfully", response.value); | ||
log.debug(type, seq, "↘️ replied successfully", response.value); | ||
return response.value; | ||
} | ||
function messenger(type, options, target, ...args) { | ||
// Not a UID. Signal / console noise compromise. They repeat every 100 seconds | ||
options.seq = Date.now() % 100000; | ||
const { seq } = options; | ||
// Message goes to extension page | ||
@@ -107,3 +113,3 @@ if ("page" in target) { | ||
if (handler) { | ||
warn(type, "is being handled locally"); | ||
log.warn(type, seq, "is being handled locally"); | ||
return handler.apply({ trace: [] }, args); | ||
@@ -113,4 +119,4 @@ } | ||
} | ||
const sendMessage = async () => { | ||
debug(type, "↗️ sending message to runtime"); | ||
const sendMessage = async (attemptCount) => { | ||
log.debug(type, seq, "↗️ sending message to runtime", attemptLog(attemptCount)); | ||
return browser.runtime.sendMessage(makeMessage(type, args, target, options)); | ||
@@ -122,4 +128,4 @@ }; | ||
if (!browser.tabs) { | ||
return manageConnection(type, options, target, async () => { | ||
debug(type, "↗️ sending message to runtime"); | ||
return manageConnection(type, options, target, async (attemptCount) => { | ||
log.debug(type, seq, "↗️ sending message to runtime", attemptLog(attemptCount)); | ||
return browser.runtime.sendMessage(makeMessage(type, args, target, options)); | ||
@@ -131,4 +137,4 @@ }); | ||
// Message tab directly | ||
return manageConnection(type, options, target, async () => { | ||
debug(type, "↗️ sending message to tab", tabId, "frame", frameId); | ||
return manageConnection(type, options, target, async (attemptCount) => { | ||
log.debug(type, seq, "↗️ sending message to tab", tabId, "frame", frameId, attemptLog(attemptCount)); | ||
return browser.tabs.sendMessage(tabId, makeMessage(type, args, target, options), { | ||
@@ -135,0 +141,0 @@ frameId, |
@@ -13,4 +13,7 @@ import { type JsonObject } from "type-fest"; | ||
} | ||
export declare const debug: (...args: any[]) => void; | ||
export declare const warn: (...args: any[]) => void; | ||
export declare const log: { | ||
debug: (...args: any[]) => void; | ||
warn: (...args: any[]) => void; | ||
}; | ||
export declare function toggleLogging(enabled: boolean): void; | ||
export declare function isErrorObject(error: unknown): error is ErrorObject; | ||
@@ -17,0 +20,0 @@ export declare function delay(milliseconds: number): Promise<void>; |
import { errorConstructors } from "serialize-error"; | ||
const logging = (() => { | ||
try { | ||
// @ts-expect-error it would break Webpack | ||
return process.env.WEBEXT_MESSENGER_LOGGING === "true"; | ||
} | ||
catch { | ||
return false; | ||
} | ||
})(); | ||
function noop() { | ||
/* */ | ||
} | ||
export const __webextMessenger = true; | ||
@@ -18,2 +6,5 @@ export function isObject(value) { | ||
} | ||
function noop() { | ||
/* */ | ||
} | ||
export class MessengerError extends Error { | ||
@@ -33,4 +24,11 @@ constructor() { | ||
// .bind preserves the call location in the console | ||
export const debug = logging ? console.debug.bind(console, "Messenger:") : noop; | ||
export const warn = logging ? console.warn.bind(console, "Messenger:") : noop; | ||
const debug = console.debug.bind(console, "Messenger:"); | ||
const warn = console.warn.bind(console, "Messenger:"); | ||
export const log = { debug, warn }; | ||
export function toggleLogging(enabled) { | ||
log.debug = enabled ? debug : noop; | ||
log.warn = enabled ? warn : noop; | ||
} | ||
// Default to "no logs" | ||
toggleLogging(false); | ||
export function isErrorObject(error) { | ||
@@ -37,0 +35,0 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a type guard function and it uses ?. |
import { isBackground, isContentScript, isExtensionContext, } from "webext-detect-page"; | ||
import { messenger } from "./sender.js"; | ||
import { registerMethods } from "./receiver.js"; | ||
import { debug, MessengerError, once } from "./shared.js"; | ||
import { log, MessengerError, once } from "./shared.js"; | ||
/** | ||
@@ -87,3 +87,3 @@ * @file This file exists because `runtime.sendMessage` acts as a broadcast to | ||
if (!isThisTarget) { | ||
debug(message.type, "🤫 ignored due to target mismatch", { | ||
log.debug(message.type, "🤫 ignored due to target mismatch", { | ||
requestedTarget: to, | ||
@@ -90,0 +90,0 @@ thisTarget, |
@@ -31,3 +31,3 @@ import { type Runtime } from "webextension-polyfill"; | ||
}; | ||
type Arguments = any[]; | ||
type Arguments = unknown[]; | ||
export type Method = (this: MessengerMeta, ...args: Arguments) => Promise<unknown>; | ||
@@ -41,2 +41,4 @@ export interface Options { | ||
trace?: Sender[]; | ||
/** Automatically generated internally */ | ||
seq?: number; | ||
} | ||
@@ -43,0 +45,0 @@ export type Message<LocalArguments extends Arguments = Arguments> = { |
{ | ||
"name": "webext-messenger", | ||
"version": "0.22.0", | ||
"version": "0.23.0", | ||
"description": "Browser Extension component messaging framework", | ||
@@ -13,3 +13,3 @@ "keywords": [], | ||
"build": "tsc", | ||
"demo:watch": "WEBEXT_MESSENGER_LOGGING=true parcel watch --no-cache --no-hmr", | ||
"demo:watch": "parcel watch --no-cache --no-hmr", | ||
"demo:build": "parcel build --no-cache", | ||
@@ -23,26 +23,26 @@ "prepack": "tsc --sourceMap false", | ||
"dependencies": { | ||
"p-retry": "^5.1.1", | ||
"serialize-error": "^11.0.0", | ||
"type-fest": "^3.2.0", | ||
"webext-detect-page": "^4.0.1", | ||
"webext-tools": "^1.1.0" | ||
"p-retry": "^6.0.0", | ||
"serialize-error": "^11.0.2", | ||
"type-fest": "^4.3.1", | ||
"webext-detect-page": "^4.1.1", | ||
"webext-tools": "^1.1.3" | ||
}, | ||
"devDependencies": { | ||
"@parcel/config-webextension": "^2.8.0", | ||
"@sindresorhus/tsconfig": "^3.0.1", | ||
"@types/chrome": "^0.0.203", | ||
"@types/tape": "^4.13.2", | ||
"@types/webextension-polyfill": "^0.9.0", | ||
"@parcel/config-webextension": "^2.6.2", | ||
"@sindresorhus/tsconfig": "^4.0.0", | ||
"@types/chrome": "^0.0.245", | ||
"@types/tape": "^5.6.1", | ||
"@types/webextension-polyfill": "^0.10.2", | ||
"buffer": "^6.0.3", | ||
"eslint": "^8.28.0", | ||
"eslint-config-pixiebrix": "^0.20.0", | ||
"eslint": "^8.29.0", | ||
"eslint-config-pixiebrix": "^0.27.2", | ||
"events": "^3.3.0", | ||
"npm-run-all": "^4.1.5", | ||
"parcel": "^2.6.0", | ||
"parcel": "^2.6.2", | ||
"path-browserify": "^1.0.1", | ||
"process": "^0.11.10", | ||
"stream-browserify": "^3.0.0", | ||
"tape": "^5.5.3", | ||
"typescript": "^4.9.3", | ||
"webext-content-scripts": "^1.0.2", | ||
"tape": "^5.6.6", | ||
"typescript": "^5.2.2", | ||
"webext-content-scripts": "^2.5.5", | ||
"webextension-polyfill": "^0.10.0" | ||
@@ -49,0 +49,0 @@ }, |
@@ -6,3 +6,3 @@ # webext-messenger [![][badge-gzip]][link-bundlephobia] | ||
> WIP: Browser Extension component messaging framework | ||
> Browser Extension component messaging framework | ||
@@ -21,2 +21,7 @@ ## Install | ||
- [Initial considerations for this library](https://github.com/pixiebrix/webext-messenger/issues/1) | ||
- [Initial considertions for this library](https://github.com/pixiebrix/webext-messenger/issues/1) | ||
## npm publishing | ||
Collaborators can publish a new version of what's on main [via "workflow_dispatch"](https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/) under [Actions » Publish](https://github.com/pixiebrix/webext-messenger/actions/workflows/npm-publish.yml) |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
29241
582
26
1
1
+ Added@types/retry@0.12.2(transitive)
+ Addedis-network-error@1.1.0(transitive)
+ Addedp-retry@6.2.1(transitive)
+ Addedtype-fest@4.33.0(transitive)
- Removed@types/retry@0.12.1(transitive)
- Removedp-retry@5.1.2(transitive)
- Removedtype-fest@3.13.1(transitive)
Updatedp-retry@^6.0.0
Updatedserialize-error@^11.0.2
Updatedtype-fest@^4.3.1
Updatedwebext-detect-page@^4.1.1
Updatedwebext-tools@^1.1.3