@nitpicker/crawler 0.13.0 → 0.14.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 (71) 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 +11 -0
  4. package/lib/archive/archive.js +13 -0
  5. package/lib/archive/create-adjunct-tables.d.ts +7 -0
  6. package/lib/archive/create-adjunct-tables.js +150 -0
  7. package/lib/archive/create-entity-tables.d.ts +14 -0
  8. package/lib/archive/create-entity-tables.js +32 -1
  9. package/lib/archive/database.d.ts +55 -1
  10. package/lib/archive/database.js +81 -0
  11. package/lib/archive/db-ops/analysis/replace-page-templates.d.ts +20 -0
  12. package/lib/archive/db-ops/analysis/replace-page-templates.js +52 -0
  13. package/lib/archive/db-ops/config/info-column-allowlist.js +1 -0
  14. package/lib/archive/db-ops/lifecycle/init.d.ts +16 -10
  15. package/lib/archive/db-ops/lifecycle/init.js +20 -10
  16. package/lib/archive/db-ops/meta/get-audios-of-page.d.ts +11 -0
  17. package/lib/archive/db-ops/meta/get-audios-of-page.js +15 -0
  18. package/lib/archive/db-ops/meta/get-buttons-of-page.d.ts +11 -0
  19. package/lib/archive/db-ops/meta/get-buttons-of-page.js +15 -0
  20. package/lib/archive/db-ops/meta/get-canvases-of-page.d.ts +11 -0
  21. package/lib/archive/db-ops/meta/get-canvases-of-page.js +15 -0
  22. package/lib/archive/db-ops/meta/get-headings-of-page.d.ts +11 -0
  23. package/lib/archive/db-ops/meta/get-headings-of-page.js +15 -0
  24. package/lib/archive/db-ops/meta/get-iframes-of-page.d.ts +11 -0
  25. package/lib/archive/db-ops/meta/get-iframes-of-page.js +15 -0
  26. package/lib/archive/db-ops/meta/get-main-content-images-of-page.d.ts +11 -0
  27. package/lib/archive/db-ops/meta/get-main-content-images-of-page.js +15 -0
  28. package/lib/archive/db-ops/meta/get-main-content-tables-of-page.d.ts +11 -0
  29. package/lib/archive/db-ops/meta/get-main-content-tables-of-page.js +15 -0
  30. package/lib/archive/db-ops/meta/get-videos-of-page.d.ts +11 -0
  31. package/lib/archive/db-ops/meta/get-videos-of-page.js +15 -0
  32. package/lib/archive/db-ops/pages/read/build-page-query.js +1 -1
  33. package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +19 -10
  34. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +5 -3
  35. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +13 -3
  36. package/lib/archive/db-ops/pages/write/insert-audios.d.ts +14 -0
  37. package/lib/archive/db-ops/pages/write/insert-audios.js +25 -0
  38. package/lib/archive/db-ops/pages/write/insert-buttons.d.ts +14 -0
  39. package/lib/archive/db-ops/pages/write/insert-buttons.js +29 -0
  40. package/lib/archive/db-ops/pages/write/insert-canvases.d.ts +14 -0
  41. package/lib/archive/db-ops/pages/write/insert-canvases.js +26 -0
  42. package/lib/archive/db-ops/pages/write/insert-headings.d.ts +15 -0
  43. package/lib/archive/db-ops/pages/write/insert-headings.js +27 -0
  44. package/lib/archive/db-ops/pages/write/insert-iframes.d.ts +14 -0
  45. package/lib/archive/db-ops/pages/write/insert-iframes.js +28 -0
  46. package/lib/archive/db-ops/pages/write/insert-main-content-images.d.ts +20 -0
  47. package/lib/archive/db-ops/pages/write/insert-main-content-images.js +32 -0
  48. package/lib/archive/db-ops/pages/write/insert-main-content-tables.d.ts +14 -0
  49. package/lib/archive/db-ops/pages/write/insert-main-content-tables.js +29 -0
  50. package/lib/archive/db-ops/pages/write/insert-page.js +15 -2
  51. package/lib/archive/db-ops/pages/write/insert-videos.d.ts +14 -0
  52. package/lib/archive/db-ops/pages/write/insert-videos.js +28 -0
  53. package/lib/archive/db-ops/pages/write/update-page.js +21 -0
  54. package/lib/archive/init-schema.js +2 -1
  55. package/lib/archive/meta/compute-main-contents-denormalized.d.ts +22 -0
  56. package/lib/archive/meta/compute-main-contents-denormalized.js +63 -0
  57. package/lib/archive/meta/types.d.ts +263 -0
  58. package/lib/archive/migrate-info-main-content-selector.d.ts +11 -0
  59. package/lib/archive/migrate-info-main-content-selector.js +24 -0
  60. package/lib/archive/migrate-main-contents-columns.d.ts +21 -0
  61. package/lib/archive/migrate-main-contents-columns.js +50 -0
  62. package/lib/archive/page.d.ts +144 -2
  63. package/lib/archive/page.js +195 -0
  64. package/lib/archive/types.d.ts +39 -0
  65. package/lib/crawler/crawler.js +4 -2
  66. package/lib/crawler/fetch-destination.js +2 -0
  67. package/lib/crawler/link-to-page-data.js +2 -0
  68. package/lib/crawler/resource-to-page-data.js +2 -0
  69. package/lib/crawler/types.d.ts +5 -0
  70. package/lib/crawler-orchestrator.js +2 -0
  71. package/package.json +3 -3
