From 9f95a4f602745c5617c911a7c6bcecbac2ac97ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Gaudencio=20do=20R=C3=AAgo?= Date: Mon, 9 Sep 2024 13:43:25 -0300 Subject: [PATCH] Avoid ctx binding (#842) * Avoid signal binding on server side * Avoid ctx binding on arrow func --- vtex/hooks/context.ts | 6 ++-- website/handlers/fresh.ts | 33 ++++++++++++++++++++-- website/handlers/router.ts | 56 ++++++++++++++++++++------------------ 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/vtex/hooks/context.ts b/vtex/hooks/context.ts index 7775114bc..6fbe86d43 100644 --- a/vtex/hooks/context.ts +++ b/vtex/hooks/context.ts @@ -13,9 +13,9 @@ export interface Context { const loading = signal(true); const context = { - cart: signal(null), - user: signal(null), - wishlist: signal(null), + cart: IS_BROWSER && signal(null) || null, + user: IS_BROWSER && signal(null) || null, + wishlist: IS_BROWSER && signal(null) || null, }; let queue = Promise.resolve(); diff --git a/website/handlers/fresh.ts b/website/handlers/fresh.ts index 4c65d0462..8a77109b7 100644 --- a/website/handlers/fresh.ts +++ b/website/handlers/fresh.ts @@ -27,6 +27,33 @@ export const isFreshCtx = ( return typeof (ctx as HandlerContext).render === "function"; }; +function abortHandler(ctrl: AbortController, signal: AbortSignal) { + let aborted = false; + const abortCtrlInstance = () => { + if (aborted) return; // Early return if already handled + + try { + if (!ctrl.signal.aborted) { + ctrl?.abort(); + aborted = true; // Mark as aborted after calling abort + } + } catch (_err) { + // We tried our best, but it is already dead... so.. lets ignore it :) + } finally { + signal.removeEventListener("abort", abortCtrlInstance); + } + }; + return abortCtrlInstance; +} + +function registerFinilizer(req: Request, abortCtrl: () => void) { + const finalizer = new FinalizationRegistry((abortCtrl: () => void) => { + req.signal.removeEventListener("abort", abortCtrl); + }); + + finalizer.register(req, abortCtrl); +} + /** * @title Fresh Page * @description Renders a fresh page. @@ -57,10 +84,12 @@ export default function Fresh( /** Controller to abort third party fetch (loaders) */ const ctrl = new AbortController(); + const abortCtrl = abortHandler(ctrl, req.signal); /** Aborts when: Incomming request is aborted */ - const abortHandler = () => ctrl.abort(); - req.signal.addEventListener("abort", abortHandler); + req.signal.addEventListener("abort", abortCtrl, { once: true }); + + registerFinilizer(req, abortCtrl); /** * Aborts when: diff --git a/website/handlers/router.ts b/website/handlers/router.ts index 02746ec04..6ab3481d1 100644 --- a/website/handlers/router.ts +++ b/website/handlers/router.ts @@ -159,6 +159,34 @@ const RouterId = { const routerCache = new weakcache.WeakLRUCache({ cacheSize: 16, // up to 16 different routers stored here. }); + +const prepareRoutes = (audiences: Routes[], ctx: AppContext) => { + const routesFromProps = Array.isArray(audiences) ? audiences : []; + // everyone should come first in the list given that we override the everyone value with the upcoming flags. + const [routes, hrefRoutes] = buildRoutes( + Array.isArray(ctx.routes) + ? [...ctx.routes, ...routesFromProps] + : routesFromProps, + ); + // build the router from entries + const builtRoutes = Object.entries(routes).sort(( + [routeStringA, { highPriority: highPriorityA }], + [routeStringB, { highPriority: highPriorityB }], + ) => + (highPriorityB ? HIGH_PRIORITY_ROUTE_RANK_BASE_VALUE : 0) + + rankRoute(routeStringB) - + ((highPriorityA ? HIGH_PRIORITY_ROUTE_RANK_BASE_VALUE : 0) + + rankRoute(routeStringA)) + ); + return { + routes: builtRoutes.map((route) => ({ + pathTemplate: route[0], + handler: { value: route[1].func }, + })), + hrefRoutes, + }; +}; + /** * @title Router * @description Route requests based on audience @@ -181,35 +209,9 @@ export default function RoutesSelection( const timing = monitoring?.timings.start("router"); - const prepareRoutes = () => { - const routesFromProps = Array.isArray(audiences) ? audiences : []; - // everyone should come first in the list given that we override the everyone value with the upcoming flags. - const [routes, hrefRoutes] = buildRoutes( - Array.isArray(ctx.routes) - ? [...ctx.routes, ...routesFromProps] - : routesFromProps, - ); - // build the router from entries - const builtRoutes = Object.entries(routes).sort(( - [routeStringA, { highPriority: highPriorityA }], - [routeStringB, { highPriority: highPriorityB }], - ) => - (highPriorityB ? HIGH_PRIORITY_ROUTE_RANK_BASE_VALUE : 0) + - rankRoute(routeStringB) - - ((highPriorityA ? HIGH_PRIORITY_ROUTE_RANK_BASE_VALUE : 0) + - rankRoute(routeStringA)) - ); - return { - routes: builtRoutes.map((route) => ({ - pathTemplate: route[0], - handler: { value: route[1].func }, - })), - hrefRoutes, - }; - }; const routerId = `${RouterId.fromFlags(ctx.flags)}/${ctx.revision ?? ""}`; if (!routerCache.has(routerId)) { - routerCache.setValue(routerId, prepareRoutes()); + routerCache.setValue(routerId, prepareRoutes(audiences, ctx)); } const { routes, hrefRoutes }: { routes: Route[];