@tokenoftrust/storefront-runner 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The document's server response time for the evidence panel
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The document's server response time for the evidence panel, measured in the viewer's browser
|
|
3
|
+
* (a Worker fetching its own zone is answered by the origin, never by this Worker, so a
|
|
4
|
+
* server-side self-fetch cannot see the render). The panel script fetches the home document
|
|
5
|
+
* same-origin with the viewer's credentials and `__preview` grant, then reports the `total` the
|
|
6
|
+
* Worker stamps in `Server-Timing` (`lib/observability/shared/serverTiming.ts`) beside the time to
|
|
7
|
+
* first byte. Advisory — never gates promotion.
|
|
5
8
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* than as a number.
|
|
9
|
+
* A gate page or any non-200 answer is not the render a reviewer gets, so it is reported as not
|
|
10
|
+
* measured rather than as a number.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
export interface ServerResponseTime {
|
|
12
14
|
measured: true;
|
|
13
15
|
/** `total;dur` from the document's `Server-Timing`, or null when the header is absent. */
|
|
14
16
|
serverTotalMs: number | null;
|
|
15
|
-
/**
|
|
17
|
+
/** Time to first byte. */
|
|
16
18
|
ttfbMs: number;
|
|
17
19
|
/** The isolate was serving its first request (`isolate;desc="cold"`). */
|
|
18
20
|
cold: boolean;
|
|
@@ -23,6 +25,8 @@ export interface ServerResponseTimeUnmeasured {
|
|
|
23
25
|
reason: string;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
export type ServerResponseTimeResult = ServerResponseTime | ServerResponseTimeUnmeasured;
|
|
29
|
+
|
|
26
30
|
/** Pull `total;dur` and the cold marker out of a `Server-Timing` header value. */
|
|
27
31
|
export function parseServerTiming(header: string | null | undefined): Pick<ServerResponseTime, "serverTotalMs" | "cold"> {
|
|
28
32
|
if (!header) return { serverTotalMs: null, cold: false };
|
|
@@ -34,35 +38,54 @@ export function parseServerTiming(header: string | null | undefined): Pick<Serve
|
|
|
34
38
|
};
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const headers = new Headers();
|
|
47
|
-
if (cookie) headers.set("cookie", cookie);
|
|
48
|
-
return { url, headers };
|
|
41
|
+
/** Turn the home document's response into a measurement, or the reason it is not one. */
|
|
42
|
+
export function classifyHomeResponse(
|
|
43
|
+
res: { status: number; headers: { get(name: string): string | null } },
|
|
44
|
+
ttfbMs: number,
|
|
45
|
+
): ServerResponseTimeResult {
|
|
46
|
+
const gate = res.headers.get("x-tot-gate");
|
|
47
|
+
if (gate) return { measured: false, reason: `the fetch was answered by the access gate (${gate}), not the store render` };
|
|
48
|
+
if (res.status !== 200) return { measured: false, reason: `the home document answered ${res.status}, not a render` };
|
|
49
|
+
return { measured: true, ...parseServerTiming(res.headers.get("server-timing")), ttfbMs: Math.round(ttfbMs) };
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
/**
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
/** The sentence the panel shows for a result. */
|
|
53
|
+
export function describeServerResponseTime(result: ServerResponseTimeResult): string {
|
|
54
|
+
if (!result.measured) return `Not measured: ${result.reason}.`;
|
|
55
|
+
const server = result.serverTotalMs !== null ? `${result.serverTotalMs} ms server total (Server-Timing)` : "no Server-Timing header";
|
|
56
|
+
return `Home document: ${server} · ${result.ttfbMs} ms to first byte${result.cold ? " · cold isolate" : ""}. A hosted preview is a Worker render; a published static site is served from the CDN and is faster.`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface BrowserDeps {
|
|
60
|
+
fetch: typeof fetch;
|
|
61
|
+
/** Resource-timing entries for a URL (`performance.getEntriesByName`). */
|
|
62
|
+
entries: (url: string) => PerformanceEntry[];
|
|
63
|
+
now: () => number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fetch the home document as the viewer and measure it. TTFB is the resource-timing
|
|
68
|
+
* `responseStart - requestStart` for that request, falling back to the wall time to headers.
|
|
69
|
+
*/
|
|
70
|
+
export async function measureHomeDocument(homeUrl: URL, deps: BrowserDeps): Promise<ServerResponseTimeResult> {
|
|
56
71
|
try {
|
|
57
|
-
const started = now();
|
|
58
|
-
const res = await fetch(
|
|
59
|
-
const
|
|
60
|
-
await res.
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return { measured: true, ...parseServerTiming(res.headers.get("server-timing")), ttfbMs: Math.round(ttfbMs) };
|
|
72
|
+
const started = deps.now();
|
|
73
|
+
const res = await deps.fetch(homeUrl.href, { credentials: "include", redirect: "manual", cache: "no-store" });
|
|
74
|
+
const wallMs = deps.now() - started;
|
|
75
|
+
await res.arrayBuffer();
|
|
76
|
+
const timing = deps.entries(homeUrl.href).at(-1) as PerformanceResourceTiming | undefined;
|
|
77
|
+
const ttfbMs = timing && timing.responseStart > 0 && timing.requestStart > 0 ? timing.responseStart - timing.requestStart : wallMs;
|
|
78
|
+
return classifyHomeResponse(res, ttfbMs);
|
|
65
79
|
} catch {
|
|
66
80
|
return { measured: false, reason: "the home document could not be fetched" };
|
|
67
81
|
}
|
|
68
82
|
}
|
|
83
|
+
|
|
84
|
+
/** Measure the home document and write the sentence into `target`. */
|
|
85
|
+
export async function mountServerResponseTime(target: Element, pageUrl: string, deps: BrowserDeps): Promise<void> {
|
|
86
|
+
const page = new URL(pageUrl);
|
|
87
|
+
const homeUrl = new URL(target.getAttribute("data-home-path") ?? "/", page.origin);
|
|
88
|
+
const grant = page.searchParams.get("__preview");
|
|
89
|
+
if (grant) homeUrl.searchParams.set("__preview", grant);
|
|
90
|
+
target.textContent = describeServerResponseTime(await measureHomeDocument(homeUrl, deps));
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|