Skip to content

Commit

Permalink
Merge pull request #4 from janek26/bugfix/cross-origin-loading-times
Browse files Browse the repository at this point in the history
fix: cross origin loading times
  • Loading branch information
janek26 authored Jul 6, 2023
2 parents 69305f3 + 8919ebf commit 7dd795a
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/adapter/window.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AnyProcedure, AnyRouter, TRPCError } from '@trpc/server';
import { Unsubscribable, isObservable } from '@trpc/server/observable';

import { TRPC_BROWSER_LOADED_EVENT } from '../shared/constants';
import { isTRPCRequestWithId } from '../shared/trpcMessage';
import type { MinimalWindow, TRPCChromeResponse } from '../types';
import { CreateHandlerOptions } from './base';
Expand All @@ -22,6 +23,9 @@ export const createWindowHandler = <TRouter extends AnyRouter>(
return;
}

const loadListener = opts.postWindow ?? window.opener ?? window;
loadListener.postMessage(TRPC_BROWSER_LOADED_EVENT, { targetOrigin: postOrigin });

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { transformer } = router._def._config;
const subscriptions = new Map<number | string, Unsubscribable>();
Expand Down
28 changes: 22 additions & 6 deletions src/link/popup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { TRPCLink } from '@trpc/client';
import type { AnyRouter } from '@trpc/server';

import { TRPC_BROWSER_LOADED_EVENT } from '../shared/constants';
import type { MinimalPopupWindow, MinimalWindow, TRPCChromeMessage } from '../types';
import { createBaseLink } from './internal/base';

Expand All @@ -18,17 +19,32 @@ export const popupLink = <TRouter extends AnyRouter>(opts: PopupLinkOptions): TR
const closeHandlerSet = new Set<() => void>();

let popupWindow: MinimalPopupWindow | null = null;
async function getPopup() {
async function getPopup(loadListenWindow: MinimalWindow) {
if (!popupWindow || popupWindow.closed) {
popupWindow = opts.createPopup();
// wait til window is loaded
await Promise.race([
// wait til window is loaded (same origin)
new Promise((resolve) => {
popupWindow?.addEventListener?.('load', resolve);
try {
popupWindow?.addEventListener?.('load', resolve);
} catch {
// if this throws, it's a cross-origin popup and should stay pending (never resolve)
}
}),
// this is needed for cross-origin popups as they don't have a load event
new Promise<void>((resolve) => {
loadListenWindow.addEventListener('message', (event) => {
if (event.data === TRPC_BROWSER_LOADED_EVENT) {
resolve();
}
});
}),
// expect the popup to load within 2.5s, this is needed for cross-origin popups as they don't have a load event
// expect the popup to load after 15s max, in case non of the above events fire
new Promise((resolve) => {
setTimeout(resolve, 2500);
console.warn(
'Could not detect if popup loading succeeded after 15s timeout, continuing anyway',
);
setTimeout(resolve, 15000);
}),
]);

Expand Down Expand Up @@ -59,7 +75,7 @@ export const popupLink = <TRouter extends AnyRouter>(opts: PopupLinkOptions): TR

return createBaseLink({
async postMessage(message) {
const popup = await getPopup();
const popup = await getPopup(opts.listenWindow);
return popup.postMessage(message, {
targetOrigin: opts.postOrigin,
});
Expand Down
1 change: 1 addition & 0 deletions src/shared/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TRPC_BROWSER_LOADED_EVENT = 'TRPC_BROWSER::POPUP_LOADED';
9 changes: 5 additions & 4 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type TRPCChromeRequest = {
};

export type TRPCChromeSuccessResponse = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
trpc: TRPCResultMessage<any>;
};

Expand All @@ -22,10 +23,10 @@ export type TRPCChromeResponse = TRPCChromeSuccessResponse | TRPCChromeErrorResp
export type TRPCChromeMessage = TRPCChromeRequest | TRPCChromeResponse;
export type RelayedTRPCMessage = TRPCChromeMessage & { relayed?: true };

export type MinimalWindow = Pick<
Window,
'postMessage' | 'addEventListener' | 'removeEventListener'
>;
export interface MinimalWindow
extends Pick<Window, 'postMessage' | 'addEventListener' | 'removeEventListener'> {
opener?: MinimalWindow;
}

export type MinimalPopupWindow = Pick<Window, 'postMessage' | 'closed'> &
// addEventListener/removeEventListener are only available on the same origin
Expand Down

0 comments on commit 7dd795a

Please sign in to comment.