crawlscope 0.10.0 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da58b612eb199f881ec2263858feea4bf93e11355ffce7b19921bb266451818e
4
- data.tar.gz: bcfa91a8bd74cf5ae670dcc5a1b3e762ad0c6a74037cd82f92b700f8b3a2d7d0
3
+ metadata.gz: 1ff240a395648f3047e4fd24fe2728ad5c7ad3c22406029f7dbb0316f43bee73
4
+ data.tar.gz: 93e012e28037929afadba5df00f8ad441665ddda8cbc55775278fcc427d5af00
5
5
  SHA512:
6
- metadata.gz: e3001de98212ddd2dca5f15feec4bf32eb9b3a975bef21c948c15c588c9a6d9f18bd130985fc3a6cfa78d1dfd34c2c5c4e944563ecd27be87cc49819475aed46
7
- data.tar.gz: 784d47ba0dbf3effdbacb53d9211472e044b22da28c2d00e21bbcb8844aa2a1309eba072c0c631412b1dd7bad10f6713ad0adb107f2805df42310b1b31ad7991
6
+ metadata.gz: d7f51921917c37fd1cb6576d1c1c56957cdc6be141906b5958153c548296248a76316832efd7dcd562835a08b6393a3009e577ed59f676b72786ec3dc99cd81b
7
+ data.tar.gz: 7db8cd080f1256e42e17cf09cbd3a34f2a62bcbd0e48238e58f420359519fd4e737516c21ad48212c17bc34e093914e641d2a8fbbd60ec94f68600466ae098bf
data/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.10.1] - 2026-11-08
9
+
10
+
11
+ ### Documentation
12
+
13
+ - make Ethos Link the canonical documentation home
14
+
15
+ - keep audited technical guidance alongside the code
16
+
17
+
18
+
8
19
  ## [0.10.0] - 2026-11-06
9
20
 
10
21
 
