@nitpicker/crawler 0.13.0 → 0.15.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.
Files changed (171) hide show
  1. package/lib/archive/archive-accessor.d.ts +56 -0
  2. package/lib/archive/archive-accessor.js +72 -0
  3. package/lib/archive/archive.d.ts +108 -9
  4. package/lib/archive/archive.js +109 -7
  5. package/lib/archive/body-hash/compute-body-hash.d.ts +22 -0
  6. package/lib/archive/body-hash/compute-body-hash.js +31 -0
  7. package/lib/archive/body-hash/extract-body.d.ts +18 -0
  8. package/lib/archive/body-hash/extract-body.js +29 -0
  9. package/lib/archive/body-hash/mask-dynamic-ids.d.ts +33 -0
  10. package/lib/archive/body-hash/mask-dynamic-ids.js +44 -0
  11. package/lib/archive/body-hash/normalize-url-like-strings.d.ts +16 -0
  12. package/lib/archive/body-hash/normalize-url-like-strings.js +19 -0
  13. package/lib/archive/cache/clear-archive-cache-entry.d.ts +19 -0
  14. package/lib/archive/cache/clear-archive-cache-entry.js +25 -0
  15. package/lib/archive/cache/clear-archive-cache-root.d.ts +26 -0
  16. package/lib/archive/cache/clear-archive-cache-root.js +32 -0
  17. package/lib/archive/cache/list-archive-cache-entries.d.ts +23 -0
  18. package/lib/archive/cache/list-archive-cache-entries.js +125 -0
  19. package/lib/archive/cache/path-exists.d.ts +9 -0
  20. package/lib/archive/cache/path-exists.js +18 -0
  21. package/lib/archive/cache/types.d.ts +33 -0
  22. package/lib/archive/cache/types.js +1 -0
  23. package/lib/archive/create-adjunct-tables.d.ts +18 -0
  24. package/lib/archive/create-adjunct-tables.js +277 -0
  25. package/lib/archive/create-entity-tables.d.ts +14 -0
  26. package/lib/archive/create-entity-tables.js +52 -1
  27. package/lib/archive/database.d.ts +91 -2
  28. package/lib/archive/database.js +128 -0
  29. package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +1 -0
  30. package/lib/archive/db-ops/_shared/compress-payload.d.ts +26 -0
  31. package/lib/archive/db-ops/_shared/compress-payload.js +30 -0
  32. package/lib/archive/db-ops/_shared/create-write-ref-caches.js +1 -0
  33. package/lib/archive/db-ops/_shared/types.d.ts +2 -0
  34. package/lib/archive/db-ops/_shared/upsert-json-ref.js +3 -3
  35. package/lib/archive/db-ops/analysis/replace-page-templates.d.ts +29 -0
  36. package/lib/archive/db-ops/analysis/replace-page-templates.js +84 -0
  37. package/lib/archive/db-ops/analysis/types.d.ts +61 -0
  38. package/lib/archive/db-ops/analysis/types.js +1 -0
  39. package/lib/archive/db-ops/config/info-column-allowlist.js +1 -0
  40. package/lib/archive/db-ops/console-logs/compute-console-log-hash.d.ts +19 -0
  41. package/lib/archive/db-ops/console-logs/compute-console-log-hash.js +30 -0
  42. package/lib/archive/db-ops/console-logs/replace-console-logs.d.ts +34 -0
  43. package/lib/archive/db-ops/console-logs/replace-console-logs.js +123 -0
  44. package/lib/archive/db-ops/console-logs/stringify-console-log-args.d.ts +16 -0
  45. package/lib/archive/db-ops/console-logs/stringify-console-log-args.js +26 -0
  46. package/lib/archive/db-ops/console-logs/types.d.ts +38 -0
  47. package/lib/archive/db-ops/console-logs/types.js +1 -0
  48. package/lib/archive/db-ops/console-logs/upsert-console-log-item.d.ts +29 -0
  49. package/lib/archive/db-ops/console-logs/upsert-console-log-item.js +52 -0
  50. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +11 -0
  51. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +21 -4
  52. package/lib/archive/db-ops/inventory/record-inventory-run.js +1 -0
  53. package/lib/archive/db-ops/lifecycle/init.d.ts +25 -10
  54. package/lib/archive/db-ops/lifecycle/init.js +39 -10
  55. package/lib/archive/db-ops/meta/get-audios-of-page.d.ts +11 -0
  56. package/lib/archive/db-ops/meta/get-audios-of-page.js +15 -0
  57. package/lib/archive/db-ops/meta/get-buttons-of-page.d.ts +11 -0
  58. package/lib/archive/db-ops/meta/get-buttons-of-page.js +15 -0
  59. package/lib/archive/db-ops/meta/get-canvases-of-page.d.ts +11 -0
  60. package/lib/archive/db-ops/meta/get-canvases-of-page.js +15 -0
  61. package/lib/archive/db-ops/meta/get-headings-of-page.d.ts +11 -0
  62. package/lib/archive/db-ops/meta/get-headings-of-page.js +15 -0
  63. package/lib/archive/db-ops/meta/get-iframes-of-page.d.ts +11 -0
  64. package/lib/archive/db-ops/meta/get-iframes-of-page.js +15 -0
  65. package/lib/archive/db-ops/meta/get-main-content-images-of-page.d.ts +11 -0
  66. package/lib/archive/db-ops/meta/get-main-content-images-of-page.js +15 -0
  67. package/lib/archive/db-ops/meta/get-main-content-tables-of-page.d.ts +11 -0
  68. package/lib/archive/db-ops/meta/get-main-content-tables-of-page.js +15 -0
  69. package/lib/archive/db-ops/meta/get-videos-of-page.d.ts +11 -0
  70. package/lib/archive/db-ops/meta/get-videos-of-page.js +15 -0
  71. package/lib/archive/db-ops/outages/close-network-outage.d.ts +12 -0
  72. package/lib/archive/db-ops/outages/close-network-outage.js +15 -0
  73. package/lib/archive/db-ops/outages/close-stale-open-network-outages.d.ts +26 -0
  74. package/lib/archive/db-ops/outages/close-stale-open-network-outages.js +42 -0
  75. package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.d.ts +26 -0
  76. package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.js +31 -0
  77. package/lib/archive/db-ops/outages/insert-network-outage.d.ts +14 -0
  78. package/lib/archive/db-ops/outages/insert-network-outage.js +29 -0
  79. package/lib/archive/db-ops/outages/list-network-outages.d.ts +28 -0
  80. package/lib/archive/db-ops/outages/list-network-outages.js +42 -0
  81. package/lib/archive/db-ops/pages/read/build-page-query.js +1 -1
  82. package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +19 -10
  83. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +18 -3
  84. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +48 -10
  85. package/lib/archive/db-ops/pages/write/insert-audios.d.ts +14 -0
  86. package/lib/archive/db-ops/pages/write/insert-audios.js +25 -0
  87. package/lib/archive/db-ops/pages/write/insert-buttons.d.ts +14 -0
  88. package/lib/archive/db-ops/pages/write/insert-buttons.js +29 -0
  89. package/lib/archive/db-ops/pages/write/insert-canvases.d.ts +14 -0
  90. package/lib/archive/db-ops/pages/write/insert-canvases.js +26 -0
  91. package/lib/archive/db-ops/pages/write/insert-headings.d.ts +15 -0
  92. package/lib/archive/db-ops/pages/write/insert-headings.js +27 -0
  93. package/lib/archive/db-ops/pages/write/insert-iframes.d.ts +14 -0
  94. package/lib/archive/db-ops/pages/write/insert-iframes.js +28 -0
  95. package/lib/archive/db-ops/pages/write/insert-main-content-images.d.ts +20 -0
  96. package/lib/archive/db-ops/pages/write/insert-main-content-images.js +32 -0
  97. package/lib/archive/db-ops/pages/write/insert-main-content-tables.d.ts +14 -0
  98. package/lib/archive/db-ops/pages/write/insert-main-content-tables.js +29 -0
  99. package/lib/archive/db-ops/pages/write/insert-page.js +15 -2
  100. package/lib/archive/db-ops/pages/write/insert-videos.d.ts +14 -0
  101. package/lib/archive/db-ops/pages/write/insert-videos.js +28 -0
  102. package/lib/archive/db-ops/pages/write/update-page.js +33 -0
  103. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +6 -6
  104. package/lib/archive/filesystem/output-binary.d.ts +12 -0
  105. package/lib/archive/filesystem/output-binary.js +17 -0
  106. package/lib/archive/get-failed-page-messages.d.ts +20 -8
  107. package/lib/archive/get-failed-page-messages.js +29 -15
  108. package/lib/archive/init-schema.js +2 -1
  109. package/lib/archive/meta/compute-main-contents-denormalized.d.ts +22 -0
  110. package/lib/archive/meta/compute-main-contents-denormalized.js +63 -0
  111. package/lib/archive/meta/types.d.ts +263 -0
  112. package/lib/archive/migrate-content-items-alias-of-id.d.ts +47 -0
  113. package/lib/archive/migrate-content-items-alias-of-id.js +58 -0
  114. package/lib/archive/migrate-info-main-content-selector.d.ts +11 -0
  115. package/lib/archive/migrate-info-main-content-selector.js +24 -0
  116. package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +11 -0
  117. package/lib/archive/migrate-inventory-runs-invalid-skipped.js +24 -0
  118. package/lib/archive/migrate-main-contents-columns.d.ts +21 -0
  119. package/lib/archive/migrate-main-contents-columns.js +50 -0
  120. package/lib/archive/migrate-page-meta-body-hash.d.ts +39 -0
  121. package/lib/archive/migrate-page-meta-body-hash.js +52 -0
  122. package/lib/archive/migrate-page-meta-console-error-count.d.ts +22 -0
  123. package/lib/archive/migrate-page-meta-console-error-count.js +34 -0
  124. package/lib/archive/page.d.ts +145 -3
  125. package/lib/archive/page.js +195 -0
  126. package/lib/archive/types.d.ts +86 -2
  127. package/lib/archive/url-alias/compute-tier-a-alias-key.d.ts +31 -0
  128. package/lib/archive/url-alias/compute-tier-a-alias-key.js +36 -0
  129. package/lib/archive/url-alias/compute-tier-b-alias-key.d.ts +19 -0
  130. package/lib/archive/url-alias/compute-tier-b-alias-key.js +30 -0
  131. package/lib/archive/url-alias/format-alias-key.d.ts +13 -0
  132. package/lib/archive/url-alias/format-alias-key.js +14 -0
  133. package/lib/archive/url-alias/parse-alias-key-parts.d.ts +15 -0
  134. package/lib/archive/url-alias/parse-alias-key-parts.js +40 -0
  135. package/lib/archive/url-alias/types.d.ts +7 -0
  136. package/lib/archive/url-alias/types.js +1 -0
  137. package/lib/crawler/choose-probe-host.d.ts +24 -0
  138. package/lib/crawler/choose-probe-host.js +38 -0
  139. package/lib/crawler/clear-dns-burned-host-cache.js +2 -0
  140. package/lib/crawler/crawler.js +261 -2
  141. package/lib/crawler/dns-burned-host-burn-timestamps.d.ts +17 -0
  142. package/lib/crawler/dns-burned-host-burn-timestamps.js +17 -0
  143. package/lib/crawler/evict-network-classified-destination-cache-entries.d.ts +26 -0
  144. package/lib/crawler/evict-network-classified-destination-cache-entries.js +34 -0
  145. package/lib/crawler/evict-outage-tainted-dns-burns.d.ts +23 -0
  146. package/lib/crawler/evict-outage-tainted-dns-burns.js +26 -0
  147. package/lib/crawler/fetch-destination.js +2 -0
  148. package/lib/crawler/link-to-page-data.js +2 -0
  149. package/lib/crawler/network-gate.d.ts +49 -0
  150. package/lib/crawler/network-gate.js +78 -0
  151. package/lib/crawler/network-outage-detector.d.ts +51 -0
  152. package/lib/crawler/network-outage-detector.js +81 -0
  153. package/lib/crawler/network-outage-summary-counter.d.ts +21 -0
  154. package/lib/crawler/network-outage-summary-counter.js +18 -0
  155. package/lib/crawler/probe-network.d.ts +26 -0
  156. package/lib/crawler/probe-network.js +21 -0
  157. package/lib/crawler/resource-to-page-data.js +2 -0
  158. package/lib/crawler/types.d.ts +152 -1
  159. package/lib/crawler-orchestrator.d.ts +62 -18
  160. package/lib/crawler-orchestrator.js +163 -26
  161. package/lib/crawler.d.ts +21 -0
  162. package/lib/crawler.js +17 -0
  163. package/lib/is-within-outage-window.d.ts +49 -0
  164. package/lib/is-within-outage-window.js +33 -0
  165. package/lib/network-related-error-kinds.d.ts +23 -0
  166. package/lib/network-related-error-kinds.js +28 -0
  167. package/lib/types.d.ts +9 -1
  168. package/lib/utils/array/each-splitted.d.ts +1 -1
  169. package/lib/utils/compute-file-sha256.d.ts +17 -17
  170. package/lib/utils/compute-file-sha256.js +18 -47
  171. package/package.json +3 -3
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Adds the `page_meta.body_hash` column to archives created before this
3
+ * feature, then ensures its index exists.
4
+ *
5
+ * `page_meta` is provisioned via a bare `CREATE TABLE IF NOT EXISTS` in
6
+ * {@link import('./create-entity-tables.js').createEntityTables}, which
7
+ * self-heals a *missing table* on every `initSchema` call but is a no-op
8
+ * against an *existing* table — adding a column to the DDL string never
9
+ * reaches an archive whose `page_meta` predates this change. This mirrors
10
+ * {@link import('./migrate-main-contents-columns.js').migrateMainContentsColumns}'s
11
+ * catch-up: a `hasColumn`-guarded `ALTER TABLE` for the one column
12
+ * `CREATE TABLE IF NOT EXISTS` cannot retrofit.
13
+ *
14
+ * The index is created here — unconditionally, after the column-add guard,
15
+ * not inside `createEntityTables`'s DDL — for both a fresh archive (where
16
+ * `body_hash` already exists from the DDL, so only the index still needs
17
+ * creating) and a legacy archive (where the column is added just above,
18
+ * then the index follows in the same call). `createEntityTables` runs
19
+ * unconditionally on every archive open, including legacy archives that
20
+ * still lack `body_hash` at that point; an unconditional
21
+ * `CREATE INDEX ... body_hash` there would fail with `no such column` before
22
+ * this migration ever runs. This function is the one place guaranteed to
23
+ * run only after the column is confirmed present, for both archive kinds.
24
+ *
25
+ * Only adds the column and its index — it does not backfill values for
26
+ * existing rows (they stay `NULL`). That backfill runs separately, from
27
+ * `backfillBodyHashFromHtmlBlobs` during a viewer-read-model build, since it
28
+ * requires decompressing every page's stored HTML and is too heavy to run on
29
+ * every archive open.
30
+ *
31
+ * Idempotent: adding the column is a no-op once it exists (the index
32
+ * creation always runs, but `IF NOT EXISTS` makes repeat runs a no-op too).
33
+ * Guards on `page_meta`'s existence defensively, though by the time this
34
+ * runs (after `initSchema`, itself after `assertCompatibleVersion` rejects
35
+ * pre-0.13 archives) the table is always present.
36
+ * @param instance - The Knex query builder instance connected to the database.
37
+ */
38
+ export async function migratePageMetaBodyHash(instance) {
39
+ const hasPageMeta = await instance.schema.hasTable('page_meta');
40
+ if (!hasPageMeta) {
41
+ return;
42
+ }
43
+ const hasColumn = await instance.schema.hasColumn('page_meta', 'body_hash');
44
+ if (!hasColumn) {
45
+ await instance.schema.table('page_meta', (t) => {
46
+ t.binary('body_hash');
47
+ });
48
+ // eslint-disable-next-line no-console
49
+ console.error('[migrate] page_meta.body_hash column added');
50
+ }
51
+ await instance.raw('CREATE INDEX IF NOT EXISTS idx_page_meta_body_hash ON page_meta(body_hash)');
52
+ }
@@ -0,0 +1,22 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Adds the `page_meta.console_error_count` column to archives created
4
+ * before this feature (issue #228).
5
+ *
6
+ * `page_meta` is provisioned via a bare `CREATE TABLE IF NOT EXISTS` in
7
+ * {@link import('./create-entity-tables.js').createEntityTables}, which
8
+ * self-heals a *missing table* on every `initSchema` call but is a no-op
9
+ * against an *existing* table — adding a column to the DDL string never
10
+ * reaches an archive whose `page_meta` predates this change. Same
11
+ * catch-up shape as {@link import('./migrate-page-meta-body-hash.js').migratePageMetaBodyHash}.
12
+ *
13
+ * Unlike `body_hash`, there is no separate backfill step: a page that has
14
+ * never been re-scraped since this feature shipped has no
15
+ * `page_console_logs` rows either, so `0` (the value `replaceConsoleLogs`
16
+ * writes for a page with no error/pageerror entries) is already the
17
+ * correct value, not a placeholder pending backfill.
18
+ *
19
+ * Idempotent: adding the column is a no-op once it exists.
20
+ * @param instance - The Knex query builder instance connected to the database.
21
+ */
22
+ export declare function migratePageMetaConsoleErrorCount(instance: Knex): Promise<void>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Adds the `page_meta.console_error_count` column to archives created
3
+ * before this feature (issue #228).
4
+ *
5
+ * `page_meta` is provisioned via a bare `CREATE TABLE IF NOT EXISTS` in
6
+ * {@link import('./create-entity-tables.js').createEntityTables}, which
7
+ * self-heals a *missing table* on every `initSchema` call but is a no-op
8
+ * against an *existing* table — adding a column to the DDL string never
9
+ * reaches an archive whose `page_meta` predates this change. Same
10
+ * catch-up shape as {@link import('./migrate-page-meta-body-hash.js').migratePageMetaBodyHash}.
11
+ *
12
+ * Unlike `body_hash`, there is no separate backfill step: a page that has
13
+ * never been re-scraped since this feature shipped has no
14
+ * `page_console_logs` rows either, so `0` (the value `replaceConsoleLogs`
15
+ * writes for a page with no error/pageerror entries) is already the
16
+ * correct value, not a placeholder pending backfill.
17
+ *
18
+ * Idempotent: adding the column is a no-op once it exists.
19
+ * @param instance - The Knex query builder instance connected to the database.
20
+ */
21
+ export async function migratePageMetaConsoleErrorCount(instance) {
22
+ const hasPageMeta = await instance.schema.hasTable('page_meta');
23
+ if (!hasPageMeta) {
24
+ return;
25
+ }
26
+ const hasColumn = await instance.schema.hasColumn('page_meta', 'console_error_count');
27
+ if (!hasColumn) {
28
+ await instance.schema.table('page_meta', (t) => {
29
+ t.integer('console_error_count');
30
+ });
31
+ // eslint-disable-next-line no-console
32
+ console.error('[migrate] page_meta.console_error_count column added');
33
+ }
34
+ }
@@ -9,7 +9,7 @@ import type { Anchor, Redirect, Referrer, DB_Anchor, DB_Page, DB_Redirect, DB_Re
9
9
  * Keep in sync with {@link import('./meta/types.js').FlatPageMetaColumns} —
10
10
  * one row of `pages` has the same shape.
11
11
  */
12
- declare const FLAT_META_COLUMNS: readonly ["lang", "dir", "charset", "baseHref", "viewport_raw", "themeColor", "applicationName", "author", "generator", "publisher", "title", "description", "keywords", "robots_raw", "robots_noindex", "robots_nofollow", "robots_noarchive", "robots_noimageindex", "googlebot", "canonical", "amphtml", "manifest", "icon_href", "appleTouchIcon_href", "og_type", "og_title", "og_url", "og_site_name", "og_description", "og_image", "og_image_alt", "og_image_width", "og_image_height", "og_locale", "og_article_published_time", "og_article_modified_time", "twitter_card", "twitter_site", "twitter_creator", "twitter_title", "twitter_description", "twitter_image", "fb_app_id", "verification_google", "formatDetection_telephone", "tag_count", "jsonld_count", "tags_providers_csv"];
12
+ declare const FLAT_META_COLUMNS: readonly ["lang", "dir", "charset", "baseHref", "viewport_raw", "themeColor", "applicationName", "author", "generator", "publisher", "title", "description", "keywords", "robots_raw", "robots_noindex", "robots_nofollow", "robots_noarchive", "robots_noimageindex", "googlebot", "canonical", "amphtml", "manifest", "icon_href", "appleTouchIcon_href", "og_type", "og_title", "og_url", "og_site_name", "og_description", "og_image", "og_image_alt", "og_image_width", "og_image_height", "og_locale", "og_article_published_time", "og_article_modified_time", "twitter_card", "twitter_site", "twitter_creator", "twitter_title", "twitter_description", "twitter_image", "fb_app_id", "verification_google", "formatDetection_telephone", "tag_count", "jsonld_count", "tags_providers_csv", "main_content_node_name", "main_content_id", "main_content_role", "main_content_selector", "main_content_class_list", "main_content_word_count", "main_content_body_word_count", "main_content_heading_count", "main_content_image_count", "main_content_table_count", "main_content_button_count", "main_content_iframe_count", "main_content_video_count", "main_content_audio_count", "main_content_canvas_count", "scroll_height_desktop", "scroll_height_mobile"];
13
13
  /**
14
14
  * Represents a crawled page stored in the archive.
15
15
  *
@@ -90,6 +90,71 @@ export default class Page {
90
90
  * or null for legacy rows / never-scraped pages.
91
91
  */
92
92
  get lastCrawledAt(): number | null;
93
+ /**
94
+ * Detected main-content element's `nodeName` (e.g. `'MAIN'`), or null when
95
+ * no main region was found or the page was not fully rendered.
96
+ */
97
+ get mainContentNodeName(): string | null;
98
+ /**
99
+ * Detected main-content element's `id`, or null.
100
+ */
101
+ get mainContentId(): string | null;
102
+ /**
103
+ * Detected main-content element's `role` attribute, or null.
104
+ */
105
+ get mainContentRole(): string | null;
106
+ /**
107
+ * Diagnostic tag+id+class selector for the detected main-content element, or null.
108
+ */
109
+ get mainContentSelector(): string | null;
110
+ /**
111
+ * Detected main-content element's CSS classes, or null when no main region
112
+ * was found. Parsed from the JSON-encoded `main_content_class_list` column.
113
+ */
114
+ get mainContentClassList(): string[] | null;
115
+ /**
116
+ * Character count of the main region's text content (denormalised
117
+ * aggregate written at scrape time), or null.
118
+ */
119
+ get mainContentWordCount(): number | null;
120
+ /**
121
+ * Character count of `document.body`'s text content (denormalised
122
+ * aggregate written at scrape time), or null.
123
+ */
124
+ get mainContentBodyWordCount(): number | null;
125
+ /**
126
+ * Number of headings within the main region (denormalised aggregate), or null.
127
+ */
128
+ get mainContentHeadingCount(): number | null;
129
+ /**
130
+ * Number of images within the main region (denormalised aggregate), or null.
131
+ */
132
+ get mainContentImageCount(): number | null;
133
+ /**
134
+ * Number of tables within the main region (denormalised aggregate), or null.
135
+ */
136
+ get mainContentTableCount(): number | null;
137
+ /**
138
+ * Number of button-like elements within the main region (denormalised
139
+ * aggregate), or null.
140
+ */
141
+ get mainContentButtonCount(): number | null;
142
+ /**
143
+ * Number of iframes within the main region (denormalised aggregate), or null.
144
+ */
145
+ get mainContentIframeCount(): number | null;
146
+ /**
147
+ * Number of videos within the main region (denormalised aggregate), or null.
148
+ */
149
+ get mainContentVideoCount(): number | null;
150
+ /**
151
+ * Number of audios within the main region (denormalised aggregate), or null.
152
+ */
153
+ get mainContentAudioCount(): number | null;
154
+ /**
155
+ * Number of canvases within the main region (denormalised aggregate), or null.
156
+ */
157
+ get mainContentCanvasCount(): number | null;
93
158
  /**
94
159
  * Iterable view over every flat meta column (~47 fields). Returns a frozen
95
160
  * record so consumers can pick fields by name without re-enumerating
@@ -157,6 +222,16 @@ export default class Page {
157
222
  * Use for diagnostics; specific directive flags live on `robots_*` getters.
158
223
  */
159
224
  get robots_raw(): string | null;
225
+ /**
226
+ * `document.body.scrollHeight` at the desktop-compact preset (denormalised
227
+ * aggregate written at scrape time), or null.
228
+ */
229
+ get scrollHeightDesktop(): number | null;
230
+ /**
231
+ * `document.body.scrollHeight` at the mobile-small preset (denormalised
232
+ * aggregate written at scrape time), or null.
233
+ */
234
+ get scrollHeightMobile(): number | null;
160
235
  /**
161
236
  * The reason this page was skipped during crawling, or null if it was not skipped.
162
237
  */
@@ -210,6 +285,31 @@ export default class Page {
210
285
  * @returns An array of {@link Anchor} objects representing the links on this page.
211
286
  */
212
287
  getAnchors(): Promise<Anchor[]>;
288
+ /**
289
+ * Retrieves the audios within this page's detected main content region
290
+ * from `page_main_content_audios`. Lazy — runs a single SELECT per call.
291
+ * @returns Ordered audio rows.
292
+ */
293
+ getAudios(): Promise<import("./meta/types.js").MainContentAudioRow[]>;
294
+ /**
295
+ * Retrieves the button-like elements within this page's detected main
296
+ * content region from `page_main_content_buttons`. Lazy — runs a single
297
+ * SELECT per call.
298
+ * @returns Ordered button rows.
299
+ */
300
+ getButtons(): Promise<import("./meta/types.js").MainContentButtonRow[]>;
301
+ /**
302
+ * Retrieves the canvases within this page's detected main content region
303
+ * from `page_main_content_canvases`. Lazy — runs a single SELECT per call.
304
+ * @returns Ordered canvas rows.
305
+ */
306
+ getCanvases(): Promise<import("./meta/types.js").MainContentCanvasRow[]>;
307
+ /**
308
+ * Retrieves the headings within this page's detected main content region
309
+ * from `page_main_content_headings`. Lazy — runs a single SELECT per call.
310
+ * @returns Ordered heading rows.
311
+ */
312
+ getHeadings(): Promise<import("./meta/types.js").MainContentHeadingRow[]>;
213
313
  /**
214
314
  * Thin wrapper that forwards this page's id to the accessor's
215
315
  * BLOB-read path. Lets callers hold a `Page` reference and ask for its
@@ -223,6 +323,12 @@ export default class Page {
223
323
  * }
224
324
  */
225
325
  getHtml(): Promise<string | null>;
326
+ /**
327
+ * Retrieves the iframes within this page's detected main content region
328
+ * from `page_main_content_iframes`. Lazy — runs a single SELECT per call.
329
+ * @returns Ordered iframe rows.
330
+ */
331
+ getIframes(): Promise<import("./meta/types.js").MainContentIframeRow[]>;
226
332
  /**
227
333
  * Retrieves the JSON-LD entries for this page from `page_jsonld`.
228
334
  * Lazy — runs a single SELECT per call. Returns entries in insertion
@@ -230,6 +336,19 @@ export default class Page {
230
336
  * @returns Ordered JSON-LD / SpeculationRules rows.
231
337
  */
232
338
  getJsonLd(): Promise<readonly JsonLdRow[]>;
339
+ /**
340
+ * Retrieves the images within this page's detected main content region
341
+ * from `page_main_content_images`. Lazy — runs a single SELECT per call.
342
+ * Distinct from the whole-page image scan (`image_items`).
343
+ * @returns Ordered image rows.
344
+ */
345
+ getMainContentImages(): Promise<import("./meta/types.js").MainContentImageRow[]>;
346
+ /**
347
+ * Retrieves the tables within this page's detected main content region
348
+ * from `page_main_content_tables`. Lazy — runs a single SELECT per call.
349
+ * @returns Ordered table rows.
350
+ */
351
+ getMainContentTables(): Promise<import("./meta/types.js").MainContentTableRow[]>;
233
352
  /**
234
353
  * Retrieves the referrers (incoming links) pointing to this page.
235
354
  * Uses pre-loaded data if available, otherwise queries the database.
@@ -248,6 +367,12 @@ export default class Page {
248
367
  * @returns Ordered tag rows.
249
368
  */
250
369
  getTags(): Promise<readonly TagRow[]>;
370
+ /**
371
+ * Retrieves the videos within this page's detected main content region
372
+ * from `page_main_content_videos`. Lazy — runs a single SELECT per call.
373
+ * @returns Ordered video rows.
374
+ */
375
+ getVideos(): Promise<import("./meta/types.js").MainContentVideoRow[]>;
251
376
  /**
252
377
  * Checks whether this page is an internal HTML page (not external and has `text/html` content type).
253
378
  * @returns `true` if this is an internal HTML page, `false` otherwise.
@@ -281,8 +406,26 @@ export default class Page {
281
406
  isInternalPage: boolean;
282
407
  getAnchors: Anchor[];
283
408
  getReferrers: Referrer[];
284
- lang: string | number | null;
285
409
  dir: string | number | null;
410
+ title: string | number | null;
411
+ main_content_word_count: string | number | null;
412
+ main_content_node_name: string | number | null;
413
+ main_content_id: string | number | null;
414
+ main_content_role: string | number | null;
415
+ main_content_selector: string | number | null;
416
+ main_content_class_list: string | number | null;
417
+ main_content_body_word_count: string | number | null;
418
+ main_content_heading_count: string | number | null;
419
+ main_content_image_count: string | number | null;
420
+ main_content_table_count: string | number | null;
421
+ main_content_button_count: string | number | null;
422
+ main_content_iframe_count: string | number | null;
423
+ main_content_video_count: string | number | null;
424
+ main_content_audio_count: string | number | null;
425
+ main_content_canvas_count: string | number | null;
426
+ scroll_height_desktop: string | number | null;
427
+ scroll_height_mobile: string | number | null;
428
+ lang: string | number | null;
286
429
  charset: string | number | null;
287
430
  baseHref: string | number | null;
288
431
  viewport_raw: string | number | null;
@@ -291,7 +434,6 @@ export default class Page {
291
434
  author: string | number | null;
292
435
  generator: string | number | null;
293
436
  publisher: string | number | null;
294
- title: string | number | null;
295
437
  description: string | number | null;
296
438
  keywords: string | number | null;
297
439
  robots_raw: string | number | null;
@@ -60,6 +60,23 @@ const FLAT_META_COLUMNS = [
60
60
  'tag_count',
61
61
  'jsonld_count',
62
62
  'tags_providers_csv',
63
+ 'main_content_node_name',
64
+ 'main_content_id',
65
+ 'main_content_role',
66
+ 'main_content_selector',
67
+ 'main_content_class_list',
68
+ 'main_content_word_count',
69
+ 'main_content_body_word_count',
70
+ 'main_content_heading_count',
71
+ 'main_content_image_count',
72
+ 'main_content_table_count',
73
+ 'main_content_button_count',
74
+ 'main_content_iframe_count',
75
+ 'main_content_video_count',
76
+ 'main_content_audio_count',
77
+ 'main_content_canvas_count',
78
+ 'scroll_height_desktop',
79
+ 'scroll_height_mobile',
63
80
  ];
64
81
  /**
65
82
  * Represents a crawled page stored in the archive.
@@ -171,6 +188,104 @@ export default class Page {
171
188
  get lastCrawledAt() {
172
189
  return this.#raw.lastCrawledAt;
173
190
  }
191
+ /**
192
+ * Detected main-content element's `nodeName` (e.g. `'MAIN'`), or null when
193
+ * no main region was found or the page was not fully rendered.
194
+ */
195
+ get mainContentNodeName() {
196
+ return this.#raw.main_content_node_name;
197
+ }
198
+ /**
199
+ * Detected main-content element's `id`, or null.
200
+ */
201
+ get mainContentId() {
202
+ return this.#raw.main_content_id;
203
+ }
204
+ /**
205
+ * Detected main-content element's `role` attribute, or null.
206
+ */
207
+ get mainContentRole() {
208
+ return this.#raw.main_content_role;
209
+ }
210
+ /**
211
+ * Diagnostic tag+id+class selector for the detected main-content element, or null.
212
+ */
213
+ get mainContentSelector() {
214
+ return this.#raw.main_content_selector;
215
+ }
216
+ /**
217
+ * Detected main-content element's CSS classes, or null when no main region
218
+ * was found. Parsed from the JSON-encoded `main_content_class_list` column.
219
+ */
220
+ get mainContentClassList() {
221
+ if (this.#raw.main_content_class_list === null) {
222
+ return null;
223
+ }
224
+ return JSON.parse(this.#raw.main_content_class_list);
225
+ }
226
+ /**
227
+ * Character count of the main region's text content (denormalised
228
+ * aggregate written at scrape time), or null.
229
+ */
230
+ get mainContentWordCount() {
231
+ return this.#raw.main_content_word_count;
232
+ }
233
+ /**
234
+ * Character count of `document.body`'s text content (denormalised
235
+ * aggregate written at scrape time), or null.
236
+ */
237
+ get mainContentBodyWordCount() {
238
+ return this.#raw.main_content_body_word_count;
239
+ }
240
+ /**
241
+ * Number of headings within the main region (denormalised aggregate), or null.
242
+ */
243
+ get mainContentHeadingCount() {
244
+ return this.#raw.main_content_heading_count;
245
+ }
246
+ /**
247
+ * Number of images within the main region (denormalised aggregate), or null.
248
+ */
249
+ get mainContentImageCount() {
250
+ return this.#raw.main_content_image_count;
251
+ }
252
+ /**
253
+ * Number of tables within the main region (denormalised aggregate), or null.
254
+ */
255
+ get mainContentTableCount() {
256
+ return this.#raw.main_content_table_count;
257
+ }
258
+ /**
259
+ * Number of button-like elements within the main region (denormalised
260
+ * aggregate), or null.
261
+ */
262
+ get mainContentButtonCount() {
263
+ return this.#raw.main_content_button_count;
264
+ }
265
+ /**
266
+ * Number of iframes within the main region (denormalised aggregate), or null.
267
+ */
268
+ get mainContentIframeCount() {
269
+ return this.#raw.main_content_iframe_count;
270
+ }
271
+ /**
272
+ * Number of videos within the main region (denormalised aggregate), or null.
273
+ */
274
+ get mainContentVideoCount() {
275
+ return this.#raw.main_content_video_count;
276
+ }
277
+ /**
278
+ * Number of audios within the main region (denormalised aggregate), or null.
279
+ */
280
+ get mainContentAudioCount() {
281
+ return this.#raw.main_content_audio_count;
282
+ }
283
+ /**
284
+ * Number of canvases within the main region (denormalised aggregate), or null.
285
+ */
286
+ get mainContentCanvasCount() {
287
+ return this.#raw.main_content_canvas_count;
288
+ }
174
289
  /**
175
290
  * Iterable view over every flat meta column (~47 fields). Returns a frozen
176
291
  * record so consumers can pick fields by name without re-enumerating
@@ -280,6 +395,20 @@ export default class Page {
280
395
  get robots_raw() {
281
396
  return this.#raw.robots_raw;
282
397
  }
398
+ /**
399
+ * `document.body.scrollHeight` at the desktop-compact preset (denormalised
400
+ * aggregate written at scrape time), or null.
401
+ */
402
+ get scrollHeightDesktop() {
403
+ return this.#raw.scroll_height_desktop;
404
+ }
405
+ /**
406
+ * `document.body.scrollHeight` at the mobile-small preset (denormalised
407
+ * aggregate written at scrape time), or null.
408
+ */
409
+ get scrollHeightMobile() {
410
+ return this.#raw.scroll_height_mobile;
411
+ }
283
412
  /**
284
413
  * The reason this page was skipped during crawling, or null if it was not skipped.
285
414
  */
@@ -376,6 +505,39 @@ export default class Page {
376
505
  }
377
506
  return this.#archive.getAnchorsOnPage(this.#raw.id);
378
507
  }
508
+ /**
509
+ * Retrieves the audios within this page's detected main content region
510
+ * from `page_main_content_audios`. Lazy — runs a single SELECT per call.
511
+ * @returns Ordered audio rows.
512
+ */
513
+ async getAudios() {
514
+ return this.#archive.getAudiosOfPage(this.#raw.id);
515
+ }
516
+ /**
517
+ * Retrieves the button-like elements within this page's detected main
518
+ * content region from `page_main_content_buttons`. Lazy — runs a single
519
+ * SELECT per call.
520
+ * @returns Ordered button rows.
521
+ */
522
+ async getButtons() {
523
+ return this.#archive.getButtonsOfPage(this.#raw.id);
524
+ }
525
+ /**
526
+ * Retrieves the canvases within this page's detected main content region
527
+ * from `page_main_content_canvases`. Lazy — runs a single SELECT per call.
528
+ * @returns Ordered canvas rows.
529
+ */
530
+ async getCanvases() {
531
+ return this.#archive.getCanvasesOfPage(this.#raw.id);
532
+ }
533
+ /**
534
+ * Retrieves the headings within this page's detected main content region
535
+ * from `page_main_content_headings`. Lazy — runs a single SELECT per call.
536
+ * @returns Ordered heading rows.
537
+ */
538
+ async getHeadings() {
539
+ return this.#archive.getHeadingsOfPage(this.#raw.id);
540
+ }
379
541
  /**
380
542
  * Thin wrapper that forwards this page's id to the accessor's
381
543
  * BLOB-read path. Lets callers hold a `Page` reference and ask for its
@@ -391,6 +553,14 @@ export default class Page {
391
553
  async getHtml() {
392
554
  return this.#archive.getHtmlOfPage(this.#raw.id);
393
555
  }
556
+ /**
557
+ * Retrieves the iframes within this page's detected main content region
558
+ * from `page_main_content_iframes`. Lazy — runs a single SELECT per call.
559
+ * @returns Ordered iframe rows.
560
+ */
561
+ async getIframes() {
562
+ return this.#archive.getIframesOfPage(this.#raw.id);
563
+ }
394
564
  /**
395
565
  * Retrieves the JSON-LD entries for this page from `page_jsonld`.
396
566
  * Lazy — runs a single SELECT per call. Returns entries in insertion
@@ -400,6 +570,23 @@ export default class Page {
400
570
  async getJsonLd() {
401
571
  return this.#archive.getJsonLdOfPage(this.#raw.id);
402
572
  }
573
+ /**
574
+ * Retrieves the images within this page's detected main content region
575
+ * from `page_main_content_images`. Lazy — runs a single SELECT per call.
576
+ * Distinct from the whole-page image scan (`image_items`).
577
+ * @returns Ordered image rows.
578
+ */
579
+ async getMainContentImages() {
580
+ return this.#archive.getMainContentImagesOfPage(this.#raw.id);
581
+ }
582
+ /**
583
+ * Retrieves the tables within this page's detected main content region
584
+ * from `page_main_content_tables`. Lazy — runs a single SELECT per call.
585
+ * @returns Ordered table rows.
586
+ */
587
+ async getMainContentTables() {
588
+ return this.#archive.getMainContentTablesOfPage(this.#raw.id);
589
+ }
403
590
  /**
404
591
  * Retrieves the referrers (incoming links) pointing to this page.
405
592
  * Uses pre-loaded data if available, otherwise queries the database.
@@ -447,6 +634,14 @@ export default class Page {
447
634
  async getTags() {
448
635
  return this.#archive.getTagsOfPage(this.#raw.id);
449
636
  }
637
+ /**
638
+ * Retrieves the videos within this page's detected main content region
639
+ * from `page_main_content_videos`. Lazy — runs a single SELECT per call.
640
+ * @returns Ordered video rows.
641
+ */
642
+ async getVideos() {
643
+ return this.#archive.getVideosOfPage(this.#raw.id);
644
+ }
450
645
  /**
451
646
  * Checks whether this page is an internal HTML page (not external and has `text/html` content type).
452
647
  * @returns `true` if this is an internal HTML page, `false` otherwise.