sxg_checker 0.1.0 → 1.0.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.
- checksums.yaml +4 -4
- data/README.md +20 -10
- data/exe/sxg-checker +2 -1
- data/lib/basic_loader.rb +1 -1
- data/lib/sxg_checker/checker.rb +17 -8
- data/lib/sxg_checker/cli.rb +108 -21
- data/lib/sxg_checker/document.rb +4 -0
- data/lib/sxg_checker/{formatter.rb → printer.rb} +10 -4
- data/lib/sxg_checker/subresource.rb +4 -0
- data/lib/sxg_checker/version.rb +1 -1
- data/lib/sxg_checker.rb +2 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0e170be7a494ed2f4c5d03ce592d49360d51cdaa4bcf487bf2fed7c7e3c47cd
|
4
|
+
data.tar.gz: 948f14c9a90571044f5c873b5686cf3eac582529830a6274e6d49391fc00129b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2d88e2fb7c90d1cb5397bc51a1561f458d5370a1ebb6e3147d2ea1a9a0d8beeb10801e70be87822ba63c2170da8d0ff84d0e66499ea586a908d8e3defa3fee2
|
7
|
+
data.tar.gz: a4d2bcb7468d44d929882662e8f1e773f260908ffbc626bb9b1e05e30e593f7fb5e7b4bc5f837f3bf66a7d976d69499194e0a4c3539eaeec481020bcbf53ef0d
|
data/README.md
CHANGED
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/sxg_checker)
|
4
4
|
|
5
|
-
A library and command-line tool for checking the Google SXG cache for the presence of a document and its subresources.
|
5
|
+
A library and command-line tool for checking the Google Signed Exchanges (SXG) cache for the presence of a document and its subresources.
|
6
6
|
|
7
|
-
It verifies if your web page and its resources are properly available in Google's
|
7
|
+
It verifies if your web page and its resources are properly available in Google's SXG cache,
|
8
8
|
helping you troubleshoot SXG implementation issues with detailed status reporting for each resource.
|
9
9
|
|
10
|
+
SXG enables websites to optimize Largest Contentful Paint (LCP) by allowing prefetching directly from the Google search results page.
|
11
|
+
For a step-by-step guide, see my [SXG tutorial](https://www.pawelpokrywka.com/p/how-i-took-lcp-down-under-350ms).
|
12
|
+
This tool is also referenced in the section on [monitoring and measuring SXG](https://www.pawelpokrywka.com/p/measuring-signed-exchanges-impact).
|
13
|
+
|
10
14
|
## Status indicators
|
11
15
|
|
12
16
|
| Symbol | Status | Description |
|
@@ -51,14 +55,13 @@ gem contents sxg_checker | grep sxg-checker
|
|
51
55
|
To check a URL in Google's SXG cache:
|
52
56
|
|
53
57
|
```shell
|
54
|
-
sxg-checker https://www.yourwebsite.com/your-page
|
58
|
+
sxg-checker validate https://www.yourwebsite.com/your-page
|
55
59
|
```
|
56
60
|
|
57
|
-
|
58
|
-
path in the `DSXG_PATH` environment variable:
|
61
|
+
To get full usage instructions, run:
|
59
62
|
|
60
63
|
```shell
|
61
|
-
|
64
|
+
sxg-checker --help
|
62
65
|
```
|
63
66
|
|
64
67
|
### In Ruby applications
|
@@ -69,16 +72,23 @@ You can also use SXG Checker as a library in your Ruby applications:
|
|
69
72
|
require 'sxg_checker'
|
70
73
|
|
71
74
|
checker = SxgChecker::Checker.new(tool: '/usr/local/bin/dump-signedexchange') # The `tool` parameter is optional
|
72
|
-
|
75
|
+
document = checker.validate(url)
|
73
76
|
|
74
77
|
# Access the results
|
75
|
-
puts
|
76
|
-
puts
|
78
|
+
puts document.url # The URL of the cached document
|
79
|
+
puts document.status # The status symbol (:ok, :missing, etc.)
|
77
80
|
|
78
81
|
# Iterate through subresources
|
79
|
-
|
82
|
+
document.subresources.each do |subresource|
|
80
83
|
puts "#{subresource.url}: #{subresource.status}"
|
81
84
|
end
|
85
|
+
|
86
|
+
# If you want to skip validating subresources
|
87
|
+
result = checker.validate(url, subresources: false)
|
88
|
+
|
89
|
+
# If you want to only warm the SXG cache for a given URL
|
90
|
+
checker.warm_cache(url)
|
91
|
+
|
82
92
|
```
|
83
93
|
|
84
94
|
## Development
|
data/exe/sxg-checker
CHANGED
data/lib/basic_loader.rb
CHANGED
data/lib/sxg_checker/checker.rb
CHANGED
@@ -4,9 +4,13 @@ require "parallel"
|
|
4
4
|
|
5
5
|
module SxgChecker
|
6
6
|
class Checker
|
7
|
-
def
|
8
|
-
|
7
|
+
def warm_cache(document_url)
|
8
|
+
url = cacheify_document_url(document_url)
|
9
|
+
fetch_sxg(url)
|
10
|
+
nil
|
11
|
+
end
|
9
12
|
|
13
|
+
def validate(document_url, subresources: true)
|
10
14
|
url = cacheify_document_url(document_url)
|
11
15
|
response = fetch_sxg(url)
|
12
16
|
return Document.new(url, :missing) unless response
|
@@ -20,6 +24,8 @@ module SxgChecker
|
|
20
24
|
fresh_integrity = extract_integrity(sxg.fetch("ResponseHeaders").fetch("Link", [""]))
|
21
25
|
return Document.new(url, :links_mismatch) if fresh_cached.keys.sort != fresh_integrity.keys.sort
|
22
26
|
|
27
|
+
return Document.new(url, :ok) unless subresources
|
28
|
+
|
23
29
|
cached_integrity = fresh_integrity.map do |fresh, integrity|
|
24
30
|
cached = fresh_cached.fetch(fresh)
|
25
31
|
[cached, integrity]
|
@@ -89,27 +95,30 @@ module SxgChecker
|
|
89
95
|
end
|
90
96
|
|
91
97
|
def parse_sxg_file(path)
|
98
|
+
raise ToolNotFound.new("executable not found: #{tool}") unless tool_available?
|
99
|
+
|
92
100
|
command = "#{tool} -json -verify -i #{path}"
|
93
101
|
result = `#{command} 2> /dev/null`
|
94
102
|
return nil if result.empty?
|
95
103
|
JSON.parse(result)
|
96
104
|
end
|
97
105
|
|
106
|
+
def tool_available?
|
107
|
+
@tool_available ||= File.executable?(tool)
|
108
|
+
end
|
109
|
+
|
98
110
|
def cacheify_document_url(url)
|
99
111
|
uri = URI.parse(url)
|
100
|
-
raise InvalidUrl.new("invalid URL") unless uri.scheme == "https" && uri.host && uri.fragment.nil?
|
112
|
+
raise InvalidUrl.new("invalid URL: #{url}") unless uri.scheme == "https" && uri.host && uri.fragment.nil?
|
101
113
|
|
102
|
-
|
103
|
-
"https://#{host}.webpkgcache.com/doc/-/s/#{url.sub("https://", "")}"
|
114
|
+
"https://#{uri.host.tr(".", "-")}.webpkgcache.com/doc/-/s/#{uri.host}#{uri.request_uri}"
|
104
115
|
rescue URI::InvalidURIError
|
105
116
|
raise InvalidUrl.new("can't parse URL")
|
106
117
|
end
|
107
118
|
|
108
119
|
attr_reader :tool, :mapper
|
109
120
|
|
110
|
-
def initialize(mapper: Parallel.method(:map),
|
111
|
-
default_tool: "#{ENV["HOME"]}/go/bin/dump-signedexchange",
|
112
|
-
tool: ENV["DSXG_PATH"] || default_tool)
|
121
|
+
def initialize(mapper: Parallel.method(:map), tool: DUMP_BINARY)
|
113
122
|
@mapper = mapper
|
114
123
|
@default_tool = default_tool
|
115
124
|
@tool = tool
|
data/lib/sxg_checker/cli.rb
CHANGED
@@ -1,49 +1,136 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
1
3
|
module SxgChecker
|
2
4
|
class Cli
|
3
|
-
def call(
|
4
|
-
|
5
|
+
def call(argv)
|
6
|
+
argv = argv.dup # argv will be changed
|
7
|
+
options = {
|
8
|
+
subresources: true,
|
9
|
+
errors_only: false,
|
10
|
+
dump_binary: DUMP_BINARY
|
11
|
+
}
|
12
|
+
|
13
|
+
parser = OptionParser.new do |opts|
|
14
|
+
opts.on("-h", "--help") do
|
15
|
+
show_usage
|
16
|
+
return 0
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on("-v", "--version") do
|
20
|
+
puts VERSION
|
21
|
+
return 0
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-n", "--no-subresources") do
|
25
|
+
options[:subresources] = false
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-e", "--errors-only") do
|
29
|
+
options[:errors_only] = true
|
30
|
+
end
|
5
31
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
32
|
+
opts.on("-d", "--dump-binary=PATH", String) do |path|
|
33
|
+
options[:dump_binary] = path
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if argv.empty?
|
38
|
+
print_error "command required, use -h for help"
|
39
|
+
return 1
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
# Parse options, leaving the command and URLs in argv
|
44
|
+
parser.parse!(argv)
|
45
|
+
rescue OptionParser::InvalidOption => e
|
46
|
+
print_error e.message
|
47
|
+
return 1
|
48
|
+
end
|
49
|
+
|
50
|
+
command = argv.shift
|
51
|
+
urls = argv
|
52
|
+
|
53
|
+
checker = SxgChecker::Checker.new(tool: options[:dump_binary])
|
54
|
+
|
55
|
+
case command
|
56
|
+
when "warm"
|
57
|
+
if urls.empty?
|
58
|
+
print_error "no URLs provided for warming"
|
59
|
+
return 1
|
60
|
+
end
|
61
|
+
|
62
|
+
urls.each do |url|
|
63
|
+
checker.warm_cache(url)
|
64
|
+
end
|
65
|
+
0
|
66
|
+
when "validate"
|
67
|
+
if urls.empty?
|
68
|
+
print_error "no URLs provided for validation"
|
69
|
+
return 1
|
70
|
+
end
|
71
|
+
|
72
|
+
urls.each do |url|
|
73
|
+
document = checker.validate(url, subresources: options[:subresources])
|
74
|
+
printer.call(document) unless document.ok? && options[:errors_only]
|
75
|
+
end
|
76
|
+
0
|
77
|
+
else
|
78
|
+
print_error "unknown command '#{command}'"
|
79
|
+
1
|
10
80
|
end
|
11
81
|
rescue Error => e
|
12
|
-
|
13
|
-
|
82
|
+
print_error e.message
|
83
|
+
1
|
14
84
|
end
|
15
85
|
|
16
86
|
private
|
17
87
|
|
88
|
+
def print_error(message)
|
89
|
+
puts "#{exe_name}: error: #{message}"
|
90
|
+
end
|
91
|
+
|
18
92
|
def show_usage
|
19
|
-
puts "SXG Checker
|
20
|
-
puts "
|
93
|
+
puts "SXG Checker #{VERSION}"
|
94
|
+
puts " Queries the Google SXG cache for the presence of documents and their subresources."
|
21
95
|
puts
|
22
96
|
puts "Usage:"
|
23
|
-
puts " #{exe_name}
|
97
|
+
puts " #{exe_name} [global options]"
|
98
|
+
puts " #{exe_name} warm <URLs>"
|
99
|
+
puts " #{exe_name} validate [validate options] <URLs>"
|
24
100
|
puts
|
25
|
-
puts "
|
26
|
-
puts "
|
101
|
+
puts "Global options:"
|
102
|
+
puts " -h, --help Show this help message and exit"
|
103
|
+
puts " -v, --version Show version information and exit"
|
27
104
|
puts
|
28
|
-
puts "
|
29
|
-
puts "
|
105
|
+
puts "Commands:"
|
106
|
+
puts " warm Warm the cache for specified URLs"
|
107
|
+
puts " validate Validate SXG documents and their subresources"
|
108
|
+
puts
|
109
|
+
puts "Validate options:"
|
110
|
+
puts " -n, --no-subresources Skip validating subresources (validate documents only)"
|
111
|
+
puts " -e, --errors-only Display only errors (skip successfully validated URLs)"
|
112
|
+
puts " -d, --dump-binary=PATH Path to dump-signedexchange binary"
|
113
|
+
puts " Default: #{DUMP_BINARY}"
|
30
114
|
puts
|
31
115
|
show_statuses
|
116
|
+
puts
|
117
|
+
puts "Examples:"
|
118
|
+
puts " #{exe_name} warm https://example.com/page1"
|
119
|
+
puts " #{exe_name} validate -n -e https://example.com/page1 https://another.com"
|
32
120
|
end
|
33
121
|
|
34
122
|
def show_statuses
|
35
|
-
puts "
|
36
|
-
|
37
|
-
puts " #{icon} #{status.to_s.tr("_", " ")}"
|
123
|
+
puts "Validation status indicators:"
|
124
|
+
printer.icons.map do |status, icon|
|
125
|
+
puts " #{icon} #{status.to_s.tr("_", " ").capitalize}"
|
38
126
|
end
|
39
127
|
end
|
40
128
|
|
41
|
-
attr_reader :exe_name, :
|
129
|
+
attr_reader :exe_name, :printer, :default_dump_binary
|
42
130
|
|
43
131
|
def initialize(exe_name)
|
44
132
|
@exe_name = exe_name
|
45
|
-
@
|
46
|
-
@checker = SxgChecker::Checker.new
|
133
|
+
@printer = SxgChecker::Printer.new
|
47
134
|
end
|
48
135
|
end
|
49
136
|
end
|
data/lib/sxg_checker/document.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
module SxgChecker
|
2
|
-
class
|
3
|
-
def
|
4
|
-
icon
|
5
|
-
|
2
|
+
class Printer
|
3
|
+
def call(document)
|
4
|
+
puts "#{icon(document)} #{document.url}"
|
5
|
+
document.subresources.sort.each do |subresource|
|
6
|
+
puts "#{icon(subresource)} #{subresource.url}"
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
attr_reader :icons
|
9
11
|
|
10
12
|
private
|
11
13
|
|
14
|
+
def icon(resource)
|
15
|
+
icons.fetch(resource.status)
|
16
|
+
end
|
17
|
+
|
12
18
|
def initialize(icons = {
|
13
19
|
ok: "✓",
|
14
20
|
missing: "?",
|
data/lib/sxg_checker/version.rb
CHANGED
data/lib/sxg_checker.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sxg_checker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paweł Pokrywka
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
@@ -59,7 +59,7 @@ files:
|
|
59
59
|
- lib/sxg_checker/checker.rb
|
60
60
|
- lib/sxg_checker/cli.rb
|
61
61
|
- lib/sxg_checker/document.rb
|
62
|
-
- lib/sxg_checker/
|
62
|
+
- lib/sxg_checker/printer.rb
|
63
63
|
- lib/sxg_checker/subresource.rb
|
64
64
|
- lib/sxg_checker/version.rb
|
65
65
|
homepage: https://github.com/pepawel/sxg_checker
|