@nitpicker/crawler 0.21.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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Config } from './archive/types.js';
|
|
2
|
+
import type { DedupeCapObservation } from './crawler/dedupe/types.js';
|
|
2
3
|
import type { NetworkProbe } from './crawler/probe-network.js';
|
|
3
|
-
import type { InventoryMode } from './crawler/types.js';
|
|
4
|
+
import type { CrawlRuntimeOptions, CrawlRuntimeOptionsPatch, InventoryMode } from './crawler/types.js';
|
|
4
5
|
import type { CrawlEvent, SetupProgressCallbacks } from './types.js';
|
|
6
|
+
import type { Lanes } from '@d-zero/dealer';
|
|
5
7
|
import type { ExURL } from '@d-zero/shared/parse-url';
|
|
6
8
|
import { TypedAwaitEventEmitter as EventEmitter } from '@d-zero/shared/typed-await-event-emitter';
|
|
7
9
|
import Archive from './archive/archive.js';
|
|
@@ -37,6 +39,17 @@ interface CrawlConfig extends Config {
|
|
|
37
39
|
list: boolean;
|
|
38
40
|
/** Maximum number of retry attempts per URL on scrape failure. */
|
|
39
41
|
retry: number;
|
|
42
|
+
/**
|
|
43
|
+
* Maximum number of whole-session auto-retry attempts (issue #350) when
|
|
44
|
+
* a crawl session ends with pages still pending — each attempt re-queues
|
|
45
|
+
* the current pending set and re-runs the crawl loop, with an
|
|
46
|
+
* exponential backoff between attempts (see
|
|
47
|
+
* `computeAutoRetryBackoffDelayMs`). `0` disables auto-retry entirely:
|
|
48
|
+
* any pending pages after the session's first (and only) crawl pass
|
|
49
|
+
* abort the session immediately. See
|
|
50
|
+
* `CrawlerOrchestrator`'s `#crawlUntilPendingClears` for the full loop.
|
|
51
|
+
*/
|
|
52
|
+
maxAutoRetry: number;
|
|
40
53
|
/** Whether to enable verbose logging output. */
|
|
41
54
|
verbose: boolean;
|
|
42
55
|
/** Custom User-Agent string for HTTP requests. */
|
|
@@ -75,12 +88,29 @@ interface CrawlConfig extends Config {
|
|
|
75
88
|
dedupeMapCap: number;
|
|
76
89
|
/**
|
|
77
90
|
* See {@link CrawlerOptions.preloadedStickyShapeKeys}. Set internally by
|
|
78
|
-
* the
|
|
79
|
-
* (`append`/`inventory`/`retryFailed`/`resume`), each
|
|
80
|
-
* calling `archive.listDedupeCapShapeKeys()`; not part of
|
|
81
|
-
* options a caller of those methods passes directly.
|
|
91
|
+
* the five resuming-session static methods
|
|
92
|
+
* (`append`/`inventory`/`recrawl`/`retryFailed`/`resume`), each
|
|
93
|
+
* independently calling `archive.listDedupeCapShapeKeys()`; not part of
|
|
94
|
+
* the public options a caller of those methods passes directly.
|
|
82
95
|
*/
|
|
83
96
|
preloadedStickyShapeKeys: readonly string[];
|
|
97
|
+
/**
|
|
98
|
+
* See {@link CrawlerOptions.preloadedDedupeObservations}. Set internally
|
|
99
|
+
* by the same five resuming-session static methods as
|
|
100
|
+
* {@link preloadedStickyShapeKeys}, each calling
|
|
101
|
+
* `archive.listDedupeCapObservations()` and mapping the rows through
|
|
102
|
+
* `buildDedupeCapObservation`; not part of the public options a caller
|
|
103
|
+
* of those methods passes directly.
|
|
104
|
+
*/
|
|
105
|
+
preloadedDedupeObservations: readonly DedupeCapObservation[];
|
|
106
|
+
/**
|
|
107
|
+
* A `Lanes` instance owned by the caller (typically the CLI), forwarded
|
|
108
|
+
* to {@link Crawler} so `deal()` reuses it instead of creating its own —
|
|
109
|
+
* see {@link CrawlerOptions.lanes}. Not part of {@link Config}: it is a
|
|
110
|
+
* live object, never persisted to (or read back from) the archive's
|
|
111
|
+
* `info` row.
|
|
112
|
+
*/
|
|
113
|
+
lanes?: Lanes;
|
|
84
114
|
}
|
|
85
115
|
/**
|
|
86
116
|
* Callback invoked after the CrawlerOrchestrator instance is fully initialized
|
|
@@ -100,7 +130,7 @@ interface InventorySource {
|
|
|
100
130
|
sha256: string;
|
|
101
131
|
/** The exact bytes of the source list file, archived verbatim. */
|
|
102
132
|
bytes: Buffer;
|
|
103
|
-
/** Number of source-file lines the CLI warned-and-dropped for failing URL validation, before `inventoryUrls` was ever built. Recorded on the audit row as `
|
|
133
|
+
/** Number of source-file lines the CLI warned-and-dropped for failing URL validation, before `inventoryUrls` was ever built. Recorded on the audit row as `list_reconcile_runs.invalid_skipped`. */
|
|
104
134
|
invalidLineCount: number;
|
|
105
135
|
}
|
|
106
136
|
/**
|
|
@@ -159,16 +189,43 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
159
189
|
* Sets up event listeners on the crawler, starts crawling, and resolves
|
|
160
190
|
* when the crawl completes. Discovered pages, external pages, skipped pages,
|
|
161
191
|
* and resources are forwarded to the archive for storage.
|
|
192
|
+
*
|
|
193
|
+
* Safe to call more than once on the same instance —
|
|
194
|
+
* `#crawlUntilPendingClears` (issue #350) re-invokes this for each
|
|
195
|
+
* auto-retry attempt against the same long-lived `#crawler`. `Crawler`
|
|
196
|
+
* (`TypedAwaitEventEmitter`) has no listener-removal API, so a second
|
|
197
|
+
* call cannot replace the first call's listeners — it can only stack
|
|
198
|
+
* another set alongside them. Every listener this method attaches is
|
|
199
|
+
* instead guarded by a monotonic generation counter (`isCurrent()`,
|
|
200
|
+
* defined below): once a later call bumps it, every earlier call's
|
|
201
|
+
* listeners permanently fail the check and become inert no-ops, leaving
|
|
202
|
+
* exactly the latest call's listeners actually writing anything.
|
|
162
203
|
* @param list - The list of parsed URLs to crawl. May be empty when a resumed
|
|
163
204
|
* session already has pending pages queued (for example `--retry-failed`).
|
|
164
205
|
* @param opts - Optional crawl overrides.
|
|
165
206
|
* @param opts.recursive - Whether discovered URLs are followed. Defaults to
|
|
166
207
|
* `!fromList` (recursive unless the archive was created from a URL list), so
|
|
167
208
|
* existing callers keep their behaviour; the retry flow passes it explicitly.
|
|
209
|
+
* @param opts.suppressFlushNotice - Skip emitting `flushingPendingWrites`
|
|
210
|
+
* (issue #350). Set by `#crawlUntilPendingClears` for every auto-retry
|
|
211
|
+
* attempt after the first: that event starts the CLI's crawl-tail
|
|
212
|
+
* `TaskList` (`attach-crawl-display.ts`), which must stay closed until
|
|
213
|
+
* the whole retry loop is done — a second `deal()`/`Lanes` cycle
|
|
214
|
+
* starting while that `TaskList` is still open would corrupt the
|
|
215
|
+
* display (see ARCHITECTURE.md's `Lanes`/`Display` single-instance
|
|
216
|
+
* invariant). The write-queue drain itself is unaffected; only the
|
|
217
|
+
* CLI-facing progress event is skipped.
|
|
218
|
+
* @param opts.isRetryContinuation - Forwarded to `Crawler#start()`
|
|
219
|
+
* (issue #350). Set by `#crawlUntilPendingClears` for every auto-retry
|
|
220
|
+
* attempt after the first, so `#runDeal` preserves cross-attempt
|
|
221
|
+
* learned state (known-good hosts, outage-detector window) instead of
|
|
222
|
+
* discarding it as if this were an unrelated fresh session.
|
|
168
223
|
* @returns A promise that resolves when crawling is complete.
|
|
169
224
|
*/
|
|
170
225
|
crawling(list: ExURL[], opts?: {
|
|
171
226
|
recursive?: boolean;
|
|
227
|
+
suppressFlushNotice?: boolean;
|
|
228
|
+
isRetryContinuation?: boolean;
|
|
172
229
|
}): Promise<void>;
|
|
173
230
|
/**
|
|
174
231
|
* Kill any zombie Chromium processes that were not properly cleaned up.
|
|
@@ -184,6 +241,34 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
184
241
|
* @returns An array of process IDs that should be terminated.
|
|
185
242
|
*/
|
|
186
243
|
getUndeadPid(): never[];
|
|
244
|
+
/**
|
|
245
|
+
* Applies a runtime change to the in-progress crawl's tunable options
|
|
246
|
+
* (`parallels`/`interval`/the three exclude arrays — see
|
|
247
|
+
* {@link Crawler.updateRuntimeOptions}) and persists the resulting
|
|
248
|
+
* values to the archive's `info` row so a later `--resume` / `--append`
|
|
249
|
+
* / `--retry-failed` reuses them (`Archive#updateConfig`).
|
|
250
|
+
*
|
|
251
|
+
* The in-memory change takes effect immediately, before this method
|
|
252
|
+
* returns. The archive write is enqueued on {@link #writeQueue} — so it
|
|
253
|
+
* is ordered relative to the crawl's own page/resource writes — but not
|
|
254
|
+
* awaited: this method is synchronous so a caller (e.g. the CLI reading
|
|
255
|
+
* a console command) can report the new value back without waiting on
|
|
256
|
+
* disk I/O. A failure in that write does NOT abort the crawl or set
|
|
257
|
+
* {@link #archiveFailure} — see {@link #persistingRuntimeOptionsPatch}'s
|
|
258
|
+
* JSDoc for why this one write is deliberately exempted from the fatal
|
|
259
|
+
* handling every other archive write gets; it is only debug-logged
|
|
260
|
+
* (`DEBUG=Nitpicker:*`).
|
|
261
|
+
* @param patch - The runtime change to apply.
|
|
262
|
+
* @returns A snapshot of the tunable options after applying `patch`.
|
|
263
|
+
* @throws {RangeError} If `parallels` is present and not an integer `>= 1`, or `interval` is present and not an integer `>= 0`.
|
|
264
|
+
* @throws {TypeError} If any exclude entry is present and not a non-empty string.
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* const snapshot = orchestrator.updateRuntimeOptions({ parallels: 4 });
|
|
268
|
+
* console.log(snapshot.parallels); // 4
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
updateRuntimeOptions(patch: CrawlRuntimeOptionsPatch): CrawlRuntimeOptions;
|
|
187
272
|
/**
|
|
188
273
|
* Write the archive to its configured file path.
|
|
189
274
|
*
|
|
@@ -215,6 +300,7 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
215
300
|
* @param initializedCallback - Optional callback invoked after initialization but before crawling starts.
|
|
216
301
|
* @returns A promise that resolves to the CrawlerOrchestrator instance after crawling completes.
|
|
217
302
|
* @throws {Error} If the URL list is empty or contains no valid URLs.
|
|
303
|
+
* @throws {PendingUrlsRemainError} When the crawl session ends with pages still pending after exhausting auto-retry.
|
|
218
304
|
*/
|
|
219
305
|
static crawling(url: string[], options?: Partial<CrawlConfig>, initializedCallback?: CrawlInitializedCallback): Promise<CrawlerOrchestrator>;
|
|
220
306
|
/**
|
|
@@ -226,7 +312,11 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
226
312
|
* the expanded scope are demoted back to "needs scraping" so the next pass
|
|
227
313
|
* re-fetches them as full internal pages. A `<archive>.bak` is created
|
|
228
314
|
* before the crawl and removed on success; if the crawl throws, the backup
|
|
229
|
-
* is restored to keep the original archive intact
|
|
315
|
+
* is restored to keep the original archive intact — except when the crawl
|
|
316
|
+
* ends with {@link PendingUrlsRemainError} (issue #350), where the
|
|
317
|
+
* un-packaged stub itself is the recovery path and the backup is instead
|
|
318
|
+
* left untouched (deleted, not restored — see
|
|
319
|
+
* {@link CrawlerOrchestrator.#abandonBackupOnPendingRemains}).
|
|
230
320
|
*
|
|
231
321
|
* List-mode archives (`info.fromList === true`) are rejected because their
|
|
232
322
|
* pages are all metadata-only and cannot host a recursive append.
|
|
@@ -240,6 +330,7 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
240
330
|
* this can't go through the orchestrator's event emitter (issue #294).
|
|
241
331
|
* @returns The orchestrator instance after the append crawl completes.
|
|
242
332
|
* @throws {Error} When `newUrls` is empty, the archive is in list mode, or it cannot be parsed.
|
|
333
|
+
* @throws {PendingUrlsRemainError} When the crawl session ends with pages still pending after exhausting auto-retry.
|
|
243
334
|
*/
|
|
244
335
|
static append(archivePath: string, newUrls: string[], options?: Partial<CrawlConfig>, initializedCallback?: CrawlInitializedCallback, setupProgress?: SetupProgressCallbacks): Promise<CrawlerOrchestrator>;
|
|
245
336
|
/**
|
|
@@ -326,8 +417,75 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
326
417
|
* #294).
|
|
327
418
|
* @returns The orchestrator instance after a successful inventory pass.
|
|
328
419
|
* @throws {Error} When `inventoryUrls` is empty or the archive is in list mode. Unresolved pending URLs from a previous crawl do NOT throw — see step 3.
|
|
420
|
+
* @throws {PendingUrlsRemainError} When the crawl session ends with pages still pending after exhausting auto-retry.
|
|
329
421
|
*/
|
|
330
422
|
static inventory(archivePath: string, inventoryUrls: string[], options?: Partial<CrawlConfig>, initializedCallback?: CrawlInitializedCallback, source?: InventorySource | null, setupProgress?: SetupProgressCallbacks): Promise<CrawlerOrchestrator>;
|
|
423
|
+
/**
|
|
424
|
+
* Re-fetch pages named by an operator-supplied URL list, importing any
|
|
425
|
+
* URL the archive does not yet track as a new inventory seed.
|
|
426
|
+
*
|
|
427
|
+
* `recrawl` is `retryFailed`'s un-scrape combined with `inventory`'s
|
|
428
|
+
* novel-URL ingestion, run inside one `.bak`-protected window: URLs in
|
|
429
|
+
* `recrawlUrls` that already exist as `content_items` rows are reset back
|
|
430
|
+
* to pending via {@link Archive.resetPagesByUrls} (see that method for the
|
|
431
|
+
* conservative exclusion rules — redirect sources, intentionally-skipped
|
|
432
|
+
* pages, and external pages are matched but never reset), while URLs the
|
|
433
|
+
* archive has never seen are ingested exactly as `inventory` does (see
|
|
434
|
+
* {@link CrawlerOrchestrator.inventory}'s JSDoc for that half's contract).
|
|
435
|
+
* Existing *resources* matched by the list are neither resettable nor
|
|
436
|
+
* novel — `resource_items` is first-write-wins (a known deviation, see
|
|
437
|
+
* ARCHITECTURE.md), so a resource re-fetch would not update anything; the
|
|
438
|
+
* function reports how many list entries fell into this bucket via
|
|
439
|
+
* `setupProgress.onLog` without acting on them.
|
|
440
|
+
*
|
|
441
|
+
* Unlike `inventory`, whose sole early-return condition is "no novel
|
|
442
|
+
* URLs", `recrawl` also has existing pages to act on — the `.bak` is
|
|
443
|
+
* skipped only when BOTH `existingPageUrls` (reset candidates) AND
|
|
444
|
+
* `novelUrls` (ingestion candidates) are empty.
|
|
445
|
+
*
|
|
446
|
+
* **Strict-pending gap**: `getCrawlingState()`'s pending set only includes
|
|
447
|
+
* a `scraped = 0` row that is either anchor-referenced or explicitly
|
|
448
|
+
* labelled (see that function's JSDoc). When `recrawlUrls` contains pages
|
|
449
|
+
* that link to each other, resetting one page also deletes its outgoing
|
|
450
|
+
* `anchor_edges` — so a `source = 'crawled'` sibling that was reset in the
|
|
451
|
+
* same pass can lose its only anchor referrer and fall out of the strict
|
|
452
|
+
* pending set, silently skipping its re-fetch. `retryFailed` never hits
|
|
453
|
+
* this because a failed page's referrers are not themselves reset. The
|
|
454
|
+
* fix: every URL `Archive.resetPagesByUrls` actually reset is merged into
|
|
455
|
+
* the pending list handed to `Crawler#resume` regardless of what the
|
|
456
|
+
* strict scan finds, deduplicated by `LinkList.add`'s `protocolAgnosticKey`
|
|
457
|
+
* check. A Ctrl+C between the reset and the scrape phase loses this
|
|
458
|
+
* synthetic merge (it lives only in memory) — `crawl --resume` recovers
|
|
459
|
+
* whatever the strict-pending scan finds on its own, and re-running
|
|
460
|
+
* `--recrawl` with the same list recovers the rest, matching the
|
|
461
|
+
* "un-picked seeds" recovery contract `getCrawlingState`'s JSDoc already
|
|
462
|
+
* documents for `inventory`.
|
|
463
|
+
*
|
|
464
|
+
* **Stale analyze findings**: resetting a page deletes its
|
|
465
|
+
* `analysis_violations` rows (see {@link resetPagesByUrls}'s JSDoc) so a
|
|
466
|
+
* re-fetched page never shows findings from HTML that no longer exists,
|
|
467
|
+
* but other `analyze` outputs (e.g. Discrepancies plugin reports) are not
|
|
468
|
+
* page-scoped and cannot be selectively invalidated. When at least one
|
|
469
|
+
* page was reset, a `crawlSessionNotice` is emitted after the crawl
|
|
470
|
+
* completes recommending `analyze` be re-run before the next `report`.
|
|
471
|
+
* @param archivePath - Absolute or relative path to the existing `.nitpicker`.
|
|
472
|
+
* @param recrawlUrls - URLs to match against the archive (existing pages
|
|
473
|
+
* are reset; unknown URLs are ingested as new inventory seeds).
|
|
474
|
+
* @param options - Optional config overrides applied on top of the archived config.
|
|
475
|
+
* @param initializedCallback - Optional callback invoked after initialization but before crawling resumes.
|
|
476
|
+
* @param source - The CLI's already-read URL list source bytes, archived
|
|
477
|
+
* for audit purposes — see {@link InventorySource}. `null` for
|
|
478
|
+
* programmatic callers with no source file.
|
|
479
|
+
* @param setupProgress - Optional progress callbacks for the setup phase
|
|
480
|
+
* (untar, `.bak` copy, URL classification, reset, seed ingestion, state
|
|
481
|
+
* rebuild) that runs before `initializedCallback` — see
|
|
482
|
+
* {@link SetupProgressCallbacks} for why this can't go through the
|
|
483
|
+
* orchestrator's event emitter (issue #294).
|
|
484
|
+
* @returns The orchestrator instance after the recrawl completes.
|
|
485
|
+
* @throws {Error} When `recrawlUrls` is empty or the archive is in list mode.
|
|
486
|
+
* @throws {PendingUrlsRemainError} When the crawl session ends with pages still pending after exhausting auto-retry.
|
|
487
|
+
*/
|
|
488
|
+
static recrawl(archivePath: string, recrawlUrls: string[], options?: Partial<CrawlConfig>, initializedCallback?: CrawlInitializedCallback, source?: InventorySource | null, setupProgress?: SetupProgressCallbacks): Promise<CrawlerOrchestrator>;
|
|
331
489
|
/**
|
|
332
490
|
* Re-fetch previously-failed pages in an existing `.nitpicker` archive.
|
|
333
491
|
*
|
|
@@ -351,7 +509,10 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
351
509
|
*
|
|
352
510
|
* A `<archive>.bak` is created before any DB mutation and removed on success;
|
|
353
511
|
* if the crawl throws, the backup is restored to keep the original archive
|
|
354
|
-
* intact
|
|
512
|
+
* intact — except when the crawl ends with {@link PendingUrlsRemainError}
|
|
513
|
+
* (issue #350), where the un-packaged stub itself is the recovery path and
|
|
514
|
+
* the backup is instead left untouched (deleted, not restored — see
|
|
515
|
+
* {@link CrawlerOrchestrator.#abandonBackupOnPendingRemains}).
|
|
355
516
|
*
|
|
356
517
|
* List-mode archives (`info.fromList === true`) are rejected for the same
|
|
357
518
|
* reason as {@link CrawlerOrchestrator.append}: their pages are metadata-only.
|
|
@@ -364,6 +525,7 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
364
525
|
* this can't go through the orchestrator's event emitter (issue #294).
|
|
365
526
|
* @returns The orchestrator instance after the retry crawl completes.
|
|
366
527
|
* @throws {Error} When the archive is in list mode or has no parseable roots.
|
|
528
|
+
* @throws {PendingUrlsRemainError} When the crawl session ends with pages still pending after exhausting auto-retry.
|
|
367
529
|
*/
|
|
368
530
|
static retryFailed(archivePath: string, options?: Partial<CrawlConfig>, initializedCallback?: CrawlInitializedCallback, setupProgress?: SetupProgressCallbacks): Promise<CrawlerOrchestrator>;
|
|
369
531
|
/**
|
|
@@ -385,6 +547,7 @@ export declare class CrawlerOrchestrator extends EventEmitter<CrawlEvent> {
|
|
|
385
547
|
* tmpDir IS the source of truth).
|
|
386
548
|
* @returns A promise that resolves to the CrawlerOrchestrator instance after crawling completes.
|
|
387
549
|
* @throws {Error} If the archived URL is invalid.
|
|
550
|
+
* @throws {PendingUrlsRemainError} When the crawl session ends with pages still pending after exhausting auto-retry.
|
|
388
551
|
*/
|
|
389
552
|
static resume(stubPath: string, options?: Partial<CrawlConfig>, initializedCallback?: CrawlInitializedCallback, setupProgress?: SetupProgressCallbacks): Promise<CrawlerOrchestrator>;
|
|
390
553
|
}
|