diff --git a/Readme.md b/Readme.md index a9bfc24..fdd9046 100644 --- a/Readme.md +++ b/Readme.md @@ -9,16 +9,16 @@ background script from a tab’s content script, or vice versa. In `background.js`: - import { makeRemotelyCallable } from 'webextension-rpc' + import { RpcServer } from 'webextension-rpc' async function myFunc(arg) { return arg * 2 } - makeRemotelyCallable({ myFunc }) + new RpcServer({ myFunc }) In `content_script.js`: - import { remoteFunction } from 'webextension-rpc' - const myRemoteFunc = remoteFunction('myFunc') + import { RpcClient } from 'webextension-rpc' + const myRemoteFunc = new RpcClient().func('myFunc') await myRemoteFunc(21) // 42! Note that the remote function always returns a `Promise`, which resolves with the remote function’s @@ -39,30 +39,42 @@ This module is published [on npm](https://www.npmjs.com/package/webextension-rpc Run `npm install webextension-rpc` or equivalent, and in your code import what you need, e.g.: - import { makeRemotelyCallable } from 'webextension-rpc' - -Or copy its `lib/index.js` and import from that if you prefer (this module has no dependencies). + import { RpcClient, RpcServer } from 'webextension-rpc' ## API -### `remoteFunction(functionName, { tabId })` +### RpcClient + +#### `new RpcClient(options?)` (constructor) + +Instantiate the RpcClient. + +Arguments: +- `options` (object, optional): + - `options.tabId` (number): The id of the tab whose content script is the remote side. Leave undefined or + null to invoke functions in the background script (from a content script). + +#### `func(functionName, options?)` Create a proxy function that invokes the specified remote function. +Arguments: - `functionName` (string, required): name of the function as registered on the remote side. -- `options` (object, optional): - - `tabId` (number): The id of the tab whose content script is the remote side. Leave undefined - to call the background script (from a content script). +- `options` (object, optional): override any options passed to the constructor. + + +### RpcServer -### `makeRemotelyCallable(functions, { insertExtraArg })` +#### `new RpcServer(functions)` (constructor) -Register one or more functions to enable remote scripts to call them. Arguments: +Register one or more functions to enable remote scripts to call them. + +Arguments: - `functions` (object, required): An object with a `{ functionName: function }` mapping. Each function will be remotely callable using the given name. - ### `injectRpcInfo` If the special symbol `injectRpcInfo` is passed as the first argument to a proxy function, this diff --git a/example/background-script.ts b/example/background-script.ts index 1e3faa4..037a564 100644 --- a/example/background-script.ts +++ b/example/background-script.ts @@ -1,19 +1,19 @@ -import { remoteFunction, makeRemotelyCallable } from 'webextension-rpc'; +import { RpcClient, RpcServer } from 'webextension-rpc'; import type { RpcInfo } from 'webextension-rpc'; // Only import the *types* of the remote script’s functions. -import type { contentScriptRemoteFunctions } from './content-script'; +import type { contentScriptRpcServer } from './content-script'; // From background to content script. -const setColour = remoteFunction( - 'setColour', - { tabId: 123 }, -); +const contentScriptRpc = new RpcClient({ tabId: 123 }); +const setColour = contentScriptRpc.func('setColour'); await setColour('blue'); // From content to background script. -export const backgroundScriptRemoteFunctions = makeRemotelyCallable({ +const backgroundScriptRpcServer = new RpcServer({ async duplicateTab(rpcInfo: RpcInfo, active: boolean) { const newTab = await browser.tabs.duplicate(rpcInfo.tab.id, { active }); return newTab.id; }, + async timesTwo(x: number) { return 2 * x }, }); +export type { backgroundScriptRpcServer } diff --git a/example/content-script.ts b/example/content-script.ts index e381709..d9ff878 100644 --- a/example/content-script.ts +++ b/example/content-script.ts @@ -1,22 +1,20 @@ import { - remoteFunction, - makeRemotelyCallable, + RpcClient, + RpcServer, injectRpcInfo, } from 'webextension-rpc'; // Only import the *types* of the remote script’s functions. -import type { backgroundScriptRemoteFunctions } from './background-script'; +import type { backgroundScriptRpcServer } from './background-script'; // From background to content script. -export const contentScriptRemoteFunctions = makeRemotelyCallable({ +const contentScriptRpcServer = new RpcServer({ async setColour(colour: string) { document.body.style.backgroundColor = colour; }, }); +export type { contentScriptRpcServer } // From content to background script. -const duplicateMe = - remoteFunction( - 'duplicateTab', - ); +const duplicateMe = new RpcClient().func('duplicateTab'); // The injectRpcInfo placeholder will be replaced by actual info. const newTabId = await duplicateMe(injectRpcInfo, true); diff --git a/package.json b/package.json index 93edcda..8f8d1da 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,21 @@ "description": "Remote Procedure Call implementation for WebExtensions, to easily call functions across content scripts and background script.", "main": "./lib/index.js", "types": "./lib/index.d.ts", + "exports": { + ".": { + "import": "./lib/index.js", + "types": "./lib/index.d.ts" + }, + "./RpcClient": { + "import": "./lib/RpcClient.js", + "types": "./lib/RpcClient.d.ts" + }, + "./RpcServer": { + "import": "./lib/RpcServer.js", + "types": "./lib/RpcServer.d.ts" + } + }, + "files": ["lib"], "scripts": { "prepare": "tsc", "test": "ava" diff --git a/src/RpcClient.ts b/src/RpcClient.ts new file mode 100644 index 0000000..04c21f8 --- /dev/null +++ b/src/RpcClient.ts @@ -0,0 +1,138 @@ +import type { RpcServer } from "./RpcServer" +import { isRpcResponseMessage, RPC_CALL } from "./common" +import type { AsyncFunction, RpcCallMessage, RpcResponseMessage, ReplaceRpcInfo } from "./common" + +export type { AsyncFunction } + +/** + * Error thrown when the remote function could not be found/executed. + */ +export class RpcError extends Error { + constructor(message: string) { + super(message) + this.name = this.constructor.name + } +} + +/** + * Error thrown when the remote function threw an error. + */ +export class RemoteError extends Error { + constructor(message: string) { + super(message) + this.name = this.constructor.name + } +} + +/** + * If the special symbol `injectRpcInfo` is passed as the first argument to a proxy function, this + * argument will be replaced on the executing side by an `RpcInfo` object. + */ +export const injectRpcInfo = Symbol('RpcInfo') + +export interface RpcOptions { + /** + * The id of the tab whose content script is the remote side. Leave undefined + * to call the background script (from a content script). + */ + tabId?: number, +} + +/** + * RpcFunction equals the function F, except for two tweaks: + * - In the parameters, any RpcInfo is swapped for rpcInfoSymbol. + * - In the return type, nested promises become one promise. + */ +export type RpcFunction = ( + ...args: ReplaceRpcInfo, typeof injectRpcInfo> +) => Promise>> + +type FunctionsOf = R['functions'] + +/** + * Define a ‘connection’ for remote procedure calls. + * @param options.tabId - The id of the tab whose content script is the remote side. Leave undefined + * to call functions of the background script (from a content script). + */ +export class RpcClient { + tabId: number | undefined + + constructor(options: RpcOptions = {}) { + this.tabId = options.tabId; + + } + + /** + * Create a proxy function that invokes the specified remote function. + * @param funcName - Name of the function as registered on the remote side. + * @param options.tabId - Overrides the `tabId` passed to `RpcClient`. + * @returns The proxy function. + */ + func>( + funcName: FunctionName, + { + tabId = this.tabId, + }: RpcOptions = {}, + ): RpcFunction[FunctionName]> { + type RemoteFunction = FunctionsOf[FunctionName] + + const otherSide = (tabId !== undefined) + ? "the tab's content script" + : 'the background script' + + const f: RpcFunction = async function (...args): ReturnType> { + const message: RpcCallMessage = { + __WEBEXTENSION_RPC_MESSAGE__: RPC_CALL, + funcName, + args, + addRpcInfoAsArgument: false, + } + + if (args.includes(injectRpcInfo)) { + const argIndex = args.indexOf(injectRpcInfo) + message.addRpcInfoAsArgument = argIndex + message.args[argIndex] = null + } + + // Try send the message and await the response. + let response: RpcResponseMessage + try { + response = (tabId !== undefined && tabId !== null) + ? await browser.tabs.sendMessage(tabId, message) + : await browser.runtime.sendMessage(message) + } catch (err) {} + + // Check if we got an error or no response. + if (response === undefined) { + throw new RpcError( + `Got no response when trying to call '${funcName}'. ` + + `Did you enable RPC in ${otherSide}?` + ) + } + + // Check if it was *our* listener that responded. + if (!isRpcResponseMessage(response)) { + throw new RpcError( + `RPC got a response from an interfering listener while calling '${funcName}' in ` + + `${otherSide}` + ) + } + + // If we could not invoke the function on the other side, throw an error. + if ('rpcError' in response) { + throw new RpcError(response.rpcError) + } + + // Return the value or throw the error we received from the other side. + if ('errorMessage' in response) { + throw new RemoteError(response.errorMessage) + } else { + return response.returnValue + } + } + + // Give it a name, could be helpful in debugging + Object.defineProperty(f, 'name', { value: `${funcName}_RPC` }) + return f + } +} diff --git a/src/RpcServer.ts b/src/RpcServer.ts new file mode 100644 index 0000000..021277f --- /dev/null +++ b/src/RpcServer.ts @@ -0,0 +1,75 @@ +import { isRpcCallMessage, RPC_RESPONSE } from './common' +import type { AsyncFunction, RpcCallMessage, RpcResponseMessage } from './common' + +export type { AsyncFunction } + +/** + * If the special symbol `injectRpcInfo` is passed as the first argument to a proxy function, this + * argument will be replaced on the executing side by an `RpcInfo` object. + */ + export interface RpcInfo { + tab: browser.tabs.Tab, +} + +/** + * Register one or more functions to enable remote scripts to call them. + * + * @param functions - A `{ functionName: function }` mapping. Each function will be remotely + * callable using the given name. + */ +export class RpcServer = Record> { + constructor( + public readonly functions: Fs, + ) { + browser.runtime.onMessage.addListener(this.incomingRPCListener.bind(this)) + } + + // TODO Avoid conflict if there are multiple listeners. + private incomingRPCListener( + message: any, + sender: browser.runtime.MessageSender, + ): undefined | Promise { + if (!isRpcCallMessage(message)) return + + // TODO Support extension popups and other pages, not just background script & tabs. + // Each page gets the message, so we may need to name each endpoint. + // Then here we should return if the message was not for us. + + return this.executeRpc(message, sender) + } + + private async executeRpc(message: RpcCallMessage, sender: browser.runtime.MessageSender): Promise { + const funcName = message.funcName + const func = this.functions[funcName] + if (func === undefined) { + console.error(`Received RPC for unknown function: ${funcName}`) + return { + rpcError: `No such function registered for RPC: ${funcName}`, + __WEBEXTENSION_RPC_MESSAGE__: RPC_RESPONSE, + } + } + + const args = message.args + + if (message.addRpcInfoAsArgument !== false) { + const rpcInfo: RpcInfo = { + tab: sender.tab, + } + args[message.addRpcInfoAsArgument] = rpcInfo + } + + // Run the function, return the result. + try { + const returnValue = await func(...args) + return { + returnValue, + __WEBEXTENSION_RPC_MESSAGE__: RPC_RESPONSE, + } + } catch (error) { + return { + errorMessage: error.message, + __WEBEXTENSION_RPC_MESSAGE__: RPC_RESPONSE, + } + } + } +} diff --git a/src/common.ts b/src/common.ts new file mode 100644 index 0000000..bbfb65e --- /dev/null +++ b/src/common.ts @@ -0,0 +1,55 @@ +import type { RpcInfo } from './RpcServer' + +export type AsyncFunction = Function & ((...args: any[]) => Promise) + +// Our secret tokens to recognise our messages +export const RPC_CALL = '__RPC_CALL__' +export const RPC_RESPONSE = '__RPC_RESPONSE__' + +export interface RpcMessage { + __WEBEXTENSION_RPC_MESSAGE__: typeof RPC_CALL | typeof RPC_RESPONSE, + funcName: string, +} + +export interface RpcCallMessage extends RpcMessage { + __WEBEXTENSION_RPC_MESSAGE__: typeof RPC_CALL, + funcName: F['name'], + args: ReplaceRpcInfo, null>, + addRpcInfoAsArgument: number | false, +} + +export type RpcResponseMessage = + | RpcResponseResolve + | RpcResponseReject + | RpcResponseRpcError + +interface RpcResponseMessage_base { +__WEBEXTENSION_RPC_MESSAGE__: typeof RPC_RESPONSE, +} + +interface RpcResponseResolve extends RpcResponseMessage_base { + returnValue: ReturnType, +} + +interface RpcResponseReject extends RpcResponseMessage_base { + errorMessage: string, +} + +interface RpcResponseRpcError extends RpcResponseMessage_base { + rpcError: string, +} + +export function isRpcCallMessage(message: any): message is RpcCallMessage { + return !!(message && message['__WEBEXTENSION_RPC_MESSAGE__'] === RPC_CALL) +} + +export function isRpcResponseMessage(message: any): message is RpcResponseMessage { + return !!(message && message['__WEBEXTENSION_RPC_MESSAGE__'] === RPC_RESPONSE) +} + +type Tail = T extends [infer _Head, ...infer Tail] ? Tail : []; + +export type ReplaceRpcInfo, Replacement extends any> = + Params extends [RpcInfo, ...any] + ? [Replacement, ...Tail] + : Params diff --git a/src/index.ts b/src/index.ts index 867e57a..9522103 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,215 +1,2 @@ -// Our secret tokens to recognise our messages -const RPC_CALL = '__RPC_CALL__' -const RPC_RESPONSE = '__RPC_RESPONSE__' - -export class RpcError extends Error { - constructor(message) { - super(message) - this.name = this.constructor.name - } -} - -export class RemoteError extends Error { - constructor(message) { - super(message) - this.name = this.constructor.name - } -} - -export const injectRpcInfo = Symbol('RpcInfo') - -export interface RpcInfo { - tab: browser.tabs.Tab, -} - -type AsyncFunction = Function & ((...args: any[]) => Promise) - -interface RpcMessage { - __WEBEXTENSION_RPC_MESSAGE__: typeof RPC_CALL | typeof RPC_RESPONSE, - funcName: string, -} - -interface RpcCallMessage extends RpcMessage { - __WEBEXTENSION_RPC_MESSAGE__: typeof RPC_CALL, - funcName: F['name'], - args: ParametersWithRpcInfo, - addRpcInfoAsArgument: number | false, -} - -type RpcResponseMessage = - | RpcResponseResolve - | RpcResponseReject - | RpcResponseRpcError - -interface RpcResponseMessage_base { -__WEBEXTENSION_RPC_MESSAGE__: typeof RPC_RESPONSE, -} - -interface RpcResponseResolve extends RpcResponseMessage_base { - returnValue: ReturnType, -} - -interface RpcResponseReject extends RpcResponseMessage_base { - errorMessage: string, -} - -interface RpcResponseRpcError extends RpcResponseMessage_base { - rpcError: string, -} - -function isRpcCallMessage(message: any): message is RpcCallMessage { - return !!(message && message['__WEBEXTENSION_RPC_MESSAGE__'] === RPC_CALL) -} - -function isRpcResponseMessage(message: any): message is RpcResponseMessage { - return !!(message && message['__WEBEXTENSION_RPC_MESSAGE__'] === RPC_RESPONSE) -} - -type Tail = T extends [infer _Head, ...infer Tail] ? Tail : []; - -type ParametersWithRpcInfo = Parameters extends [RpcInfo, ...any] - ? [typeof injectRpcInfo, ...Tail>] - : Parameters - -// I thought the type below should allow putting the RpcInfo argument at any position, but it does not work. -// type ParametersWithRpcInfo = Array & { -// [N in keyof Parameters]: Parameters[N] extends RpcInfo -// ? typeof injectRpcInfo -// : Parameters[N] -// } - -/** - * RpcFunction equals the function F, except for two tweaks: - * - In the parameters, any RpcInfo is swapped for rpcInfoSymbol. - * - In the return type, nested promises become one promise. - */ -export type RpcFunction = (...args: ParametersWithRpcInfo) => Promise>> - - -// === Initiating side === - -/** - * Create a proxy function that invokes the specified remote function. - * @param funcName - Name of the function as registered on the remote side. - * @param options.tabId - The id of the tab whose content script is the remote side. Leave undefined - to call the background script (from a content script). - * @returns The proxy function. - */ -export function remoteFunction( - funcName: string, - { tabId }: { tabId?: number } = {}, -): RpcFunction { - const otherSide = (tabId !== undefined) - ? "the tab's content script" - : 'the background script' - - const f: RpcFunction = async function (...args): Promise>> { - const message: RpcCallMessage = { - __WEBEXTENSION_RPC_MESSAGE__: RPC_CALL, - funcName, - args, - addRpcInfoAsArgument: false, - } - - if (args.includes(injectRpcInfo)) { - const argIndex = args.indexOf(injectRpcInfo) - message.addRpcInfoAsArgument = argIndex - message.args[argIndex] = null - } - - // Try send the message and await the response. - let response: RpcResponseMessage - try { - response = (tabId !== undefined) - ? await browser.tabs.sendMessage(tabId, message) - : await browser.runtime.sendMessage(message) - } catch (err) {} - - // Check if we got an error or no response. - if (response === undefined) { - throw new RpcError( - `Got no response when trying to call '${funcName}'. ` - + `Did you enable RPC in ${otherSide}?` - ) - } - - // Check if it was *our* listener that responded. - if (!isRpcResponseMessage(response)) { - throw new RpcError( - `RPC got a response from an interfering listener while calling '${funcName}' in ` - + `${otherSide}` - ) - } - - // If we could not invoke the function on the other side, throw an error. - if ('rpcError' in response) { - throw new RpcError(response.rpcError) - } - - // Return the value or throw the error we received from the other side. - if ('errorMessage' in response) { - throw new RemoteError(response.errorMessage) - } else { - return response.returnValue - } - } - - // Give it a name, could be helpful in debugging - Object.defineProperty(f, 'name', { value: `${funcName}_RPC` }) - return f -} - - -// === Executing side === - - -/** - * Register one or more functions to enable remote scripts to call them. - * - * @param functions - A `{ functionName: function }` mapping. Each function will be remotely - * callable using the given name. - * @returns The passed `functions` object. - */ -export function makeRemotelyCallable>( - functions: Fs, -): typeof functions { - browser.runtime.onMessage.addListener(incomingRPCListener) - return functions - - async function incomingRPCListener(message: any, sender: browser.runtime.MessageSender): Promise { - if (!isRpcCallMessage(message)) return - - const funcName = message.funcName - const func = functions[funcName] - if (func === undefined) { - console.error(`Received RPC for unknown function: ${funcName}`) - return { - rpcError: `No such function registered for RPC: ${funcName}`, - __WEBEXTENSION_RPC_MESSAGE__: RPC_RESPONSE, - } - } - - const args = message.args - - if (message.addRpcInfoAsArgument !== false) { - const rpcInfo: RpcInfo = { - tab: sender.tab, - } - args[message.addRpcInfoAsArgument] = rpcInfo - } - - // Run the function, return the result. - try { - const returnValue = await func(...args) - return { - returnValue, - __WEBEXTENSION_RPC_MESSAGE__: RPC_RESPONSE, - } - } catch (error) { - return { - errorMessage: error.message, - __WEBEXTENSION_RPC_MESSAGE__: RPC_RESPONSE, - } - } - } -} +export * from './RpcClient' +export * from './RpcServer' diff --git a/test/index.ts b/test/RpcClient.ts similarity index 71% rename from test/index.ts rename to test/RpcClient.ts index 7434260..c7bce12 100644 --- a/test/index.ts +++ b/test/RpcClient.ts @@ -1,7 +1,7 @@ import test from 'ava' import sinon from 'ts-sinon' -import { remoteFunction, RpcError, RemoteError, injectRpcInfo } from '../src/index' +import { RpcClient, RpcError, RemoteError, injectRpcInfo } from '../src/RpcClient' function mockBrowser() { return { @@ -23,13 +23,13 @@ test.beforeEach(() => { }) test.serial('should create a function', t => { - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') t.is(remoteFunc.name, 'remoteFunc_RPC') t.is(typeof remoteFunc, 'function') }) test.serial('should throw an error when unable to sendMessage', async t => { - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') browser.tabs.sendMessage = async () => { throw new Error() } await t.throwsAsync(remoteFunc, { instanceOf: RpcError, @@ -38,7 +38,7 @@ test.serial('should throw an error when unable to sendMessage', async t => { }) test.serial('should call the browser.tabs function when tabId is given', async t => { - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') try { await remoteFunc() } catch (e) {} @@ -47,7 +47,25 @@ test.serial('should call the browser.tabs function when tabId is given', async t }) test.serial('should call the browser.runtime function when tabId is undefined', async t => { - const remoteFunc = remoteFunction('remoteFunc') + const remoteFunc = new RpcClient().func('remoteFunc') + try { + await remoteFunc() + } catch (e) {} + t.true(browser.tabs.sendMessage.notCalled) + t.true(browser.runtime.sendMessage.calledOnce) +}) + +test.serial('should call the browser.tabs function when tabId is overridden', async t => { + const remoteFunc = new RpcClient().func('remoteFunc', { tabId: 123 }) + try { + await remoteFunc() + } catch (e) {} + t.true(browser.tabs.sendMessage.calledOnce) + t.true(browser.runtime.sendMessage.notCalled) +}) + +test.serial('should call the browser.runtime function when tabId is overridden as null', async t => { + const remoteFunc = new RpcClient({ tabId: 123 }).func('remoteFunc', { tabId: null }) try { await remoteFunc() } catch (e) {} @@ -56,7 +74,7 @@ test.serial('should call the browser.runtime function when tabId is undefined', }) test.serial('should send the call message correctly', async t => { - const remoteFunc = remoteFunction('remoteFunc') + const remoteFunc = new RpcClient().func('remoteFunc') try { await remoteFunc('a', 'b', 'c', 'd') } catch {} @@ -70,7 +88,7 @@ test.serial('should send the call message correctly', async t => { }) test.serial('should handle the RpcInfoSymbol', async t => { - const remoteFunc = remoteFunction('remoteFunc') + const remoteFunc = new RpcClient().func('remoteFunc') try { await remoteFunc('a', 'b', injectRpcInfo, 'd') } catch {} @@ -78,14 +96,14 @@ test.serial('should handle the RpcInfoSymbol', async t => { t.deepEqual(browser.runtime.sendMessage.lastCall.args, [{ __WEBEXTENSION_RPC_MESSAGE__: '__RPC_CALL__', funcName: 'remoteFunc', - args: ['a', 'b', injectRpcInfo, 'd'], + args: ['a', 'b', null, 'd'], addRpcInfoAsArgument: 2, }]) }) test.serial('should throw an "interfering listener" error if response is unrecognised', async t => { browser.tabs.sendMessage = async () => 'some unexpected return value' - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') await t.throwsAsync(remoteFunc, { instanceOf: RpcError, message: /RPC got a response from an interfering listener/, @@ -96,7 +114,7 @@ test.serial('should throw a "no response" error if response is undefined', async // It seems we can get back undefined when the tab is closed before the response is sent. // In such cases 'no response' seems a better error message than 'interfering listener'. browser.tabs.sendMessage = async () => undefined - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') await t.throwsAsync(remoteFunc, { instanceOf: RpcError, message: /Got no response/, @@ -108,7 +126,7 @@ test.serial('should throw RemoteError if the response contains an error message' __WEBEXTENSION_RPC_MESSAGE__: '__RPC_RESPONSE__', errorMessage: 'Remote function error', }) - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') await t.throwsAsync(remoteFunc, { instanceOf: RemoteError, message: 'Remote function error', @@ -120,8 +138,6 @@ test.serial('should return the value contained in the response', async t => { __WEBEXTENSION_RPC_MESSAGE__: '__RPC_RESPONSE__', returnValue: 'Remote function return value', }) - const remoteFunc = remoteFunction('remoteFunc', { tabId: 1 }) + const remoteFunc = new RpcClient({ tabId: 1 }).func('remoteFunc') t.is(await remoteFunc(), 'Remote function return value') }) - -// TODO Test behaviour of executing side. diff --git a/tsconfig.json b/tsconfig.json index 6f0cc17..0503ca3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,9 +4,7 @@ "declaration": true, "outDir": "lib" }, - "files": [ - "src/index.ts" - ], + "include": ["src"], "ts-node": { "transpileOnly": true }