trpc-browser
Advanced tools
Comparing version
@@ -7,2 +7,3 @@ import { AnyRouter } from '@trpc/server'; | ||
postWindow?: MinimalWindow; | ||
postOrigin?: string; | ||
}; | ||
@@ -9,0 +10,0 @@ type WindowContextOptions = { |
@@ -9,3 +9,3 @@ "use strict"; | ||
const createWindowHandler = (opts) => { | ||
const { router, createContext, onError, window } = opts; | ||
const { router, createContext, onError, window, postOrigin } = opts; | ||
if (!window) { | ||
@@ -32,3 +32,3 @@ console.warn("Skipping window handler creation: 'opts.window' not defined"); | ||
trpc: Object.assign({ id: trpc.id, jsonrpc: trpc.jsonrpc }, response), | ||
}); | ||
}, { targetOrigin: postOrigin }); | ||
}; | ||
@@ -35,0 +35,0 @@ if (trpc.method === 'subscription.stop') { |
@@ -7,4 +7,5 @@ import type { TRPCLink } from '@trpc/client'; | ||
listenWindow: MinimalWindow; | ||
postOrigin?: string; | ||
}; | ||
export declare const popupLink: <TRouter extends AnyRouter>(opts: PopupLinkOptions) => TRPCLink<TRouter>; | ||
//# sourceMappingURL=popup.d.ts.map |
@@ -7,20 +7,4 @@ "use strict"; | ||
const messageHandlerMap = new Map(); | ||
const popupHandlerMap = new Map(); | ||
const closeHandlerSet = new Set(); | ||
let popupWindow = null; | ||
function attachHandlerMap() { | ||
if (!popupWindow) { | ||
return; | ||
} | ||
for (const [event, handler] of popupHandlerMap) { | ||
popupWindow.addEventListener(event, handler); | ||
} | ||
} | ||
function detachHandlerMap() { | ||
if (!popupWindow) { | ||
return; | ||
} | ||
for (const [event, handler] of popupHandlerMap) { | ||
popupWindow.removeEventListener(event, handler); | ||
} | ||
} | ||
async function getPopup() { | ||
@@ -30,11 +14,33 @@ if (!popupWindow || popupWindow.closed) { | ||
// wait til window is loaded | ||
await new Promise((resolve) => { | ||
popupWindow === null || popupWindow === void 0 ? void 0 : popupWindow.addEventListener('load', resolve); | ||
}); | ||
// subscribe to close events | ||
attachHandlerMap(); | ||
await Promise.race([ | ||
new Promise((resolve) => { | ||
var _a; | ||
(_a = popupWindow === null || popupWindow === void 0 ? void 0 : popupWindow.addEventListener) === null || _a === void 0 ? void 0 : _a.call(popupWindow, 'load', resolve); | ||
}), | ||
// expect the popup to load within 2.5s, this is needed for cross-origin popups as they don't have a load event | ||
new Promise((resolve) => { | ||
setTimeout(resolve, 2500); | ||
}), | ||
]); | ||
// subscribe to popup closing | ||
popupWindow.addEventListener('beforeunload', () => { | ||
popupWindow = null; | ||
}); | ||
try { | ||
if (!popupWindow.addEventListener) { | ||
throw new Error('popupWindow.addEventListener is not a function'); | ||
} | ||
popupWindow.addEventListener('beforeunload', () => { | ||
popupWindow = null; | ||
}); | ||
} | ||
catch (_a) { | ||
// this throws on cross-origin popups, fallback to polling to check if popup is closed | ||
const pid = setInterval(() => { | ||
if (popupWindow && popupWindow.closed) { | ||
popupWindow = null; | ||
closeHandlerSet.forEach((handler) => { | ||
handler(); | ||
}); | ||
clearInterval(pid); | ||
} | ||
}, 1000); | ||
} | ||
} | ||
@@ -44,7 +50,6 @@ return popupWindow; | ||
return (0, base_1.createBaseLink)({ | ||
postMessage(message) { | ||
return getPopup().then((popup) => { | ||
return popup.postMessage(message, '*'); | ||
}, () => { | ||
throw new Error('Could not open popup'); | ||
async postMessage(message) { | ||
const popup = await getPopup(); | ||
return popup.postMessage(message, { | ||
targetOrigin: opts.postOrigin, | ||
}); | ||
@@ -67,9 +72,7 @@ }, | ||
opts.listenWindow.addEventListener('beforeunload', listener); | ||
popupHandlerMap.set('beforeunload', listener); | ||
attachHandlerMap(); | ||
closeHandlerSet.add(listener); | ||
}, | ||
removeCloseListener(listener) { | ||
opts.listenWindow.removeEventListener('beforeunload', listener); | ||
popupHandlerMap.delete('beforeunload'); | ||
detachHandlerMap(); | ||
closeHandlerSet.delete(listener); | ||
}, | ||
@@ -76,0 +79,0 @@ }); |
@@ -7,4 +7,5 @@ import type { TRPCLink } from '@trpc/client'; | ||
postWindow?: MinimalWindow; | ||
postOrigin?: string; | ||
}; | ||
export declare const windowLink: <TRouter extends AnyRouter>(opts: WindowLinkOptions) => TRPCLink<TRouter>; | ||
//# sourceMappingURL=window.d.ts.map |
@@ -12,3 +12,5 @@ "use strict"; | ||
postMessage(message) { | ||
postWindow.postMessage(message, '*'); | ||
postWindow.postMessage(message, { | ||
targetOrigin: opts.postOrigin, | ||
}); | ||
}, | ||
@@ -15,0 +17,0 @@ addMessageListener(listener) { |
{ | ||
"name": "trpc-browser", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "tRPC adapters and links for everywhere in the browser", | ||
@@ -5,0 +5,0 @@ "author": "Janek Rahrt <janek.rahrt@me.com>, James Berry <jb@jamesbe.com>", |
@@ -22,2 +22,4 @@  | ||
- [**π tRPC for everything in the browser**](#-trpc-for-everything-in-the-browser) | ||
- [π Table of contents](#-table-of-contents) | ||
- [π¦ Installation](#-installation) | ||
@@ -29,2 +31,8 @@ - [π§© Example usage for extensions](#-example-usage-for-extensions) | ||
- [π Types](#-types) | ||
- [ChromeLinkOptions](#chromelinkoptions) | ||
- [WindowLinkOptions](#windowlinkoptions) | ||
- [PopupLinkOptions](#popuplinkoptions) | ||
- [CreateChromeHandlerOptions](#createchromehandleroptions) | ||
- [CreateWindowHandlerOptions](#createwindowhandleroptions) | ||
- [Cross-origin support](#cross-origin-support) | ||
- [Β©οΈ License](#οΈ-license) | ||
@@ -191,5 +199,7 @@ - [ποΈ Contact](#οΈ-contact) | ||
| Property | Type | Description | Required | | ||
| -------- | -------- | ----------------------------------------------- | -------- | | ||
| `window` | `Window` | A window object which is listened to by a relay | `true` | | ||
| Property | Type | Description | Required | | ||
| ------------ | -------- | --------------------------------------------------------- | -------- | | ||
| `window` | `Window` | A window object which is listened to | `true` | | ||
| `postWindow` | `Window` | A window object which is posted to (defaults to `window`) | `false` | | ||
| `postOrigin` | `string` | The targetOrigin passed to `opts.postWindow.postMessage` | `false` | | ||
@@ -200,6 +210,7 @@ ### PopupLinkOptions | ||
| Property | Type | Description | Required | | ||
| -------------- | ---------- | ----------------------------------------------- | -------- | | ||
| `listenWindow` | `Window` | A window object which is listened to by a relay | `true` | | ||
| `createPopup` | `Function` | A function that returns a window object. | `true` | | ||
| Property | Type | Description | Required | | ||
| -------------- | ---------- | ---------------------------------------------- | -------- | | ||
| `listenWindow` | `Window` | A window object which is listened to | `true` | | ||
| `createPopup` | `Function` | A function that returns a window object. | `true` | | ||
| `postOrigin` | `string` | The targetOrigin passed to `popup.postMessage` | `false` | | ||
@@ -221,12 +232,17 @@ ### CreateChromeHandlerOptions | ||
| Property | Type | Description | Required | | ||
| --------------- | ---------- | -------------------------------------------------------------------------------------------------- | -------- | | ||
| `router` | `Router` | Your application tRPC router. | `true` | | ||
| `createContext` | `Function` | Passes contextual (`ctx`) data to procedure resolvers. | `false` | | ||
| `onError` | `Function` | Called if error occurs inside handler. | `false` | | ||
| `window` | `Window` | Window object to subscribe to | `true` | | ||
| `postWindow` | `Window` | Window object to post messages to. (default: `MessageEvent.source` with fallback to `opts.window`) | `false` | | ||
| Property | Type | Description | Required | | ||
| --------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | | ||
| `router` | `Router` | Your application tRPC router. | `true` | | ||
| `createContext` | `Function` | Passes contextual (`ctx`) data to procedure resolvers. | `false` | | ||
| `onError` | `Function` | Called if error occurs inside handler. | `false` | | ||
| `window` | `Window` | Window object to subscribe to | `true` | | ||
| `postWindow` | `Window` | Window object to post messages to. (default: `MessageEvent.source` with fallback to `opts.window`) | `false` | | ||
| `postOrigin` | `string` | The targetOrigin passed to `opts.postWindow.postMessage`. If you want to answer to all messages from all origins you should pass `'*'`, by default it will only work on same origin | `false` | | ||
--- | ||
## Cross-origin support | ||
When posting from a link you can specify the `postOrigin` option. This will be passed to `postMessage` as the `targetOrigin` argument. If you want to answer to all messages from all origins you should pass `'*'`, by default it will only work on same origin. | ||
## Β©οΈ License | ||
@@ -233,0 +249,0 @@ |
@@ -11,8 +11,5 @@ "use strict"; | ||
const relayedMessage = Object.assign(Object.assign({}, message), { relayed: true }); | ||
if (windowPostOrigin) { | ||
window.postMessage(relayedMessage, windowPostOrigin); | ||
} | ||
else { | ||
window.postMessage(relayedMessage); | ||
} | ||
window.postMessage(relayedMessage, { | ||
targetOrigin: windowPostOrigin, | ||
}); | ||
} | ||
@@ -19,0 +16,0 @@ function relayToPort(message) { |
@@ -17,5 +17,3 @@ import type { TRPCClientOutgoingMessage, TRPCErrorResponse, TRPCRequest, TRPCResultMessage } from '@trpc/server/rpc'; | ||
export type MinimalWindow = Pick<Window, 'postMessage' | 'addEventListener' | 'removeEventListener'>; | ||
export type MinimalPopupWindow = MinimalWindow & { | ||
closed: boolean; | ||
}; | ||
export type MinimalPopupWindow = Pick<Window, 'postMessage' | 'closed'> & Partial<Pick<Window, 'addEventListener' | 'removeEventListener'>>; | ||
export interface MessengerMethods { | ||
@@ -22,0 +20,0 @@ postMessage: (message: TRPCChromeMessage) => void; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
65993
4.22%670
0.45%251
6.81%