data/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/crawlscope.svg)](https://badge.fury.io/rb/crawlscope)
4
4
  [![Commit messages](https://github.com/ethos-link/crawlscope/actions/workflows/commitlint.yml/badge.svg)](https://github.com/ethos-link/crawlscope/actions/workflows/commitlint.yml)
5
5
 
6
- `crawlscope` is a small Ruby gem for sitemap-driven SEO validation.
6
+ `crawlscope` audits sitemap URLs for indexability, metadata, structured data and internal links.
7
7
 
8
8
  It is built by [Ethos Link](https://www.ethos-link.com) and used in production by [Reviato](https://www.reviato.com).
9
9
 
10
10
  It is designed for Rails apps and plain Ruby scripts that want:
11
11
 
12
- - deterministic sitemap crawling
12
+ - sitemap-driven crawling
13
13
  - structured validation issues instead of free-form strings
14
14
  - app-configurable rule and schema registries
15
15
  - first-party rake tasks instead of a large DSL
@@ -30,6 +30,16 @@ The default rule set includes:
30
30
  - content-quality checks
31
31
  - internal-link checks
32
32
 
33
+ ## Documentation
34
+
35
+ This README and the linked repository guides describe the checked-out code.
36
+ The [Ethos Link project page](https://www.ethos-link.com/opensource/crawlscope)
37
+ provides the canonical public project guide, overview and resources.
38
+
39
+ - [Rails integration and tasks](docs/rails.md)
40
+ - [Rule catalogue and schema registries](docs/rules.md)
41
+ - [Server timing and diagnostic headers](docs/server-timing.md)
42
+
33
43
  ## Installation
34
44
 
35
45
  Crawlscope requires Ruby 4.0 or newer. Rails is optional; the CLI and Ruby
@@ -63,7 +73,7 @@ If you want browser rendering, also add:
63
73
  gem "ferrum"
64
74
  ```
65
75
 
66
- `crawlscope` only loads Ferrum when you run in browser mode.
76
+ `crawlscope` only loads Ferrum when you run in browser mode. Install a compatible Chrome or Chromium executable as well.
67
77
 
68
78
  ## CLI Usage
69
79
 
@@ -163,277 +173,46 @@ puts result.issues.to_a.map(&:message)
163
173
  `result.ok?` returns `false` when an error is present. Warnings and notices
164
174
  remain available through `result.issues` without making the result fail.
165
175
 
166
- ## Server Timing
167
-
168
- Crawlscope parses the
169
- [`Server-Timing`](https://www.w3.org/TR/server-timing/) response header for both
170
- HTTP and browser-rendered crawls. Each page exposes its parsed header through
171
- `page.server_timing`:
172
-
173
- ```ruby
174
- page.server_timing.each do |metric|
175
- puts [metric.name, metric.duration, metric.description].compact.join(": ")
176
- end
177
- ```
178
-
179
- The validation report adds a `Server Timing` section only when at least one page
180
- publishes the header. It includes:
181
-
182
- - header coverage and the number of pages publishing durations
183
- - sample and page counts with average, p50, p95, and maximum per metric
184
- - non-duration signals, including cache status and routing descriptions
185
- - the ten pages with the largest individual metric
186
- - the number of malformed entries ignored during parsing
187
-
188
- `dur` values are reported as milliseconds, as recommended by the specification.
189
- Crawlscope does not add durations together because metrics such as `total`,
190
- `app`, and `db` may overlap. A page's worst-offender entry is its largest
191
- individual metric instead.
192
-
193
- The current HTTP and browser transports expose response headers, not response
194
- trailers. Metrics published only in a `Server-Timing` trailer are therefore not
195
- available to Crawlscope.
176
+ ## Rails integration
196
177
 
197
- Rails 8 applications can enable `ActionDispatch::ServerTiming` with:
178
+ Add `gem "crawlscope", require: false` to the host application's Gemfile, run
179
+ `bundle install`, then install the task/configuration integration:
198
180
 
199
- ```ruby
200
- config.server_timing = true
201
- ```
202
-
203
- Applications that expose timing only to authenticated diagnostics can set
204
- `config.profile_token`. When this value is present, Crawlscope sends it as
205
- `X-Profile-Token` on same-origin sitemap, HTTP, and browser document requests.
206
- The header survives same-origin redirects but is removed before a cross-origin
207
- redirect, and browser subresources never receive it. Keep the token in the host
208
- application's secret store rather than a URL, command argument, report, or
209
- checked-in configuration.
210
-
211
- `CRAWLSCOPE_PROFILE_TOKEN` is the portable default for Rails, the standalone
212
- CLI, Rake tasks, and plain Ruby callers. Rails applications may instead assign
213
- `config.profile_token` from encrypted credentials when that is their established
214
- secret-management boundary; explicit configuration takes precedence over the
215
- environment.
216
-
217
- New Rails applications enable it in development by default; production remains
218
- opt-in. Rails publishes the Active Support notification names observed during
219
- each request and sums repeated events with the same name. The exact metrics
220
- therefore depend on the application and request, but commonly include controller,
221
- view, database, and cache instrumentation. Crawlscope accepts Rails' dotted
222
- metric names and reports each metric independently.
223
-
224
- ## Rails Usage
225
-
226
- Install Rails 8 in the host application; Crawlscope does not install Railties.
227
- For a process that loads Crawlscope before Rails, require `crawlscope/rails`
228
- after Rails is available. The Ruby and CLI entry points remain independent of Rails.
229
-
230
- Run the install generator after adding the gem:
231
-
232
- ```bash
181
+ ```sh
233
182
  bin/rails generate crawlscope:install
183
+ bin/rails crawlscope:validate
234
184
  ```
235
185
 
236
- The generator creates `config/initializers/crawlscope.rb` with an idempotent
237
- `CrawlscopeConfiguration.apply` loader and adds conditional `crawlscope/tasks`
238
- loading to the application's `Rakefile`. This preserves `bin/rails --tasks` and
239
- the `crawlscope:*` tasks without loading Crawlscope during normal Rails boot.
240
-
241
- Customize the `Crawlscope.configure` block inside the generated initializer:
242
-
243
- ```ruby
244
- Crawlscope.configure do |config|
245
- config.base_url = -> { ENV.fetch("CRAWLSCOPE_BASE_URL", "http://localhost:3000") }
246
- config.sitemap_path = lambda {
247
- ENV.fetch("SITEMAP", "#{config.base_url.to_s.chomp("/")}/sitemap.xml")
248
- }
249
- config.site_name = ENV.fetch("CRAWLSCOPE_SITE_NAME", "Application")
250
- end
251
- ```
252
-
253
- Runtime callers must apply the generated configuration before using
254
- Crawlscope:
186
+ Edit the generated `Crawlscope.configure` block to set `base_url`,
187
+ `sitemap_path` and any custom rule/schema registries. Tasks load and apply it
188
+ automatically. From runtime application code, apply the generated loader and
189
+ execute the crawl explicitly:
255
190
 
256
191
  ```ruby
257
192
  CrawlscopeConfiguration.apply
258
- Crawlscope.configuration.audit
259
- ```
260
-
261
- Rake tasks apply it automatically because `crawlscope/tasks` loads the gem
262
- before Rails evaluates the initializer. Each Rake entry point passes the shared
263
- `Crawlscope.configuration` object to the CLI, so configured base URLs, sitemap
264
- paths, registries, and runtime settings are preserved unless a task argument or
265
- environment override replaces them.
266
-
267
- Then run:
268
-
269
- ```bash
270
- bin/rails crawlscope:validate
271
- ```
272
-
273
- Available environment overrides:
274
-
275
- - `URL`
276
- - `SITEMAP`
277
- - `CRAWLSCOPE_PROFILE_TOKEN`
278
- - `RULES=metadata,links`
279
- - `JS=1` or `RENDERER=browser`
280
- - `TIMEOUT=30`
281
- - `NETWORK_IDLE_TIMEOUT=10`
282
- - `CONCURRENCY=5`
283
- - `FETCH_EXECUTOR=threaded` or `FETCH_EXECUTOR=async`
284
-
285
- Available tasks:
286
-
287
- ```bash
288
- bin/rails crawlscope:validate
289
- bin/rails crawlscope:validate:indexability
290
- bin/rails crawlscope:validate:metadata
291
- bin/rails crawlscope:validate:structured_data
292
- bin/rails crawlscope:validate:uniqueness
293
- bin/rails crawlscope:validate:content_quality
294
- bin/rails crawlscope:validate:links
295
- bin/rails crawlscope:validate:ldjson
296
- ```
297
-
298
- The same validation surface is also available in the gem repository itself through plain `rake`:
299
-
300
- ```bash
301
- bundle exec rake crawlscope:validate URL=https://example.com
302
- bundle exec rake 'crawlscope:validate[https://example.com]'
303
- bundle exec rake crawlscope:validate:metadata URL=https://example.com
304
- bundle exec rake 'crawlscope:validate:metadata[https://example.com]'
305
- bundle exec rake crawlscope:validate:ldjson URL=https://example.com/article
306
- bundle exec rake 'crawlscope:validate:ldjson[https://example.com/article]'
307
- ```
308
-
309
- `crawlscope:validate` runs all default sitemap rules: indexability, metadata,
310
- structured data, uniqueness, content quality, and links. `URL` is the site
311
- base. Without `SITEMAP`, Crawlscope uses the configured sitemap URL, then fetches
312
- `/sitemap.xml` from `URL` over HTTP. With `SITEMAP`, Crawlscope uses `URL` as
313
- the site base and validates URLs from that sitemap. `SITEMAP` may be a full URL
314
- or an explicitly selected local file path.
315
-
316
- Plain `rake` does not pass `--url` style flags to tasks. Use `URL=...` or the
317
- task-argument form above instead.
318
-
319
- `FETCH_EXECUTOR=async` is the default for HTTP crawling. It uses Ruby's fiber
320
- scheduler and Async::HTTP through Faraday, preserving the same `CONCURRENCY`
321
- bound. Use `FETCH_EXECUTOR=threaded` or `--fetch-executor threaded` for the
322
- thread-pool executor. Browser rendering uses the threaded executor by default
323
- because async fetch execution is only supported with HTTP rendering.
324
-
325
- `crawlscope:validate:ldjson` is separate because it directly checks the URL or semicolon-separated URLs in `URL`; it does not crawl the sitemap. Without `URL`, it checks the configured base URL, falling back to `http://localhost:3000`.
326
-
327
- ### Structured Data URL Audit
328
-
329
- For one-off structured-data checks:
330
-
331
- ```bash
332
- bin/rails crawlscope:validate:ldjson URL=https://example.com/article
333
- bin/rails crawlscope:validate:ldjson URL='https://example.com/a;https://example.com/b' SUMMARY=1
334
- bin/rails crawlscope:validate:ldjson URL=https://example.com/article REPORT_PATH=tmp/structured-data.json
193
+ result = Crawlscope.configuration.audit.call
335
194
  ```
336
195
 
337
- Optional flags:
338
-
339
- - `DEBUG=1`: print detected items
340
- - `SUMMARY=1`: print grouped failures
341
- - `REPORT_PATH=...`: write a JSON report. Treat this as trusted operator input; Crawlscope writes to the path the task process can access.
342
- - `JS=1` or `RENDERER=browser`: render with Ferrum
343
-
344
- ## Rules
345
-
346
- Built-in rules:
347
-
348
- - `indexability`
349
- - `metadata`
350
- - `structured_data`
351
- - `uniqueness`
352
- - `content_quality`
353
- - `links`
354
-
355
- ### Indexability
356
-
357
- Checks:
358
-
359
- - page-level meta robots `noindex`
360
- - `X-Robots-Tag: noindex`
361
-
362
- ### Metadata
363
-
364
- Checks:
365
-
366
- - missing `<h1>`
367
- - missing `<title>`
368
- - title length
369
- - repeated site name in the title
370
- - missing meta description
371
- - meta description length
372
- - missing canonical link
373
- - canonical mismatch
374
-
375
- ### Structured Data
196
+ See [Rails integration](docs/rails.md) for lazy loading, all tasks,
197
+ environment overrides and structured-data URL audits.
376
198
 
377
- Checks:
199
+ ## Configuration and limits
378
200
 
379
- - malformed JSON-LD
380
- - missing required fields for supported schema types
381
- - schema validation failures from the configured registry
382
- - direct URL structured-data audits through `crawlscope:validate:ldjson`
201
+ The shared configuration defaults to HTTP rendering, the async fetch executor,
202
+ 10 concurrent requests and a 20-second request timeout. Browser rendering uses
203
+ the threaded executor with at most four concurrent browser fetches. Configure
204
+ `concurrency`, `timeout_seconds`, `renderer`, `fetch_executor`, `rule_registry`
205
+ and `schema_registry` through `Crawlscope.configure` when using that shared
206
+ configuration; direct `Crawlscope::Crawl.new` callers pass their own options.
383
207
 
384
- ### Uniqueness
208
+ Use [rule and schema registries](docs/rules.md) to select or extend checks.
209
+ Near-duplicate scanning is limited to 250 HTML pages by default; exact duplicate
210
+ checks still run for larger crawls. Validation describes observed responses and
211
+ configured rules; it does not establish Google rich-result eligibility or
212
+ search ranking.
385
213
 
386
- Checks:
387
-
388
- - duplicate titles
389
- - duplicate meta descriptions
390
- - duplicate content fingerprints
391
- - near-duplicate visible content for up to 250 HTML pages
392
-
393
- For larger crawls, exact duplicate checks still run and Crawlscope reports
394
- `near_duplicate_scan_skipped`. Configure `Rules::Uniqueness` with
395
- `max_near_duplicate_pages:` in a custom rule registry to change the limit.
396
-
397
- ### Content Quality
398
-
399
- Checks:
400
-
401
- - thin visible text
402
- - low visible-text-to-HTML ratio
403
- - low unique-token ratio
404
-
405
- ### Links
406
-
407
- Checks:
408
-
409
- - broken internal links
410
- - unresolved internal links
411
- - low inbound anchor-link counts
412
-
413
- ## Schema Registry
414
-
415
- `crawlscope` ships with a default schema registry for common types such as:
416
-
417
- - `Article`
418
- - `FAQPage`
419
- - `Organization`
420
- - `Product`
421
- - `Review`
422
- - `SoftwareApplication`
423
- - `WebApplication`
424
- - `WebSite`
425
-
426
- The default schema definitions live in `Crawlscope::Schemas`; `Crawlscope::SchemaRegistry` owns registration and validation.
427
-
428
- Host apps can replace or extend the registry:
429
-
430
- ```ruby
431
- Crawlscope.configure do |config|
432
- config.schema_registry = -> { MyApp::StructuredData::SchemaRegistry.new }
433
- end
434
- ```
435
-
436
- That makes `crawlscope` useful as the audit engine while the app remains the owner of stricter product-specific schema rules.
214
+ [Server timing](docs/server-timing.md) covers response timings, diagnostic
215
+ profile tokens and the limits of those measurements.
437
216
 
438
217
  ## Contributing
439
218
 
@@ -463,3 +242,15 @@ bundle exec rake verify
463
242
  Tests use controlled local fixtures and need no service account.
464
243
  Release preparation, publication, registry setup and recovery are documented
465
244
  in the [Gemkit release guide](https://github.com/ethos-link/ethos-gemkit#releasing-a-gem).
245
+
246
+ ## About Ethos Link
247
+
248
+ [Ethos Link](https://www.ethos-link.com) is a software company founded by
249
+ [Paulo Fidalgo](https://www.fidalgo.pt). This gem is part of the open-source
250
+ tooling we publish alongside our business products:
251
+
252
+ - [Reviato](https://www.reviato.com): collect private customer feedback and analyse public reviews.
253
+ - [Nuntio](https://www.nuntiohq.com): prepare, review and schedule social posts.
254
+ - [EthosPlaces](https://www.ethos.place): find places, opening hours and contact details.
255
+
256
+ Explore our [open-source projects](https://www.ethos-link.com/opensource).
data/docs/rails.md ADDED
@@ -0,0 +1,121 @@
1
+ # Rails integration
2
+
3
+ [README](../README.md) · [Project guide](https://www.ethos-link.com/opensource/crawlscope)
4
+
5
+ Install Rails 8 in the host application; Crawlscope does not install Railties.
6
+ For a process that loads Crawlscope before Rails, require `crawlscope/rails`
7
+ after Rails is available. The Ruby and CLI entry points remain independent of Rails.
8
+
9
+ Run the install generator after adding the gem:
10
+
11
+ ```bash
12
+ bin/rails generate crawlscope:install
13
+ ```
14
+
15
+ The generator creates `config/initializers/crawlscope.rb` with an idempotent
16
+ `CrawlscopeConfiguration.apply` loader and adds conditional `crawlscope/tasks`
17
+ loading to the application's `Rakefile`. This preserves `bin/rails --tasks` and
18
+ the `crawlscope:*` tasks without loading Crawlscope during normal Rails boot.
19
+
20
+ Customize the `Crawlscope.configure` block inside the generated initializer:
21
+
22
+ ```ruby
23
+ Crawlscope.configure do |config|
24
+ config.base_url = -> { ENV.fetch("CRAWLSCOPE_BASE_URL", "http://localhost:3000") }
25
+ config.sitemap_path = lambda {
26
+ ENV.fetch("SITEMAP", "#{config.base_url.to_s.chomp("/")}/sitemap.xml")
27
+ }
28
+ config.site_name = ENV.fetch("CRAWLSCOPE_SITE_NAME", "Application")
29
+ end
30
+ ```
31
+
32
+ Runtime callers must apply the generated configuration before using
33
+ Crawlscope:
34
+
35
+ ```ruby
36
+ CrawlscopeConfiguration.apply
37
+ Crawlscope.configuration.audit.call
38
+ ```
39
+
40
+ Rake tasks apply it automatically because `crawlscope/tasks` loads the gem
41
+ before Rails evaluates the initializer. Each Rake entry point passes the shared
42
+ `Crawlscope.configuration` object to the CLI, so configured base URLs, sitemap
43
+ paths, registries, and runtime settings are preserved unless a task argument or
44
+ environment override replaces them.
45
+
46
+ Then run:
47
+
48
+ ```bash
49
+ bin/rails crawlscope:validate
50
+ ```
51
+
52
+ Available environment overrides:
53
+
54
+ - `URL`
55
+ - `SITEMAP`
56
+ - `CRAWLSCOPE_PROFILE_TOKEN`
57
+ - `RULES=metadata,links`
58
+ - `JS=1` or `RENDERER=browser`
59
+ - `TIMEOUT=30`
60
+ - `NETWORK_IDLE_TIMEOUT=10`
61
+ - `CONCURRENCY=5`
62
+ - `FETCH_EXECUTOR=threaded` or `FETCH_EXECUTOR=async`
63
+
64
+ Available tasks:
65
+
66
+ ```bash
67
+ bin/rails crawlscope:validate
68
+ bin/rails crawlscope:validate:indexability
69
+ bin/rails crawlscope:validate:metadata
70
+ bin/rails crawlscope:validate:structured_data
71
+ bin/rails crawlscope:validate:uniqueness
72
+ bin/rails crawlscope:validate:content_quality
73
+ bin/rails crawlscope:validate:links
74
+ bin/rails crawlscope:validate:ldjson
75
+ ```
76
+
77
+ The same validation surface is also available in the gem repository itself through plain `rake`:
78
+
79
+ ```bash
80
+ bundle exec rake crawlscope:validate URL=https://example.com
81
+ bundle exec rake 'crawlscope:validate[https://example.com]'
82
+ bundle exec rake crawlscope:validate:metadata URL=https://example.com
83
+ bundle exec rake 'crawlscope:validate:metadata[https://example.com]'
84
+ bundle exec rake crawlscope:validate:ldjson URL=https://example.com/article
85
+ bundle exec rake 'crawlscope:validate:ldjson[https://example.com/article]'
86
+ ```
87
+
88
+ `crawlscope:validate` runs all default sitemap rules: indexability, metadata,
89
+ structured data, uniqueness, content quality, and links. `URL` is the site
90
+ base. Without `SITEMAP`, Crawlscope uses the configured sitemap URL, then fetches
91
+ `/sitemap.xml` from `URL` over HTTP. With `SITEMAP`, Crawlscope uses `URL` as
92
+ the site base and validates URLs from that sitemap. `SITEMAP` may be a full URL
93
+ or an explicitly selected local file path.
94
+
95
+ Plain `rake` does not pass `--url` style flags to tasks. Use `URL=...` or the
96
+ task-argument form above instead.
97
+
98
+ `FETCH_EXECUTOR=async` is the default for HTTP crawling. It uses Ruby's fiber
99
+ scheduler and Async::HTTP through Faraday, preserving the same `CONCURRENCY`
100
+ bound. Use `FETCH_EXECUTOR=threaded` or `--fetch-executor threaded` for the
101
+ thread-pool executor. Browser rendering uses the threaded executor by default
102
+ because async fetch execution is only supported with HTTP rendering.
103
+
104
+ `crawlscope:validate:ldjson` is separate because it directly checks the URL or semicolon-separated URLs in `URL`; it does not crawl the sitemap. Without `URL`, it checks the configured base URL, falling back to `http://localhost:3000`.
105
+
106
+ ## Structured Data URL Audit
107
+
108
+ For one-off structured-data checks:
109
+
110
+ ```bash
111
+ bin/rails crawlscope:validate:ldjson URL=https://example.com/article
112
+ bin/rails crawlscope:validate:ldjson URL='https://example.com/a;https://example.com/b' SUMMARY=1
113
+ bin/rails crawlscope:validate:ldjson URL=https://example.com/article REPORT_PATH=tmp/structured-data.json
114
+ ```
115
+
116
+ Optional flags:
117
+
118
+ - `DEBUG=1`: print detected items
119
+ - `SUMMARY=1`: print grouped failures
120
+ - `REPORT_PATH=...`: write a JSON report. Treat this as trusted operator input; Crawlscope writes to the path the task process can access.
121
+ - `JS=1` or `RENDERER=browser`: render with Ferrum
data/docs/rules.md ADDED
@@ -0,0 +1,95 @@
1
+ # Rules and schemas
2
+
3
+ [README](../README.md) · [Project guide](https://www.ethos-link.com/opensource/crawlscope)
4
+
5
+ Built-in rules:
6
+
7
+ - `indexability`
8
+ - `metadata`
9
+ - `structured_data`
10
+ - `uniqueness`
11
+ - `content_quality`
12
+ - `links`
13
+
14
+ ## Indexability
15
+
16
+ Checks:
17
+
18
+ - page-level meta robots `noindex`
19
+ - `X-Robots-Tag: noindex`
20
+
21
+ ## Metadata
22
+
23
+ Checks:
24
+
25
+ - missing `<h1>`
26
+ - missing `<title>`
27
+ - title length
28
+ - repeated site name in the title
29
+ - missing meta description
30
+ - meta description length
31
+ - missing canonical link
32
+ - canonical mismatch
33
+
34
+ ## Structured Data
35
+
36
+ Checks:
37
+
38
+ - malformed JSON-LD
39
+ - missing required fields for supported schema types
40
+ - schema validation failures from the configured registry
41
+ - direct URL structured-data audits through `crawlscope:validate:ldjson`
42
+
43
+ ## Uniqueness
44
+
45
+ Checks:
46
+
47
+ - duplicate titles
48
+ - duplicate meta descriptions
49
+ - duplicate content fingerprints
50
+ - near-duplicate visible content for up to 250 HTML pages
51
+
52
+ For larger crawls, exact duplicate checks still run and Crawlscope reports
53
+ `near_duplicate_scan_skipped`. Configure `Rules::Uniqueness` with
54
+ `max_near_duplicate_pages:` in a custom rule registry to change the limit.
55
+
56
+ ## Content Quality
57
+
58
+ Checks:
59
+
60
+ - thin visible text
61
+ - low visible-text-to-HTML ratio
62
+ - low unique-token ratio
63
+
64
+ ## Links
65
+
66
+ Checks:
67
+
68
+ - broken internal links
69
+ - unresolved internal links
70
+ - low inbound anchor-link counts
71
+
72
+ ## Schema Registry
73
+
74
+ `crawlscope` ships with a default schema registry for common types such as:
75
+
76
+ - `Article`
77
+ - `FAQPage`
78
+ - `Organization`
79
+ - `Product`
80
+ - `Review`
81
+ - `SoftwareApplication`
82
+ - `WebApplication`
83
+ - `WebSite`
84
+
85
+ The default schema definitions live in `Crawlscope::Schemas`; `Crawlscope::SchemaRegistry` owns registration and validation.
86
+
87
+ Host apps can replace or extend the registry:
88
+
89
+ ```ruby
90
+ Crawlscope.configure do |config|
91
+ config.schema_registry = -> { MyApp::StructuredData::SchemaRegistry.new }
92
+ end
93
+ ```
94
+
95
+ That makes `crawlscope` useful as the audit engine while the app remains the owner of stricter product-specific schema rules.
@@ -0,0 +1,59 @@
1
+ # Server timing
2
+
3
+ [README](../README.md) · [Project guide](https://www.ethos-link.com/opensource/crawlscope)
4
+
5
+ Crawlscope parses the
6
+ [`Server-Timing`](https://www.w3.org/TR/server-timing/) response header for both
7
+ HTTP and browser-rendered crawls. Each page exposes its parsed header through
8
+ `page.server_timing`:
9
+
10
+ ```ruby
11
+ page.server_timing.each do |metric|
12
+ puts [metric.name, metric.duration, metric.description].compact.join(": ")
13
+ end
14
+ ```
15
+
16
+ The validation report adds a `Server Timing` section only when at least one page
17
+ publishes the header. It includes:
18
+
19
+ - header coverage and the number of pages publishing durations
20
+ - sample and page counts with average, p50, p95, and maximum per metric
21
+ - non-duration signals, including cache status and routing descriptions
22
+ - the ten pages with the largest individual metric
23
+ - the number of malformed entries ignored during parsing
24
+
25
+ `dur` values are reported as milliseconds, as recommended by the specification.
26
+ Crawlscope does not add durations together because metrics such as `total`,
27
+ `app`, and `db` may overlap. A page's worst-offender entry is its largest
28
+ individual metric instead.
29
+
30
+ The current HTTP and browser transports expose response headers, not response
31
+ trailers. Metrics published only in a `Server-Timing` trailer are therefore not
32
+ available to Crawlscope.
33
+
34
+ Rails 8 applications can enable `ActionDispatch::ServerTiming` with:
35
+
36
+ ```ruby
37
+ config.server_timing = true
38
+ ```
39
+
40
+ Applications that expose timing only to authenticated diagnostics can set
41
+ `config.profile_token`. When this value is present, Crawlscope sends it as
42
+ `X-Profile-Token` on same-origin sitemap, HTTP, and browser document requests.
43
+ The header survives same-origin redirects but is removed before a cross-origin
44
+ redirect, and browser subresources never receive it. Keep the token in the host
45
+ application's secret store rather than a URL, command argument, report, or
46
+ checked-in configuration.
47
+
48
+ `CRAWLSCOPE_PROFILE_TOKEN` is the portable default for Rails, the standalone
49
+ CLI, Rake tasks, and plain Ruby callers. Rails applications may instead assign
50
+ `config.profile_token` from encrypted credentials when that is their established
51
+ secret-management boundary; explicit configuration takes precedence over the
52
+ environment.
53
+
54
+ New Rails applications enable Server-Timing in development by default;
55
+ production remains opt-in. Rails publishes the Active Support notification names observed during
56
+ each request and sums repeated events with the same name. The exact metrics
57
+ therefore depend on the application and request, but commonly include controller,
58
+ view, database, and cache instrumentation. Crawlscope accepts Rails' dotted
59
+ metric names and reports each metric independently.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crawlscope
4
- VERSION = "0.10.0"
4
+ VERSION = "0.10.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crawlscope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Fidalgo
@@ -178,6 +178,9 @@ files:
178
178
  - LICENSE.txt
179
179
  - README.md
180
180
  - UPGRADE.md
181
+ - docs/rails.md
182
+ - docs/rules.md
183
+ - docs/server-timing.md
181
184
  - exe/crawlscope
182
185
  - lib/crawlscope.rb
183
186
  - lib/crawlscope/browser.rb
@@ -270,7 +273,7 @@ metadata:
270
273
  source_code_uri: https://github.com/ethos-link/crawlscope
271
274
  bug_tracker_uri: https://github.com/ethos-link/crawlscope/issues
272
275
  changelog_uri: https://github.com/ethos-link/crawlscope/blob/main/CHANGELOG.md
273
- documentation_uri: https://github.com/ethos-link/crawlscope/blob/main/README.md
276
+ documentation_uri: https://www.ethos-link.com/opensource/crawlscope
274
277
  funding_uri: https://www.reviato.com/
275
278
  github_repo: ssh://github.com/ethos-link/crawlscope
276
279
  allowed_push_host: https://rubygems.org