@@ -93,6 +93,269 @@ export interface PageDenormalizedColumns {
93
93
  /** Sorted unique providers, comma-separated (empty string when no tags). */
94
94
  tags_providers_csv: string;
95
95
  }
96
+ /**
97
+ * Denormalised aggregates computed at write time from beholder's
98
+ * `MainContentsData` / `ScrollHeightData`.
99
+ *
100
+ * Stored on `page_meta` following the same pattern as
101
+ * {@link PageDenormalizedColumns} (`tag_count` / `jsonld_count`): the full
102
+ * per-element detail lives in the `page_main_content_*` child tables, while
103
+ * these scalar columns let list / detail reads answer "how many headings?"
104
+ * without joining them. All fields are `null` when the page was not fully
105
+ * rendered (external / non-HTML / metadata-only scrape) — see
106
+ * `compute-main-contents-denormalized.ts` for the `null`-in-null-out contract.
107
+ * @see compute-main-contents-denormalized.ts
108
+ * @example
109
+ * const denorm: MainContentsDenormalizedColumns = {
110
+ * main_content_node_name: 'MAIN',
111
+ * main_content_id: null,
112
+ * main_content_role: null,
113
+ * main_content_selector: 'main.l-main',
114
+ * main_content_class_list: '["l-main"]',
115
+ * main_content_word_count: 1240,
116
+ * main_content_body_word_count: 1580,
117
+ * main_content_heading_count: 6,
118
+ * main_content_image_count: 3,
119
+ * main_content_table_count: 0,
120
+ * main_content_button_count: 1,
121
+ * main_content_iframe_count: 0,
122
+ * main_content_video_count: 0,
123
+ * main_content_audio_count: 0,
124
+ * main_content_canvas_count: 0,
125
+ * scroll_height_desktop: 3200,
126
+ * scroll_height_mobile: 5400,
127
+ * };
128
+ */
129
+ export interface MainContentsDenormalizedColumns {
130
+ /** Detected main-content element's `nodeName` (e.g. `'MAIN'`), or `null`. */
131
+ main_content_node_name: string | null;
132
+ /** Detected main-content element's `id`, or `null`. */
133
+ main_content_id: string | null;
134
+ /** Detected main-content element's `role` attribute, or `null`. */
135
+ main_content_role: string | null;
136
+ /** Diagnostic tag+id+class selector for the detected element, or `null`. */
137
+ main_content_selector: string | null;
138
+ /** JSON-encoded array of the detected element's CSS classes, or `null`. */
139
+ main_content_class_list: string | null;
140
+ /** Character count of the main region's text content, or `null`. */
141
+ main_content_word_count: number | null;
142
+ /** Character count of `document.body`'s text content, or `null`. */
143
+ main_content_body_word_count: number | null;
144
+ /** Number of headings within the main region, or `null`. */
145
+ main_content_heading_count: number | null;
146
+ /** Number of images within the main region, or `null`. */
147
+ main_content_image_count: number | null;
148
+ /** Number of tables within the main region, or `null`. */
149
+ main_content_table_count: number | null;
150
+ /** Number of button-like elements within the main region, or `null`. */
151
+ main_content_button_count: number | null;
152
+ /** Number of iframes within the main region, or `null`. */
153
+ main_content_iframe_count: number | null;
154
+ /** Number of videos within the main region, or `null`. */
155
+ main_content_video_count: number | null;
156
+ /** Number of audios within the main region, or `null`. */
157
+ main_content_audio_count: number | null;
158
+ /** Number of canvases within the main region, or `null`. */
159
+ main_content_canvas_count: number | null;
160
+ /** `document.body.scrollHeight` at the desktop-compact preset, or `null`. */
161
+ scroll_height_desktop: number | null;
162
+ /** `document.body.scrollHeight` at the mobile-small preset, or `null`. */
163
+ scroll_height_mobile: number | null;
164
+ }
165
+ /**
166
+ * One row in the `page_main_content_headings` table.
167
+ * @example
168
+ * const row: MainContentHeadingRow = { id: 1, pageId: 42, order: 0, text: 'Welcome', level: 1 };
169
+ */
170
+ export interface MainContentHeadingRow {
171
+ /** Auto-increment primary key. */
172
+ id: number;
173
+ /** FK to `content_items.id`. */
174
+ pageId: number;
175
+ /** 0-based DOM traversal order within the main content region. */
176
+ order: number;
177
+ /** Heading text after whitespace removal, or `null` when empty. */
178
+ text: string | null;
179
+ /** Heading level (1-6) from the tag name. */
180
+ level: 1 | 2 | 3 | 4 | 5 | 6;
181
+ }
182
+ /**
183
+ * One row in the `page_main_content_images` table.
184
+ * @example
185
+ * const row: MainContentImageRow = {
186
+ * id: 1,
187
+ * pageId: 42,
188
+ * order: 0,
189
+ * src: 'https://example.com/a.png',
190
+ * alt: 'A photo',
191
+ * };
192
+ */
193
+ export interface MainContentImageRow {
194
+ /** Auto-increment primary key. */
195
+ id: number;
196
+ /** FK to `content_items.id`. */
197
+ pageId: number;
198
+ /** 0-based DOM traversal order within the main content region. */
199
+ order: number;
200
+ /** Resolved absolute `src` URL. */
201
+ src: string;
202
+ /** `alt` attribute value (may be an empty string). */
203
+ alt: string;
204
+ }
205
+ /**
206
+ * One row in the `page_main_content_tables` table.
207
+ * @example
208
+ * const row: MainContentTableRow = {
209
+ * id: 1,
210
+ * pageId: 42,
211
+ * order: 0,
212
+ * rows: 3,
213
+ * cols: 4,
214
+ * hasHeader: 1,
215
+ * hasFooter: 0,
216
+ * hasMergedCell: 0,
217
+ * };
218
+ */
219
+ export interface MainContentTableRow {
220
+ /** Auto-increment primary key. */
221
+ id: number;
222
+ /** FK to `content_items.id`. */
223
+ pageId: number;
224
+ /** 0-based DOM traversal order within the main content region. */
225
+ order: number;
226
+ /** Number of `<tr>` elements. */
227
+ rows: number;
228
+ /** Number of `th`/`td` cells in the first row. */
229
+ cols: number;
230
+ /** Whether the table contains a `<thead>` (raw SQLite 0/1; knex does not round-trip `.boolean()` columns back to JS `boolean` on read). */
231
+ hasHeader: 0 | 1;
232
+ /** Whether the table contains a `<tfoot>` (raw SQLite 0/1). */
233
+ hasFooter: 0 | 1;
234
+ /** Whether any cell uses `colspan` or `rowspan` (raw SQLite 0/1). */
235
+ hasMergedCell: 0 | 1;
236
+ }
237
+ /**
238
+ * One row in the `page_main_content_buttons` table.
239
+ * @example
240
+ * const row: MainContentButtonRow = {
241
+ * id: 1,
242
+ * pageId: 42,
243
+ * order: 0,
244
+ * nodeName: 'BUTTON',
245
+ * role: null,
246
+ * type: 'submit',
247
+ * text: 'Send',
248
+ * disabled: 0,
249
+ * };
250
+ */
251
+ export interface MainContentButtonRow {
252
+ /** Auto-increment primary key. */
253
+ id: number;
254
+ /** FK to `content_items.id`. */
255
+ pageId: number;
256
+ /** 0-based DOM traversal order within the main content region. */
257
+ order: number;
258
+ /** Element tag name (e.g. `'BUTTON'`, `'A'`, `'DIV'`). */
259
+ nodeName: string;
260
+ /** `role` attribute, or `null` when absent. */
261
+ role: string | null;
262
+ /** `type` for `<button>` / `<input>`, otherwise `null`. */
263
+ type: string | null;
264
+ /** Label text after whitespace removal, or `null` when empty. */
265
+ text: string | null;
266
+ /** `true` when `disabled` or `aria-disabled="true"` (raw SQLite 0/1). */
267
+ disabled: 0 | 1;
268
+ }
269
+ /**
270
+ * One row in the `page_main_content_iframes` table.
271
+ * @example
272
+ * const row: MainContentIframeRow = {
273
+ * id: 1,
274
+ * pageId: 42,
275
+ * order: 0,
276
+ * src: 'https://example.com/embed',
277
+ * title: null,
278
+ * width: '640',
279
+ * height: '360',
280
+ * };
281
+ */
282
+ export interface MainContentIframeRow {
283
+ /** Auto-increment primary key. */
284
+ id: number;
285
+ /** FK to `content_items.id`. */
286
+ pageId: number;
287
+ /** 0-based DOM traversal order within the main content region. */
288
+ order: number;
289
+ /** Resolved absolute `src` URL. */
290
+ src: string;
291
+ /** `title` attribute, or `null` when absent. */
292
+ title: string | null;
293
+ /** Raw `width` attribute string, or `null` when absent. */
294
+ width: string | null;
295
+ /** Raw `height` attribute string, or `null` when absent. */
296
+ height: string | null;
297
+ }
298
+ /**
299
+ * One row in the `page_main_content_videos` table.
300
+ * @example
301
+ * const row: MainContentVideoRow = {
302
+ * id: 1,
303
+ * pageId: 42,
304
+ * order: 0,
305
+ * src: 'https://example.com/v.mp4',
306
+ * poster: null,
307
+ * width: 640,
308
+ * height: 360,
309
+ * };
310
+ */
311
+ export interface MainContentVideoRow {
312
+ /** Auto-increment primary key. */
313
+ id: number;
314
+ /** FK to `content_items.id`. */
315
+ pageId: number;
316
+ /** 0-based DOM traversal order within the main content region. */
317
+ order: number;
318
+ /** Resolved media URL. */
319
+ src: string;
320
+ /** Resolved `poster` URL, or `null` when unset. */
321
+ poster: string | null;
322
+ /** IDL `width` in pixels. */
323
+ width: number;
324
+ /** IDL `height` in pixels. */
325
+ height: number;
326
+ }
327
+ /**
328
+ * One row in the `page_main_content_audios` table.
329
+ * @example
330
+ * const row: MainContentAudioRow = { id: 1, pageId: 42, order: 0, src: 'https://example.com/a.mp3' };
331
+ */
332
+ export interface MainContentAudioRow {
333
+ /** Auto-increment primary key. */
334
+ id: number;
335
+ /** FK to `content_items.id`. */
336
+ pageId: number;
337
+ /** 0-based DOM traversal order within the main content region. */
338
+ order: number;
339
+ /** Resolved media URL. */
340
+ src: string;
341
+ }
342
+ /**
343
+ * One row in the `page_main_content_canvases` table.
344
+ * @example
345
+ * const row: MainContentCanvasRow = { id: 1, pageId: 42, order: 0, width: 300, height: 150 };
346
+ */
347
+ export interface MainContentCanvasRow {
348
+ /** Auto-increment primary key. */
349
+ id: number;
350
+ /** FK to `content_items.id`. */
351
+ pageId: number;
352
+ /** 0-based DOM traversal order within the main content region. */
353
+ order: number;
354
+ /** IDL bitmap width. */
355
+ width: number;
356
+ /** IDL bitmap height. */
357
+ height: number;
358
+ }
96
359
  /**
97
360
  * One row in the `page_jsonld` table.
98
361
  *
@@ -0,0 +1,11 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Adds the `info.mainContentSelector` column to archives created before it
4
+ * existed. `CREATE TABLE IF NOT EXISTS` (used for `info` itself) cannot
5
+ * retrofit a new column onto an already-existing table, so this lightweight,
6
+ * `hasColumn`-guarded `ALTER TABLE` runs on every `initSchema` call —
7
+ * idempotent, and self-healing for archives whose provisioning crashed
8
+ * partway through.
9
+ * @param instance - The Knex query builder instance connected to the database.
10
+ */
11
+ export declare function migrateInfoMainContentSelector(instance: Knex): Promise<void>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Adds the `info.mainContentSelector` column to archives created before it
3
+ * existed. `CREATE TABLE IF NOT EXISTS` (used for `info` itself) cannot
4
+ * retrofit a new column onto an already-existing table, so this lightweight,
5
+ * `hasColumn`-guarded `ALTER TABLE` runs on every `initSchema` call —
6
+ * idempotent, and self-healing for archives whose provisioning crashed
7
+ * partway through.
8
+ * @param instance - The Knex query builder instance connected to the database.
9
+ */
10
+ export async function migrateInfoMainContentSelector(instance) {
11
+ const hasInfo = await instance.schema.hasTable('info');
12
+ if (!hasInfo) {
13
+ return;
14
+ }
15
+ const hasColumn = await instance.schema.hasColumn('info', 'mainContentSelector');
16
+ if (hasColumn) {
17
+ return;
18
+ }
19
+ await instance.schema.table('info', (t) => {
20
+ t.string('mainContentSelector');
21
+ });
22
+ // eslint-disable-next-line no-console
23
+ console.error('[migrate] info.mainContentSelector column added');
24
+ }
@@ -0,0 +1,21 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Add the beholder `MainContentsData` / `ScrollHeightData` denormalised
4
+ * aggregate columns to `page_meta` on archives created before this feature.
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. This mirrors
11
+ * {@link import('./migrate-info-roots.js').migrateInfoRoots}'s `info.roots`
12
+ * catch-up: a `hasColumn`-guarded `ALTER TABLE` for the one column set that
13
+ * `CREATE TABLE IF NOT EXISTS` cannot retrofit.
14
+ *
15
+ * Idempotent: a no-op once the columns exist. Guards on `page_meta`'s
16
+ * existence defensively, though by the time this runs (after `initSchema`,
17
+ * itself after `assertCompatibleVersion` rejects pre-0.13 archives) the
18
+ * table is always present.
19
+ * @param instance - The Knex query builder instance connected to the database.
20
+ */
21
+ export declare function migrateMainContentsColumns(instance: Knex): Promise<void>;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Add the beholder `MainContentsData` / `ScrollHeightData` denormalised
3
+ * aggregate columns to `page_meta` on archives created before this feature.
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-info-roots.js').migrateInfoRoots}'s `info.roots`
11
+ * catch-up: a `hasColumn`-guarded `ALTER TABLE` for the one column set that
12
+ * `CREATE TABLE IF NOT EXISTS` cannot retrofit.
13
+ *
14
+ * Idempotent: a no-op once the columns exist. Guards on `page_meta`'s
15
+ * existence defensively, though by the time this runs (after `initSchema`,
16
+ * itself after `assertCompatibleVersion` rejects pre-0.13 archives) the
17
+ * table is always present.
18
+ * @param instance - The Knex query builder instance connected to the database.
19
+ */
20
+ export async function migrateMainContentsColumns(instance) {
21
+ const hasPageMeta = await instance.schema.hasTable('page_meta');
22
+ if (!hasPageMeta) {
23
+ return;
24
+ }
25
+ const hasColumn = await instance.schema.hasColumn('page_meta', 'main_content_word_count');
26
+ if (hasColumn) {
27
+ return;
28
+ }
29
+ await instance.schema.table('page_meta', (t) => {
30
+ t.string('main_content_node_name');
31
+ t.string('main_content_id');
32
+ t.string('main_content_role');
33
+ t.string('main_content_selector');
34
+ t.text('main_content_class_list');
35
+ t.integer('main_content_word_count');
36
+ t.integer('main_content_body_word_count');
37
+ t.integer('main_content_heading_count');
38
+ t.integer('main_content_image_count');
39
+ t.integer('main_content_table_count');
40
+ t.integer('main_content_button_count');
41
+ t.integer('main_content_iframe_count');
42
+ t.integer('main_content_video_count');
43
+ t.integer('main_content_audio_count');
44
+ t.integer('main_content_canvas_count');
45
+ t.integer('scroll_height_desktop');
46
+ t.integer('scroll_height_mobile');
47
+ });
48
+ // eslint-disable-next-line no-console
49
+ console.error('[migrate] page_meta.main_content_* / scroll_height_* columns added');
50
+ }
@@ -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,6 +406,24 @@ export default class Page {
281
406
  isInternalPage: boolean;
282
407
  getAnchors: Anchor[];
283
408
  getReferrers: Referrer[];
409
+ title: string | number | null;
410
+ main_content_word_count: string | number | null;
411
+ main_content_node_name: string | number | null;
412
+ main_content_id: string | number | null;
413
+ main_content_role: string | number | null;
414
+ main_content_selector: string | number | null;
415
+ main_content_class_list: string | number | null;
416
+ main_content_body_word_count: string | number | null;
417
+ main_content_heading_count: string | number | null;
418
+ main_content_image_count: string | number | null;
419
+ main_content_table_count: string | number | null;
420
+ main_content_button_count: string | number | null;
421
+ main_content_iframe_count: string | number | null;
422
+ main_content_video_count: string | number | null;
423
+ main_content_audio_count: string | number | null;
424
+ main_content_canvas_count: string | number | null;
425
+ scroll_height_desktop: string | number | null;
426
+ scroll_height_mobile: string | number | null;
284
427
  lang: string | number | null;
285
428
  dir: string | number | null;
286
429
  charset: 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;