Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Min threshold #400

Merged
merged 3 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"std/": "https://deno.land/std@0.204.0/",
"partytown/": "https://deno.land/x/partytown@0.4.8/",
"deco-sites/std/": "https://denopkg.com/deco-sites/std@1.24.2/",
"deco/": "https://denopkg.com/deco-cx/deco@1.53.9/"
"deco/": "https://denopkg.com/deco-cx/deco@1.54.0/"
},
"lock": false,
"tasks": {
Expand Down
48 changes: 40 additions & 8 deletions website/handlers/fresh.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HandlerContext } from "$fresh/server.ts";
import { Page } from "deco/blocks/page.tsx";
import { RequestContext } from "deco/deco.ts";
import {
asResolved,
BaseContext,
Expand Down Expand Up @@ -30,22 +31,52 @@ export const isFreshCtx = <TState>(
*/
export default function Fresh(
freshConfig: FreshConfig,
appContext: Pick<AppContext, "monitoring" | "response" | "caching">,
appContext: Pick<
AppContext,
"monitoring" | "response" | "caching" | "firstByteThresholdMS" | "isBot"
>,
) {
return async (req: Request, ctx: ConnInfo) => {
if (req.method === "HEAD") {
return new Response(null, { status: 200 });
}
const timing = appContext?.monitoring?.timings?.start?.("load-data");
const url = new URL(req.url);
const asJson = url.searchParams.get("asJson");
const delay = Number(
url.searchParams.get("__decoFBT") ?? appContext.firstByteThresholdMS,
);

/** Controller to abort third party fetch (loaders) */
const ctrl = new AbortController();

/** Aborts when: Incomming request is aborted */
req.signal.addEventListener("abort", () => ctrl.abort());

/**
* Aborts when:
*
* 1. Page is HTML
* 2. Async Rendering Feature is activated
* 3. Is not a bot (bot requires the whole page html for boosting SEO)
*/
const firstByteThreshold = !asJson && delay && !appContext.isBot
? setTimeout(() => ctrl.abort(), delay)
: undefined;

const getPage = RequestContext.bind(
{ signal: ctrl.signal },
async () =>
isDeferred<Page, BaseContext & { context: ConnInfo }>(freshConfig.page)
? await freshConfig.page({ context: ctx })
: freshConfig.page,
);

const page = await appContext?.monitoring?.tracer?.startActiveSpan?.(
"load-data",
async (span) => {
try {
return isDeferred<Page, BaseContext & { context: ConnInfo }>(
freshConfig.page,
)
? await freshConfig.page({ context: ctx })
: freshConfig.page;
return await getPage();
} catch (e) {
span.recordException(e);
throw e;
Expand All @@ -56,8 +87,9 @@ export default function Fresh(
},
);

const url = new URL(req.url);
if (url.searchParams.get("asJson") !== null) {
clearTimeout(firstByteThreshold);

if (asJson !== null) {
return Response.json(page, { headers: allowCorsFor(req) });
}
if (isFreshCtx<DecoState>(ctx)) {
Expand Down
26 changes: 2 additions & 24 deletions website/matchers/device.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MatchContext } from "deco/blocks/matcher.ts";
import { UAParser } from "https://esm.sh/ua-parser-js@1.0.35";

/**
* @title {{{.}}}
Expand Down Expand Up @@ -29,42 +28,21 @@ interface OldProps {
devices: Device[];
}

const ideviceToDevice: Record<string, Device> = {
mobile: "mobile",
tablet: "tablet",
console: "desktop",
smarttv: "desktop",
wearable: "desktop",
embedded: "desktop",
};

/**
* @title Device
* @description Target users based on their device type, such as desktop, tablet, or mobile
* @icon device-mobile
*/
const MatchDevice = (
{ mobile, tablet, desktop, ...rest }: Props,
{ request }: MatchContext,
{ device }: MatchContext,
) => {
const devices = (rest as OldProps)?.devices ?? [];
mobile && devices.push("mobile");
tablet && devices.push("tablet");
desktop && devices.push("desktop");
const url = new URL(request.url);
const ua: string | null = request.headers.get("user-agent") || "";
// use cf hint at first and then fallback to user-agent parser.
const cfDeviceHint: string | null = request.headers.get("cf-device-type") ||
"";

const device = cfDeviceHint ||
(ua && new UAParser(ua).getDevice().type) ||
url.searchParams.get("deviceHint") ||
"desktop"; // console, mobile, tablet, smarttv, wearable, embedded

const normalizedDevice = ideviceToDevice[device] ?? "desktop";

return devices.includes(normalizedDevice);
return devices.includes(device);
};

export default MatchDevice;
7 changes: 7 additions & 0 deletions website/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ export interface Props {
* @description the caching configuration
*/
caching?: Caching;

/**
* @title Async Rendering
* @description Number of milliseconds to wait before rendering preview. Set to 0 to disable it.
* @default 0
*/
firstByteThresholdMS?: 0 | 1 | 100 | 300 | 500 | 700;
}

/**
Expand Down
Loading