@nitpicker/crawler 0.18.0 → 0.19.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/append-retry-failed-common-setup-phases.d.ts +15 -0
- package/lib/append-retry-failed-common-setup-phases.js +21 -0
- package/lib/append-setup-phases.d.ts +17 -0
- package/lib/append-setup-phases.js +22 -0
- package/lib/archive/archive-accessor.d.ts +31 -5
- package/lib/archive/archive-accessor.js +46 -24
- package/lib/archive/archive.d.ts +115 -37
- package/lib/archive/archive.js +118 -52
- package/lib/archive/cache/extract-archive-to-cache.d.ts +10 -1
- package/lib/archive/cache/extract-archive-to-cache.js +14 -4
- package/lib/archive/create-adjunct-tables.d.ts +12 -2
- package/lib/archive/create-adjunct-tables.js +91 -29
- package/lib/archive/create-entity-tables.d.ts +5 -1
- package/lib/archive/create-entity-tables.js +6 -1
- package/lib/archive/database.d.ts +39 -9
- package/lib/archive/database.js +63 -18
- package/lib/archive/db-ops/lifecycle/init.d.ts +14 -2
- package/lib/archive/db-ops/lifecycle/init.js +29 -11
- package/lib/archive/db-ops/meta/get-custom-elements-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-custom-elements-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-page-technologies-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-page-technologies-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-technology-signals-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-technology-signals-of-page.js +15 -0
- package/lib/archive/db-ops/pages/order/set-url-order.d.ts +6 -1
- package/lib/archive/db-ops/pages/order/set-url-order.js +7 -1
- package/lib/archive/db-ops/pages/read/build-page-query.js +1 -1
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +6 -1
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +12 -3
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +6 -1
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +10 -2
- package/lib/archive/db-ops/pages/write/insert-custom-elements.d.ts +27 -0
- package/lib/archive/db-ops/pages/write/insert-custom-elements.js +40 -0
- package/lib/archive/db-ops/pages/write/insert-page.d.ts +24 -4
- package/lib/archive/db-ops/pages/write/insert-page.js +40 -4
- package/lib/archive/db-ops/pages/write/insert-technologies.d.ts +25 -0
- package/lib/archive/db-ops/pages/write/insert-technologies.js +45 -0
- package/lib/archive/db-ops/pages/write/link-redirect-sources.d.ts +7 -1
- package/lib/archive/db-ops/pages/write/link-redirect-sources.js +8 -2
- package/lib/archive/db-ops/pages/write/update-page.d.ts +4 -1
- package/lib/archive/db-ops/pages/write/update-page.js +38 -16
- package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +12 -1
- package/lib/archive/db-ops/resources/get-resource-url-list.js +43 -5
- package/lib/archive/filesystem/copy-file-with-progress.d.ts +20 -0
- package/lib/archive/filesystem/copy-file-with-progress.js +34 -0
- package/lib/archive/filesystem/parse-pax-path.d.ts +18 -0
- package/lib/archive/filesystem/parse-pax-path.js +47 -0
- package/lib/archive/filesystem/parse-tar-size-field.d.ts +13 -0
- package/lib/archive/filesystem/parse-tar-size-field.js +40 -0
- package/lib/archive/filesystem/peek-tar-top-dir.d.ts +10 -3
- package/lib/archive/filesystem/peek-tar-top-dir.js +166 -16
- package/lib/archive/filesystem/tar.d.ts +13 -1
- package/lib/archive/filesystem/tar.js +63 -7
- package/lib/archive/filesystem/untar.d.ts +13 -0
- package/lib/archive/filesystem/untar.js +68 -8
- package/lib/archive/init-schema.d.ts +1 -1
- package/lib/archive/init-schema.js +6 -4
- package/lib/archive/meta/compute-main-contents-denormalized.d.ts +13 -2
- package/lib/archive/meta/compute-main-contents-denormalized.js +15 -2
- package/lib/archive/meta/technologies/combine-technology-confidence.d.ts +46 -0
- package/lib/archive/meta/technologies/combine-technology-confidence.js +93 -0
- package/lib/archive/meta/technologies/convert-legacy-page-tags-to-inserts.d.ts +45 -0
- package/lib/archive/meta/technologies/convert-legacy-page-tags-to-inserts.js +53 -0
- package/lib/archive/meta/technologies/convert-tag-row-to-wappalyzer-signal.d.ts +33 -0
- package/lib/archive/meta/technologies/convert-tag-row-to-wappalyzer-signal.js +46 -0
- package/lib/archive/meta/technologies/extract-technologies-for-archive.d.ts +30 -0
- package/lib/archive/meta/technologies/extract-technologies-for-archive.js +27 -0
- package/lib/archive/meta/technologies/match-structural-technology-signals.d.ts +13 -0
- package/lib/archive/meta/technologies/match-structural-technology-signals.js +44 -0
- package/lib/archive/meta/technologies/normalize-wappalyzer-entries.d.ts +25 -0
- package/lib/archive/meta/technologies/normalize-wappalyzer-entries.js +35 -0
- package/lib/archive/meta/technologies/technology-signal-definitions.d.ts +43 -0
- package/lib/archive/meta/technologies/technology-signal-definitions.js +193 -0
- package/lib/archive/meta/technologies/types.d.ts +68 -0
- package/lib/archive/meta/technologies/types.js +13 -0
- package/lib/archive/meta/types.d.ts +97 -30
- package/lib/archive/migrate-content-items-alias-of-id.d.ts +6 -1
- package/lib/archive/migrate-content-items-alias-of-id.js +14 -3
- package/lib/archive/migrate-content-items-dedupe-cap-event-id.d.ts +6 -1
- package/lib/archive/migrate-content-items-dedupe-cap-event-id.js +14 -3
- package/lib/archive/migrate-info-main-content-selector.d.ts +6 -1
- package/lib/archive/migrate-info-main-content-selector.js +14 -3
- package/lib/archive/migrate-info-roots.d.ts +6 -1
- package/lib/archive/migrate-info-roots.js +14 -3
- package/lib/archive/migrate-inventory-runs-exclude-skipped.d.ts +6 -1
- package/lib/archive/migrate-inventory-runs-exclude-skipped.js +14 -3
- package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +6 -1
- package/lib/archive/migrate-inventory-runs-invalid-skipped.js +14 -3
- package/lib/archive/migrate-main-contents-columns.d.ts +6 -1
- package/lib/archive/migrate-main-contents-columns.js +14 -3
- package/lib/archive/migrate-page-meta-body-hash.d.ts +6 -1
- package/lib/archive/migrate-page-meta-body-hash.js +14 -3
- package/lib/archive/migrate-page-meta-console-error-count.d.ts +6 -1
- package/lib/archive/migrate-page-meta-console-error-count.js +14 -3
- package/lib/archive/migrate-page-meta-custom-element-count.d.ts +33 -0
- package/lib/archive/migrate-page-meta-custom-element-count.js +51 -0
- package/lib/archive/migrate-page-tags-to-page-technologies.d.ts +39 -0
- package/lib/archive/migrate-page-tags-to-page-technologies.js +79 -0
- package/lib/archive/page.d.ts +37 -13
- package/lib/archive/page.js +45 -17
- package/lib/archive/retarget-legacy-fk-tables.d.ts +27 -14
- package/lib/archive/retarget-legacy-fk-tables.js +61 -15
- package/lib/archive/types.d.ts +18 -0
- package/lib/crawler/assert-puppeteer-shared-with-beholder.d.ts +24 -0
- package/lib/crawler/assert-puppeteer-shared-with-beholder.js +36 -0
- package/lib/crawler/capture-custom-elements.d.ts +33 -0
- package/lib/crawler/capture-custom-elements.js +39 -0
- package/lib/crawler/close-browser-safely.d.ts +3 -3
- package/lib/crawler/close-browser-safely.js +8 -17
- package/lib/crawler/collect-custom-elements.d.ts +33 -0
- package/lib/crawler/collect-custom-elements.js +110 -0
- package/lib/crawler/crawler.js +101 -46
- package/lib/crawler/fetch-destination.js +6 -13
- package/lib/crawler/find-package-dir.d.ts +24 -0
- package/lib/crawler/find-package-dir.js +39 -0
- package/lib/crawler/scan-js-resource-for-license-comment.d.ts +32 -0
- package/lib/crawler/scan-js-resource-for-license-comment.js +140 -0
- package/lib/crawler/scan-js-resources-for-technology-signals.d.ts +58 -0
- package/lib/crawler/scan-js-resources-for-technology-signals.js +196 -0
- package/lib/crawler/types.d.ts +23 -0
- package/lib/crawler-orchestrator.d.ts +53 -9
- package/lib/crawler-orchestrator.js +223 -46
- package/lib/crawler.d.ts +11 -1
- package/lib/crawler.js +9 -0
- package/lib/inventory-setup-phases.d.ts +32 -0
- package/lib/inventory-setup-phases.js +45 -0
- package/lib/resume-setup-phases.d.ts +16 -0
- package/lib/resume-setup-phases.js +24 -0
- package/lib/retry-failed-setup-phases.d.ts +19 -0
- package/lib/retry-failed-setup-phases.js +24 -0
- package/lib/setup-recovery-phase-labels.d.ts +21 -0
- package/lib/setup-recovery-phase-labels.js +24 -0
- package/lib/types.d.ts +145 -0
- package/package.json +12 -9
- package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +0 -12
- package/lib/archive/db-ops/meta/get-tags-of-page.js +0 -28
- package/lib/archive/db-ops/pages/write/insert-tags.d.ts +0 -16
- package/lib/archive/db-ops/pages/write/insert-tags.js +0 -34
- package/lib/archive/meta/summarize-tags.d.ts +0 -16
- package/lib/archive/meta/summarize-tags.js +0 -33
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TemplateClusterReason } from './db-ops/analysis/types.js';
|
|
2
|
-
import type { JsonLdRow, MainContentAudioRow, MainContentButtonRow, MainContentCanvasRow, MainContentHeadingRow, MainContentIframeRow, MainContentImageRow, MainContentTableRow, MainContentVideoRow,
|
|
2
|
+
import type { JsonLdRow, MainContentAudioRow, MainContentButtonRow, MainContentCanvasRow, MainContentCustomElementRow, MainContentHeadingRow, MainContentIframeRow, MainContentImageRow, MainContentTableRow, MainContentVideoRow, PageTechnologyRow, TechnologySignalRow } from './meta/types.js';
|
|
3
3
|
import type { Config, DatabaseOption, DB_Redirect, DB_Resource, DatabaseEvent, InsertDedupeCapEventParams, InsertNetworkOutageParams, InventoryRunMeta, PageFilter, PageSource } from './types.js';
|
|
4
4
|
import type { OutageWindow } from '../is-within-outage-window.js';
|
|
5
5
|
import type { PageData, Resource } from '../utils/types/types.js';
|
|
@@ -115,6 +115,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
115
115
|
scraped: string[];
|
|
116
116
|
pending: string[];
|
|
117
117
|
}>;
|
|
118
|
+
/**
|
|
119
|
+
* Retrieves all `page_main_content_custom_elements` rows for the given
|
|
120
|
+
* page id. Delegates to {@link getCustomElementsOfPageOp}.
|
|
121
|
+
* @param pageId
|
|
122
|
+
*/
|
|
123
|
+
getCustomElementsOfPage(pageId: number): Promise<MainContentCustomElementRow[]>;
|
|
118
124
|
/**
|
|
119
125
|
* Return the subset of `urls` that already exist in the `pages` table.
|
|
120
126
|
* Delegates to {@link getExistingPageUrlsOp}.
|
|
@@ -217,6 +223,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
217
223
|
anchors: import("./types.js").DB_Anchor[];
|
|
218
224
|
referrers: import("./types.js").DB_Referrer[];
|
|
219
225
|
}>;
|
|
226
|
+
/**
|
|
227
|
+
* Retrieves all `page_technologies` rows for the given page id.
|
|
228
|
+
* Delegates to {@link getPageTechnologiesOfPageOp}.
|
|
229
|
+
* @param pageId
|
|
230
|
+
*/
|
|
231
|
+
getPageTechnologiesOfPage(pageId: number): Promise<PageTechnologyRow[]>;
|
|
220
232
|
/**
|
|
221
233
|
* Retrieves redirect sources for the given page IDs in bulk.
|
|
222
234
|
* Delegates to {@link getRedirectsForPagesOp}.
|
|
@@ -260,9 +272,18 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
260
272
|
/**
|
|
261
273
|
* Retrieves a flat list of all resource URLs from the `resources` table.
|
|
262
274
|
* Delegates to {@link getResourceUrlListOp}.
|
|
275
|
+
*
|
|
276
|
+
* `onProgress` is clamped to a high-water mark across retries: a
|
|
277
|
+
* transient failure partway through the keyset scan restarts
|
|
278
|
+
* {@link getResourceUrlListOp} from `id = 0` (its own `lastId` is a local
|
|
279
|
+
* closure variable, reset on every `fn` invocation `retryCall` retries),
|
|
280
|
+
* which would otherwise visibly rewind the reported progress backwards
|
|
281
|
+
* before catching back up.
|
|
282
|
+
* @param onProgress - Forwarded to {@link getResourceUrlListOp} — see
|
|
283
|
+
* that function's docs.
|
|
263
284
|
* @returns An array of resource URL strings.
|
|
264
285
|
*/
|
|
265
|
-
getResourceUrlList(): Promise<string[]>;
|
|
286
|
+
getResourceUrlList(onProgress?: (scannedUpToId: number, maxId: number) => void): Promise<string[]>;
|
|
266
287
|
/**
|
|
267
288
|
* Counts pages that were scraped as crawl targets (full HTML render).
|
|
268
289
|
* Delegates to {@link getScrapedHtmlPageCountOp}.
|
|
@@ -270,11 +291,11 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
270
291
|
*/
|
|
271
292
|
getScrapedHtmlPageCount(): Promise<number>;
|
|
272
293
|
/**
|
|
273
|
-
* Retrieves all `
|
|
274
|
-
*
|
|
294
|
+
* Retrieves all `technology_signals` rows for the given page id.
|
|
295
|
+
* Delegates to {@link getTechnologySignalsOfPageOp}.
|
|
275
296
|
* @param pageId
|
|
276
297
|
*/
|
|
277
|
-
|
|
298
|
+
getTechnologySignalsOfPage(pageId: number): Promise<TechnologySignalRow[]>;
|
|
278
299
|
/**
|
|
279
300
|
* Retrieves all `page_main_content_videos` rows for the given page id.
|
|
280
301
|
* Delegates to {@link getVideosOfPageOp}.
|
|
@@ -425,17 +446,21 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
425
446
|
* Delegates to {@link repromoteExternalPagesOp}.
|
|
426
447
|
* @param scopes - The hostname-indexed scope map after the new roots are merged.
|
|
427
448
|
* @param options - URL parsing options forwarded to the scope matcher.
|
|
449
|
+
* @param onProgress - Forwarded to {@link repromoteExternalPagesOp} — see
|
|
450
|
+
* that function's docs.
|
|
428
451
|
* @returns The URLs of the pages that were promoted.
|
|
429
452
|
*/
|
|
430
|
-
repromoteExternalPages(scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions): Promise<string[]>;
|
|
453
|
+
repromoteExternalPages(scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions, onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
431
454
|
/**
|
|
432
455
|
* Reset previously-attempted pages that ended in a recoverable failure so a
|
|
433
456
|
* follow-up crawl can re-fetch them from scratch. Delegates to
|
|
434
457
|
* {@link resetFailedPagesOp} — see the op for the permanent-failure
|
|
435
458
|
* exclusion rationale.
|
|
459
|
+
* @param onProgress - Forwarded to {@link resetFailedPagesOp} — see that
|
|
460
|
+
* function's docs.
|
|
436
461
|
* @returns The URLs of the pages that were reset to pending.
|
|
437
462
|
*/
|
|
438
|
-
resetFailedPages(): Promise<string[]>;
|
|
463
|
+
resetFailedPages(onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
439
464
|
/**
|
|
440
465
|
* Stores the crawl configuration in the `info` table.
|
|
441
466
|
* Delegates to {@link setConfigOp}.
|
|
@@ -453,8 +478,10 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
453
478
|
/**
|
|
454
479
|
* Assigns natural URL sort order values to all internal pages.
|
|
455
480
|
* Delegates to {@link setUrlOrderOp}.
|
|
481
|
+
* @param onProgress - Forwarded to {@link setUrlOrderOp} — see that
|
|
482
|
+
* function's docs.
|
|
456
483
|
*/
|
|
457
|
-
setUrlOrder(): Promise<void>;
|
|
484
|
+
setUrlOrder(onProgress?: (processed: number, total: number) => void): Promise<void>;
|
|
458
485
|
/**
|
|
459
486
|
* Update the single row in the `info` table with a partial config patch.
|
|
460
487
|
* Delegates to {@link updateConfigOp}.
|
|
@@ -473,9 +500,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
473
500
|
* @param isTarget - Whether this page is a crawl target.
|
|
474
501
|
* @param source - Provenance label written ONLY when the row is freshly
|
|
475
502
|
* inserted. Existing rows keep their original `source`.
|
|
503
|
+
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
504
|
+
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
505
|
+
* computing it from the HTML instead.
|
|
476
506
|
* @returns The database `pageId` of the inserted/updated row.
|
|
477
507
|
*/
|
|
478
|
-
updatePage(page: PageData, writeHtml: boolean, isTarget: boolean, source?: PageSource): Promise<number>;
|
|
508
|
+
updatePage(page: PageData, writeHtml: boolean, isTarget: boolean, source?: PageSource, bodyHash?: Buffer | null): Promise<number>;
|
|
479
509
|
/**
|
|
480
510
|
* Creates and initializes a new Database instance.
|
|
481
511
|
*
|
package/lib/archive/database.js
CHANGED
|
@@ -31,12 +31,14 @@ import { init as initOp } from './db-ops/lifecycle/init.js';
|
|
|
31
31
|
import { getAudiosOfPage as getAudiosOfPageOp } from './db-ops/meta/get-audios-of-page.js';
|
|
32
32
|
import { getButtonsOfPage as getButtonsOfPageOp } from './db-ops/meta/get-buttons-of-page.js';
|
|
33
33
|
import { getCanvasesOfPage as getCanvasesOfPageOp } from './db-ops/meta/get-canvases-of-page.js';
|
|
34
|
+
import { getCustomElementsOfPage as getCustomElementsOfPageOp } from './db-ops/meta/get-custom-elements-of-page.js';
|
|
34
35
|
import { getHeadingsOfPage as getHeadingsOfPageOp } from './db-ops/meta/get-headings-of-page.js';
|
|
35
36
|
import { getIframesOfPage as getIframesOfPageOp } from './db-ops/meta/get-iframes-of-page.js';
|
|
36
37
|
import { getJsonLdOfPage as getJsonLdOfPageOp } from './db-ops/meta/get-jsonld-of-page.js';
|
|
37
38
|
import { getMainContentImagesOfPage as getMainContentImagesOfPageOp } from './db-ops/meta/get-main-content-images-of-page.js';
|
|
38
39
|
import { getMainContentTablesOfPage as getMainContentTablesOfPageOp } from './db-ops/meta/get-main-content-tables-of-page.js';
|
|
39
|
-
import {
|
|
40
|
+
import { getPageTechnologiesOfPage as getPageTechnologiesOfPageOp } from './db-ops/meta/get-page-technologies-of-page.js';
|
|
41
|
+
import { getTechnologySignalsOfPage as getTechnologySignalsOfPageOp } from './db-ops/meta/get-technology-signals-of-page.js';
|
|
40
42
|
import { getVideosOfPage as getVideosOfPageOp } from './db-ops/meta/get-videos-of-page.js';
|
|
41
43
|
import { closeNetworkOutage as closeNetworkOutageOp } from './db-ops/outages/close-network-outage.js';
|
|
42
44
|
import { insertNetworkOutage as insertNetworkOutageOp } from './db-ops/outages/insert-network-outage.js';
|
|
@@ -227,6 +229,14 @@ export class Database extends EventEmitter {
|
|
|
227
229
|
async getCrawlingState() {
|
|
228
230
|
return emitErrorAndRetry(this, 'Database.getCrawlingState', async () => await getCrawlingStateOp(this.#instance), retrySetting);
|
|
229
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Retrieves all `page_main_content_custom_elements` rows for the given
|
|
234
|
+
* page id. Delegates to {@link getCustomElementsOfPageOp}.
|
|
235
|
+
* @param pageId
|
|
236
|
+
*/
|
|
237
|
+
async getCustomElementsOfPage(pageId) {
|
|
238
|
+
return emitErrorAndRetry(this, 'Database.getCustomElementsOfPage', async () => await getCustomElementsOfPageOp(this.#instance, pageId), retrySetting);
|
|
239
|
+
}
|
|
230
240
|
/**
|
|
231
241
|
* Return the subset of `urls` that already exist in the `pages` table.
|
|
232
242
|
* Delegates to {@link getExistingPageUrlsOp}.
|
|
@@ -352,6 +362,14 @@ export class Database extends EventEmitter {
|
|
|
352
362
|
async getPagesWithRels(offset, limit) {
|
|
353
363
|
return emitErrorAndRetry(this, 'Database.getPagesWithRels', async () => await getPagesWithRelsOp(this.#instance, offset, limit), retrySetting);
|
|
354
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* Retrieves all `page_technologies` rows for the given page id.
|
|
367
|
+
* Delegates to {@link getPageTechnologiesOfPageOp}.
|
|
368
|
+
* @param pageId
|
|
369
|
+
*/
|
|
370
|
+
async getPageTechnologiesOfPage(pageId) {
|
|
371
|
+
return emitErrorAndRetry(this, 'Database.getPageTechnologiesOfPage', async () => await getPageTechnologiesOfPageOp(this.#instance, pageId), retrySetting);
|
|
372
|
+
}
|
|
355
373
|
/**
|
|
356
374
|
* Retrieves redirect sources for the given page IDs in bulk.
|
|
357
375
|
* Delegates to {@link getRedirectsForPagesOp}.
|
|
@@ -408,10 +426,26 @@ export class Database extends EventEmitter {
|
|
|
408
426
|
/**
|
|
409
427
|
* Retrieves a flat list of all resource URLs from the `resources` table.
|
|
410
428
|
* Delegates to {@link getResourceUrlListOp}.
|
|
429
|
+
*
|
|
430
|
+
* `onProgress` is clamped to a high-water mark across retries: a
|
|
431
|
+
* transient failure partway through the keyset scan restarts
|
|
432
|
+
* {@link getResourceUrlListOp} from `id = 0` (its own `lastId` is a local
|
|
433
|
+
* closure variable, reset on every `fn` invocation `retryCall` retries),
|
|
434
|
+
* which would otherwise visibly rewind the reported progress backwards
|
|
435
|
+
* before catching back up.
|
|
436
|
+
* @param onProgress - Forwarded to {@link getResourceUrlListOp} — see
|
|
437
|
+
* that function's docs.
|
|
411
438
|
* @returns An array of resource URL strings.
|
|
412
439
|
*/
|
|
413
|
-
async getResourceUrlList() {
|
|
414
|
-
|
|
440
|
+
async getResourceUrlList(onProgress) {
|
|
441
|
+
let highWaterMark = 0;
|
|
442
|
+
const monotonicProgress = onProgress
|
|
443
|
+
? (scannedUpToId, maxId) => {
|
|
444
|
+
highWaterMark = Math.max(highWaterMark, scannedUpToId);
|
|
445
|
+
onProgress(highWaterMark, maxId);
|
|
446
|
+
}
|
|
447
|
+
: undefined;
|
|
448
|
+
return emitErrorAndRetry(this, 'Database.getResourceUrlList', async () => await getResourceUrlListOp(this.#instance, monotonicProgress), retrySetting);
|
|
415
449
|
}
|
|
416
450
|
/**
|
|
417
451
|
* Counts pages that were scraped as crawl targets (full HTML render).
|
|
@@ -422,12 +456,12 @@ export class Database extends EventEmitter {
|
|
|
422
456
|
return emitErrorAndRetry(this, 'Database.getScrapedHtmlPageCount', async () => await getScrapedHtmlPageCountOp(this.#instance), retrySetting);
|
|
423
457
|
}
|
|
424
458
|
/**
|
|
425
|
-
* Retrieves all `
|
|
426
|
-
*
|
|
459
|
+
* Retrieves all `technology_signals` rows for the given page id.
|
|
460
|
+
* Delegates to {@link getTechnologySignalsOfPageOp}.
|
|
427
461
|
* @param pageId
|
|
428
462
|
*/
|
|
429
|
-
async
|
|
430
|
-
return emitErrorAndRetry(this, 'Database.
|
|
463
|
+
async getTechnologySignalsOfPage(pageId) {
|
|
464
|
+
return emitErrorAndRetry(this, 'Database.getTechnologySignalsOfPage', async () => await getTechnologySignalsOfPageOp(this.#instance, pageId), retrySetting);
|
|
431
465
|
}
|
|
432
466
|
/**
|
|
433
467
|
* Retrieves all `page_main_content_videos` rows for the given page id.
|
|
@@ -609,20 +643,24 @@ export class Database extends EventEmitter {
|
|
|
609
643
|
* Delegates to {@link repromoteExternalPagesOp}.
|
|
610
644
|
* @param scopes - The hostname-indexed scope map after the new roots are merged.
|
|
611
645
|
* @param options - URL parsing options forwarded to the scope matcher.
|
|
646
|
+
* @param onProgress - Forwarded to {@link repromoteExternalPagesOp} — see
|
|
647
|
+
* that function's docs.
|
|
612
648
|
* @returns The URLs of the pages that were promoted.
|
|
613
649
|
*/
|
|
614
|
-
async repromoteExternalPages(scopes, options) {
|
|
615
|
-
return emitErrorAndRetry(this, 'Database.repromoteExternalPages', async () => await repromoteExternalPagesOp(this.#instance, scopes, options), retrySetting);
|
|
650
|
+
async repromoteExternalPages(scopes, options, onProgress) {
|
|
651
|
+
return emitErrorAndRetry(this, 'Database.repromoteExternalPages', async () => await repromoteExternalPagesOp(this.#instance, scopes, options, onProgress), retrySetting);
|
|
616
652
|
}
|
|
617
653
|
/**
|
|
618
654
|
* Reset previously-attempted pages that ended in a recoverable failure so a
|
|
619
655
|
* follow-up crawl can re-fetch them from scratch. Delegates to
|
|
620
656
|
* {@link resetFailedPagesOp} — see the op for the permanent-failure
|
|
621
657
|
* exclusion rationale.
|
|
658
|
+
* @param onProgress - Forwarded to {@link resetFailedPagesOp} — see that
|
|
659
|
+
* function's docs.
|
|
622
660
|
* @returns The URLs of the pages that were reset to pending.
|
|
623
661
|
*/
|
|
624
|
-
async resetFailedPages() {
|
|
625
|
-
return emitErrorAndRetry(this, 'Database.resetFailedPages', async () => await resetFailedPagesOp(this.#instance), retrySetting);
|
|
662
|
+
async resetFailedPages(onProgress) {
|
|
663
|
+
return emitErrorAndRetry(this, 'Database.resetFailedPages', async () => await resetFailedPagesOp(this.#instance, onProgress), retrySetting);
|
|
626
664
|
}
|
|
627
665
|
/**
|
|
628
666
|
* Stores the crawl configuration in the `info` table.
|
|
@@ -645,9 +683,11 @@ export class Database extends EventEmitter {
|
|
|
645
683
|
/**
|
|
646
684
|
* Assigns natural URL sort order values to all internal pages.
|
|
647
685
|
* Delegates to {@link setUrlOrderOp}.
|
|
686
|
+
* @param onProgress - Forwarded to {@link setUrlOrderOp} — see that
|
|
687
|
+
* function's docs.
|
|
648
688
|
*/
|
|
649
|
-
async setUrlOrder() {
|
|
650
|
-
await setUrlOrderOp(this.#instance);
|
|
689
|
+
async setUrlOrder(onProgress) {
|
|
690
|
+
await setUrlOrderOp(this.#instance, onProgress);
|
|
651
691
|
}
|
|
652
692
|
/**
|
|
653
693
|
* Update the single row in the `info` table with a partial config patch.
|
|
@@ -669,19 +709,24 @@ export class Database extends EventEmitter {
|
|
|
669
709
|
* @param isTarget - Whether this page is a crawl target.
|
|
670
710
|
* @param source - Provenance label written ONLY when the row is freshly
|
|
671
711
|
* inserted. Existing rows keep their original `source`.
|
|
712
|
+
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
713
|
+
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
714
|
+
* computing it from the HTML instead.
|
|
672
715
|
* @returns The database `pageId` of the inserted/updated row.
|
|
673
716
|
*/
|
|
674
|
-
async updatePage(page, writeHtml, isTarget, source) {
|
|
675
|
-
return emitErrorAndRetry(this, 'Database.updatePage', async () => await updatePageOp(this.#instance, this.#writeRefCaches, page, writeHtml, isTarget, source), retrySetting);
|
|
717
|
+
async updatePage(page, writeHtml, isTarget, source, bodyHash) {
|
|
718
|
+
return emitErrorAndRetry(this, 'Database.updatePage', async () => await updatePageOp(this.#instance, this.#writeRefCaches, page, writeHtml, isTarget, source, bodyHash), retrySetting);
|
|
676
719
|
}
|
|
677
720
|
/**
|
|
678
721
|
* Initializes the database schema if tables do not exist, then runs
|
|
679
722
|
* lightweight migrations; in read-only mode both are skipped.
|
|
680
723
|
* Delegates to {@link initOp}.
|
|
681
724
|
* @param readOnly - When true, skip schema init + migrations.
|
|
725
|
+
* @param onLog - Forwarded to {@link initOp} — see
|
|
726
|
+
* {@link DatabaseOption.onLog}'s docs.
|
|
682
727
|
*/
|
|
683
|
-
async #init(readOnly) {
|
|
684
|
-
await initOp(this.#instance, readOnly);
|
|
728
|
+
async #init(readOnly, onLog) {
|
|
729
|
+
await initOp(this.#instance, readOnly, onLog);
|
|
685
730
|
}
|
|
686
731
|
/**
|
|
687
732
|
* Creates and initializes a new Database instance.
|
|
@@ -714,7 +759,7 @@ export class Database extends EventEmitter {
|
|
|
714
759
|
mkdir(options.filename);
|
|
715
760
|
}
|
|
716
761
|
const db = new Database(options);
|
|
717
|
-
await db.#init(options.readOnly ?? false);
|
|
762
|
+
await db.#init(options.readOnly ?? false, options.onLog);
|
|
718
763
|
return db;
|
|
719
764
|
}
|
|
720
765
|
}
|
|
@@ -19,7 +19,15 @@ import type { Knex } from 'knex';
|
|
|
19
19
|
* additions to an existing 0.13 table are therefore the one case that still
|
|
20
20
|
* needs an explicit `hasColumn`-guarded `ALTER TABLE` here (`migrateInfoRoots`,
|
|
21
21
|
* `migrateMainContentsColumns`, `migratePageMetaBodyHash`,
|
|
22
|
-
* `migratePageMetaConsoleErrorCount`, `
|
|
22
|
+
* `migratePageMetaConsoleErrorCount`, `migratePageMetaCustomElementCount`,
|
|
23
|
+
* `migrateContentItemsAliasOfId`,
|
|
24
|
+
*
|
|
25
|
+
* `migratePageTagsToPageTechnologies` is the one exception to "column adds
|
|
26
|
+
* only": it converts `page_tags` (removed) rows into `technology_signals`/
|
|
27
|
+
* `page_technologies` and DROPS the old table — a one-time table-level ETL,
|
|
28
|
+
* not a column add, but it belongs in this same boot phase for the same
|
|
29
|
+
* reason (self-healing an old archive's schema before any reader runs).
|
|
30
|
+
*
|
|
23
31
|
* `migrateContentItemsDedupeCapEventId`, `migrateInventoryRunsInvalidSkipped`,
|
|
24
32
|
* `migrateInventoryRunsExcludeSkipped`) rather than a DDL-string change alone.
|
|
25
33
|
*
|
|
@@ -35,5 +43,9 @@ import type { Knex } from 'knex';
|
|
|
35
43
|
* crawl without rewriting the user's tmpDir.
|
|
36
44
|
* @param knex - Knex query builder connected to the archive DB.
|
|
37
45
|
* @param readOnly - When true, skip schema init + migrations.
|
|
46
|
+
* @param onLog - Forwarded to every `migrate*` call below (issue #294) —
|
|
47
|
+
* see {@link import('../../migrate-page-meta-body-hash.js').migratePageMetaBodyHash}'s
|
|
48
|
+
* docs for why a bare `console.error` at this point is unsafe (it can
|
|
49
|
+
* fire while a caller's `Lanes`/`TaskList` display is mid-redraw).
|
|
38
50
|
*/
|
|
39
|
-
export declare function init(knex: Knex, readOnly: boolean): Promise<void>;
|
|
51
|
+
export declare function init(knex: Knex, readOnly: boolean, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -9,6 +9,8 @@ import { migrateInventoryRunsInvalidSkipped } from '../../migrate-inventory-runs
|
|
|
9
9
|
import { migrateMainContentsColumns } from '../../migrate-main-contents-columns.js';
|
|
10
10
|
import { migratePageMetaBodyHash } from '../../migrate-page-meta-body-hash.js';
|
|
11
11
|
import { migratePageMetaConsoleErrorCount } from '../../migrate-page-meta-console-error-count.js';
|
|
12
|
+
import { migratePageMetaCustomElementCount } from '../../migrate-page-meta-custom-element-count.js';
|
|
13
|
+
import { migratePageTagsToPageTechnologies } from '../../migrate-page-tags-to-page-technologies.js';
|
|
12
14
|
import { closeStaleOpenNetworkOutages } from '../outages/close-stale-open-network-outages.js';
|
|
13
15
|
/**
|
|
14
16
|
* Initializes the database schema if tables do not exist, then runs the
|
|
@@ -30,7 +32,15 @@ import { closeStaleOpenNetworkOutages } from '../outages/close-stale-open-networ
|
|
|
30
32
|
* additions to an existing 0.13 table are therefore the one case that still
|
|
31
33
|
* needs an explicit `hasColumn`-guarded `ALTER TABLE` here (`migrateInfoRoots`,
|
|
32
34
|
* `migrateMainContentsColumns`, `migratePageMetaBodyHash`,
|
|
33
|
-
* `migratePageMetaConsoleErrorCount`, `
|
|
35
|
+
* `migratePageMetaConsoleErrorCount`, `migratePageMetaCustomElementCount`,
|
|
36
|
+
* `migrateContentItemsAliasOfId`,
|
|
37
|
+
*
|
|
38
|
+
* `migratePageTagsToPageTechnologies` is the one exception to "column adds
|
|
39
|
+
* only": it converts `page_tags` (removed) rows into `technology_signals`/
|
|
40
|
+
* `page_technologies` and DROPS the old table — a one-time table-level ETL,
|
|
41
|
+
* not a column add, but it belongs in this same boot phase for the same
|
|
42
|
+
* reason (self-healing an old archive's schema before any reader runs).
|
|
43
|
+
*
|
|
34
44
|
* `migrateContentItemsDedupeCapEventId`, `migrateInventoryRunsInvalidSkipped`,
|
|
35
45
|
* `migrateInventoryRunsExcludeSkipped`) rather than a DDL-string change alone.
|
|
36
46
|
*
|
|
@@ -46,8 +56,12 @@ import { closeStaleOpenNetworkOutages } from '../outages/close-stale-open-networ
|
|
|
46
56
|
* crawl without rewriting the user's tmpDir.
|
|
47
57
|
* @param knex - Knex query builder connected to the archive DB.
|
|
48
58
|
* @param readOnly - When true, skip schema init + migrations.
|
|
59
|
+
* @param onLog - Forwarded to every `migrate*` call below (issue #294) —
|
|
60
|
+
* see {@link import('../../migrate-page-meta-body-hash.js').migratePageMetaBodyHash}'s
|
|
61
|
+
* docs for why a bare `console.error` at this point is unsafe (it can
|
|
62
|
+
* fire while a caller's `Lanes`/`TaskList` display is mid-redraw).
|
|
49
63
|
*/
|
|
50
|
-
export async function init(knex, readOnly) {
|
|
64
|
+
export async function init(knex, readOnly, onLog) {
|
|
51
65
|
// Connection-level PRAGMAs (foreign_keys, mmap_size, …) must be
|
|
52
66
|
// reapplied on every connect — they are not persisted across opens.
|
|
53
67
|
// They are safe in read-only mode because they don't write to the
|
|
@@ -64,18 +78,22 @@ export async function init(knex, readOnly) {
|
|
|
64
78
|
return;
|
|
65
79
|
}
|
|
66
80
|
await initSchema(knex);
|
|
67
|
-
await migrateInfoRoots(knex);
|
|
68
|
-
await migrateInfoMainContentSelector(knex);
|
|
69
|
-
await migrateMainContentsColumns(knex);
|
|
70
|
-
await migratePageMetaBodyHash(knex);
|
|
71
|
-
await migratePageMetaConsoleErrorCount(knex);
|
|
72
|
-
await
|
|
81
|
+
await migrateInfoRoots(knex, onLog);
|
|
82
|
+
await migrateInfoMainContentSelector(knex, onLog);
|
|
83
|
+
await migrateMainContentsColumns(knex, onLog);
|
|
84
|
+
await migratePageMetaBodyHash(knex, onLog);
|
|
85
|
+
await migratePageMetaConsoleErrorCount(knex, onLog);
|
|
86
|
+
await migratePageMetaCustomElementCount(knex, onLog);
|
|
87
|
+
// Table-level migration (converts + drops page_tags), not a column
|
|
88
|
+
// add — see its own JSDoc for why it still belongs in this boot phase.
|
|
89
|
+
await migratePageTagsToPageTechnologies(knex, onLog);
|
|
90
|
+
await migrateContentItemsAliasOfId(knex, onLog);
|
|
73
91
|
// Runs after `initSchema` above, which already created
|
|
74
92
|
// `dedupe_cap_events` (an adjunct table) unconditionally — so the new
|
|
75
93
|
// column's `REFERENCES dedupe_cap_events(id)` target always exists by
|
|
76
94
|
// this point, for both fresh and legacy archives.
|
|
77
|
-
await migrateContentItemsDedupeCapEventId(knex);
|
|
78
|
-
await migrateInventoryRunsInvalidSkipped(knex);
|
|
79
|
-
await migrateInventoryRunsExcludeSkipped(knex);
|
|
95
|
+
await migrateContentItemsDedupeCapEventId(knex, onLog);
|
|
96
|
+
await migrateInventoryRunsInvalidSkipped(knex, onLog);
|
|
97
|
+
await migrateInventoryRunsExcludeSkipped(knex, onLog);
|
|
80
98
|
await closeStaleOpenNetworkOutages(knex);
|
|
81
99
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MainContentCustomElementRow } from '../../meta/types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves all `page_main_content_custom_elements` rows for the given page
|
|
5
|
+
* id, in DOM traversal order.
|
|
6
|
+
*
|
|
7
|
+
* Read-side counterpart to `insertCustomElements`.
|
|
8
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
9
|
+
* @param pageId
|
|
10
|
+
*/
|
|
11
|
+
export declare function getCustomElementsOfPage(knex: Knex, pageId: number): Promise<MainContentCustomElementRow[]>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves all `page_main_content_custom_elements` rows for the given page
|
|
3
|
+
* id, in DOM traversal order.
|
|
4
|
+
*
|
|
5
|
+
* Read-side counterpart to `insertCustomElements`.
|
|
6
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
7
|
+
* @param pageId
|
|
8
|
+
*/
|
|
9
|
+
export async function getCustomElementsOfPage(knex, pageId) {
|
|
10
|
+
return knex
|
|
11
|
+
.select('id', 'pageId', 'order', 'nodeName', 'elementId', 'classList')
|
|
12
|
+
.from('page_main_content_custom_elements')
|
|
13
|
+
.where('pageId', pageId)
|
|
14
|
+
.orderBy('order', 'asc');
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PageTechnologyRow } from '../../meta/types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves all `page_technologies` rows for the given page id — the
|
|
5
|
+
* confidence-combined roll-up (one row per detected technology).
|
|
6
|
+
*
|
|
7
|
+
* Read-side counterpart to `insertTechnologies`.
|
|
8
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
9
|
+
* @param pageId
|
|
10
|
+
*/
|
|
11
|
+
export declare function getPageTechnologiesOfPage(knex: Knex, pageId: number): Promise<PageTechnologyRow[]>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves all `page_technologies` rows for the given page id — the
|
|
3
|
+
* confidence-combined roll-up (one row per detected technology).
|
|
4
|
+
*
|
|
5
|
+
* Read-side counterpart to `insertTechnologies`.
|
|
6
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
7
|
+
* @param pageId
|
|
8
|
+
*/
|
|
9
|
+
export async function getPageTechnologiesOfPage(knex, pageId) {
|
|
10
|
+
return knex
|
|
11
|
+
.select('id', 'pageId', 'technology', 'category', 'version', 'confidence', 'signalCount')
|
|
12
|
+
.from('page_technologies')
|
|
13
|
+
.where('pageId', pageId)
|
|
14
|
+
.orderBy('confidence', 'desc');
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TechnologySignalRow } from '../../meta/types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves all `technology_signals` rows for the given page id — the raw,
|
|
5
|
+
* per-signal evidence behind its `page_technologies` roll-up.
|
|
6
|
+
*
|
|
7
|
+
* Read-side counterpart to `insertTechnologies`.
|
|
8
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
9
|
+
* @param pageId
|
|
10
|
+
*/
|
|
11
|
+
export declare function getTechnologySignalsOfPage(knex: Knex, pageId: number): Promise<TechnologySignalRow[]>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves all `technology_signals` rows for the given page id — the raw,
|
|
3
|
+
* per-signal evidence behind its `page_technologies` roll-up.
|
|
4
|
+
*
|
|
5
|
+
* Read-side counterpart to `insertTechnologies`.
|
|
6
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
7
|
+
* @param pageId
|
|
8
|
+
*/
|
|
9
|
+
export async function getTechnologySignalsOfPage(knex, pageId) {
|
|
10
|
+
return knex
|
|
11
|
+
.select('id', 'pageId', 'technology', 'signalType', 'evidence', 'weight')
|
|
12
|
+
.from('technology_signals')
|
|
13
|
+
.where('pageId', pageId)
|
|
14
|
+
.orderBy('id', 'asc');
|
|
15
|
+
}
|
|
@@ -4,5 +4,10 @@ import type { Knex } from 'knex';
|
|
|
4
4
|
* all internal pages. Pages are sorted using {@link pathComparator} and
|
|
5
5
|
* assigned sequential order numbers.
|
|
6
6
|
* @param knex - Knex query builder connected to the archive DB.
|
|
7
|
+
* @param onProgress - Called after each chunk's UPDATE completes, with
|
|
8
|
+
* pages ordered so far and the total (issue #294: a large archive's page
|
|
9
|
+
* count can make this a multi-second-to-minutes pass — a single SELECT
|
|
10
|
+
* plus a JS sort followed by chunked UPDATEs — with no other signal it
|
|
11
|
+
* hasn't hung). Omit for no reporting (the default; e.g. tests).
|
|
7
12
|
*/
|
|
8
|
-
export declare function setUrlOrder(knex: Knex): Promise<void>;
|
|
13
|
+
export declare function setUrlOrder(knex: Knex, onProgress?: (processed: number, total: number) => void): Promise<void>;
|
|
@@ -5,8 +5,13 @@ import { dbLog } from '../../../debug.js';
|
|
|
5
5
|
* all internal pages. Pages are sorted using {@link pathComparator} and
|
|
6
6
|
* assigned sequential order numbers.
|
|
7
7
|
* @param knex - Knex query builder connected to the archive DB.
|
|
8
|
+
* @param onProgress - Called after each chunk's UPDATE completes, with
|
|
9
|
+
* pages ordered so far and the total (issue #294: a large archive's page
|
|
10
|
+
* count can make this a multi-second-to-minutes pass — a single SELECT
|
|
11
|
+
* plus a JS sort followed by chunked UPDATEs — with no other signal it
|
|
12
|
+
* hasn't hung). Omit for no reporting (the default; e.g. tests).
|
|
8
13
|
*/
|
|
9
|
-
export async function setUrlOrder(knex) {
|
|
14
|
+
export async function setUrlOrder(knex, onProgress) {
|
|
10
15
|
dbLog('Set URL Order');
|
|
11
16
|
const res = (await knex
|
|
12
17
|
.select('ci.id', 'ur.url')
|
|
@@ -28,5 +33,6 @@ export async function setUrlOrder(knex) {
|
|
|
28
33
|
.join(' ');
|
|
29
34
|
const placeholders = ids.map(() => '?').join(',');
|
|
30
35
|
await knex.raw(`UPDATE content_items SET crawl_order = CASE id ${cases} END WHERE id IN (${placeholders})`, [...bindings, ...ids]);
|
|
36
|
+
onProgress?.(Math.min(i + BATCH_SIZE, sorted.length), sorted.length);
|
|
31
37
|
}
|
|
32
38
|
}
|
|
@@ -36,5 +36,5 @@ export function buildPageQuery(knex) {
|
|
|
36
36
|
.leftJoin('url_refs as og_image_ur', 'og_image_ur.id', 'pm.og_image_url_id')
|
|
37
37
|
.leftJoin('url_refs as twitter_image_ur', 'twitter_image_ur.id', 'pm.twitter_image_url_id')
|
|
38
38
|
.leftJoin('json_refs as extras_ref', 'extras_ref.id', 'pm.meta_extras_json_id')
|
|
39
|
-
.select('ci.id as id', 'ur.url as url', 'ci.redirect_dest_id as redirectDestId', 'ci.scraped as scraped', 'ci.is_target as isTarget', 'ci.is_external as isExternal', 'ci.status as status', 'ci.status_text as statusText', 'ctr.raw as contentType', 'ci.content_length as contentLength', 'ci.header_set_id as headerSetId', 'pm.lang as lang', 'pm.dir as dir', 'pm.charset as charset', 'pm.base_href as baseHref', 'pm.viewport_raw as viewport_raw', 'pm.theme_color as themeColor', 'pm.application_name as applicationName', 'pm.author as author', 'pm.generator as generator', 'pm.publisher as publisher', 'title_ref.text as title', 'description_ref.text as description', 'keywords_ref.text as keywords', 'robots_raw_ref.text as robots_raw', 'pm.robots_noindex as robots_noindex', 'pm.robots_nofollow as robots_nofollow', 'pm.robots_noarchive as robots_noarchive', 'pm.robots_noimageindex as robots_noimageindex', 'pm.googlebot as googlebot', 'canonical_ur.url as canonical', 'amphtml_ur.url as amphtml', 'manifest_ur.url as manifest', 'icon_ur.url as icon_href', 'apple_ur.url as appleTouchIcon_href', 'pm.og_type as og_type', 'og_title_ref.text as og_title', 'og_url_ur.url as og_url', 'pm.og_site_name as og_site_name', 'og_description_ref.text as og_description', 'og_image_ur.url as og_image', 'pm.og_image_alt as og_image_alt', 'pm.og_image_width as og_image_width', 'pm.og_image_height as og_image_height', 'pm.og_locale as og_locale', 'pm.og_article_published_time as og_article_published_time', 'pm.og_article_modified_time as og_article_modified_time', 'pm.twitter_card as twitter_card', 'pm.twitter_site as twitter_site', 'pm.twitter_creator as twitter_creator', 'twitter_title_ref.text as twitter_title', 'twitter_description_ref.text as twitter_description', 'twitter_image_ur.url as twitter_image', 'pm.fb_app_id as fb_app_id', 'pm.verification_google as verification_google', 'pm.format_detection_telephone as formatDetection_telephone', 'ci.first_crawled_at as firstCrawledAt', 'ci.last_crawled_at as lastCrawledAt', 'pm.tag_count as tag_count', 'pm.jsonld_count as jsonld_count', 'pm.tags_providers_csv as tags_providers_csv', 'pm.main_content_node_name as main_content_node_name', 'pm.main_content_id as main_content_id', 'pm.main_content_role as main_content_role', 'pm.main_content_selector as main_content_selector', 'pm.main_content_class_list as main_content_class_list', 'pm.main_content_word_count as main_content_word_count', 'pm.main_content_body_word_count as main_content_body_word_count', 'pm.main_content_heading_count as main_content_heading_count', 'pm.main_content_image_count as main_content_image_count', 'pm.main_content_table_count as main_content_table_count', 'pm.main_content_button_count as main_content_button_count', 'pm.main_content_iframe_count as main_content_iframe_count', 'pm.main_content_video_count as main_content_video_count', 'pm.main_content_audio_count as main_content_audio_count', 'pm.main_content_canvas_count as main_content_canvas_count', 'pm.scroll_height_desktop as scroll_height_desktop', 'pm.scroll_height_mobile as scroll_height_mobile', 'extras_ref.json_text as extras_body', 'extras_ref.codec as extras_codec', 'ci.is_skipped as isSkipped', 'ci.skip_reason as skipReason', 'ci.crawl_order as order', 'ci.source as source');
|
|
39
|
+
.select('ci.id as id', 'ur.url as url', 'ci.redirect_dest_id as redirectDestId', 'ci.scraped as scraped', 'ci.is_target as isTarget', 'ci.is_external as isExternal', 'ci.status as status', 'ci.status_text as statusText', 'ctr.raw as contentType', 'ci.content_length as contentLength', 'ci.header_set_id as headerSetId', 'pm.lang as lang', 'pm.dir as dir', 'pm.charset as charset', 'pm.base_href as baseHref', 'pm.viewport_raw as viewport_raw', 'pm.theme_color as themeColor', 'pm.application_name as applicationName', 'pm.author as author', 'pm.generator as generator', 'pm.publisher as publisher', 'title_ref.text as title', 'description_ref.text as description', 'keywords_ref.text as keywords', 'robots_raw_ref.text as robots_raw', 'pm.robots_noindex as robots_noindex', 'pm.robots_nofollow as robots_nofollow', 'pm.robots_noarchive as robots_noarchive', 'pm.robots_noimageindex as robots_noimageindex', 'pm.googlebot as googlebot', 'canonical_ur.url as canonical', 'amphtml_ur.url as amphtml', 'manifest_ur.url as manifest', 'icon_ur.url as icon_href', 'apple_ur.url as appleTouchIcon_href', 'pm.og_type as og_type', 'og_title_ref.text as og_title', 'og_url_ur.url as og_url', 'pm.og_site_name as og_site_name', 'og_description_ref.text as og_description', 'og_image_ur.url as og_image', 'pm.og_image_alt as og_image_alt', 'pm.og_image_width as og_image_width', 'pm.og_image_height as og_image_height', 'pm.og_locale as og_locale', 'pm.og_article_published_time as og_article_published_time', 'pm.og_article_modified_time as og_article_modified_time', 'pm.twitter_card as twitter_card', 'pm.twitter_site as twitter_site', 'pm.twitter_creator as twitter_creator', 'twitter_title_ref.text as twitter_title', 'twitter_description_ref.text as twitter_description', 'twitter_image_ur.url as twitter_image', 'pm.fb_app_id as fb_app_id', 'pm.verification_google as verification_google', 'pm.format_detection_telephone as formatDetection_telephone', 'ci.first_crawled_at as firstCrawledAt', 'ci.last_crawled_at as lastCrawledAt', 'pm.tag_count as tag_count', 'pm.jsonld_count as jsonld_count', 'pm.tags_providers_csv as tags_providers_csv', 'pm.main_content_node_name as main_content_node_name', 'pm.main_content_id as main_content_id', 'pm.main_content_role as main_content_role', 'pm.main_content_selector as main_content_selector', 'pm.main_content_class_list as main_content_class_list', 'pm.main_content_word_count as main_content_word_count', 'pm.main_content_body_word_count as main_content_body_word_count', 'pm.main_content_heading_count as main_content_heading_count', 'pm.main_content_image_count as main_content_image_count', 'pm.main_content_table_count as main_content_table_count', 'pm.main_content_button_count as main_content_button_count', 'pm.main_content_iframe_count as main_content_iframe_count', 'pm.main_content_video_count as main_content_video_count', 'pm.main_content_audio_count as main_content_audio_count', 'pm.main_content_canvas_count as main_content_canvas_count', 'pm.main_content_custom_element_count as main_content_custom_element_count', 'pm.scroll_height_desktop as scroll_height_desktop', 'pm.scroll_height_mobile as scroll_height_mobile', 'extras_ref.json_text as extras_body', 'extras_ref.codec as extras_codec', 'ci.is_skipped as isSkipped', 'ci.skip_reason as skipReason', 'ci.crawl_order as order', 'ci.source as source');
|
|
40
40
|
}
|
|
@@ -19,6 +19,11 @@ import type { Knex } from 'knex';
|
|
|
19
19
|
* @param knex - Knex query builder connected to the archive DB.
|
|
20
20
|
* @param scopes - The hostname-indexed scope map after the new roots are merged.
|
|
21
21
|
* @param options - URL parsing options forwarded to {@link findScopeEntry}.
|
|
22
|
+
* @param onProgress - Called after each chunk's DELETE/UPDATE statements
|
|
23
|
+
* complete, with the pages processed so far and the total to promote
|
|
24
|
+
* (issue #294: a large `--append` can promote thousands of pages across
|
|
25
|
+
* 14 tables, running for seconds to minutes with no other signal it
|
|
26
|
+
* hasn't hung). Omit for no reporting (the default; e.g. tests).
|
|
22
27
|
* @returns The URLs of the pages that were promoted.
|
|
23
28
|
*/
|
|
24
|
-
export declare function repromoteExternalPages(knex: Knex, scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions): Promise<string[]>;
|
|
29
|
+
export declare function repromoteExternalPages(knex: Knex, scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions, onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
@@ -20,9 +20,14 @@ import { dbLog } from '../../../debug.js';
|
|
|
20
20
|
* @param knex - Knex query builder connected to the archive DB.
|
|
21
21
|
* @param scopes - The hostname-indexed scope map after the new roots are merged.
|
|
22
22
|
* @param options - URL parsing options forwarded to {@link findScopeEntry}.
|
|
23
|
+
* @param onProgress - Called after each chunk's DELETE/UPDATE statements
|
|
24
|
+
* complete, with the pages processed so far and the total to promote
|
|
25
|
+
* (issue #294: a large `--append` can promote thousands of pages across
|
|
26
|
+
* 14 tables, running for seconds to minutes with no other signal it
|
|
27
|
+
* hasn't hung). Omit for no reporting (the default; e.g. tests).
|
|
23
28
|
* @returns The URLs of the pages that were promoted.
|
|
24
29
|
*/
|
|
25
|
-
export async function repromoteExternalPages(knex, scopes, options) {
|
|
30
|
+
export async function repromoteExternalPages(knex, scopes, options, onProgress) {
|
|
26
31
|
if (scopes.size === 0) {
|
|
27
32
|
return [];
|
|
28
33
|
}
|
|
@@ -73,7 +78,8 @@ export async function repromoteExternalPages(knex, scopes, options) {
|
|
|
73
78
|
// Deleting the `page_meta` row (rather than nulling every column)
|
|
74
79
|
// clears title / description / og:* / twitter:* / meta_extras /
|
|
75
80
|
// main_content_* in one statement; a re-scrape re-inserts it via
|
|
76
|
-
// `ON CONFLICT(page_id) DO UPDATE`. `
|
|
81
|
+
// `ON CONFLICT(page_id) DO UPDATE`. `technology_signals` /
|
|
82
|
+
// `page_technologies` / `page_jsonld` /
|
|
77
83
|
// `page_main_content_*` are cleared explicitly even though all of
|
|
78
84
|
// them also carry ON DELETE CASCADE — we keep the existing pattern
|
|
79
85
|
// of explicit chunked DELETEs rather than relying on CASCADE
|
|
@@ -86,7 +92,8 @@ export async function repromoteExternalPages(knex, scopes, options) {
|
|
|
86
92
|
await knex('image_items').whereIn('page_id', chunk).delete();
|
|
87
93
|
await knex('resource_ref_edges').whereIn('page_id', chunk).delete();
|
|
88
94
|
await knex('page_html_ref').whereIn('page_id', chunk).delete();
|
|
89
|
-
await knex('
|
|
95
|
+
await knex('technology_signals').whereIn('pageId', chunk).delete();
|
|
96
|
+
await knex('page_technologies').whereIn('pageId', chunk).delete();
|
|
90
97
|
await knex('page_jsonld').whereIn('pageId', chunk).delete();
|
|
91
98
|
await knex('page_main_content_headings').whereIn('pageId', chunk).delete();
|
|
92
99
|
await knex('page_main_content_images').whereIn('pageId', chunk).delete();
|
|
@@ -96,6 +103,8 @@ export async function repromoteExternalPages(knex, scopes, options) {
|
|
|
96
103
|
await knex('page_main_content_videos').whereIn('pageId', chunk).delete();
|
|
97
104
|
await knex('page_main_content_audios').whereIn('pageId', chunk).delete();
|
|
98
105
|
await knex('page_main_content_canvases').whereIn('pageId', chunk).delete();
|
|
106
|
+
await knex('page_main_content_custom_elements').whereIn('pageId', chunk).delete();
|
|
107
|
+
onProgress?.(Math.min(i + chunkSize, promotedIds.length), promotedIds.length);
|
|
99
108
|
}
|
|
100
109
|
dbLog('Repromoted %d external pages back to pending', promotedUrls.length);
|
|
101
110
|
return promotedUrls;
|
|
@@ -57,6 +57,11 @@ import type { Knex } from 'knex';
|
|
|
57
57
|
* SELECT and UPDATE/DELETE statements are chunked to stay below SQLite's
|
|
58
58
|
* `SQLITE_LIMIT_VARIABLE_NUMBER`.
|
|
59
59
|
* @param knex - Knex query builder connected to the archive DB.
|
|
60
|
+
* @param onProgress - Called after each chunk's DELETE/UPDATE statements
|
|
61
|
+
* complete, with the pages processed so far and the total to reset (issue
|
|
62
|
+
* #294: a large `--retry-failed` can reset thousands of pages across 13
|
|
63
|
+
* tables, running for seconds to minutes with no other signal it hasn't
|
|
64
|
+
* hung). Omit for no reporting (the default; e.g. tests).
|
|
60
65
|
* @returns The URLs of the pages that were reset to pending.
|
|
61
66
|
*/
|
|
62
|
-
export declare function resetFailedPages(knex: Knex): Promise<string[]>;
|
|
67
|
+
export declare function resetFailedPages(knex: Knex, onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|