Skip to content

Commit

Permalink
Upgrade web-vitals to v4 (#11094)
Browse files Browse the repository at this point in the history
  • Loading branch information
delucis authored May 18, 2024
1 parent b41cec6 commit 3c7a4fa
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/many-icons-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/web-vitals": minor
---

Upgrades the `web-vitals` dependency to v4 and stops collecting data for the deprecated FID (First Input Delay) metric.
2 changes: 1 addition & 1 deletion packages/integrations/web-vitals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"test": "astro-scripts test --timeout 50000 \"test/**/*.test.js\""
},
"dependencies": {
"web-vitals": "^3.5.2"
"web-vitals": "^4.0.0"
},
"peerDependencies": {
"@astrojs/db": "^0.11.0"
Expand Down
4 changes: 2 additions & 2 deletions packages/integrations/web-vitals/src/client-script.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Metric, onCLS, onFCP, onFID, onINP, onLCP, onTTFB } from 'web-vitals';
import { type Metric, onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals';
import { WEB_VITALS_ENDPOINT_PATH } from './constants.js';
import type { ClientMetric } from './schemas.js';

Expand Down Expand Up @@ -26,7 +26,7 @@ function flushQueue() {
queue.clear();
}

for (const listener of [onCLS, onLCP, onINP, onFID, onFCP, onTTFB]) {
for (const listener of [onCLS, onLCP, onINP, onFCP, onTTFB]) {
listener(addToQueue);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/integrations/web-vitals/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ const MetricTypeSchema = z.enum(['CLS', 'INP', 'LCP', 'FCP', 'FID', 'TTFB']);
const MetricIdSchema = z
.string()
// Match https://github.com/GoogleChrome/web-vitals/blob/main/src/lib/generateUniqueID.ts
.regex(/^v3-\d{13}-\d{13}$/)
.regex(/^v4-\d{13}-\d{13}$/)
// Avoid collecting higher resolution timestamp in ID.
// Transforms `'v3-1711484350895-3748043125387'` to `'v3-17114843-3748043125387'`
.transform((id) => id.replace(/^(v3-\d{8})\d{5}(-\d{13})$/, '$1$2'));
// Transforms `'v4-1711484350895-3748043125387'` to `'v4-17114843-3748043125387'`
.transform((id) => id.replace(/^(v4-\d{8})\d{5}(-\d{13})$/, '$1$2'));

/** Shape of the data submitted from clients to the collection API. */
const ClientMetricSchema = z.object({
Expand Down
59 changes: 47 additions & 12 deletions packages/integrations/web-vitals/test/basics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { after, before, beforeEach, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
import { loadFixture } from './test-utils.js';

function startOfHourISOString() {
const date = new Date();
date.setMinutes(0, 0, 0);
return date.toISOString();
}

/**
* @template {Record<K, (...args: any[]) => void>} T
* @template {keyof T} K
Expand Down Expand Up @@ -94,25 +100,54 @@ describe('Web Vitals integration basics', () => {
assert.equal(call.name, 'ZodError');
});

it('inserts data via the injected endpoint', async () => {
const res = await fixture.fetch('/_web-vitals', {
method: 'POST',
body: JSON.stringify([
describe('inserting data via the injected endpoint', () => {
/** @type {Response} */
let res;
before(async () => {
res = await fixture.fetch('/_web-vitals', {
method: 'POST',
body: JSON.stringify([
{
pathname: '/',
route: '/',
name: 'CLS',
id: 'v4-1711484350895-3748043125387',
value: 0,
rating: 'good',
},
]),
});
});

it('inserting data does not error', () => {
assert.equal(res.status, 200);
assert.equal(
consoleErrorMock.calls.length,
0,
'Endpoint logged errors:\n' + consoleErrorMock.calls[0]?.join(' ')
);
})

it('inserted data can be retrieved from the database', async () => {
const dbRows = await fixture.fetch('/rows.json', {}).then((r) => r.json());
assert.deepEqual(dbRows, [
{
pathname: '/',
route: '/',
name: 'CLS',
id: 'v3-1711484350895-3748043125387',
id: 'v4-17114843-3748043125387',
value: 0,
rating: 'good',
timestamp: startOfHourISOString(),
},
]),
]);
});
assert.equal(res.status, 200);
assert.equal(
consoleErrorMock.calls.length,
0,
'Endpoint logged errors:\n' + consoleErrorMock.calls[0]?.join(' ')
);
});

it('inserted data uses a truncated timestamp in the ID', async () => {
// The IDs generated by the `web-vitals` package include a high resolution timestamp as the second portion,
// e.g. 'v4-1711484350895-3748043125387'. We reduce this data to an hourly resolution to lessen privacy concerns.
const dbRows = await fixture.fetch('/rows.json', {}).then((r) => r.json());
assert.deepEqual(dbRows[0].id, 'v4-17114843-3748043125387');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { db, AstrojsWebVitals_Metric } from "astro:db";

export const prerender = false;

export async function GET() {
const rows = await db.select().from(AstrojsWebVitals_Metric).all();
return new Response(JSON.stringify(rows));
}
8 changes: 6 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3c7a4fa

Please sign in to comment.