pagespeed-quest 0.11.0 → 0.13.0
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.
- package/CHANGELOG.md +21 -1
- package/README.ja.md +9 -4
- package/README.md +10 -4
- package/build/cli.js +1 -3
- package/build/formatting.d.ts +1 -1
- package/build/lighthouse-summary.d.ts +3 -1
- package/build/lighthouse-summary.js +31 -46
- package/build/lighthouse.d.ts +0 -1
- package/build/lighthouse.js +6 -2
- package/build/recording.js +2 -2
- package/llmdocs/00-guide.md +42 -20
- package/llmdocs/10-inventory.md +48 -24
- package/llmdocs/20-experiments.md +70 -171
- package/llmdocs/30-measuring.md +60 -19
- package/llmdocs/90-commands.md +14 -7
- package/package.json +30 -47
- package/scripts/build-llm-docs.js +9 -0
- package/scripts/postinstall.js +2 -3
package/llmdocs/10-inventory.md
CHANGED
|
@@ -6,8 +6,9 @@ receive during playback comes from here, and nothing else does.
|
|
|
6
6
|
```
|
|
7
7
|
inventory/
|
|
8
8
|
├── index.json ← the resource list. THIS is what the proxy reads
|
|
9
|
+
├── originals/ ← the recorded wire bytes of reformatted bodies. Never edit
|
|
9
10
|
└── contents/
|
|
10
|
-
└──
|
|
11
|
+
└── get/
|
|
11
12
|
└── https/
|
|
12
13
|
└── example.com/
|
|
13
14
|
├── index.html
|
|
@@ -22,25 +23,32 @@ inventory/
|
|
|
22
23
|
traffic figures while the proxy serves from `index.json` — the two silently
|
|
23
24
|
disagree. Delete `inventory.json`, or keep it byte-identical.
|
|
24
25
|
- **`contents/` holds one file per response body.** Text bodies are stored
|
|
25
|
-
decoded
|
|
26
|
-
|
|
26
|
+
decoded and converted to UTF-8; minified CSS and JavaScript are also
|
|
27
|
+
pretty-printed (and marked `minify: true`), HTML is kept as recorded.
|
|
28
|
+
- **`originals/` keeps the bytes that went over the wire** for every body that
|
|
29
|
+
was stored differently (decompressed, re-encoded or pretty-printed). Playback
|
|
30
|
+
serves them for unedited resources. Do not edit or delete it.
|
|
27
31
|
|
|
28
32
|
## How a URL becomes a path
|
|
29
33
|
|
|
30
|
-
`contents/<
|
|
34
|
+
`contents/<method, lower-case>/<protocol>/<host>/<pathname>`
|
|
31
35
|
|
|
32
36
|
| URL | File |
|
|
33
37
|
| --- | --- |
|
|
34
|
-
| `https://example.com/` | `contents/
|
|
35
|
-
| `https://example.com/assets/app.css` | `contents/
|
|
36
|
-
| `https://example.com/
|
|
37
|
-
| `https://cdn.example.com:8443/a.js` | `contents/GET/https/cdn.example.com~8443/a.js` |
|
|
38
|
+
| `https://example.com/` | `contents/get/https/example.com/index.html` |
|
|
39
|
+
| `https://example.com/assets/app.css` | `contents/get/https/example.com/assets/app.css` |
|
|
40
|
+
| `https://example.com/app.js?v=1` | `contents/get/https/example.com/app~v%3D1.js` |
|
|
38
41
|
|
|
39
|
-
Rules: a trailing `/`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
by hand —
|
|
43
|
-
|
|
42
|
+
Rules: a trailing `/` gets `index.html`; the query string is URL-encoded and
|
|
43
|
+
inserted after `~`, before the extension; a query longer than 32 characters is
|
|
44
|
+
cut there and suffixed with `.~<sha1 of the rest>`. Do not guess and do not
|
|
45
|
+
rename these files by hand — `contentFilePath` in `index.json` (relative to the
|
|
46
|
+
inventory directory, so it starts with `contents/`) is the mapping, and the file
|
|
47
|
+
name is only a convenience:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
jq -r '.resources[] | [.url, .contentFilePath] | @tsv' inventory/index.json
|
|
51
|
+
```
|
|
44
52
|
|
|
45
53
|
## `index.json`
|
|
46
54
|
|
|
@@ -48,6 +56,7 @@ name is only a convenience.
|
|
|
48
56
|
{
|
|
49
57
|
"entryUrl": "https://example.com/",
|
|
50
58
|
"deviceType": "mobile",
|
|
59
|
+
"formatVersion": 2,
|
|
51
60
|
"resources": [ ... ]
|
|
52
61
|
}
|
|
53
62
|
```
|
|
@@ -55,35 +64,49 @@ name is only a convenience.
|
|
|
55
64
|
`entryUrl` and `deviceType` are what `playback` uses instead of asking you for a
|
|
56
65
|
URL. Changing `deviceType` here changes the Lighthouse form factor and the
|
|
57
66
|
loadshow preset on the next playback — it does **not** re-record.
|
|
67
|
+
`formatVersion` is `2` for recordings made with psq 0.12 or later; without it
|
|
68
|
+
the older timing model applies (see below).
|
|
58
69
|
|
|
59
70
|
### A resource
|
|
60
71
|
|
|
61
72
|
| Field | Effect at playback time |
|
|
62
73
|
| --- | --- |
|
|
63
|
-
| `method`, `url` | how the request is matched. A request that matches nothing gets a 404 |
|
|
64
|
-
| `ttfbMs` | **the proxy sleeps this long after receiving the request, before the first byte.** Per request, not an offset from page start |
|
|
65
|
-
| `durationMs` | recorded transfer duration.
|
|
66
|
-
| `mbps` | transfer speed for the body. Missing →
|
|
74
|
+
| `method`, `url` | how the request is matched. A request that matches nothing gets a 404. Required |
|
|
75
|
+
| `ttfbMs` | **the proxy sleeps this long after receiving the request, before the first byte.** Per request, not an offset from page start. Required |
|
|
76
|
+
| `durationMs` | recorded transfer duration. Used as-is only by recordings without `formatVersion: 2` |
|
|
77
|
+
| `mbps` | transfer speed for the body, measured at recording. Used for an edited body. Missing → the median of the same host |
|
|
67
78
|
| `statusCode` | the status served |
|
|
68
79
|
| `rawHeaders` | headers served, minus `content-length` and `content-encoding`, which are recomputed |
|
|
69
80
|
| `contentEncoding` | `gzip` / `br` / `deflate` / `zstd` / `compress` / `identity`. The body is **re-compressed with this before it goes on the wire**, so it decides the transferred size |
|
|
70
81
|
| `contentTypeMime`, `contentCharset` | the `Content-Type` served. `contentCharset` re-encodes the body from UTF-8 on the way out |
|
|
71
82
|
| `contentFilePath` | the file under `contents/` to serve |
|
|
72
|
-
| `contentUtf8` / `contentBase64` | inline body, used
|
|
83
|
+
| `contentUtf8` / `contentBase64` | inline body, used only when there is no `contentFilePath`. Recordings do not write it |
|
|
73
84
|
| `minify` | `true` → the body is **re-minified before serving**. Set during recording when the original was minified |
|
|
74
85
|
| `httpVersion` | protocol version reported for the response |
|
|
86
|
+
| `original` | written by the recorder: the path of the recorded wire bytes under `originals/`, a SHA-256 of the file in `contents/`, and the `contentEncoding` / `contentCharset` / `minify` at recording time. **Do not edit** |
|
|
75
87
|
|
|
76
88
|
### The timing model, precisely
|
|
77
89
|
|
|
78
90
|
```
|
|
79
|
-
|
|
91
|
+
unedited body: recorded wire bytes, ttfbMs + the recorded transfer time
|
|
92
|
+
edited body: ttfbMs + (bytes on the wire × 8) / (mbps × 1,000,000)
|
|
80
93
|
```
|
|
81
94
|
|
|
95
|
+
- A body is **unedited** while its file still matches the hash in `original`
|
|
96
|
+
and `contentEncoding`, `contentCharset` and `minify` are as recorded. It is
|
|
97
|
+
then served byte for byte as the site sent it — same compression, same size,
|
|
98
|
+
same time. Changing any of those makes it edited.
|
|
82
99
|
- `ttfbMs` is a fixed per-request delay. It models server think time plus
|
|
83
100
|
connection latency. Lower it to model a faster origin or a nearer CDN edge.
|
|
84
|
-
-
|
|
85
|
-
minification and after `contentEncoding` compression. So
|
|
86
|
-
makes it arrive sooner,
|
|
101
|
+
- For an edited body the transfer time is recomputed **from the bytes you leave
|
|
102
|
+
in the file**, after minification and after `contentEncoding` compression. So
|
|
103
|
+
shrinking a file makes it arrive sooner, with no other edit. `mbps` was
|
|
104
|
+
measured per resource at recording, so the same bytes take the time they took
|
|
105
|
+
on that connection.
|
|
106
|
+
- **Recordings without `formatVersion: 2`** (psq before 0.12) replay every body
|
|
107
|
+
in its recorded `durationMs` whatever its size, and re-minify text bodies with
|
|
108
|
+
an older minifier. Shrinking a file does not make it faster there. Re-record
|
|
109
|
+
to get the model above.
|
|
87
110
|
- `--full-throttle` (used internally by `psq capture`) skips both. Never compare
|
|
88
111
|
a full-throttle run with a normal one.
|
|
89
112
|
|
|
@@ -104,9 +127,10 @@ discarded.
|
|
|
104
127
|
|
|
105
128
|
## Editing safely
|
|
106
129
|
|
|
107
|
-
1. Copy `inventory/` to `baseline/` before the first edit.
|
|
130
|
+
1. Copy `inventory/` to `baseline/` (and to `accepted/`) before the first edit.
|
|
108
131
|
2. Change **one thing at a time**, then measure. Two edits in one run means you
|
|
109
|
-
cannot attribute the delta.
|
|
132
|
+
cannot attribute the delta. Start each trial from a fresh copy of
|
|
133
|
+
`accepted/` (see "The loop" in the guide).
|
|
110
134
|
3. After a structural edit (removing a script, changing a URL), run
|
|
111
135
|
`psq capture --compare` and look at the picture. A faster broken page is easy
|
|
112
136
|
to produce and easy to mistake for a win.
|
|
@@ -1,175 +1,72 @@
|
|
|
1
|
-
# Experiments —
|
|
1
|
+
# Experiments — how an edit reaches the replay
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
This chapter is about the mechanics of changing a recording so that a replay
|
|
4
|
+
reflects a change you want to test. **It does not prescribe optimizations** —
|
|
5
|
+
deciding what to try is the agent's or the engineer's judgement. What matters
|
|
6
|
+
here is that the edit models what you intend, and nothing else.
|
|
7
7
|
|
|
8
8
|
Paths below are relative to the inventory directory.
|
|
9
9
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
## Scripts
|
|
61
|
-
|
|
62
|
-
### Defer or async a render-blocking script
|
|
63
|
-
|
|
64
|
-
Edit the document, add `defer` (order preserved) or `async` (order not).
|
|
65
|
-
|
|
66
|
-
**Moves** FCP, LCP, SI. TBT often moves too, because the work lands after the
|
|
67
|
-
first paint.
|
|
68
|
-
|
|
69
|
-
### Remove a third-party tag
|
|
70
|
-
|
|
71
|
-
Delete the `<script>` from the document. Optionally also delete the resource
|
|
72
|
-
from `index.json` — but only after the reference is gone, or the browser will
|
|
73
|
-
record a failed request (see the 404 rule).
|
|
74
|
-
|
|
75
|
-
**Moves** TBT most visibly, plus whatever the tag was blocking. This is the
|
|
76
|
-
single most informative experiment on most commercial pages: it puts a number on
|
|
77
|
-
"what is the tag manager costing us".
|
|
78
|
-
|
|
79
|
-
**Watch out** Some tags inject further requests. Removing the parent removes
|
|
80
|
-
those too, which is realistic — but it makes the delta larger than the tag's own
|
|
81
|
-
weight, so say so when reporting.
|
|
82
|
-
|
|
83
|
-
### Shrink the bundle
|
|
84
|
-
|
|
85
|
-
Replace the file under `contents/` with a smaller build (drop a polyfill, remove
|
|
86
|
-
a library, run a real minifier). Because the browser actually executes what you
|
|
87
|
-
serve, **TBT and main-thread time respond for real** — this is not a simulated
|
|
88
|
-
number.
|
|
89
|
-
|
|
90
|
-
## CSS and fonts
|
|
91
|
-
|
|
92
|
-
### Inline critical CSS, defer the rest
|
|
93
|
-
|
|
94
|
-
In the document, add a `<style>` block with the above-the-fold rules and switch
|
|
95
|
-
the stylesheet link to a deferred load:
|
|
96
|
-
|
|
97
|
-
```html
|
|
98
|
-
<link rel="preload" href="/assets/app.css" as="style" onload="this.rel='stylesheet'">
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Moves** FCP, LCP, SI. **Watch out** CLS often gets *worse* if the inlined
|
|
102
|
-
subset is incomplete — check `psq capture --compare` as well as the score.
|
|
103
|
-
|
|
104
|
-
### Remove unused CSS
|
|
105
|
-
|
|
106
|
-
Edit the stylesheet under `contents/`. Remember `minify: true` re-minifies, so
|
|
107
|
-
the win shows up as fewer bytes, not as tidier formatting.
|
|
108
|
-
|
|
109
|
-
### `font-display: swap` and font preloading
|
|
110
|
-
|
|
111
|
-
Edit the `@font-face` block in the stylesheet, and/or add
|
|
112
|
-
`<link rel="preload" as="font" crossorigin>` to the document.
|
|
113
|
-
|
|
114
|
-
**Moves** FCP and LCP when text was the largest element; CLS if the swap causes
|
|
115
|
-
reflow. Subsetting the font file itself is the transfer-size half of the same
|
|
116
|
-
experiment.
|
|
117
|
-
|
|
118
|
-
## Network and server
|
|
119
|
-
|
|
120
|
-
These are the levers that do not exist in a normal development environment, and
|
|
121
|
-
they are the reason for the `index.json` fields.
|
|
122
|
-
|
|
123
|
-
### "What if the server responded faster?"
|
|
124
|
-
|
|
125
|
-
Lower `ttfbMs` on the document resource (often 200–800 ms on a real recording):
|
|
126
|
-
|
|
127
|
-
```json
|
|
128
|
-
{ "url": "https://example.com/", "ttfbMs": 80 }
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
**Moves** everything downstream, roughly one-for-one. Run this early — it tells
|
|
132
|
-
you how much of the budget is back-end and therefore *not* fixable in the
|
|
133
|
-
front-end.
|
|
134
|
-
|
|
135
|
-
### "What if we put it on a CDN / on a faster link?"
|
|
136
|
-
|
|
137
|
-
Raise `mbps` on the resources served from the slow host. To model a slower
|
|
138
|
-
network instead, lower it. Apply it to a whole host, not a single file, or the
|
|
139
|
-
result will not resemble anything real.
|
|
140
|
-
|
|
141
|
-
### "What if we finally enabled compression?"
|
|
142
|
-
|
|
143
|
-
Set `contentEncoding` to `br` (or `gzip`) on the text resources that had
|
|
144
|
-
`identity`. The proxy re-compresses and the transfer time follows.
|
|
145
|
-
|
|
146
|
-
The reverse — setting `identity` on everything — quantifies what compression is
|
|
147
|
-
already buying you, which is a useful number when someone proposes turning it
|
|
148
|
-
off for CPU reasons.
|
|
149
|
-
|
|
150
|
-
### "What if there were fewer requests?"
|
|
151
|
-
|
|
152
|
-
Concatenate two files into one under `contents/`, update the document to
|
|
153
|
-
reference the survivor, and delete the other entry from `index.json`. The win
|
|
154
|
-
here is mostly the removed `ttfbMs`, so it is only worth it where that value is
|
|
155
|
-
large.
|
|
156
|
-
|
|
157
|
-
## Layout stability
|
|
158
|
-
|
|
159
|
-
### Reserve space for images and embeds
|
|
160
|
-
|
|
161
|
-
Add `width` / `height` (or an `aspect-ratio` rule) in the document or the
|
|
162
|
-
stylesheet. **Moves** CLS, which carries the same weight as LCP — a cheap edit
|
|
163
|
-
with a disproportionate score effect.
|
|
10
|
+
## Content edits
|
|
11
|
+
|
|
12
|
+
Edit the file a resource's `contentFilePath` points to. The browser receives and
|
|
13
|
+
executes exactly those bytes, so effects on parsing, script execution, layout
|
|
14
|
+
and paint are **measured, not estimated**, and the transfer time follows the new
|
|
15
|
+
size (see the timing model in "The inventory" — this needs a recording with
|
|
16
|
+
`formatVersion: 2`, i.e. made with psq 0.12 or later).
|
|
17
|
+
|
|
18
|
+
psq-specific rules:
|
|
19
|
+
|
|
20
|
+
- **Changing an image's format** — replace the bytes at the same path, then set
|
|
21
|
+
the served type to match:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{ "contentTypeMime": "image/webp", "rawHeaders": { "content-type": "image/webp" } }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The URL may keep its old extension; browsers go by `Content-Type`. Never edit
|
|
28
|
+
`content-length`.
|
|
29
|
+
- **`minify: true`** — the file is re-minified before serving. Whitespace has no
|
|
30
|
+
effect on the wire; a syntax error may surface as a minifier failure.
|
|
31
|
+
- **Leave `original` and `originals/` alone.** They are how playback recognises
|
|
32
|
+
the resources you did not touch and serves them exactly as recorded.
|
|
33
|
+
- **`ttfbMs` does not move with the content.** A preloaded, reprioritised or
|
|
34
|
+
shrunk resource still waits its recorded `ttfbMs` before its first byte.
|
|
35
|
+
|
|
36
|
+
## Adding and removing resources
|
|
37
|
+
|
|
38
|
+
- **Removing**: delete the reference from the HTML (or CSS, or script) in the
|
|
39
|
+
same edit. An entry deleted while still referenced becomes a 404 — a failed
|
|
40
|
+
request, which makes the result look worse than the truth. Removing a parent
|
|
41
|
+
tag also removes whatever it would have injected; say so when reporting.
|
|
42
|
+
- **Adding** (a split file, a self-hosted font, a new preload): add an entry to
|
|
43
|
+
`resources`. `method`, `url` and `ttfbMs` are required — an entry missing them
|
|
44
|
+
makes `index.json` unreadable. Set `statusCode`, `contentTypeMime` and
|
|
45
|
+
`contentFilePath`, and copy `ttfbMs` / `mbps` from a comparable resource on
|
|
46
|
+
the same host so the timing is not invented. Any URL the browser requests
|
|
47
|
+
that has no entry gets a 404.
|
|
48
|
+
|
|
49
|
+
## Metadata edits — what a front-end change cannot do
|
|
50
|
+
|
|
51
|
+
These levers do not exist in a normal development environment, and they are the
|
|
52
|
+
reason for the `index.json` fields.
|
|
53
|
+
|
|
54
|
+
| To model | Edit | Note |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| a faster or slower origin | lower / raise `ttfbMs`, the document first | separates the back-end share of the load from the front-end share |
|
|
57
|
+
| a faster or slower link, a CDN | `mbps` on every resource of that host | per host, not per file, or the result resembles nothing real |
|
|
58
|
+
| compression on or off | `contentEncoding` (`br`, `gzip`, `identity`) on text resources | the proxy re-compresses; the reverse quantifies what compression already buys |
|
|
59
|
+
| a different form factor | top-level `deviceType` | changes the Lighthouse form factor and the loadshow preset; does not re-record |
|
|
164
60
|
|
|
165
61
|
## What this cannot model
|
|
166
62
|
|
|
167
63
|
State these plainly when reporting results.
|
|
168
64
|
|
|
169
|
-
- **Caching across page views.** Every playback is a cold load
|
|
65
|
+
- **Caching across page views.** Every playback is a cold load, so
|
|
66
|
+
`cache-insight` and `bf-cache` findings cannot be acted on in a replay.
|
|
170
67
|
- **HTTP/2 prioritisation and connection contention.** Each host is served by
|
|
171
|
-
|
|
172
|
-
reproduced.
|
|
68
|
+
the local proxy; the real contention on a shared connection is not
|
|
69
|
+
reproduced, and `modern-http-insight` describes the proxy, not the origin.
|
|
173
70
|
- **Server-side changes that alter the HTML.** You can hand-edit the HTML to
|
|
174
71
|
what the server *would* emit, but the edit is your hypothesis, not a
|
|
175
72
|
measurement of it.
|
|
@@ -179,16 +76,18 @@ State these plainly when reporting results.
|
|
|
179
76
|
moment of recording, which is a feature for comparison and a limitation for
|
|
180
77
|
representativeness.
|
|
181
78
|
|
|
182
|
-
## Reporting a
|
|
79
|
+
## Reporting a trial
|
|
183
80
|
|
|
184
|
-
A useful
|
|
185
|
-
whether the page
|
|
81
|
+
A useful line answers four questions: what was changed, what evidence led to it,
|
|
82
|
+
what moved (medians of several runs, reference → trial), and whether the page
|
|
83
|
+
still looks right.
|
|
186
84
|
|
|
187
85
|
```
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
86
|
+
Trial : t01 defer tag manager
|
|
87
|
+
Evidence : render-blocking-insight lists gtm.js
|
|
88
|
+
Edit : contents/get/https/example.com/index.html — added defer to <script src="gtm.js">
|
|
89
|
+
Reference : Score 61 LCP 3.41s TBT 890ms (median of 3, artifacts/baseline-*)
|
|
90
|
+
Trial : Score 68 LCP 2.95s TBT 640ms (median of 3, artifacts/t01-defer-gtm-*)
|
|
91
|
+
Visual : no diff (artifacts/t01-capture/capture-diff.txt)
|
|
92
|
+
Decision : adopted
|
|
194
93
|
```
|
package/llmdocs/30-measuring.md
CHANGED
|
@@ -12,40 +12,78 @@ see the same page. Choose by the question being asked.
|
|
|
12
12
|
Keep the two sides in separate artifact directories. `-a` is an option of the
|
|
13
13
|
`lighthouse` / `loadshow` group, so it goes before the sub-subcommand.
|
|
14
14
|
|
|
15
|
-
## Lighthouse
|
|
15
|
+
## Lighthouse, several runs per state
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
psq -i ./baseline
|
|
19
|
-
psq -i ./inventory lighthouse -a ./artifacts
|
|
20
|
-
diff artifacts-baseline/summary.md artifacts-after/summary.md
|
|
18
|
+
for n in 1 2 3; do psq -i ./baseline lighthouse -a ./artifacts/baseline-$n playback; done
|
|
19
|
+
for n in 1 2 3; do psq -i ./inventory lighthouse -a ./artifacts/t01-$n playback; done
|
|
21
20
|
```
|
|
22
21
|
|
|
22
|
+
One directory per run — the same `-a` twice overwrites. Name them
|
|
23
|
+
`<label>-<n>`, the label naming the state.
|
|
24
|
+
|
|
23
25
|
Each run writes:
|
|
24
26
|
|
|
25
27
|
| File | Contents |
|
|
26
28
|
| --- | --- |
|
|
27
29
|
| `summary.md` | metrics, per-metric scores with weights, overall score, traffic and resource size by type |
|
|
28
30
|
| `lighthouse.report.html` | the full report (open it when a number needs explaining) |
|
|
29
|
-
| `lighthouse.report.json` | the same, machine-readable |
|
|
31
|
+
| `lighthouse.report.json` | the same, machine-readable — the source for medians and for the diagnosis |
|
|
30
32
|
| `lighthouse.digest.png` | score gauge and metrics as one image — the thing to paste into a report |
|
|
31
33
|
| `lighthouse-0.trace.json`, `*.devtoolslog.json` | saved assets (`--save-assets`) |
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
A playback run takes roughly 20–30 seconds: Lighthouse measures the load as
|
|
36
|
+
observed (`--throttling-method=provided`) and waits for the page to go quiet.
|
|
35
37
|
|
|
36
38
|
Options on the `lighthouse` group: `-a/--artifacts` (default `./artifacts`),
|
|
37
39
|
`-t/--timeout` (ms, default 30000), `-l/--laud` — show the browser window and
|
|
38
40
|
open the report at the end. `--laud` is for watching a run, not for measuring;
|
|
39
41
|
a visible browser is not the same environment as the headless one.
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
### Medians and spread
|
|
44
|
+
|
|
45
|
+
**Never conclude from one run.** Runs of the same recording are close but not
|
|
46
|
+
identical, and an occasional run is an outlier. Take the median of at least three runs
|
|
47
|
+
per state, and call a difference real only when it is larger than the spread
|
|
48
|
+
(max − min) within each state:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
jq -s -r '
|
|
52
|
+
def median: sort | if length % 2 == 1 then .[length/2|floor] else (.[length/2-1] + .[length/2]) / 2 end;
|
|
53
|
+
map({Score: (.categories.performance.score*100),
|
|
54
|
+
LCP: .audits["largest-contentful-paint"].numericValue,
|
|
55
|
+
FCP: .audits["first-contentful-paint"].numericValue,
|
|
56
|
+
SI: .audits["speed-index"].numericValue,
|
|
57
|
+
TBT: .audits["total-blocking-time"].numericValue,
|
|
58
|
+
CLS: .audits["cumulative-layout-shift"].numericValue}) as $r
|
|
59
|
+
| ["Score","LCP","FCP","SI","TBT","CLS"][] as $m | [$r[][$m]] as $v
|
|
60
|
+
| "\($m)\t\($v|median)\t\($v|min)\t\($v|max)"' artifacts/baseline-*/lighthouse.report.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Where the time goes
|
|
64
|
+
|
|
65
|
+
`lighthouse.report.json` holds the evidence. With
|
|
66
|
+
`J=artifacts/baseline-1/lighthouse.report.json`:
|
|
67
|
+
|
|
68
|
+
| Question | jq |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| which audits flag a problem | `.audits[] \| select(.score != null and .score < 1) \| [.id, .displayValue, .metricSavings]` |
|
|
71
|
+
| the LCP element | `.audits["lcp-breakdown-insight"].details.items[] \| select(.type=="node") \| .snippet` |
|
|
72
|
+
| how LCP time splits | `.audits["lcp-breakdown-insight"].details.items[] \| select(.type=="table") \| .items` |
|
|
73
|
+
| the request waterfall | `.audits["network-requests"].details.items` (`networkRequestTime`, `networkEndTime`, `transferSize`, `url`) |
|
|
74
|
+
| script cost per URL | `.audits["bootup-time"].details.items` |
|
|
75
|
+
| third parties | `.audits["third-parties-insight"].details.items` |
|
|
76
|
+
|
|
77
|
+
Lighthouse 13 keeps most of its diagnosis in `*-insight` audits. To act on a
|
|
78
|
+
URL, look up its `contentFilePath` in `index.json`. `cache-insight`, `bf-cache`,
|
|
79
|
+
`modern-http-insight` and interaction metrics describe things a replay cannot
|
|
80
|
+
model (see "Experiments").
|
|
43
81
|
|
|
44
82
|
## Video of the load, before and after
|
|
45
83
|
|
|
46
84
|
```sh
|
|
47
|
-
psq -i ./baseline
|
|
48
|
-
psq -i ./
|
|
85
|
+
psq -i ./baseline loadshow -a ./artifacts/video-baseline playback
|
|
86
|
+
psq -i ./accepted loadshow -a ./artifacts/video-after playback
|
|
49
87
|
```
|
|
50
88
|
|
|
51
89
|
Each writes `<artifacts>/loadshow.mp4` — a filmstrip of the load, laid out in
|
|
@@ -57,15 +95,15 @@ Use `-c/--credit` to stamp a label into the video, which is what makes a pair of
|
|
|
57
95
|
videos legible side by side:
|
|
58
96
|
|
|
59
97
|
```sh
|
|
60
|
-
psq -i ./baseline
|
|
61
|
-
psq -i ./
|
|
98
|
+
psq -i ./baseline loadshow -a ./artifacts/video-baseline -c "Before" playback
|
|
99
|
+
psq -i ./accepted loadshow -a ./artifacts/video-after -c "After — deferred GTM" playback
|
|
62
100
|
```
|
|
63
101
|
|
|
64
102
|
To place them side by side, stack the two MP4s with ffmpeg; `psq` does not
|
|
65
103
|
compose videos:
|
|
66
104
|
|
|
67
105
|
```sh
|
|
68
|
-
ffmpeg -i artifacts-baseline/loadshow.mp4 -i artifacts-after/loadshow.mp4 \
|
|
106
|
+
ffmpeg -i artifacts/video-baseline/loadshow.mp4 -i artifacts/video-after/loadshow.mp4 \
|
|
69
107
|
-filter_complex hstack -c:v libx264 before-after.mp4
|
|
70
108
|
```
|
|
71
109
|
|
|
@@ -82,8 +120,8 @@ so that the recording and the playback are driven the same way.
|
|
|
82
120
|
## Screenshot and visual diff
|
|
83
121
|
|
|
84
122
|
```sh
|
|
85
|
-
psq -i ./baseline capture -a ./artifacts-
|
|
86
|
-
psq capture --compare ./artifacts-
|
|
123
|
+
psq -i ./baseline capture -a ./artifacts/baseline-capture
|
|
124
|
+
psq capture -a ./artifacts/t01-capture --compare ./artifacts/baseline-capture/capture.png \
|
|
87
125
|
--baseline-label "Before" --current-label "After"
|
|
88
126
|
```
|
|
89
127
|
|
|
@@ -112,6 +150,9 @@ banner will make every metric better and the page wrong.
|
|
|
112
150
|
- script / 14 / 512.3 KB / 524595
|
|
113
151
|
```
|
|
114
152
|
|
|
115
|
-
*Traffic* is what
|
|
116
|
-
|
|
117
|
-
|
|
153
|
+
*Traffic* is what the browser received in this run, as Lighthouse measured it
|
|
154
|
+
(after compression, headers included); *Resource* is the decoded body size.
|
|
155
|
+
Both come from the report's `network-requests` audit, so they count every
|
|
156
|
+
request the page made, including 404s. A change that moves resource size but
|
|
157
|
+
not traffic size means you edited something that compresses away — expect
|
|
158
|
+
little timing effect.
|
package/llmdocs/90-commands.md
CHANGED
|
@@ -12,7 +12,8 @@ Record a web page through a proxy, edit the recording, and replay it to measure
|
|
|
12
12
|
```
|
|
13
13
|
Usage: psq [options] [command]
|
|
14
14
|
|
|
15
|
-
Record a web page through a proxy, edit the recording, and replay it to measure
|
|
15
|
+
Record a web page through a proxy, edit the recording, and replay it to measure
|
|
16
|
+
the difference
|
|
16
17
|
|
|
17
18
|
Options:
|
|
18
19
|
-v, --version Output the version number
|
|
@@ -23,7 +24,8 @@ Commands:
|
|
|
23
24
|
lighthouse [options] Run Lighthouse (performance category) via a proxy
|
|
24
25
|
loadshow [options] Run loadshow via a proxy
|
|
25
26
|
proxy [options] Run the playback (or recording) proxy standalone
|
|
26
|
-
capture [options] Capture a screenshot via playback proxy and optionally
|
|
27
|
+
capture [options] Capture a screenshot via playback proxy and optionally
|
|
28
|
+
compare with a baseline
|
|
27
29
|
llm [options] Print the embedded reference for AI agents
|
|
28
30
|
help [command] display help for command
|
|
29
31
|
```
|
|
@@ -39,7 +41,8 @@ Run Lighthouse (performance category) via a proxy
|
|
|
39
41
|
|
|
40
42
|
Options:
|
|
41
43
|
-a, --artifacts <dir> Artifacts directory (default: "./artifacts")
|
|
42
|
-
-l, --laud Show browser and open the report after completion
|
|
44
|
+
-l, --laud Show browser and open the report after completion
|
|
45
|
+
(default: false)
|
|
43
46
|
-t, --timeout <ms> Timeout milliseconds (default: "30000")
|
|
44
47
|
-h, --help display help for command
|
|
45
48
|
|
|
@@ -63,7 +66,8 @@ Arguments:
|
|
|
63
66
|
|
|
64
67
|
Options:
|
|
65
68
|
-d, --device <mobile|desktop> Device type (default: "mobile")
|
|
66
|
-
-x, --exclude <pattern> Regex patterns to exclude URLs from recording
|
|
69
|
+
-x, --exclude <pattern> Regex patterns to exclude URLs from recording
|
|
70
|
+
(repeatable) (default: [])
|
|
67
71
|
-h, --help display help for command
|
|
68
72
|
```
|
|
69
73
|
|
|
@@ -115,7 +119,8 @@ Arguments:
|
|
|
115
119
|
|
|
116
120
|
Options:
|
|
117
121
|
-d, --device <mobile|desktop> Device type (default: "mobile")
|
|
118
|
-
-x, --exclude <pattern> Regex patterns to exclude URLs from recording
|
|
122
|
+
-x, --exclude <pattern> Regex patterns to exclude URLs from recording
|
|
123
|
+
(repeatable) (default: [])
|
|
119
124
|
-h, --help display help for command
|
|
120
125
|
```
|
|
121
126
|
|
|
@@ -143,8 +148,10 @@ Run the playback (or recording) proxy standalone
|
|
|
143
148
|
|
|
144
149
|
Options:
|
|
145
150
|
-p, --port <number> Proxy port (default: "8080")
|
|
146
|
-
-r, --record <url> Recording URL to start the proxy as recording mode
|
|
147
|
-
|
|
151
|
+
-r, --record <url> Recording URL to start the proxy as recording mode
|
|
152
|
+
(default: "")
|
|
153
|
+
-x, --exclude <pattern> Regex patterns to exclude URLs from recording
|
|
154
|
+
(repeatable) (default: [])
|
|
148
155
|
-h, --help display help for command
|
|
149
156
|
```
|
|
150
157
|
|