@nitpicker/crawler 0.20.0 → 0.22.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 +1 -1
- package/lib/append-retry-failed-common-setup-phases.js +5 -1
- package/lib/append-setup-phases.d.ts +1 -1
- package/lib/archive/archive.d.ts +57 -8
- package/lib/archive/archive.js +83 -12
- package/lib/archive/create-adjunct-tables.d.ts +3 -2
- package/lib/archive/create-adjunct-tables.js +12 -9
- package/lib/archive/create-entity-tables.js +3 -0
- package/lib/archive/database.d.ts +39 -7
- package/lib/archive/database.js +49 -9
- package/lib/archive/db-ops/_shared/keyset-paginate-by-id.d.ts +49 -0
- package/lib/archive/db-ops/_shared/keyset-paginate-by-id.js +69 -0
- package/lib/archive/db-ops/_shared/resolve-content-item-id.d.ts +13 -12
- package/lib/archive/db-ops/_shared/resolve-content-item-id.js +63 -31
- package/lib/archive/db-ops/_shared/types.d.ts +39 -0
- package/lib/archive/db-ops/analysis/replace-analysis-violations.js +7 -4
- package/lib/archive/db-ops/config/get-created-cwd.d.ts +11 -0
- package/lib/archive/db-ops/config/get-created-cwd.js +13 -0
- package/lib/archive/db-ops/config/info-column-allowlist.js +1 -0
- package/lib/archive/db-ops/dedupe-cap/list-dedupe-cap-observations.d.ts +57 -0
- package/lib/archive/db-ops/dedupe-cap/list-dedupe-cap-observations.js +87 -0
- package/lib/archive/db-ops/errors/insert-page-error.js +3 -1
- package/lib/archive/db-ops/lifecycle/init.d.ts +13 -6
- package/lib/archive/db-ops/lifecycle/init.js +30 -10
- package/lib/archive/db-ops/list-reconcile/record-list-reconcile-run.d.ts +22 -0
- package/lib/archive/db-ops/{inventory/record-inventory-run.js → list-reconcile/record-list-reconcile-run.js} +11 -10
- package/lib/archive/db-ops/pages/read/build-page-query.js +1 -1
- package/lib/archive/db-ops/pages/read/get-crawling-state.d.ts +9 -2
- package/lib/archive/db-ops/pages/read/get-crawling-state.js +11 -3
- package/lib/archive/db-ops/pages/reset/clear-page-derived-rows.d.ts +20 -0
- package/lib/archive/db-ops/pages/reset/clear-page-derived-rows.js +37 -0
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +3 -2
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +17 -37
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +22 -1
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +79 -33
- package/lib/archive/db-ops/pages/reset/reset-pages-by-urls.d.ts +78 -0
- package/lib/archive/db-ops/pages/reset/reset-pages-by-urls.js +146 -0
- package/lib/archive/db-ops/pages/reset/retryable-image-scan-codes.d.ts +24 -0
- package/lib/archive/db-ops/pages/reset/retryable-image-scan-codes.js +24 -0
- package/lib/archive/db-ops/pages/write/insert-inventory-content-items.js +2 -1
- package/lib/archive/db-ops/pages/write/insert-page.js +4 -2
- package/lib/archive/db-ops/pages/write/link-redirect-sources.js +3 -1
- package/lib/archive/db-ops/pages/write/record-redirect.js +1 -1
- package/lib/archive/db-ops/pages/write/set-skipped-page.js +3 -1
- package/lib/archive/db-ops/pages/write/update-page.d.ts +9 -1
- package/lib/archive/db-ops/pages/write/update-page.js +28 -6
- package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +3 -6
- package/lib/archive/db-ops/resources/get-resource-url-list.js +10 -35
- package/lib/archive/init-schema.d.ts +1 -1
- package/lib/archive/init-schema.js +3 -2
- package/lib/archive/meta/compute-main-contents-denormalized.d.ts +11 -3
- package/lib/archive/meta/compute-main-contents-denormalized.js +14 -2
- package/lib/archive/meta/types.d.ts +24 -0
- package/lib/archive/migrate-content-items-is-metadata-only.d.ts +44 -0
- package/lib/archive/migrate-content-items-is-metadata-only.js +60 -0
- package/lib/archive/migrate-info-created-cwd.d.ts +17 -0
- package/lib/archive/migrate-info-created-cwd.js +36 -0
- package/lib/archive/migrate-inventory-runs-to-list-reconcile-runs.d.ts +34 -0
- package/lib/archive/migrate-inventory-runs-to-list-reconcile-runs.js +51 -0
- package/lib/archive/{migrate-inventory-runs-exclude-skipped.d.ts → migrate-list-reconcile-runs-exclude-skipped.d.ts} +8 -8
- package/lib/archive/{migrate-inventory-runs-exclude-skipped.js → migrate-list-reconcile-runs-exclude-skipped.js} +12 -12
- package/lib/archive/migrate-list-reconcile-runs-invalid-skipped.d.ts +16 -0
- package/lib/archive/migrate-list-reconcile-runs-invalid-skipped.js +35 -0
- package/lib/archive/migrate-page-meta-image-scan.d.ts +27 -0
- package/lib/archive/migrate-page-meta-image-scan.js +52 -0
- package/lib/archive/page.d.ts +12 -0
- package/lib/archive/page.js +16 -0
- package/lib/archive/types.d.ts +97 -10
- package/lib/compute-auto-retry-backoff-delay.d.ts +30 -0
- package/lib/compute-auto-retry-backoff-delay.js +36 -0
- package/lib/crawler/apply-crawl-runtime-options-patch.d.ts +32 -0
- package/lib/crawler/apply-crawl-runtime-options-patch.js +129 -0
- package/lib/crawler/crawler.d.ts +35 -3
- package/lib/crawler/crawler.js +486 -338
- package/lib/crawler/dedupe/build-dedupe-cap-observation.d.ts +74 -0
- package/lib/crawler/dedupe/build-dedupe-cap-observation.js +97 -0
- package/lib/crawler/dedupe/compute-meta-signature.d.ts +7 -5
- package/lib/crawler/dedupe/compute-meta-signature.js +0 -0
- package/lib/crawler/dedupe/compute-shape-key.d.ts +20 -0
- package/lib/crawler/dedupe/compute-shape-key.js +73 -1
- package/lib/crawler/dedupe/dedupe-cap-tracker.d.ts +6 -1
- package/lib/crawler/dedupe/dedupe-cap-tracker.js +6 -1
- package/lib/crawler/dedupe/resolve-og-url-mismatch.d.ts +7 -5
- package/lib/crawler/dedupe/resolve-og-url-mismatch.js +5 -3
- package/lib/crawler/dedupe/types.d.ts +18 -0
- package/lib/crawler/fetch-destination.js +1 -0
- package/lib/crawler/link-list.d.ts +9 -1
- package/lib/crawler/link-list.js +12 -2
- package/lib/crawler/link-to-page-data.js +1 -0
- package/lib/crawler/resource-to-page-data.js +1 -0
- package/lib/crawler/types.d.ts +75 -0
- package/lib/crawler-orchestrator.d.ts +171 -8
- package/lib/crawler-orchestrator.js +1177 -234
- package/lib/crawler.d.ts +3 -0
- package/lib/crawler.js +3 -0
- package/lib/delay-or-abort.d.ts +20 -0
- package/lib/delay-or-abort.js +35 -0
- package/lib/pending-urls-remain-error.d.ts +35 -0
- package/lib/pending-urls-remain-error.js +43 -0
- package/lib/recrawl-setup-phases.d.ts +38 -0
- package/lib/recrawl-setup-phases.js +52 -0
- package/lib/resume-setup-phases.d.ts +1 -1
- package/lib/resume-setup-phases.js +5 -1
- package/lib/retry-failed-setup-phases.d.ts +1 -1
- package/lib/setup-recovery-phase-labels.d.ts +17 -8
- package/lib/setup-recovery-phase-labels.js +17 -8
- package/lib/types.d.ts +62 -12
- package/lib/utils/compute-file-sha256.d.ts +1 -1
- package/lib/utils/compute-file-sha256.js +1 -1
- package/package.json +4 -4
- package/lib/archive/db-ops/inventory/record-inventory-run.d.ts +0 -21
- package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +0 -16
- package/lib/archive/migrate-inventory-runs-invalid-skipped.js +0 -35
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const APPEND_RETRY_FAILED_COMMON_SETUP_PHASES: {
|
|
13
13
|
readonly prefix: readonly ["Extracting archive", "Loading archive config", "Backing up archive"];
|
|
14
|
-
readonly suffix: readonly ["Loading dedupe-cap
|
|
14
|
+
readonly suffix: readonly ["Loading dedupe-cap state", "Loading crawl state", "Loading resource list", "Loading scraped page count", "Restoring crawl state"];
|
|
15
15
|
};
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
export const APPEND_RETRY_FAILED_COMMON_SETUP_PHASES = {
|
|
13
13
|
prefix: ['Extracting archive', 'Loading archive config', 'Backing up archive'],
|
|
14
14
|
suffix: [
|
|
15
|
-
'Loading dedupe-cap shape keys'
|
|
15
|
+
// Renamed from 'Loading dedupe-cap shape keys' — this phase now
|
|
16
|
+
// covers both the confirmed-capped shape-key read AND the
|
|
17
|
+
// not-yet-capped shape observation replay (`archive.listDedupeCapObservations`
|
|
18
|
+
// / `#preloadDedupeCapObservations`), not just the former.
|
|
19
|
+
'Loading dedupe-cap state',
|
|
16
20
|
'Loading crawl state',
|
|
17
21
|
'Loading resource list',
|
|
18
22
|
'Loading scraped page count',
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
* a `SETUP_RECOVERY_PHASE_LABELS` entry (`'Restoring archive from backup'`)
|
|
15
15
|
* is announced instead of continuing this sequence.
|
|
16
16
|
*/
|
|
17
|
-
export declare const APPEND_SETUP_PHASES: readonly ["Extracting archive", "Loading archive config", "Backing up archive", "Repromoting external pages", "Loading dedupe-cap
|
|
17
|
+
export declare const APPEND_SETUP_PHASES: readonly ["Extracting archive", "Loading archive config", "Backing up archive", "Repromoting external pages", "Loading dedupe-cap state", "Loading crawl state", "Loading resource list", "Loading scraped page count", "Restoring crawl state"];
|
package/lib/archive/archive.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TemplateClusterReason } from './db-ops/analysis/types.js';
|
|
2
|
-
import type { Config, InsertDedupeCapEventParams, InsertNetworkOutageParams,
|
|
2
|
+
import type { Config, DedupeCapObservationRow, InsertDedupeCapEventParams, InsertNetworkOutageParams, ListReconcileRunMeta, PageSource } from './types.js';
|
|
3
3
|
import type { OutageWindow } from '../is-within-outage-window.js';
|
|
4
4
|
import type { PageData, CrawlerError, Resource } from '../utils/types/types.js';
|
|
5
5
|
import type { ConsoleLogEntry } from '@d-zero/beholder';
|
|
@@ -101,12 +101,19 @@ export default class Archive extends ArchiveAccessor {
|
|
|
101
101
|
finalizeDedupeCapEvent(id: number, rejectedCount: number): Promise<void>;
|
|
102
102
|
/**
|
|
103
103
|
* Retrieves the current crawling state, including lists of scraped and pending URLs.
|
|
104
|
-
* @returns An object with `scraped` and `
|
|
104
|
+
* @returns An object with `scraped`, `pending`, and `pendingMetadataOnly` URL arrays.
|
|
105
105
|
*/
|
|
106
106
|
getCrawlingState(): Promise<{
|
|
107
107
|
scraped: string[];
|
|
108
108
|
pending: string[];
|
|
109
|
+
pendingMetadataOnly: string[];
|
|
109
110
|
}>;
|
|
111
|
+
/**
|
|
112
|
+
* Retrieves the `info.createdCwd` value stamped when this stub was
|
|
113
|
+
* created — see {@link Archive.resume} for how it is used.
|
|
114
|
+
* @returns The recorded cwd, or `null` if never stamped.
|
|
115
|
+
*/
|
|
116
|
+
getCreatedCwd(): Promise<string | null>;
|
|
110
117
|
/**
|
|
111
118
|
* Return the subset of `urls` that already exist as `pages.url`. Used by
|
|
112
119
|
* `CrawlerOrchestrator.inventory` to filter the user-supplied URL list
|
|
@@ -210,11 +217,26 @@ export default class Archive extends ArchiveAccessor {
|
|
|
210
217
|
* Appends one open row to the `network_outages` journal.
|
|
211
218
|
*
|
|
212
219
|
* Thin facade over {@link Database.insertNetworkOutage} — see
|
|
213
|
-
* {@link
|
|
220
|
+
* {@link recordListReconcileRun}'s docstring for why this indirection exists.
|
|
214
221
|
* @param params - The confirmed-outage fields to record.
|
|
215
222
|
* @returns The autoincremented `id` of the inserted row.
|
|
216
223
|
*/
|
|
217
224
|
insertNetworkOutage(params: InsertNetworkOutageParams): Promise<number>;
|
|
225
|
+
/**
|
|
226
|
+
* Every previously-scraped internal page's raw fields, in the shape
|
|
227
|
+
* `buildDedupeCapObservation` needs to replay this archive's prior
|
|
228
|
+
* `DedupeCapTracker` observations (not just its confirmed-capped
|
|
229
|
+
* shapes — see {@link listDedupeCapShapeKeys}) into a fresh tracker
|
|
230
|
+
* instance on `--resume` / `--append` / `--retry-failed` /
|
|
231
|
+
* `--inventory` / `--recrawl`. Without this, every not-yet-capped
|
|
232
|
+
* shape's Misra-Gries counter restarts at 0 each session, even one
|
|
233
|
+
* that was one observation away from confirming a trap.
|
|
234
|
+
* @param onProgress - Called after each internal chunk read, with the
|
|
235
|
+
* highest `content_items.id` scanned so far and the max id in the
|
|
236
|
+
* table. Omit for no reporting.
|
|
237
|
+
* @returns Every qualifying page's raw fields, in discovery order.
|
|
238
|
+
*/
|
|
239
|
+
listDedupeCapObservations(onProgress?: (scannedUpToId: number, maxId: number) => void): Promise<DedupeCapObservationRow[]>;
|
|
218
240
|
/**
|
|
219
241
|
* Every distinct `dedupe_cap_events.shape_key` recorded in this archive.
|
|
220
242
|
* Consumed by `CrawlerOrchestrator` to preload `DedupeCapTracker`'s
|
|
@@ -246,16 +268,16 @@ export default class Archive extends ArchiveAccessor {
|
|
|
246
268
|
*/
|
|
247
269
|
listNetworkOutages(): Promise<OutageWindow[]>;
|
|
248
270
|
/**
|
|
249
|
-
* Appends one row to the `
|
|
271
|
+
* Appends one row to the `list_reconcile_runs` audit log.
|
|
250
272
|
*
|
|
251
|
-
* Thin facade over {@link Database.
|
|
273
|
+
* Thin facade over {@link Database.recordListReconcileRun} — keeps the
|
|
252
274
|
* orchestrator decoupled from the knex layer and gives a single
|
|
253
275
|
* write entry point that future Archive-level concerns (locking,
|
|
254
276
|
* mirror sync, etc.) can hook into without touching every caller.
|
|
255
277
|
* @param meta - The run metadata. Only `ran_at` is required.
|
|
256
278
|
* @returns The autoincremented `id` of the inserted row.
|
|
257
279
|
*/
|
|
258
|
-
|
|
280
|
+
recordListReconcileRun(meta: ListReconcileRunMeta): Promise<number>;
|
|
259
281
|
/**
|
|
260
282
|
* Releases the SQLite handle and the advisory lock **without** writing
|
|
261
283
|
* the archive or removing `tmpDir`.
|
|
@@ -322,6 +344,19 @@ export default class Archive extends ArchiveAccessor {
|
|
|
322
344
|
* @returns The URLs of the pages that were reset to pending.
|
|
323
345
|
*/
|
|
324
346
|
resetFailedPages(onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
347
|
+
/**
|
|
348
|
+
* Reset pages matching an operator-supplied URL list back to pending so a
|
|
349
|
+
* follow-up crawl re-fetches them from scratch.
|
|
350
|
+
*
|
|
351
|
+
* Delegates to {@link Database.resetPagesByUrls}. See that method for the
|
|
352
|
+
* conservative exclusion rules (redirect sources / intentionally-skipped /
|
|
353
|
+
* external pages are matched but not reset).
|
|
354
|
+
* @param urls - URL strings to match, already in `withoutHashAndAuth` form.
|
|
355
|
+
* @param onProgress - Forwarded to {@link Database.resetPagesByUrls} — see
|
|
356
|
+
* that method's docs.
|
|
357
|
+
* @returns The reset URLs plus the excluded URLs grouped by reason.
|
|
358
|
+
*/
|
|
359
|
+
resetPagesByUrls(urls: readonly string[], onProgress?: (processed: number, total: number) => void): Promise<import("./types.js").ResetPagesByUrlsResult>;
|
|
325
360
|
/**
|
|
326
361
|
* Persists the raw bytes of an `--inventory` source URL list into the
|
|
327
362
|
* archive's tar payload, at `inventory/<sha256>.txt`.
|
|
@@ -368,8 +403,15 @@ export default class Archive extends ArchiveAccessor {
|
|
|
368
403
|
* content-type), never a rendered body.
|
|
369
404
|
* @param pageInfo - The page data to store.
|
|
370
405
|
* @param source - Provenance label for new rows. `undefined` leaves the DB DEFAULT (`'crawled'`).
|
|
406
|
+
* @param recursive - The crawl session's `recursive` option, forwarded so
|
|
407
|
+
* `is_metadata_only` on any newly-discovered anchor is computed
|
|
408
|
+
* correctly (#369). Defaults to `true` (never persist an anchor as
|
|
409
|
+
* metadata-only) since an external page's `anchorList` is always
|
|
410
|
+
* empty — this only matters for callers that pass one anyway.
|
|
411
|
+
* @example
|
|
412
|
+
* await archive.setExternalPage(externalPageData, undefined, options.recursive);
|
|
371
413
|
*/
|
|
372
|
-
setExternalPage(pageInfo: PageData, source?: PageSource): Promise<void>;
|
|
414
|
+
setExternalPage(pageInfo: PageData, source?: PageSource, recursive?: boolean): Promise<void>;
|
|
373
415
|
/**
|
|
374
416
|
* Stores a crawled page's data in the archive database, persisting the
|
|
375
417
|
* rendered HTML body as a zstd-compressed BLOB inside the same SQLite
|
|
@@ -380,9 +422,16 @@ export default class Archive extends ArchiveAccessor {
|
|
|
380
422
|
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
381
423
|
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
382
424
|
* computing it from the HTML instead.
|
|
425
|
+
* @param recursive - The crawl session's `recursive` option, forwarded so
|
|
426
|
+
* any anchor discovered on this page gets the correct
|
|
427
|
+
* `is_metadata_only` value (#369). Defaults to `true` (never persist
|
|
428
|
+
* an anchor as metadata-only) for callers that do not track the
|
|
429
|
+
* option.
|
|
383
430
|
* @returns The database ID of the stored page.
|
|
431
|
+
* @example
|
|
432
|
+
* const pageId = await archive.setPage(pageData, undefined, undefined, options.recursive);
|
|
384
433
|
*/
|
|
385
|
-
setPage(pageInfo: PageData, source?: PageSource, bodyHash?: Buffer | null): Promise<number>;
|
|
434
|
+
setPage(pageInfo: PageData, source?: PageSource, bodyHash?: Buffer | null, recursive?: boolean): Promise<number>;
|
|
386
435
|
/**
|
|
387
436
|
* Records a redirect edge without re-storing the destination's content.
|
|
388
437
|
*
|
package/lib/archive/archive.js
CHANGED
|
@@ -162,11 +162,19 @@ export default class Archive extends ArchiveAccessor {
|
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
164
164
|
* Retrieves the current crawling state, including lists of scraped and pending URLs.
|
|
165
|
-
* @returns An object with `scraped` and `
|
|
165
|
+
* @returns An object with `scraped`, `pending`, and `pendingMetadataOnly` URL arrays.
|
|
166
166
|
*/
|
|
167
167
|
async getCrawlingState() {
|
|
168
168
|
return this.#db.getCrawlingState();
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Retrieves the `info.createdCwd` value stamped when this stub was
|
|
172
|
+
* created — see {@link Archive.resume} for how it is used.
|
|
173
|
+
* @returns The recorded cwd, or `null` if never stamped.
|
|
174
|
+
*/
|
|
175
|
+
async getCreatedCwd() {
|
|
176
|
+
return this.#db.getCreatedCwd();
|
|
177
|
+
}
|
|
170
178
|
/**
|
|
171
179
|
* Return the subset of `urls` that already exist as `pages.url`. Used by
|
|
172
180
|
* `CrawlerOrchestrator.inventory` to filter the user-supplied URL list
|
|
@@ -303,7 +311,7 @@ export default class Archive extends ArchiveAccessor {
|
|
|
303
311
|
* Appends one open row to the `network_outages` journal.
|
|
304
312
|
*
|
|
305
313
|
* Thin facade over {@link Database.insertNetworkOutage} — see
|
|
306
|
-
* {@link
|
|
314
|
+
* {@link recordListReconcileRun}'s docstring for why this indirection exists.
|
|
307
315
|
* @param params - The confirmed-outage fields to record.
|
|
308
316
|
* @returns The autoincremented `id` of the inserted row.
|
|
309
317
|
*/
|
|
@@ -311,6 +319,23 @@ export default class Archive extends ArchiveAccessor {
|
|
|
311
319
|
dbLog('Insert network outage: startedAt=%d probeHost=%s', params.startedAt, params.probeHost);
|
|
312
320
|
return await this.#db.insertNetworkOutage(params);
|
|
313
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Every previously-scraped internal page's raw fields, in the shape
|
|
324
|
+
* `buildDedupeCapObservation` needs to replay this archive's prior
|
|
325
|
+
* `DedupeCapTracker` observations (not just its confirmed-capped
|
|
326
|
+
* shapes — see {@link listDedupeCapShapeKeys}) into a fresh tracker
|
|
327
|
+
* instance on `--resume` / `--append` / `--retry-failed` /
|
|
328
|
+
* `--inventory` / `--recrawl`. Without this, every not-yet-capped
|
|
329
|
+
* shape's Misra-Gries counter restarts at 0 each session, even one
|
|
330
|
+
* that was one observation away from confirming a trap.
|
|
331
|
+
* @param onProgress - Called after each internal chunk read, with the
|
|
332
|
+
* highest `content_items.id` scanned so far and the max id in the
|
|
333
|
+
* table. Omit for no reporting.
|
|
334
|
+
* @returns Every qualifying page's raw fields, in discovery order.
|
|
335
|
+
*/
|
|
336
|
+
async listDedupeCapObservations(onProgress) {
|
|
337
|
+
return this.#db.listDedupeCapObservations(onProgress);
|
|
338
|
+
}
|
|
314
339
|
/**
|
|
315
340
|
* Every distinct `dedupe_cap_events.shape_key` recorded in this archive.
|
|
316
341
|
* Consumed by `CrawlerOrchestrator` to preload `DedupeCapTracker`'s
|
|
@@ -348,18 +373,18 @@ export default class Archive extends ArchiveAccessor {
|
|
|
348
373
|
return await this.#db.listNetworkOutages();
|
|
349
374
|
}
|
|
350
375
|
/**
|
|
351
|
-
* Appends one row to the `
|
|
376
|
+
* Appends one row to the `list_reconcile_runs` audit log.
|
|
352
377
|
*
|
|
353
|
-
* Thin facade over {@link Database.
|
|
378
|
+
* Thin facade over {@link Database.recordListReconcileRun} — keeps the
|
|
354
379
|
* orchestrator decoupled from the knex layer and gives a single
|
|
355
380
|
* write entry point that future Archive-level concerns (locking,
|
|
356
381
|
* mirror sync, etc.) can hook into without touching every caller.
|
|
357
382
|
* @param meta - The run metadata. Only `ran_at` is required.
|
|
358
383
|
* @returns The autoincremented `id` of the inserted row.
|
|
359
384
|
*/
|
|
360
|
-
async
|
|
361
|
-
dbLog('Record
|
|
362
|
-
return await this.#db.
|
|
385
|
+
async recordListReconcileRun(meta) {
|
|
386
|
+
dbLog('Record list reconcile run: %s', meta.list_label ?? meta.ran_at);
|
|
387
|
+
return await this.#db.recordListReconcileRun(meta);
|
|
363
388
|
}
|
|
364
389
|
/**
|
|
365
390
|
* Releases the SQLite handle and the advisory lock **without** writing
|
|
@@ -434,6 +459,22 @@ export default class Archive extends ArchiveAccessor {
|
|
|
434
459
|
dbLog('Reset failed pages back to pending');
|
|
435
460
|
return this.#db.resetFailedPages(onProgress);
|
|
436
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Reset pages matching an operator-supplied URL list back to pending so a
|
|
464
|
+
* follow-up crawl re-fetches them from scratch.
|
|
465
|
+
*
|
|
466
|
+
* Delegates to {@link Database.resetPagesByUrls}. See that method for the
|
|
467
|
+
* conservative exclusion rules (redirect sources / intentionally-skipped /
|
|
468
|
+
* external pages are matched but not reset).
|
|
469
|
+
* @param urls - URL strings to match, already in `withoutHashAndAuth` form.
|
|
470
|
+
* @param onProgress - Forwarded to {@link Database.resetPagesByUrls} — see
|
|
471
|
+
* that method's docs.
|
|
472
|
+
* @returns The reset URLs plus the excluded URLs grouped by reason.
|
|
473
|
+
*/
|
|
474
|
+
async resetPagesByUrls(urls, onProgress) {
|
|
475
|
+
dbLog('Reset %d URL-list-matched page(s) back to pending', urls.length);
|
|
476
|
+
return this.#db.resetPagesByUrls(urls, onProgress);
|
|
477
|
+
}
|
|
437
478
|
/**
|
|
438
479
|
* Persists the raw bytes of an `--inventory` source URL list into the
|
|
439
480
|
* archive's tar payload, at `inventory/<sha256>.txt`.
|
|
@@ -489,10 +530,17 @@ export default class Archive extends ArchiveAccessor {
|
|
|
489
530
|
* content-type), never a rendered body.
|
|
490
531
|
* @param pageInfo - The page data to store.
|
|
491
532
|
* @param source - Provenance label for new rows. `undefined` leaves the DB DEFAULT (`'crawled'`).
|
|
533
|
+
* @param recursive - The crawl session's `recursive` option, forwarded so
|
|
534
|
+
* `is_metadata_only` on any newly-discovered anchor is computed
|
|
535
|
+
* correctly (#369). Defaults to `true` (never persist an anchor as
|
|
536
|
+
* metadata-only) since an external page's `anchorList` is always
|
|
537
|
+
* empty — this only matters for callers that pass one anyway.
|
|
538
|
+
* @example
|
|
539
|
+
* await archive.setExternalPage(externalPageData, undefined, options.recursive);
|
|
492
540
|
*/
|
|
493
|
-
async setExternalPage(pageInfo, source) {
|
|
541
|
+
async setExternalPage(pageInfo, source, recursive) {
|
|
494
542
|
dbLog('Set external page: %s', pageInfo.url.href);
|
|
495
|
-
await this.#db.updatePage(pageInfo, false, false, source);
|
|
543
|
+
await this.#db.updatePage(pageInfo, false, false, source, undefined, recursive);
|
|
496
544
|
}
|
|
497
545
|
/**
|
|
498
546
|
* Stores a crawled page's data in the archive database, persisting the
|
|
@@ -504,11 +552,18 @@ export default class Archive extends ArchiveAccessor {
|
|
|
504
552
|
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
505
553
|
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
506
554
|
* computing it from the HTML instead.
|
|
555
|
+
* @param recursive - The crawl session's `recursive` option, forwarded so
|
|
556
|
+
* any anchor discovered on this page gets the correct
|
|
557
|
+
* `is_metadata_only` value (#369). Defaults to `true` (never persist
|
|
558
|
+
* an anchor as metadata-only) for callers that do not track the
|
|
559
|
+
* option.
|
|
507
560
|
* @returns The database ID of the stored page.
|
|
561
|
+
* @example
|
|
562
|
+
* const pageId = await archive.setPage(pageData, undefined, undefined, options.recursive);
|
|
508
563
|
*/
|
|
509
|
-
async setPage(pageInfo, source, bodyHash) {
|
|
564
|
+
async setPage(pageInfo, source, bodyHash, recursive) {
|
|
510
565
|
dbLog('Set page: %s', pageInfo.url.href);
|
|
511
|
-
return await this.#db.updatePage(pageInfo, true, pageInfo.isTarget, source, bodyHash);
|
|
566
|
+
return await this.#db.updatePage(pageInfo, true, pageInfo.isTarget, source, bodyHash, recursive);
|
|
512
567
|
}
|
|
513
568
|
/**
|
|
514
569
|
* Records a redirect edge without re-storing the destination's content.
|
|
@@ -610,6 +665,15 @@ export default class Archive extends ArchiveAccessor {
|
|
|
610
665
|
*/
|
|
611
666
|
async write(options) {
|
|
612
667
|
saveLog('Starts: %s', this.#filePath);
|
|
668
|
+
// `.nitpicker` files are routinely shared between users — scrub the
|
|
669
|
+
// stub-local `createdCwd` (see `Config.createdCwd`'s JSDoc) before it
|
|
670
|
+
// gets folded into `db.sqlite` and tarred, so a packaged archive never
|
|
671
|
+
// carries another user's local absolute path. Bundled into the
|
|
672
|
+
// `checkpoint` step below (a single UPDATE ahead of the WAL fold-back)
|
|
673
|
+
// rather than its own `onStep` phase — both are near-instant and
|
|
674
|
+
// reporting them separately would add a phase label for no visible
|
|
675
|
+
// wait.
|
|
676
|
+
await this.#db.updateConfig({ createdCwd: null });
|
|
613
677
|
options?.onStep?.('checkpoint');
|
|
614
678
|
await this.#db.checkpoint();
|
|
615
679
|
const filePathWithoutExt = path.resolve(path.dirname(this.#filePath), path.basename(this.#filePath, path.extname(this.#filePath)));
|
|
@@ -876,7 +940,14 @@ export default class Archive extends ArchiveAccessor {
|
|
|
876
940
|
const db = await Archive.#connectDB(tmpDir, { onLog });
|
|
877
941
|
const name = (await db.getName()) ||
|
|
878
942
|
path.basename(targetPath).replace(Archive.TMP_DIR_PREFIX, '');
|
|
879
|
-
|
|
943
|
+
// Reconstruct the output path from the cwd the interrupted
|
|
944
|
+
// session was originally started from (see `Config.createdCwd`),
|
|
945
|
+
// not this invocation's own `process.cwd()` — `crawl --resume
|
|
946
|
+
// <stub>` is routinely run from a different directory than the
|
|
947
|
+
// original `crawl`/`--append`/etc. call. Falls back to this
|
|
948
|
+
// invocation's cwd for a stub that predates this column.
|
|
949
|
+
const createdCwd = await db.getCreatedCwd();
|
|
950
|
+
const filePath = path.resolve(createdCwd ?? process.cwd(), name + '.' + Archive.FILE_EXTENSION);
|
|
880
951
|
return new Archive(filePath, tmpDir, db, releaseLock);
|
|
881
952
|
}
|
|
882
953
|
catch (error) {
|
|
@@ -23,7 +23,8 @@ import type { Knex } from 'knex';
|
|
|
23
23
|
* found in the main-content region, one row per DOM element, FK →
|
|
24
24
|
* `content_items(id)`. Unlike its eight siblings above, captured by
|
|
25
25
|
* nitpicker itself (`crawler/capture-custom-elements.ts`), not beholder
|
|
26
|
-
* - `
|
|
26
|
+
* - `list_reconcile_runs` — `--inventory` / `--recrawl` audit log (no FK;
|
|
27
|
+
* append-only)
|
|
27
28
|
* - `network_outages` — operator-network-outage journal (no FK; append-only
|
|
28
29
|
* except `ended_at`, which is written once on recovery)
|
|
29
30
|
* - `dedupe_cap_events` — `--dedupe-cap` same-cluster soft-cap audit log (no
|
|
@@ -60,7 +61,7 @@ import type { Knex } from 'knex';
|
|
|
60
61
|
* Unlike `createRefTables` / `createEntityTables` (whose callers guard with
|
|
61
62
|
* a single sentinel table), each table here is guarded individually because
|
|
62
63
|
* the migration-script caller sees archives where any subset may already
|
|
63
|
-
* exist (e.g. `page_jsonld` from the 0.10 migration but no `
|
|
64
|
+
* exist (e.g. `page_jsonld` from the 0.10 migration but no `list_reconcile_runs`).
|
|
64
65
|
* Index creation stays inside each guard: an existing table keeps whatever
|
|
65
66
|
* indexes its creation path declared.
|
|
66
67
|
* @param instance - The Knex query builder instance connected to the database.
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
* found in the main-content region, one row per DOM element, FK →
|
|
23
23
|
* `content_items(id)`. Unlike its eight siblings above, captured by
|
|
24
24
|
* nitpicker itself (`crawler/capture-custom-elements.ts`), not beholder
|
|
25
|
-
* - `
|
|
25
|
+
* - `list_reconcile_runs` — `--inventory` / `--recrawl` audit log (no FK;
|
|
26
|
+
* append-only)
|
|
26
27
|
* - `network_outages` — operator-network-outage journal (no FK; append-only
|
|
27
28
|
* except `ended_at`, which is written once on recovery)
|
|
28
29
|
* - `dedupe_cap_events` — `--dedupe-cap` same-cluster soft-cap audit log (no
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
* Unlike `createRefTables` / `createEntityTables` (whose callers guard with
|
|
60
61
|
* a single sentinel table), each table here is guarded individually because
|
|
61
62
|
* the migration-script caller sees archives where any subset may already
|
|
62
|
-
* exist (e.g. `page_jsonld` from the 0.10 migration but no `
|
|
63
|
+
* exist (e.g. `page_jsonld` from the 0.10 migration but no `list_reconcile_runs`).
|
|
63
64
|
* Index creation stays inside each guard: an existing table keeps whatever
|
|
64
65
|
* indexes its creation path declared.
|
|
65
66
|
* @param instance - The Knex query builder instance connected to the database.
|
|
@@ -340,13 +341,15 @@ export async function createAdjunctTables(instance) {
|
|
|
340
341
|
t.index('pageId');
|
|
341
342
|
});
|
|
342
343
|
}
|
|
343
|
-
if (!(await instance.schema.hasTable('
|
|
344
|
-
await instance.schema.createTable('
|
|
345
|
-
// One row per successful `--inventory <list>`
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
// on
|
|
344
|
+
if (!(await instance.schema.hasTable('list_reconcile_runs'))) {
|
|
345
|
+
await instance.schema.createTable('list_reconcile_runs', (t) => {
|
|
346
|
+
// One row per successful `--inventory <list>` or `--recrawl
|
|
347
|
+
// <list>` invocation — both reconcile a URL list against the
|
|
348
|
+
// archive via the same ingestion path. The archive's audit log
|
|
349
|
+
// of "when did we apply which list at what scale". `.bak` is
|
|
350
|
+
// removed on success so this table is the only durable
|
|
351
|
+
// provenance record. Column semantics live on the
|
|
352
|
+
// `ListReconcileRunMeta` interface in `archive/types.ts`.
|
|
350
353
|
t.increments('id');
|
|
351
354
|
t.string('ran_at').notNullable();
|
|
352
355
|
t.string('list_label').nullable();
|
|
@@ -195,6 +195,7 @@ export async function createEntityTables(instance) {
|
|
|
195
195
|
is_external INTEGER NOT NULL,
|
|
196
196
|
scraped INTEGER NOT NULL,
|
|
197
197
|
is_target INTEGER NOT NULL,
|
|
198
|
+
is_metadata_only INTEGER NOT NULL DEFAULT 0,
|
|
198
199
|
status INTEGER,
|
|
199
200
|
status_text TEXT,
|
|
200
201
|
content_type_id INTEGER REFERENCES content_type_refs(id),
|
|
@@ -305,6 +306,8 @@ export async function createEntityTables(instance) {
|
|
|
305
306
|
main_content_custom_element_count INTEGER,
|
|
306
307
|
scroll_height_desktop INTEGER,
|
|
307
308
|
scroll_height_mobile INTEGER,
|
|
309
|
+
image_scan_desktop INTEGER,
|
|
310
|
+
image_scan_mobile INTEGER,
|
|
308
311
|
body_hash BLOB,
|
|
309
312
|
console_error_count INTEGER
|
|
310
313
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TemplateClusterReason } from './db-ops/analysis/types.js';
|
|
2
2
|
import type { JsonLdRow, MainContentAudioRow, MainContentButtonRow, MainContentCanvasRow, MainContentCustomElementRow, MainContentHeadingRow, MainContentIframeRow, MainContentImageRow, MainContentTableRow, MainContentVideoRow, PageTechnologyRow, TechnologySignalRow } from './meta/types.js';
|
|
3
|
-
import type { Config, DatabaseOption, DB_Redirect, DB_Resource, DatabaseEvent, InsertDedupeCapEventParams, InsertNetworkOutageParams,
|
|
3
|
+
import type { Config, DatabaseOption, DB_Redirect, DB_Resource, DatabaseEvent, DedupeCapObservationRow, InsertDedupeCapEventParams, InsertNetworkOutageParams, ListReconcileRunMeta, PageFilter, PageSource, ResetPagesByUrlsResult } 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';
|
|
6
6
|
import type { ConsoleLogEntry } from '@d-zero/beholder';
|
|
@@ -108,13 +108,22 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
108
108
|
* Retrieves the current crawling state by listing scraped and pending URLs.
|
|
109
109
|
* Delegates to {@link getCrawlingStateOp} — see the op for the strict
|
|
110
110
|
* pending-set rationale.
|
|
111
|
-
* @returns An object with `scraped` (completed URLs)
|
|
112
|
-
* strict set of in-scope, anchor-referenced, unfinished URLs)
|
|
111
|
+
* @returns An object with `scraped` (completed URLs), `pending` (the
|
|
112
|
+
* strict set of in-scope, anchor-referenced, unfinished URLs), and
|
|
113
|
+
* `pendingMetadataOnly` (the subset of `pending` fated for a
|
|
114
|
+
* metadata-only scrape — see the op's doc).
|
|
113
115
|
*/
|
|
114
116
|
getCrawlingState(): Promise<{
|
|
115
117
|
scraped: string[];
|
|
116
118
|
pending: string[];
|
|
119
|
+
pendingMetadataOnly: string[];
|
|
117
120
|
}>;
|
|
121
|
+
/**
|
|
122
|
+
* Retrieves the `info.createdCwd` value stamped at stub-creation time.
|
|
123
|
+
* Delegates to {@link getCreatedCwdOp}.
|
|
124
|
+
* @returns The recorded cwd, or `null` if never stamped.
|
|
125
|
+
*/
|
|
126
|
+
getCreatedCwd(): Promise<string | null>;
|
|
118
127
|
/**
|
|
119
128
|
* Retrieves all `page_main_content_custom_elements` rows for the given
|
|
120
129
|
* page id. Delegates to {@link getCustomElementsOfPageOp}.
|
|
@@ -370,6 +379,17 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
370
379
|
* @param pageUrl - The URL of the page that references the resource.
|
|
371
380
|
*/
|
|
372
381
|
insertResourceReferrers(src: string, pageUrl: string): Promise<void>;
|
|
382
|
+
/**
|
|
383
|
+
* Every previously-scraped internal page's raw fields, in the shape
|
|
384
|
+
* `buildDedupeCapObservation` needs to replay this archive's prior
|
|
385
|
+
* `DedupeCapTracker` observations into a fresh tracker instance.
|
|
386
|
+
* Delegates to {@link listDedupeCapObservationsOp} — see that function
|
|
387
|
+
* for the row selection criteria.
|
|
388
|
+
* @param onProgress - Forwarded to {@link listDedupeCapObservationsOp} —
|
|
389
|
+
* see that function's docs.
|
|
390
|
+
* @returns Every qualifying page's raw fields, in `content_items.id` order.
|
|
391
|
+
*/
|
|
392
|
+
listDedupeCapObservations(onProgress?: (scannedUpToId: number, maxId: number) => void): Promise<DedupeCapObservationRow[]>;
|
|
373
393
|
/**
|
|
374
394
|
* Every distinct `dedupe_cap_events.shape_key` recorded in this archive.
|
|
375
395
|
* Delegates to {@link listDedupeCapShapeKeysOp}.
|
|
@@ -392,12 +412,12 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
392
412
|
*/
|
|
393
413
|
listNetworkOutages(): Promise<OutageWindow[]>;
|
|
394
414
|
/**
|
|
395
|
-
* Appends one row to the `
|
|
396
|
-
* Delegates to {@link
|
|
415
|
+
* Appends one row to the `list_reconcile_runs` audit log.
|
|
416
|
+
* Delegates to {@link recordListReconcileRunOp}.
|
|
397
417
|
* @param meta - The run metadata to record. Only `ran_at` is required.
|
|
398
418
|
* @returns The autoincremented `id` of the newly-inserted row.
|
|
399
419
|
*/
|
|
400
|
-
|
|
420
|
+
recordListReconcileRun(meta: ListReconcileRunMeta): Promise<number>;
|
|
401
421
|
/**
|
|
402
422
|
* Records a redirect edge (source → destination) **without** re-storing the
|
|
403
423
|
* destination's content. Delegates to {@link recordRedirectOp}.
|
|
@@ -461,6 +481,17 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
461
481
|
* @returns The URLs of the pages that were reset to pending.
|
|
462
482
|
*/
|
|
463
483
|
resetFailedPages(onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
484
|
+
/**
|
|
485
|
+
* Reset pages matching an operator-supplied URL list back to pending so a
|
|
486
|
+
* follow-up crawl re-fetches them from scratch. Delegates to
|
|
487
|
+
* {@link resetPagesByUrlsOp} — see the op for the conservative exclusion
|
|
488
|
+
* rationale (redirect sources / intentionally-skipped / external pages).
|
|
489
|
+
* @param urls - URL strings to match, already in `withoutHashAndAuth` form.
|
|
490
|
+
* @param onProgress - Forwarded to {@link resetPagesByUrlsOp} — see that
|
|
491
|
+
* function's docs.
|
|
492
|
+
* @returns The reset URLs plus the excluded URLs grouped by reason.
|
|
493
|
+
*/
|
|
494
|
+
resetPagesByUrls(urls: readonly string[], onProgress?: (processed: number, total: number) => void): Promise<ResetPagesByUrlsResult>;
|
|
464
495
|
/**
|
|
465
496
|
* Stores the crawl configuration in the `info` table.
|
|
466
497
|
* Delegates to {@link setConfigOp}.
|
|
@@ -503,9 +534,10 @@ export declare class Database extends EventEmitter<DatabaseEvent> {
|
|
|
503
534
|
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
504
535
|
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
505
536
|
* computing it from the HTML instead.
|
|
537
|
+
* @param recursive - See {@link updatePageOp}'s `recursive` doc.
|
|
506
538
|
* @returns The database `pageId` of the inserted/updated row.
|
|
507
539
|
*/
|
|
508
|
-
updatePage(page: PageData, writeHtml: boolean, isTarget: boolean, source?: PageSource, bodyHash?: Buffer | null): Promise<number>;
|
|
540
|
+
updatePage(page: PageData, writeHtml: boolean, isTarget: boolean, source?: PageSource, bodyHash?: Buffer | null, recursive?: boolean): Promise<number>;
|
|
509
541
|
/**
|
|
510
542
|
* Creates and initializes a new Database instance.
|
|
511
543
|
*
|
package/lib/archive/database.js
CHANGED
|
@@ -12,6 +12,7 @@ import { replacePageTemplates as replacePageTemplatesOp } from './db-ops/analysi
|
|
|
12
12
|
import { getAnchorsOnPage as getAnchorsOnPageOp } from './db-ops/anchors/get-anchors-on-page.js';
|
|
13
13
|
import { getBaseUrl as getBaseUrlOp } from './db-ops/config/get-base-url.js';
|
|
14
14
|
import { getConfig as getConfigOp } from './db-ops/config/get-config.js';
|
|
15
|
+
import { getCreatedCwd as getCreatedCwdOp } from './db-ops/config/get-created-cwd.js';
|
|
15
16
|
import { getName as getNameOp } from './db-ops/config/get-name.js';
|
|
16
17
|
import { setConfig as setConfigOp } from './db-ops/config/set-config.js';
|
|
17
18
|
import { updateConfig as updateConfigOp } from './db-ops/config/update-config.js';
|
|
@@ -19,15 +20,16 @@ import { replaceConsoleLogs as replaceConsoleLogsOp } from './db-ops/console-log
|
|
|
19
20
|
import { accumulateDedupeCapRejectedCount as accumulateDedupeCapRejectedCountOp } from './db-ops/dedupe-cap/accumulate-dedupe-cap-rejected-count.js';
|
|
20
21
|
import { finalizeDedupeCapEvent as finalizeDedupeCapEventOp } from './db-ops/dedupe-cap/finalize-dedupe-cap-event.js';
|
|
21
22
|
import { insertDedupeCapEvent as insertDedupeCapEventOp } from './db-ops/dedupe-cap/insert-dedupe-cap-event.js';
|
|
23
|
+
import { listDedupeCapObservations as listDedupeCapObservationsOp } from './db-ops/dedupe-cap/list-dedupe-cap-observations.js';
|
|
22
24
|
import { listDedupeCapShapeKeys as listDedupeCapShapeKeysOp } from './db-ops/dedupe-cap/list-dedupe-cap-shape-keys.js';
|
|
23
25
|
import { insertCrawlError as insertCrawlErrorOp } from './db-ops/errors/insert-crawl-error.js';
|
|
24
26
|
import { insertPageError as insertPageErrorOp } from './db-ops/errors/insert-page-error.js';
|
|
25
27
|
import { listDnsBurnedHostCandidates as listDnsBurnedHostCandidatesOp } from './db-ops/errors/list-dns-burned-host-candidates.js';
|
|
26
28
|
import { getHtmlOfPageById as getHtmlOfPageByIdOp } from './db-ops/html/get-html-of-page-by-id.js';
|
|
27
|
-
import { recordInventoryRun as recordInventoryRunOp } from './db-ops/inventory/record-inventory-run.js';
|
|
28
29
|
import { checkpoint as checkpointOp } from './db-ops/lifecycle/checkpoint.js';
|
|
29
30
|
import { destroy as destroyOp } from './db-ops/lifecycle/destroy.js';
|
|
30
31
|
import { init as initOp } from './db-ops/lifecycle/init.js';
|
|
32
|
+
import { recordListReconcileRun as recordListReconcileRunOp } from './db-ops/list-reconcile/record-list-reconcile-run.js';
|
|
31
33
|
import { getAudiosOfPage as getAudiosOfPageOp } from './db-ops/meta/get-audios-of-page.js';
|
|
32
34
|
import { getButtonsOfPage as getButtonsOfPageOp } from './db-ops/meta/get-buttons-of-page.js';
|
|
33
35
|
import { getCanvasesOfPage as getCanvasesOfPageOp } from './db-ops/meta/get-canvases-of-page.js';
|
|
@@ -53,6 +55,7 @@ import { getPages as getPagesOp } from './db-ops/pages/read/get-pages.js';
|
|
|
53
55
|
import { getScrapedHtmlPageCount as getScrapedHtmlPageCountOp } from './db-ops/pages/read/get-scraped-html-page-count.js';
|
|
54
56
|
import { repromoteExternalPages as repromoteExternalPagesOp } from './db-ops/pages/reset/repromote-external-pages.js';
|
|
55
57
|
import { resetFailedPages as resetFailedPagesOp } from './db-ops/pages/reset/reset-failed-pages.js';
|
|
58
|
+
import { resetPagesByUrls as resetPagesByUrlsOp } from './db-ops/pages/reset/reset-pages-by-urls.js';
|
|
56
59
|
import { insertInventorySeeds as insertInventorySeedsOp } from './db-ops/pages/write/insert-inventory-seeds.js';
|
|
57
60
|
import { insertInventorySkippedPages as insertInventorySkippedPagesOp } from './db-ops/pages/write/insert-inventory-skipped-pages.js';
|
|
58
61
|
import { recordRedirect as recordRedirectOp } from './db-ops/pages/write/record-redirect.js';
|
|
@@ -223,12 +226,22 @@ export class Database extends EventEmitter {
|
|
|
223
226
|
* Retrieves the current crawling state by listing scraped and pending URLs.
|
|
224
227
|
* Delegates to {@link getCrawlingStateOp} — see the op for the strict
|
|
225
228
|
* pending-set rationale.
|
|
226
|
-
* @returns An object with `scraped` (completed URLs)
|
|
227
|
-
* strict set of in-scope, anchor-referenced, unfinished URLs)
|
|
229
|
+
* @returns An object with `scraped` (completed URLs), `pending` (the
|
|
230
|
+
* strict set of in-scope, anchor-referenced, unfinished URLs), and
|
|
231
|
+
* `pendingMetadataOnly` (the subset of `pending` fated for a
|
|
232
|
+
* metadata-only scrape — see the op's doc).
|
|
228
233
|
*/
|
|
229
234
|
async getCrawlingState() {
|
|
230
235
|
return emitErrorAndRetry(this, 'Database.getCrawlingState', async () => await getCrawlingStateOp(this.#instance), retrySetting);
|
|
231
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Retrieves the `info.createdCwd` value stamped at stub-creation time.
|
|
239
|
+
* Delegates to {@link getCreatedCwdOp}.
|
|
240
|
+
* @returns The recorded cwd, or `null` if never stamped.
|
|
241
|
+
*/
|
|
242
|
+
async getCreatedCwd() {
|
|
243
|
+
return emitErrorAndRetry(this, 'Database.getCreatedCwd', async () => await getCreatedCwdOp(this.#instance), retrySetting);
|
|
244
|
+
}
|
|
232
245
|
/**
|
|
233
246
|
* Retrieves all `page_main_content_custom_elements` rows for the given
|
|
234
247
|
* page id. Delegates to {@link getCustomElementsOfPageOp}.
|
|
@@ -557,6 +570,19 @@ export class Database extends EventEmitter {
|
|
|
557
570
|
async insertResourceReferrers(src, pageUrl) {
|
|
558
571
|
return emitErrorAndRetry(this, 'Database.insertResourceReferrers', async () => await insertResourceReferrersOp(this.#instance, this.#writeRefCaches, src, pageUrl), retrySetting);
|
|
559
572
|
}
|
|
573
|
+
/**
|
|
574
|
+
* Every previously-scraped internal page's raw fields, in the shape
|
|
575
|
+
* `buildDedupeCapObservation` needs to replay this archive's prior
|
|
576
|
+
* `DedupeCapTracker` observations into a fresh tracker instance.
|
|
577
|
+
* Delegates to {@link listDedupeCapObservationsOp} — see that function
|
|
578
|
+
* for the row selection criteria.
|
|
579
|
+
* @param onProgress - Forwarded to {@link listDedupeCapObservationsOp} —
|
|
580
|
+
* see that function's docs.
|
|
581
|
+
* @returns Every qualifying page's raw fields, in `content_items.id` order.
|
|
582
|
+
*/
|
|
583
|
+
async listDedupeCapObservations(onProgress) {
|
|
584
|
+
return emitErrorAndRetry(this, 'Database.listDedupeCapObservations', async () => await listDedupeCapObservationsOp(this.#instance, onProgress), retrySetting);
|
|
585
|
+
}
|
|
560
586
|
/**
|
|
561
587
|
* Every distinct `dedupe_cap_events.shape_key` recorded in this archive.
|
|
562
588
|
* Delegates to {@link listDedupeCapShapeKeysOp}.
|
|
@@ -585,13 +611,13 @@ export class Database extends EventEmitter {
|
|
|
585
611
|
return emitErrorAndRetry(this, 'Database.listNetworkOutages', async () => await listNetworkOutagesOp(this.#instance), retrySetting);
|
|
586
612
|
}
|
|
587
613
|
/**
|
|
588
|
-
* Appends one row to the `
|
|
589
|
-
* Delegates to {@link
|
|
614
|
+
* Appends one row to the `list_reconcile_runs` audit log.
|
|
615
|
+
* Delegates to {@link recordListReconcileRunOp}.
|
|
590
616
|
* @param meta - The run metadata to record. Only `ran_at` is required.
|
|
591
617
|
* @returns The autoincremented `id` of the newly-inserted row.
|
|
592
618
|
*/
|
|
593
|
-
async
|
|
594
|
-
return emitErrorAndRetry(this, 'Database.
|
|
619
|
+
async recordListReconcileRun(meta) {
|
|
620
|
+
return emitErrorAndRetry(this, 'Database.recordListReconcileRun', async () => await recordListReconcileRunOp(this.#instance, meta), retrySetting);
|
|
595
621
|
}
|
|
596
622
|
/**
|
|
597
623
|
* Records a redirect edge (source → destination) **without** re-storing the
|
|
@@ -662,6 +688,19 @@ export class Database extends EventEmitter {
|
|
|
662
688
|
async resetFailedPages(onProgress) {
|
|
663
689
|
return emitErrorAndRetry(this, 'Database.resetFailedPages', async () => await resetFailedPagesOp(this.#instance, onProgress), retrySetting);
|
|
664
690
|
}
|
|
691
|
+
/**
|
|
692
|
+
* Reset pages matching an operator-supplied URL list back to pending so a
|
|
693
|
+
* follow-up crawl re-fetches them from scratch. Delegates to
|
|
694
|
+
* {@link resetPagesByUrlsOp} — see the op for the conservative exclusion
|
|
695
|
+
* rationale (redirect sources / intentionally-skipped / external pages).
|
|
696
|
+
* @param urls - URL strings to match, already in `withoutHashAndAuth` form.
|
|
697
|
+
* @param onProgress - Forwarded to {@link resetPagesByUrlsOp} — see that
|
|
698
|
+
* function's docs.
|
|
699
|
+
* @returns The reset URLs plus the excluded URLs grouped by reason.
|
|
700
|
+
*/
|
|
701
|
+
async resetPagesByUrls(urls, onProgress) {
|
|
702
|
+
return emitErrorAndRetry(this, 'Database.resetPagesByUrls', async () => await resetPagesByUrlsOp(this.#instance, urls, onProgress), retrySetting);
|
|
703
|
+
}
|
|
665
704
|
/**
|
|
666
705
|
* Stores the crawl configuration in the `info` table.
|
|
667
706
|
* Delegates to {@link setConfigOp}.
|
|
@@ -712,10 +751,11 @@ export class Database extends EventEmitter {
|
|
|
712
751
|
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
713
752
|
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
714
753
|
* computing it from the HTML instead.
|
|
754
|
+
* @param recursive - See {@link updatePageOp}'s `recursive` doc.
|
|
715
755
|
* @returns The database `pageId` of the inserted/updated row.
|
|
716
756
|
*/
|
|
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);
|
|
757
|
+
async updatePage(page, writeHtml, isTarget, source, bodyHash, recursive) {
|
|
758
|
+
return emitErrorAndRetry(this, 'Database.updatePage', async () => await updatePageOp(this.#instance, this.#writeRefCaches, page, writeHtml, isTarget, source, bodyHash, recursive), retrySetting);
|
|
719
759
|
}
|
|
720
760
|
/**
|
|
721
761
|
* Initializes the database schema if tables do not exist, then runs
|