@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.
- package/lib/archive/archive-accessor.d.ts +56 -0
- package/lib/archive/archive-accessor.js +72 -0
- package/lib/archive/archive.d.ts +108 -9
- package/lib/archive/archive.js +109 -7
- package/lib/archive/body-hash/compute-body-hash.d.ts +22 -0
- package/lib/archive/body-hash/compute-body-hash.js +31 -0
- package/lib/archive/body-hash/extract-body.d.ts +18 -0
- package/lib/archive/body-hash/extract-body.js +29 -0
- package/lib/archive/body-hash/mask-dynamic-ids.d.ts +33 -0
- package/lib/archive/body-hash/mask-dynamic-ids.js +44 -0
- package/lib/archive/body-hash/normalize-url-like-strings.d.ts +16 -0
- package/lib/archive/body-hash/normalize-url-like-strings.js +19 -0
- package/lib/archive/cache/clear-archive-cache-entry.d.ts +19 -0
- package/lib/archive/cache/clear-archive-cache-entry.js +25 -0
- package/lib/archive/cache/clear-archive-cache-root.d.ts +26 -0
- package/lib/archive/cache/clear-archive-cache-root.js +32 -0
- package/lib/archive/cache/list-archive-cache-entries.d.ts +23 -0
- package/lib/archive/cache/list-archive-cache-entries.js +125 -0
- package/lib/archive/cache/path-exists.d.ts +9 -0
- package/lib/archive/cache/path-exists.js +18 -0
- package/lib/archive/cache/types.d.ts +33 -0
- package/lib/archive/cache/types.js +1 -0
- package/lib/archive/create-adjunct-tables.d.ts +18 -0
- package/lib/archive/create-adjunct-tables.js +277 -0
- package/lib/archive/create-entity-tables.d.ts +14 -0
- package/lib/archive/create-entity-tables.js +52 -1
- package/lib/archive/database.d.ts +91 -2
- package/lib/archive/database.js +128 -0
- package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +1 -0
- package/lib/archive/db-ops/_shared/compress-payload.d.ts +26 -0
- package/lib/archive/db-ops/_shared/compress-payload.js +30 -0
- package/lib/archive/db-ops/_shared/create-write-ref-caches.js +1 -0
- package/lib/archive/db-ops/_shared/types.d.ts +2 -0
- package/lib/archive/db-ops/_shared/upsert-json-ref.js +3 -3
- package/lib/archive/db-ops/analysis/replace-page-templates.d.ts +29 -0
- package/lib/archive/db-ops/analysis/replace-page-templates.js +84 -0
- package/lib/archive/db-ops/analysis/types.d.ts +61 -0
- package/lib/archive/db-ops/analysis/types.js +1 -0
- package/lib/archive/db-ops/config/info-column-allowlist.js +1 -0
- package/lib/archive/db-ops/console-logs/compute-console-log-hash.d.ts +19 -0
- package/lib/archive/db-ops/console-logs/compute-console-log-hash.js +30 -0
- package/lib/archive/db-ops/console-logs/replace-console-logs.d.ts +34 -0
- package/lib/archive/db-ops/console-logs/replace-console-logs.js +123 -0
- package/lib/archive/db-ops/console-logs/stringify-console-log-args.d.ts +16 -0
- package/lib/archive/db-ops/console-logs/stringify-console-log-args.js +26 -0
- package/lib/archive/db-ops/console-logs/types.d.ts +38 -0
- package/lib/archive/db-ops/console-logs/types.js +1 -0
- package/lib/archive/db-ops/console-logs/upsert-console-log-item.d.ts +29 -0
- package/lib/archive/db-ops/console-logs/upsert-console-log-item.js +52 -0
- package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +11 -0
- package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +21 -4
- package/lib/archive/db-ops/inventory/record-inventory-run.js +1 -0
- package/lib/archive/db-ops/lifecycle/init.d.ts +25 -10
- package/lib/archive/db-ops/lifecycle/init.js +39 -10
- package/lib/archive/db-ops/meta/get-audios-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-audios-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-buttons-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-buttons-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-canvases-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-canvases-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-headings-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-headings-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-iframes-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-iframes-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-main-content-images-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-main-content-images-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-main-content-tables-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-main-content-tables-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-videos-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-videos-of-page.js +15 -0
- package/lib/archive/db-ops/outages/close-network-outage.d.ts +12 -0
- package/lib/archive/db-ops/outages/close-network-outage.js +15 -0
- package/lib/archive/db-ops/outages/close-stale-open-network-outages.d.ts +26 -0
- package/lib/archive/db-ops/outages/close-stale-open-network-outages.js +42 -0
- package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.d.ts +26 -0
- package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.js +31 -0
- package/lib/archive/db-ops/outages/insert-network-outage.d.ts +14 -0
- package/lib/archive/db-ops/outages/insert-network-outage.js +29 -0
- package/lib/archive/db-ops/outages/list-network-outages.d.ts +28 -0
- package/lib/archive/db-ops/outages/list-network-outages.js +42 -0
- package/lib/archive/db-ops/pages/read/build-page-query.js +1 -1
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +19 -10
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +18 -3
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +48 -10
- package/lib/archive/db-ops/pages/write/insert-audios.d.ts +14 -0
- package/lib/archive/db-ops/pages/write/insert-audios.js +25 -0
- package/lib/archive/db-ops/pages/write/insert-buttons.d.ts +14 -0
- package/lib/archive/db-ops/pages/write/insert-buttons.js +29 -0
- package/lib/archive/db-ops/pages/write/insert-canvases.d.ts +14 -0
- package/lib/archive/db-ops/pages/write/insert-canvases.js +26 -0
- package/lib/archive/db-ops/pages/write/insert-headings.d.ts +15 -0
- package/lib/archive/db-ops/pages/write/insert-headings.js +27 -0
- package/lib/archive/db-ops/pages/write/insert-iframes.d.ts +14 -0
- package/lib/archive/db-ops/pages/write/insert-iframes.js +28 -0
- package/lib/archive/db-ops/pages/write/insert-main-content-images.d.ts +20 -0
- package/lib/archive/db-ops/pages/write/insert-main-content-images.js +32 -0
- package/lib/archive/db-ops/pages/write/insert-main-content-tables.d.ts +14 -0
- package/lib/archive/db-ops/pages/write/insert-main-content-tables.js +29 -0
- package/lib/archive/db-ops/pages/write/insert-page.js +15 -2
- package/lib/archive/db-ops/pages/write/insert-videos.d.ts +14 -0
- package/lib/archive/db-ops/pages/write/insert-videos.js +28 -0
- package/lib/archive/db-ops/pages/write/update-page.js +33 -0
- package/lib/archive/db-ops/pages/write/write-page-html-blob.js +6 -6
- package/lib/archive/filesystem/output-binary.d.ts +12 -0
- package/lib/archive/filesystem/output-binary.js +17 -0
- package/lib/archive/get-failed-page-messages.d.ts +20 -8
- package/lib/archive/get-failed-page-messages.js +29 -15
- package/lib/archive/init-schema.js +2 -1
- package/lib/archive/meta/compute-main-contents-denormalized.d.ts +22 -0
- package/lib/archive/meta/compute-main-contents-denormalized.js +63 -0
- package/lib/archive/meta/types.d.ts +263 -0
- package/lib/archive/migrate-content-items-alias-of-id.d.ts +47 -0
- package/lib/archive/migrate-content-items-alias-of-id.js +58 -0
- package/lib/archive/migrate-info-main-content-selector.d.ts +11 -0
- package/lib/archive/migrate-info-main-content-selector.js +24 -0
- package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +11 -0
- package/lib/archive/migrate-inventory-runs-invalid-skipped.js +24 -0
- package/lib/archive/migrate-main-contents-columns.d.ts +21 -0
- package/lib/archive/migrate-main-contents-columns.js +50 -0
- package/lib/archive/migrate-page-meta-body-hash.d.ts +39 -0
- package/lib/archive/migrate-page-meta-body-hash.js +52 -0
- package/lib/archive/migrate-page-meta-console-error-count.d.ts +22 -0
- package/lib/archive/migrate-page-meta-console-error-count.js +34 -0
- package/lib/archive/page.d.ts +145 -3
- package/lib/archive/page.js +195 -0
- package/lib/archive/types.d.ts +86 -2
- package/lib/archive/url-alias/compute-tier-a-alias-key.d.ts +31 -0
- package/lib/archive/url-alias/compute-tier-a-alias-key.js +36 -0
- package/lib/archive/url-alias/compute-tier-b-alias-key.d.ts +19 -0
- package/lib/archive/url-alias/compute-tier-b-alias-key.js +30 -0
- package/lib/archive/url-alias/format-alias-key.d.ts +13 -0
- package/lib/archive/url-alias/format-alias-key.js +14 -0
- package/lib/archive/url-alias/parse-alias-key-parts.d.ts +15 -0
- package/lib/archive/url-alias/parse-alias-key-parts.js +40 -0
- package/lib/archive/url-alias/types.d.ts +7 -0
- package/lib/archive/url-alias/types.js +1 -0
- package/lib/crawler/choose-probe-host.d.ts +24 -0
- package/lib/crawler/choose-probe-host.js +38 -0
- package/lib/crawler/clear-dns-burned-host-cache.js +2 -0
- package/lib/crawler/crawler.js +261 -2
- package/lib/crawler/dns-burned-host-burn-timestamps.d.ts +17 -0
- package/lib/crawler/dns-burned-host-burn-timestamps.js +17 -0
- package/lib/crawler/evict-network-classified-destination-cache-entries.d.ts +26 -0
- package/lib/crawler/evict-network-classified-destination-cache-entries.js +34 -0
- package/lib/crawler/evict-outage-tainted-dns-burns.d.ts +23 -0
- package/lib/crawler/evict-outage-tainted-dns-burns.js +26 -0
- package/lib/crawler/fetch-destination.js +2 -0
- package/lib/crawler/link-to-page-data.js +2 -0
- package/lib/crawler/network-gate.d.ts +49 -0
- package/lib/crawler/network-gate.js +78 -0
- package/lib/crawler/network-outage-detector.d.ts +51 -0
- package/lib/crawler/network-outage-detector.js +81 -0
- package/lib/crawler/network-outage-summary-counter.d.ts +21 -0
- package/lib/crawler/network-outage-summary-counter.js +18 -0
- package/lib/crawler/probe-network.d.ts +26 -0
- package/lib/crawler/probe-network.js +21 -0
- package/lib/crawler/resource-to-page-data.js +2 -0
- package/lib/crawler/types.d.ts +152 -1
- package/lib/crawler-orchestrator.d.ts +62 -18
- package/lib/crawler-orchestrator.js +163 -26
- package/lib/crawler.d.ts +21 -0
- package/lib/crawler.js +17 -0
- package/lib/is-within-outage-window.d.ts +49 -0
- package/lib/is-within-outage-window.js +33 -0
- package/lib/network-related-error-kinds.d.ts +23 -0
- package/lib/network-related-error-kinds.js +28 -0
- package/lib/types.d.ts +9 -1
- package/lib/utils/array/each-splitted.d.ts +1 -1
- package/lib/utils/compute-file-sha256.d.ts +17 -17
- package/lib/utils/compute-file-sha256.js +18 -47
- package/package.json +3 -3
|
@@ -8,11 +8,29 @@
|
|
|
8
8
|
* an external link that failed DNS, or null for a process-level error)
|
|
9
9
|
* - `page_tags` — Wappalyzer detections, FK → `content_items(id)`
|
|
10
10
|
* - `page_jsonld` — JSON-LD / SpeculationRules, FK → `content_items(id)`
|
|
11
|
+
* - `page_main_content_headings` / `_images` / `_tables` / `_buttons` /
|
|
12
|
+
* `_iframes` / `_videos` / `_audios` / `_canvases` — beholder
|
|
13
|
+
* `MainContentsData` sub-entity arrays, one row per DOM element, FK →
|
|
14
|
+
* `content_items(id)`
|
|
11
15
|
* - `inventory_runs` — `--inventory` audit log (no FK; append-only)
|
|
16
|
+
* - `network_outages` — operator-network-outage journal (no FK; append-only
|
|
17
|
+
* except `ended_at`, which is written once on recovery)
|
|
12
18
|
* - `analysis_text_refs` + `analysis_violations` — analyze-phase findings,
|
|
13
19
|
* FK → `content_items(id)`
|
|
20
|
+
* - `page_templates` — DOM-structure template classification (`--templates`,
|
|
21
|
+
* `@nitpicker/core`'s `template-classification/`), one row per classified
|
|
22
|
+
* page, FK → `content_items(id)`
|
|
23
|
+
* - `page_template_clusters` — one row per distinct `page_templates.template_key`,
|
|
24
|
+
* holding `@d-zero/page-cluster`'s cluster-selection evidence (no FK;
|
|
25
|
+
* `template_key` is not a `page_templates` FK target, so consistency is
|
|
26
|
+
* maintained by replacing both tables together, not by a foreign key)
|
|
14
27
|
* - `page_html_blobs` + `page_html_ref` — content-addressable HTML
|
|
15
28
|
* snapshots, FK → `content_items(id)`
|
|
29
|
+
* - `console_log_items` — content-addressable dictionary of distinct
|
|
30
|
+
* console messages / page errors (no FK; hash-deduplicated across every
|
|
31
|
+
* page in the archive, mirroring `text_refs` / `json_refs`)
|
|
32
|
+
* - `page_console_logs` — one row per (page, console log) occurrence, FK →
|
|
33
|
+
* `content_items(id)` and `console_log_items(id)`
|
|
16
34
|
*
|
|
17
35
|
* The DDL is shared between fresh-archive provisioning ({@link initSchema}
|
|
18
36
|
* calls this right after `createEntityTables`) and the migration script
|
|
@@ -129,6 +147,134 @@ export async function createAdjunctTables(instance) {
|
|
|
129
147
|
// `list_pages_by_jsonld_type` JOINs.
|
|
130
148
|
await instance.raw('CREATE INDEX page_jsonld_type_pageId ON page_jsonld(type, pageId)');
|
|
131
149
|
}
|
|
150
|
+
// Beholder `MainContentsData` sub-entities, one adjunct table per array
|
|
151
|
+
// (headings/images/tables/buttons/iframes/videos/audios/canvases). Same
|
|
152
|
+
// shape as `page_tags` / `page_jsonld`: `pageId` FK → `content_items(id)`
|
|
153
|
+
// ON DELETE CASCADE, individually guarded so any subset can pre-exist.
|
|
154
|
+
// `order` preserves the DOM traversal order beholder returns the array
|
|
155
|
+
// in (0-based); it is not itself an index target since these tables are
|
|
156
|
+
// always read whole-page via `WHERE pageId = ? ORDER BY "order"`.
|
|
157
|
+
if (!(await instance.schema.hasTable('page_main_content_headings'))) {
|
|
158
|
+
await instance.schema.createTable('page_main_content_headings', (t) => {
|
|
159
|
+
t.increments('id');
|
|
160
|
+
t.integer('pageId')
|
|
161
|
+
.notNullable()
|
|
162
|
+
.unsigned()
|
|
163
|
+
.references('content_items.id')
|
|
164
|
+
.onDelete('CASCADE');
|
|
165
|
+
t.integer('order').notNullable();
|
|
166
|
+
t.text('text');
|
|
167
|
+
t.integer('level').notNullable();
|
|
168
|
+
t.index('pageId');
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (!(await instance.schema.hasTable('page_main_content_images'))) {
|
|
172
|
+
await instance.schema.createTable('page_main_content_images', (t) => {
|
|
173
|
+
t.increments('id');
|
|
174
|
+
t.integer('pageId')
|
|
175
|
+
.notNullable()
|
|
176
|
+
.unsigned()
|
|
177
|
+
.references('content_items.id')
|
|
178
|
+
.onDelete('CASCADE');
|
|
179
|
+
t.integer('order').notNullable();
|
|
180
|
+
t.string('src', 8190).notNullable();
|
|
181
|
+
t.text('alt').notNullable();
|
|
182
|
+
t.index('pageId');
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (!(await instance.schema.hasTable('page_main_content_tables'))) {
|
|
186
|
+
await instance.schema.createTable('page_main_content_tables', (t) => {
|
|
187
|
+
t.increments('id');
|
|
188
|
+
t.integer('pageId')
|
|
189
|
+
.notNullable()
|
|
190
|
+
.unsigned()
|
|
191
|
+
.references('content_items.id')
|
|
192
|
+
.onDelete('CASCADE');
|
|
193
|
+
t.integer('order').notNullable();
|
|
194
|
+
t.integer('rows').notNullable();
|
|
195
|
+
t.integer('cols').notNullable();
|
|
196
|
+
t.boolean('hasHeader').notNullable();
|
|
197
|
+
t.boolean('hasFooter').notNullable();
|
|
198
|
+
t.boolean('hasMergedCell').notNullable();
|
|
199
|
+
t.index('pageId');
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (!(await instance.schema.hasTable('page_main_content_buttons'))) {
|
|
203
|
+
await instance.schema.createTable('page_main_content_buttons', (t) => {
|
|
204
|
+
t.increments('id');
|
|
205
|
+
t.integer('pageId')
|
|
206
|
+
.notNullable()
|
|
207
|
+
.unsigned()
|
|
208
|
+
.references('content_items.id')
|
|
209
|
+
.onDelete('CASCADE');
|
|
210
|
+
t.integer('order').notNullable();
|
|
211
|
+
t.string('nodeName').notNullable();
|
|
212
|
+
t.string('role');
|
|
213
|
+
t.string('type');
|
|
214
|
+
t.text('text');
|
|
215
|
+
t.boolean('disabled').notNullable();
|
|
216
|
+
t.index('pageId');
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (!(await instance.schema.hasTable('page_main_content_iframes'))) {
|
|
220
|
+
await instance.schema.createTable('page_main_content_iframes', (t) => {
|
|
221
|
+
t.increments('id');
|
|
222
|
+
t.integer('pageId')
|
|
223
|
+
.notNullable()
|
|
224
|
+
.unsigned()
|
|
225
|
+
.references('content_items.id')
|
|
226
|
+
.onDelete('CASCADE');
|
|
227
|
+
t.integer('order').notNullable();
|
|
228
|
+
t.string('src', 8190).notNullable();
|
|
229
|
+
t.text('title');
|
|
230
|
+
t.string('width');
|
|
231
|
+
t.string('height');
|
|
232
|
+
t.index('pageId');
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (!(await instance.schema.hasTable('page_main_content_videos'))) {
|
|
236
|
+
await instance.schema.createTable('page_main_content_videos', (t) => {
|
|
237
|
+
t.increments('id');
|
|
238
|
+
t.integer('pageId')
|
|
239
|
+
.notNullable()
|
|
240
|
+
.unsigned()
|
|
241
|
+
.references('content_items.id')
|
|
242
|
+
.onDelete('CASCADE');
|
|
243
|
+
t.integer('order').notNullable();
|
|
244
|
+
t.string('src', 8190).notNullable();
|
|
245
|
+
t.string('poster', 8190);
|
|
246
|
+
t.integer('width').notNullable();
|
|
247
|
+
t.integer('height').notNullable();
|
|
248
|
+
t.index('pageId');
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (!(await instance.schema.hasTable('page_main_content_audios'))) {
|
|
252
|
+
await instance.schema.createTable('page_main_content_audios', (t) => {
|
|
253
|
+
t.increments('id');
|
|
254
|
+
t.integer('pageId')
|
|
255
|
+
.notNullable()
|
|
256
|
+
.unsigned()
|
|
257
|
+
.references('content_items.id')
|
|
258
|
+
.onDelete('CASCADE');
|
|
259
|
+
t.integer('order').notNullable();
|
|
260
|
+
t.string('src', 8190).notNullable();
|
|
261
|
+
t.index('pageId');
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (!(await instance.schema.hasTable('page_main_content_canvases'))) {
|
|
265
|
+
await instance.schema.createTable('page_main_content_canvases', (t) => {
|
|
266
|
+
t.increments('id');
|
|
267
|
+
t.integer('pageId')
|
|
268
|
+
.notNullable()
|
|
269
|
+
.unsigned()
|
|
270
|
+
.references('content_items.id')
|
|
271
|
+
.onDelete('CASCADE');
|
|
272
|
+
t.integer('order').notNullable();
|
|
273
|
+
t.integer('width').notNullable();
|
|
274
|
+
t.integer('height').notNullable();
|
|
275
|
+
t.index('pageId');
|
|
276
|
+
});
|
|
277
|
+
}
|
|
132
278
|
if (!(await instance.schema.hasTable('inventory_runs'))) {
|
|
133
279
|
await instance.schema.createTable('inventory_runs', (t) => {
|
|
134
280
|
// One row per successful `--inventory <list>` invocation. The
|
|
@@ -144,10 +290,42 @@ export async function createAdjunctTables(instance) {
|
|
|
144
290
|
t.integer('new_pages').nullable();
|
|
145
291
|
t.integer('new_resources').nullable();
|
|
146
292
|
t.integer('scope_skipped').nullable();
|
|
293
|
+
t.integer('invalid_skipped').nullable();
|
|
147
294
|
t.text('notes').nullable();
|
|
148
295
|
t.index('ran_at');
|
|
149
296
|
});
|
|
150
297
|
}
|
|
298
|
+
if (!(await instance.schema.hasTable('network_outages'))) {
|
|
299
|
+
await instance.schema.createTable('network_outages', (t) => {
|
|
300
|
+
// One row per detected operator-network outage. The archive's
|
|
301
|
+
// evidence that a run of `dns`/`local-network`-shaped failures
|
|
302
|
+
// was caused by the crawl operator's own connectivity, not the
|
|
303
|
+
// target sites — used to retroactively re-classify `crawl_errors`
|
|
304
|
+
// / `page_errors` rows whose `createdAt` falls inside a window
|
|
305
|
+
// (see `is-within-outage-window.ts`). No index: a crawl session
|
|
306
|
+
// produces at most a handful of these rows, so a full in-memory
|
|
307
|
+
// scan by every consumer is cheaper than maintaining a B-tree
|
|
308
|
+
// that few queries would use (`ARCHITECTURE.md`'s
|
|
309
|
+
// perf-index-is-not-free / evidence-before-indexing rules).
|
|
310
|
+
t.increments('id');
|
|
311
|
+
// Backdated to the earliest error still inside the detector's
|
|
312
|
+
// sliding window at trigger time, NOT the trigger instant —
|
|
313
|
+
// see `NetworkOutageDetector`'s `OutageSuspect.startedAt`.
|
|
314
|
+
t.integer('started_at').notNullable();
|
|
315
|
+
// When the sliding window actually crossed both thresholds.
|
|
316
|
+
t.integer('detected_at').notNullable();
|
|
317
|
+
// NULL until a recovery probe succeeds. A row can also be left
|
|
318
|
+
// NULL forever if the crawl process is killed mid-outage; readers
|
|
319
|
+
// MUST NOT treat a NULL `ended_at` as an unbounded window (that
|
|
320
|
+
// would retroactively cover every later error as
|
|
321
|
+
// network-caused) — see `is-within-outage-window.ts` and the
|
|
322
|
+
// writer-side clamp-on-next-open in `db-ops/outages/`.
|
|
323
|
+
t.integer('ended_at').nullable();
|
|
324
|
+
t.string('probe_host').nullable();
|
|
325
|
+
t.integer('trigger_error_count').notNullable();
|
|
326
|
+
t.integer('trigger_host_count').notNullable();
|
|
327
|
+
});
|
|
328
|
+
}
|
|
151
329
|
if (!(await instance.schema.hasTable('analysis_text_refs'))) {
|
|
152
330
|
await instance.raw(`
|
|
153
331
|
CREATE TABLE analysis_text_refs (
|
|
@@ -184,6 +362,49 @@ export async function createAdjunctTables(instance) {
|
|
|
184
362
|
await instance.raw('CREATE INDEX av_code_order ON analysis_violations(code_sort_key, id)');
|
|
185
363
|
await instance.raw('CREATE INDEX av_page ON analysis_violations(page_id, id)');
|
|
186
364
|
}
|
|
365
|
+
// DOM-structure template classification (`--templates`). One row per
|
|
366
|
+
// internal HTML page that was classified; `page_id` is both the PK and
|
|
367
|
+
// the natural key (1:1 with `content_items`), so — unlike
|
|
368
|
+
// `analysis_violations`, which is 1:many and needs a surrogate `id` —
|
|
369
|
+
// there's nothing to index beyond the PK itself. `WITHOUT ROWID` packs
|
|
370
|
+
// rows directly in the PK b-tree, matching `page_html_ref`'s shape
|
|
371
|
+
// (small fixed-width row, PK-only lookups).
|
|
372
|
+
if (!(await instance.schema.hasTable('page_templates'))) {
|
|
373
|
+
await instance.raw(`
|
|
374
|
+
CREATE TABLE page_templates (
|
|
375
|
+
page_id INTEGER PRIMARY KEY REFERENCES content_items(id),
|
|
376
|
+
template_key TEXT NOT NULL
|
|
377
|
+
) WITHOUT ROWID
|
|
378
|
+
`);
|
|
379
|
+
}
|
|
380
|
+
// One row per distinct `template_key` produced by the same `--templates`
|
|
381
|
+
// classification run, holding `@d-zero/page-cluster`'s cluster-selection
|
|
382
|
+
// evidence (`ClusterReason`, renamed `TemplateClusterReason` on this side)
|
|
383
|
+
// as a zstd-compressed JSON blob — same BLOB+codec+size shape as
|
|
384
|
+
// `page_html_blobs` below. A column on `page_templates` was rejected: that
|
|
385
|
+
// table is one row per *page*, so the same cluster's reason would be
|
|
386
|
+
// duplicated across every member page (multi-GB on a large archive with a
|
|
387
|
+
// few-hundred-member cluster). A `json_refs` row was also rejected: reason
|
|
388
|
+
// payloads differ per cluster (distinct `memberCount`/token sets), so
|
|
389
|
+
// content-address dedup would not pay for itself, and `json_refs` is a
|
|
390
|
+
// shared dictionary that other tables reference — this table's full
|
|
391
|
+
// replace-on-every-run write pattern (see `replacePageTemplates`) would
|
|
392
|
+
// otherwise leave orphaned rows behind with no owner able to delete them.
|
|
393
|
+
// No FK to `page_templates`: `template_key` is not that table's primary
|
|
394
|
+
// key (`page_id` is), so there is nothing to reference — consistency is
|
|
395
|
+
// instead maintained by replacing both tables in the same transaction.
|
|
396
|
+
if (!(await instance.schema.hasTable('page_template_clusters'))) {
|
|
397
|
+
await instance.raw(`
|
|
398
|
+
CREATE TABLE page_template_clusters (
|
|
399
|
+
template_key TEXT PRIMARY KEY,
|
|
400
|
+
member_count INTEGER NOT NULL,
|
|
401
|
+
reason_json BLOB NOT NULL,
|
|
402
|
+
codec TEXT NOT NULL CHECK(codec IN ('zstd', 'none')),
|
|
403
|
+
size_raw INTEGER NOT NULL,
|
|
404
|
+
size_stored INTEGER NOT NULL
|
|
405
|
+
) WITHOUT ROWID
|
|
406
|
+
`);
|
|
407
|
+
}
|
|
187
408
|
// Content-addressable HTML blob storage. Knex's schema builder doesn't
|
|
188
409
|
// expose a WITHOUT ROWID toggle, so the BLOB tables are created via raw
|
|
189
410
|
// SQL. WITHOUT ROWID keeps the rows packed inside the b-tree leaves
|
|
@@ -210,4 +431,60 @@ export async function createAdjunctTables(instance) {
|
|
|
210
431
|
`);
|
|
211
432
|
await instance.raw('CREATE INDEX idx_page_html_ref_hash ON page_html_ref(hash)');
|
|
212
433
|
}
|
|
434
|
+
// Content-addressable dictionary of distinct console messages / page
|
|
435
|
+
// errors (beholder's `ConsoleLogEntry`, issue #228). `hash` is
|
|
436
|
+
// SHA-256 over a canonical tuple of every content field (type, text,
|
|
437
|
+
// args JSON, location, stack) — the same identical warning emitted by
|
|
438
|
+
// a shared framework on every page therefore collapses to one row
|
|
439
|
+
// regardless of how many pages or how many times it fires, mirroring
|
|
440
|
+
// `text_refs` / `json_refs`. `args_json_id` is nullable because a
|
|
441
|
+
// call with no arguments (or one whose args failed to
|
|
442
|
+
// `JSON.stringify`, e.g. a circular reference) has nothing to store.
|
|
443
|
+
// `text_id` is nullable too: `text_refs` never stores the empty
|
|
444
|
+
// string (its dictionary upsert treats `''` as "nothing to dedupe"),
|
|
445
|
+
// so a call like `console.log()` with zero arguments — whose
|
|
446
|
+
// `text` beholder reports as `''` — has no `text_refs` row to point
|
|
447
|
+
// at; `text_id = NULL` there means "empty text", read back as `''`.
|
|
448
|
+
// `type` keeps its own index for the Console Logs view's type filter.
|
|
449
|
+
if (!(await instance.schema.hasTable('console_log_items'))) {
|
|
450
|
+
await instance.raw(`
|
|
451
|
+
CREATE TABLE console_log_items (
|
|
452
|
+
id INTEGER PRIMARY KEY,
|
|
453
|
+
hash BLOB NOT NULL UNIQUE,
|
|
454
|
+
type TEXT NOT NULL,
|
|
455
|
+
text_id INTEGER REFERENCES text_refs(id),
|
|
456
|
+
args_json_id INTEGER REFERENCES json_refs(id),
|
|
457
|
+
loc_url_id INTEGER REFERENCES url_refs(id),
|
|
458
|
+
loc_line INTEGER,
|
|
459
|
+
loc_column INTEGER,
|
|
460
|
+
stack_text_id INTEGER REFERENCES text_refs(id)
|
|
461
|
+
)
|
|
462
|
+
`);
|
|
463
|
+
await instance.raw('CREATE INDEX idx_console_log_items_type ON console_log_items(type)');
|
|
464
|
+
}
|
|
465
|
+
// One row per (page, console log) occurrence — beholder captures a
|
|
466
|
+
// `ts` per firing, so the same message logged 3 times on one page
|
|
467
|
+
// yields 3 rows (unlike `anchor_edges`' first-wins dedup: an
|
|
468
|
+
// occurrence count matters here, not just presence). Replaced
|
|
469
|
+
// wholesale per page on every non-empty scrape by
|
|
470
|
+
// `replaceConsoleLogs`, the same Scoped-Replace pattern as
|
|
471
|
+
// `anchor_edges` / `image_items` — there is no natural key to UPDATE
|
|
472
|
+
// a specific prior occurrence against.
|
|
473
|
+
if (!(await instance.schema.hasTable('page_console_logs'))) {
|
|
474
|
+
await instance.schema.createTable('page_console_logs', (t) => {
|
|
475
|
+
t.increments('id');
|
|
476
|
+
t.integer('pageId')
|
|
477
|
+
.notNullable()
|
|
478
|
+
.unsigned()
|
|
479
|
+
.references('content_items.id')
|
|
480
|
+
.onDelete('CASCADE');
|
|
481
|
+
t.integer('consoleLogId')
|
|
482
|
+
.notNullable()
|
|
483
|
+
.unsigned()
|
|
484
|
+
.references('console_log_items.id');
|
|
485
|
+
t.integer('ts').notNullable();
|
|
486
|
+
t.index('pageId');
|
|
487
|
+
t.index('consoleLogId');
|
|
488
|
+
});
|
|
489
|
+
}
|
|
213
490
|
}
|
|
@@ -136,6 +136,20 @@ import type { Knex } from 'knex';
|
|
|
136
136
|
* routing so the two entities never disagree on which values count as
|
|
137
137
|
* "large data URI".
|
|
138
138
|
*
|
|
139
|
+
* **`page_meta.main_content_*` / `scroll_height_*` columns.** Denormalised
|
|
140
|
+
* aggregates derived from beholder's `MainContentsData` / `ScrollHeightData`
|
|
141
|
+
* (word/element counts, desktop+mobile scroll height), following the same
|
|
142
|
+
* write-once-at-scrape-time pattern as `tag_count` / `jsonld_count` so list
|
|
143
|
+
* / detail reads never re-derive them from the per-page child tables
|
|
144
|
+
* (`page_main_content_headings` etc., see `create-adjunct-tables.ts`).
|
|
145
|
+
* `main_content_node_name` / `_id` / `_role` / `_selector` / `_class_list`
|
|
146
|
+
* identify the detected main-content element; unlike `title_text_id` /
|
|
147
|
+
* `description_text_id` these are stored as plain `TEXT` rather than routed
|
|
148
|
+
* through `text_refs` — the values are page-specific diagnostics with low
|
|
149
|
+
* cross-page reuse, so the ref-table dedup machinery would add write-path
|
|
150
|
+
* cost without a corresponding storage win. `main_content_class_list` holds
|
|
151
|
+
* a JSON-encoded string array.
|
|
152
|
+
*
|
|
139
153
|
* ### Index rationale
|
|
140
154
|
*
|
|
141
155
|
* Every index below reflects a legacy-baseline single-column index that
|
|
@@ -135,6 +135,20 @@
|
|
|
135
135
|
* routing so the two entities never disagree on which values count as
|
|
136
136
|
* "large data URI".
|
|
137
137
|
*
|
|
138
|
+
* **`page_meta.main_content_*` / `scroll_height_*` columns.** Denormalised
|
|
139
|
+
* aggregates derived from beholder's `MainContentsData` / `ScrollHeightData`
|
|
140
|
+
* (word/element counts, desktop+mobile scroll height), following the same
|
|
141
|
+
* write-once-at-scrape-time pattern as `tag_count` / `jsonld_count` so list
|
|
142
|
+
* / detail reads never re-derive them from the per-page child tables
|
|
143
|
+
* (`page_main_content_headings` etc., see `create-adjunct-tables.ts`).
|
|
144
|
+
* `main_content_node_name` / `_id` / `_role` / `_selector` / `_class_list`
|
|
145
|
+
* identify the detected main-content element; unlike `title_text_id` /
|
|
146
|
+
* `description_text_id` these are stored as plain `TEXT` rather than routed
|
|
147
|
+
* through `text_refs` — the values are page-specific diagnostics with low
|
|
148
|
+
* cross-page reuse, so the ref-table dedup machinery would add write-path
|
|
149
|
+
* cost without a corresponding storage win. `main_content_class_list` holds
|
|
150
|
+
* a JSON-encoded string array.
|
|
151
|
+
*
|
|
138
152
|
* ### Index rationale
|
|
139
153
|
*
|
|
140
154
|
* Every index below reflects a legacy-baseline single-column index that
|
|
@@ -183,6 +197,7 @@ export async function createEntityTables(instance) {
|
|
|
183
197
|
content_length INTEGER,
|
|
184
198
|
header_set_id INTEGER REFERENCES header_sets(id),
|
|
185
199
|
redirect_dest_id INTEGER REFERENCES content_items(id) DEFERRABLE INITIALLY DEFERRED,
|
|
200
|
+
alias_of_id INTEGER REFERENCES content_items(id) DEFERRABLE INITIALLY DEFERRED,
|
|
186
201
|
source TEXT NOT NULL DEFAULT 'crawled',
|
|
187
202
|
first_crawled_at INTEGER,
|
|
188
203
|
last_crawled_at INTEGER,
|
|
@@ -194,6 +209,15 @@ export async function createEntityTables(instance) {
|
|
|
194
209
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_external ON content_items(is_external)');
|
|
195
210
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_scraped ON content_items(scraped)');
|
|
196
211
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_redirect_dest_id ON content_items(redirect_dest_id)');
|
|
212
|
+
// `idx_content_items_alias_of_id` is NOT created here even though
|
|
213
|
+
// `alias_of_id` is: this DDL runs unconditionally on every archive open
|
|
214
|
+
// (including legacy archives that still lack the column at this point,
|
|
215
|
+
// before `migrateContentItemsAliasOfId` adds it), so an unconditional
|
|
216
|
+
// `CREATE INDEX ... alias_of_id` here would fail with `no such column`
|
|
217
|
+
// on any archive that predates this feature. The index is created in
|
|
218
|
+
// `migrateContentItemsAliasOfId` instead, which runs after the
|
|
219
|
+
// column-add guard for both fresh and legacy archives (same reasoning as
|
|
220
|
+
// `page_meta.body_hash`'s index).
|
|
197
221
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_content_type_id ON content_items(content_type_id)');
|
|
198
222
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_crawl_order ON content_items(crawl_order)');
|
|
199
223
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_source ON content_items(source)');
|
|
@@ -248,10 +272,37 @@ export async function createEntityTables(instance) {
|
|
|
248
272
|
tag_count INTEGER,
|
|
249
273
|
jsonld_count INTEGER,
|
|
250
274
|
tags_providers_csv TEXT,
|
|
251
|
-
meta_extras_json_id INTEGER REFERENCES json_refs(id)
|
|
275
|
+
meta_extras_json_id INTEGER REFERENCES json_refs(id),
|
|
276
|
+
main_content_node_name TEXT,
|
|
277
|
+
main_content_id TEXT,
|
|
278
|
+
main_content_role TEXT,
|
|
279
|
+
main_content_selector TEXT,
|
|
280
|
+
main_content_class_list TEXT,
|
|
281
|
+
main_content_word_count INTEGER,
|
|
282
|
+
main_content_body_word_count INTEGER,
|
|
283
|
+
main_content_heading_count INTEGER,
|
|
284
|
+
main_content_image_count INTEGER,
|
|
285
|
+
main_content_table_count INTEGER,
|
|
286
|
+
main_content_button_count INTEGER,
|
|
287
|
+
main_content_iframe_count INTEGER,
|
|
288
|
+
main_content_video_count INTEGER,
|
|
289
|
+
main_content_audio_count INTEGER,
|
|
290
|
+
main_content_canvas_count INTEGER,
|
|
291
|
+
scroll_height_desktop INTEGER,
|
|
292
|
+
scroll_height_mobile INTEGER,
|
|
293
|
+
body_hash BLOB,
|
|
294
|
+
console_error_count INTEGER
|
|
252
295
|
)
|
|
253
296
|
`);
|
|
254
297
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_page_meta_og_type ON page_meta(og_type)');
|
|
298
|
+
// `idx_page_meta_body_hash` is NOT created here even though `body_hash`
|
|
299
|
+
// is: this DDL runs unconditionally on every archive open (including
|
|
300
|
+
// legacy archives that still lack the column at this point, before
|
|
301
|
+
// `migratePageMetaBodyHash` adds it), so an unconditional
|
|
302
|
+
// `CREATE INDEX ... body_hash` here would fail with `no such column` on
|
|
303
|
+
// any archive that predates this feature. The index is created in
|
|
304
|
+
// `migratePageMetaBodyHash` instead, which runs after the column-add
|
|
305
|
+
// guard for both fresh and legacy archives.
|
|
255
306
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_page_meta_robots_noindex ON page_meta(robots_noindex)');
|
|
256
307
|
await instance.raw(`
|
|
257
308
|
CREATE TABLE IF NOT EXISTS resource_items (
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { TemplateClusterReason } from './db-ops/analysis/types.js';
|
|
2
|
+
import type { JsonLdRow, MainContentAudioRow, MainContentButtonRow, MainContentCanvasRow, MainContentHeadingRow, MainContentIframeRow, MainContentImageRow, MainContentTableRow, MainContentVideoRow, TagRow } from './meta/types.js';
|
|
3
|
+
import type { Config, DatabaseOption, DB_Redirect, DB_Resource, DatabaseEvent, InsertNetworkOutageParams, InventoryRunMeta, PageFilter, PageSource } from './types.js';
|
|
4
|
+
import type { OutageWindow } from '../is-within-outage-window.js';
|
|
3
5
|
import type { PageData, Resource } from '../utils/types/types.js';
|
|
6
|
+
import type { ConsoleLogEntry } from '@d-zero/beholder';
|
|
4
7
|
import type { ExURL, ParseURLOptions } from '@d-zero/shared/parse-url';
|
|
5
8
|
import type { Knex } from 'knex';
|
|
6
9
|
import { TypedAwaitEventEmitter as EventEmitter } from '@d-zero/shared/typed-await-event-emitter';
|
|
@@ -34,6 +37,13 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
34
37
|
* database file. Delegates to {@link checkpointOp}.
|
|
35
38
|
*/
|
|
36
39
|
checkpoint(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Closes an outage row by stamping `ended_at` — a no-op if the row is
|
|
42
|
+
* already closed. Delegates to {@link closeNetworkOutageOp}.
|
|
43
|
+
* @param id - The `network_outages.id` to close.
|
|
44
|
+
* @param endedAt - Epoch ms the outage is considered to have ended.
|
|
45
|
+
*/
|
|
46
|
+
closeNetworkOutage(id: number, endedAt: number): Promise<void>;
|
|
37
47
|
/**
|
|
38
48
|
* Destroys the database connection, releasing all pooled resources.
|
|
39
49
|
* Delegates to {@link destroyOp}.
|
|
@@ -46,6 +56,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
46
56
|
* @returns An array of anchor records with resolved URL, title, status, and content type.
|
|
47
57
|
*/
|
|
48
58
|
getAnchorsOnPage(pageId: number): Promise<any[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Retrieves all `page_main_content_audios` rows for the given page id.
|
|
61
|
+
* Delegates to {@link getAudiosOfPageOp}.
|
|
62
|
+
* @param pageId
|
|
63
|
+
*/
|
|
64
|
+
getAudiosOfPage(pageId: number): Promise<MainContentAudioRow[]>;
|
|
49
65
|
/**
|
|
50
66
|
* Retrieves the base URL of the crawl session from the `info` table.
|
|
51
67
|
* Delegates to {@link getBaseUrlOp}.
|
|
@@ -53,6 +69,18 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
53
69
|
* @throws {Error} If no base URL is found in the database.
|
|
54
70
|
*/
|
|
55
71
|
getBaseUrl(): Promise<string>;
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves all `page_main_content_buttons` rows for the given page id.
|
|
74
|
+
* Delegates to {@link getButtonsOfPageOp}.
|
|
75
|
+
* @param pageId
|
|
76
|
+
*/
|
|
77
|
+
getButtonsOfPage(pageId: number): Promise<MainContentButtonRow[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Retrieves all `page_main_content_canvases` rows for the given page id.
|
|
80
|
+
* Delegates to {@link getCanvasesOfPageOp}.
|
|
81
|
+
* @param pageId
|
|
82
|
+
*/
|
|
83
|
+
getCanvasesOfPage(pageId: number): Promise<MainContentCanvasRow[]>;
|
|
56
84
|
/**
|
|
57
85
|
* Retrieves the full crawl configuration from the `info` table.
|
|
58
86
|
* Delegates to {@link getConfigOp}.
|
|
@@ -85,6 +113,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
85
113
|
* @returns URLs found in `resources`.
|
|
86
114
|
*/
|
|
87
115
|
getExistingResourceUrls(urls: readonly string[]): Promise<string[]>;
|
|
116
|
+
/**
|
|
117
|
+
* Retrieves all `page_main_content_headings` rows for the given page id.
|
|
118
|
+
* Delegates to {@link getHeadingsOfPageOp}.
|
|
119
|
+
* @param pageId
|
|
120
|
+
*/
|
|
121
|
+
getHeadingsOfPage(pageId: number): Promise<MainContentHeadingRow[]>;
|
|
88
122
|
/**
|
|
89
123
|
* Reads the HTML snapshot stored as a zstd-compressed BLOB for the given page.
|
|
90
124
|
* Delegates to {@link getHtmlOfPageByIdOp}.
|
|
@@ -92,6 +126,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
92
126
|
* @returns The decompressed HTML string, or `null` if no snapshot is stored.
|
|
93
127
|
*/
|
|
94
128
|
getHtmlOfPageById(pageId: number): Promise<string | null>;
|
|
129
|
+
/**
|
|
130
|
+
* Retrieves all `page_main_content_iframes` rows for the given page id.
|
|
131
|
+
* Delegates to {@link getIframesOfPageOp}.
|
|
132
|
+
* @param pageId
|
|
133
|
+
*/
|
|
134
|
+
getIframesOfPage(pageId: number): Promise<MainContentIframeRow[]>;
|
|
95
135
|
/**
|
|
96
136
|
* Retrieves all `page_jsonld` rows for the given page id, parsed back into
|
|
97
137
|
* {@link JsonLdRow} shape. Delegates to {@link getJsonLdOfPageOp}.
|
|
@@ -105,6 +145,18 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
105
145
|
* @returns The Knex instance connected to the SQLite database.
|
|
106
146
|
*/
|
|
107
147
|
getKnex(): Knex;
|
|
148
|
+
/**
|
|
149
|
+
* Retrieves all `page_main_content_images` rows for the given page id.
|
|
150
|
+
* Delegates to {@link getMainContentImagesOfPageOp}.
|
|
151
|
+
* @param pageId
|
|
152
|
+
*/
|
|
153
|
+
getMainContentImagesOfPage(pageId: number): Promise<MainContentImageRow[]>;
|
|
154
|
+
/**
|
|
155
|
+
* Retrieves all `page_main_content_tables` rows for the given page id.
|
|
156
|
+
* Delegates to {@link getMainContentTablesOfPageOp}.
|
|
157
|
+
* @param pageId
|
|
158
|
+
*/
|
|
159
|
+
getMainContentTablesOfPage(pageId: number): Promise<MainContentTableRow[]>;
|
|
108
160
|
/**
|
|
109
161
|
* Retrieves the crawl session name from the `info` table.
|
|
110
162
|
* Delegates to {@link getNameOp}.
|
|
@@ -207,6 +259,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
207
259
|
* @param pageId
|
|
208
260
|
*/
|
|
209
261
|
getTagsOfPage(pageId: number): Promise<TagRow[]>;
|
|
262
|
+
/**
|
|
263
|
+
* Retrieves all `page_main_content_videos` rows for the given page id.
|
|
264
|
+
* Delegates to {@link getVideosOfPageOp}.
|
|
265
|
+
* @param pageId
|
|
266
|
+
*/
|
|
267
|
+
getVideosOfPage(pageId: number): Promise<MainContentVideoRow[]>;
|
|
210
268
|
/**
|
|
211
269
|
* Records a crawler-level (`error` channel) failure into `crawl_errors`.
|
|
212
270
|
* Delegates to {@link insertCrawlErrorOp}.
|
|
@@ -229,6 +287,13 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
229
287
|
* @param urls - URL strings already in `withoutHashAndAuth` form.
|
|
230
288
|
*/
|
|
231
289
|
insertInventorySeeds(urls: readonly string[]): Promise<void>;
|
|
290
|
+
/**
|
|
291
|
+
* Appends one open (`ended_at = NULL`) row to the `network_outages`
|
|
292
|
+
* journal. Delegates to {@link insertNetworkOutageOp}.
|
|
293
|
+
* @param params - The confirmed-outage fields to record.
|
|
294
|
+
* @returns The autoincremented `id` of the newly-inserted row.
|
|
295
|
+
*/
|
|
296
|
+
insertNetworkOutage(params: InsertNetworkOutageParams): Promise<number>;
|
|
232
297
|
/**
|
|
233
298
|
* Records a partial scrape failure against the page identified by `url`.
|
|
234
299
|
* Delegates to {@link insertPageErrorOp}.
|
|
@@ -259,6 +324,13 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
259
324
|
* @returns Lower-cased hostnames safe to short-circuit.
|
|
260
325
|
*/
|
|
261
326
|
listDnsBurnedHostCandidates(): Promise<string[]>;
|
|
327
|
+
/**
|
|
328
|
+
* Lists every recorded outage as a resolved {@link OutageWindow}.
|
|
329
|
+
* Delegates to {@link listNetworkOutagesOp}.
|
|
330
|
+
* @returns Resolved outage windows, or `[]` on an archive that predates
|
|
331
|
+
* `network_outages` or has recorded no outages.
|
|
332
|
+
*/
|
|
333
|
+
listNetworkOutages(): Promise<OutageWindow[]>;
|
|
262
334
|
/**
|
|
263
335
|
* Appends one row to the `inventory_runs` audit log.
|
|
264
336
|
* Delegates to {@link recordInventoryRunOp}.
|
|
@@ -291,6 +363,23 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
291
363
|
line?: number | null;
|
|
292
364
|
col?: number | null;
|
|
293
365
|
}[]): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Replaces one page's `page_console_logs` rows with a freshly captured
|
|
368
|
+
* set of console messages / page errors. Delegates to
|
|
369
|
+
* {@link replaceConsoleLogsOp}.
|
|
370
|
+
* @param pageUrl - The originally-requested URL, normalised (`withoutHashAndAuth` form).
|
|
371
|
+
* @param redirectPaths - The redirect chain hops captured during fetch, in order.
|
|
372
|
+
* @param entries - The console log entries to persist.
|
|
373
|
+
*/
|
|
374
|
+
replaceConsoleLogs(pageUrl: string, redirectPaths: readonly string[], entries: readonly ConsoleLogEntry[]): Promise<void>;
|
|
375
|
+
/**
|
|
376
|
+
* Replaces the stored DOM-structure template classification with a
|
|
377
|
+
* freshly generated set. Delegates to {@link replacePageTemplatesOp}.
|
|
378
|
+
* @param templateKeysByUrl - Page URL → template key.
|
|
379
|
+
* @param clusterReasonsByTemplateKey - Template key → cluster-selection
|
|
380
|
+
* evidence, if the caller captured it.
|
|
381
|
+
*/
|
|
382
|
+
replacePageTemplates(templateKeysByUrl: ReadonlyMap<string, string>, clusterReasonsByTemplateKey?: ReadonlyMap<string, TemplateClusterReason>): Promise<void>;
|
|
294
383
|
/**
|
|
295
384
|
* Promote previously-external pages whose URL falls under any of the new
|
|
296
385
|
* scope entries back to a "needs scraping" state.
|