@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
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The setup-phase labels `append()` and `retryFailed()` announce identically
|
|
3
|
+
* — everything except the one step in between that names what each mode is
|
|
4
|
+
* actually doing (`'Repromoting external pages'` vs `'Resetting failed
|
|
5
|
+
* pages'`) — split into a `prefix` (before that step) and `suffix` (after
|
|
6
|
+
* it). Single source of truth for `APPEND_SETUP_PHASES` /
|
|
7
|
+
* `RETRY_FAILED_SETUP_PHASES` (issue #294): the two were previously
|
|
8
|
+
* hand-written as independent nine-entry literal arrays sharing eight of
|
|
9
|
+
* nine entries verbatim, with nothing to catch one drifting from the other
|
|
10
|
+
* if a shared phase were added, renamed, or reordered in only one place.
|
|
11
|
+
*/
|
|
12
|
+
export declare const APPEND_RETRY_FAILED_COMMON_SETUP_PHASES: {
|
|
13
|
+
readonly prefix: readonly ["Extracting archive", "Loading archive config", "Backing up archive"];
|
|
14
|
+
readonly suffix: readonly ["Loading dedupe-cap shape keys", "Loading crawl state", "Loading resource list", "Loading scraped page count", "Restoring crawl state"];
|
|
15
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The setup-phase labels `append()` and `retryFailed()` announce identically
|
|
3
|
+
* — everything except the one step in between that names what each mode is
|
|
4
|
+
* actually doing (`'Repromoting external pages'` vs `'Resetting failed
|
|
5
|
+
* pages'`) — split into a `prefix` (before that step) and `suffix` (after
|
|
6
|
+
* it). Single source of truth for `APPEND_SETUP_PHASES` /
|
|
7
|
+
* `RETRY_FAILED_SETUP_PHASES` (issue #294): the two were previously
|
|
8
|
+
* hand-written as independent nine-entry literal arrays sharing eight of
|
|
9
|
+
* nine entries verbatim, with nothing to catch one drifting from the other
|
|
10
|
+
* if a shared phase were added, renamed, or reordered in only one place.
|
|
11
|
+
*/
|
|
12
|
+
export const APPEND_RETRY_FAILED_COMMON_SETUP_PHASES = {
|
|
13
|
+
prefix: ['Extracting archive', 'Loading archive config', 'Backing up archive'],
|
|
14
|
+
suffix: [
|
|
15
|
+
'Loading dedupe-cap shape keys',
|
|
16
|
+
'Loading crawl state',
|
|
17
|
+
'Loading resource list',
|
|
18
|
+
'Loading scraped page count',
|
|
19
|
+
'Restoring crawl state',
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered `SetupProgressCallbacks.onPhase` labels `CrawlerOrchestrator.append`
|
|
3
|
+
* announces, in call order, before its `initializedCallback` fires. Single
|
|
4
|
+
* source of truth for both the `onPhase` call sites in
|
|
5
|
+
* `crawler-orchestrator.ts` and the CLI's setup task list (`create-setup-task-list.ts`),
|
|
6
|
+
* which pre-builds one task-list row per entry — duplicating this sequence by
|
|
7
|
+
* hand at the CLI layer would silently drift the moment a phase is added,
|
|
8
|
+
* renamed, or reordered here. Shares its prefix/suffix with
|
|
9
|
+
* `RETRY_FAILED_SETUP_PHASES` via `APPEND_RETRY_FAILED_COMMON_SETUP_PHASES` —
|
|
10
|
+
* `'Repromoting external pages'` is the one step unique to `append()`.
|
|
11
|
+
*
|
|
12
|
+
* Linear on the success path (no branching before `initializedCallback`). On
|
|
13
|
+
* failure — anywhere from `archive.updateConfig` through the crawl itself —
|
|
14
|
+
* a `SETUP_RECOVERY_PHASE_LABELS` entry (`'Restoring archive from backup'`)
|
|
15
|
+
* is announced instead of continuing this sequence.
|
|
16
|
+
*/
|
|
17
|
+
export declare const APPEND_SETUP_PHASES: readonly ["Extracting archive", "Loading archive config", "Backing up archive", "Repromoting external pages", "Loading dedupe-cap shape keys", "Loading crawl state", "Loading resource list", "Loading scraped page count", "Restoring crawl state"];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { APPEND_RETRY_FAILED_COMMON_SETUP_PHASES } from './append-retry-failed-common-setup-phases.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ordered `SetupProgressCallbacks.onPhase` labels `CrawlerOrchestrator.append`
|
|
4
|
+
* announces, in call order, before its `initializedCallback` fires. Single
|
|
5
|
+
* source of truth for both the `onPhase` call sites in
|
|
6
|
+
* `crawler-orchestrator.ts` and the CLI's setup task list (`create-setup-task-list.ts`),
|
|
7
|
+
* which pre-builds one task-list row per entry — duplicating this sequence by
|
|
8
|
+
* hand at the CLI layer would silently drift the moment a phase is added,
|
|
9
|
+
* renamed, or reordered here. Shares its prefix/suffix with
|
|
10
|
+
* `RETRY_FAILED_SETUP_PHASES` via `APPEND_RETRY_FAILED_COMMON_SETUP_PHASES` —
|
|
11
|
+
* `'Repromoting external pages'` is the one step unique to `append()`.
|
|
12
|
+
*
|
|
13
|
+
* Linear on the success path (no branching before `initializedCallback`). On
|
|
14
|
+
* failure — anywhere from `archive.updateConfig` through the crawl itself —
|
|
15
|
+
* a `SETUP_RECOVERY_PHASE_LABELS` entry (`'Restoring archive from backup'`)
|
|
16
|
+
* is announced instead of continuing this sequence.
|
|
17
|
+
*/
|
|
18
|
+
export const APPEND_SETUP_PHASES = [
|
|
19
|
+
...APPEND_RETRY_FAILED_COMMON_SETUP_PHASES.prefix,
|
|
20
|
+
'Repromoting external pages',
|
|
21
|
+
...APPEND_RETRY_FAILED_COMMON_SETUP_PHASES.suffix,
|
|
22
|
+
];
|
|
@@ -29,6 +29,16 @@ export declare class ArchiveAccessor extends EventEmitter<DatabaseEvent> {
|
|
|
29
29
|
constructor(tmpDir: string, db: Database, namespace?: string | null, options?: {
|
|
30
30
|
readOnly?: boolean;
|
|
31
31
|
});
|
|
32
|
+
/**
|
|
33
|
+
* Enables `await using accessor = ...`. Delegates to {@link close} with
|
|
34
|
+
* the default timeout — callers that need a non-default `timeoutMs`
|
|
35
|
+
* must call `close` explicitly instead of relying on disposal.
|
|
36
|
+
*
|
|
37
|
+
* Dispatches through the instance's own `close`, so an `Archive`
|
|
38
|
+
* (which overrides `close`) gets its full teardown here too — this
|
|
39
|
+
* method does not need to be re-implemented on subclasses.
|
|
40
|
+
*/
|
|
41
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
32
42
|
/**
|
|
33
43
|
* Closes the underlying database connection.
|
|
34
44
|
*
|
|
@@ -94,6 +104,13 @@ export declare class ArchiveAccessor extends EventEmitter<DatabaseEvent> {
|
|
|
94
104
|
* @returns The parsed {@link Config} object.
|
|
95
105
|
*/
|
|
96
106
|
getConfig(): Promise<Config>;
|
|
107
|
+
/**
|
|
108
|
+
* Retrieves the Web Components (custom elements) within the given page's
|
|
109
|
+
* detected main content region, from `page_main_content_custom_elements`.
|
|
110
|
+
* @param pageId - The database id of the page.
|
|
111
|
+
* @returns Ordered custom-element rows.
|
|
112
|
+
*/
|
|
113
|
+
getCustomElementsOfPage(pageId: number): Promise<import("./meta/types.js").MainContentCustomElementRow[]>;
|
|
97
114
|
/**
|
|
98
115
|
* Reads custom data stored in the archive by name.
|
|
99
116
|
* @param name - The base name of the data file (without extension).
|
|
@@ -199,6 +216,13 @@ export declare class ArchiveAccessor extends EventEmitter<DatabaseEvent> {
|
|
|
199
216
|
getPagesWithRefs(limit: number, callback: (pages: Page[], currentOffset: number, max: number) => void | Promise<void>, options?: ParseURLOptions & {
|
|
200
217
|
withRefs?: boolean;
|
|
201
218
|
}): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* Retrieves the confidence-combined technology roll-up for the given
|
|
221
|
+
* page, from the `page_technologies` table.
|
|
222
|
+
* @param pageId - The database id of the page.
|
|
223
|
+
* @returns Technology rows with category, version, confidence, signalCount.
|
|
224
|
+
*/
|
|
225
|
+
getPageTechnologiesOfPage(pageId: number): Promise<import("./meta/types.js").PageTechnologyRow[]>;
|
|
202
226
|
/**
|
|
203
227
|
* Retrieves pages that link to the specified page (incoming links).
|
|
204
228
|
* @param pageId - The database ID of the target page.
|
|
@@ -218,16 +242,18 @@ export declare class ArchiveAccessor extends EventEmitter<DatabaseEvent> {
|
|
|
218
242
|
getResources(): Promise<Resource[]>;
|
|
219
243
|
/**
|
|
220
244
|
* Retrieves a flat list of all resource URLs stored in the archive.
|
|
245
|
+
* @param onProgress - Forwarded to {@link Database.getResourceUrlList} —
|
|
246
|
+
* see that method's docs.
|
|
221
247
|
* @returns An array of resource URL strings.
|
|
222
248
|
*/
|
|
223
|
-
getResourceUrlList(): Promise<string[]>;
|
|
249
|
+
getResourceUrlList(onProgress?: (scannedUpToId: number, maxId: number) => void): Promise<string[]>;
|
|
224
250
|
/**
|
|
225
|
-
* Retrieves the
|
|
226
|
-
* from the `
|
|
251
|
+
* Retrieves the raw technology-detection signals for the given page,
|
|
252
|
+
* from the `technology_signals` table.
|
|
227
253
|
* @param pageId - The database id of the page.
|
|
228
|
-
* @returns Ordered
|
|
254
|
+
* @returns Ordered signal rows with technology, signalType, evidence, weight.
|
|
229
255
|
*/
|
|
230
|
-
|
|
256
|
+
getTechnologySignalsOfPage(pageId: number): Promise<import("./meta/types.js").TechnologySignalRow[]>;
|
|
231
257
|
/**
|
|
232
258
|
* Retrieves the videos within the given page's detected main content
|
|
233
259
|
* region, from `page_main_content_videos`.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { raceWithTimeout } from '@d-zero/shared/race-with-timeout';
|
|
2
3
|
import { TypedAwaitEventEmitter as EventEmitter } from '@d-zero/shared/typed-await-event-emitter';
|
|
3
4
|
import { log } from './debug.js';
|
|
4
5
|
import { outputJSON } from './filesystem/output-json.js';
|
|
@@ -81,6 +82,18 @@ export class ArchiveAccessor extends EventEmitter {
|
|
|
81
82
|
void this.emit('error', e);
|
|
82
83
|
});
|
|
83
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Enables `await using accessor = ...`. Delegates to {@link close} with
|
|
87
|
+
* the default timeout — callers that need a non-default `timeoutMs`
|
|
88
|
+
* must call `close` explicitly instead of relying on disposal.
|
|
89
|
+
*
|
|
90
|
+
* Dispatches through the instance's own `close`, so an `Archive`
|
|
91
|
+
* (which overrides `close`) gets its full teardown here too — this
|
|
92
|
+
* method does not need to be re-implemented on subclasses.
|
|
93
|
+
*/
|
|
94
|
+
async [Symbol.asyncDispose]() {
|
|
95
|
+
await this.close();
|
|
96
|
+
}
|
|
84
97
|
/**
|
|
85
98
|
* Closes the underlying database connection.
|
|
86
99
|
*
|
|
@@ -162,6 +175,15 @@ export class ArchiveAccessor extends EventEmitter {
|
|
|
162
175
|
async getConfig() {
|
|
163
176
|
return this.#db.getConfig();
|
|
164
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Retrieves the Web Components (custom elements) within the given page's
|
|
180
|
+
* detected main content region, from `page_main_content_custom_elements`.
|
|
181
|
+
* @param pageId - The database id of the page.
|
|
182
|
+
* @returns Ordered custom-element rows.
|
|
183
|
+
*/
|
|
184
|
+
async getCustomElementsOfPage(pageId) {
|
|
185
|
+
return this.#db.getCustomElementsOfPage(pageId);
|
|
186
|
+
}
|
|
165
187
|
async getData(name, format = 'json') {
|
|
166
188
|
const namespace = this.#namespace || '';
|
|
167
189
|
const filePath = safePath(this.#tmpDir, namespace, `${name}.${format}`);
|
|
@@ -310,6 +332,15 @@ export class ArchiveAccessor extends EventEmitter {
|
|
|
310
332
|
times++;
|
|
311
333
|
}
|
|
312
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Retrieves the confidence-combined technology roll-up for the given
|
|
337
|
+
* page, from the `page_technologies` table.
|
|
338
|
+
* @param pageId - The database id of the page.
|
|
339
|
+
* @returns Technology rows with category, version, confidence, signalCount.
|
|
340
|
+
*/
|
|
341
|
+
async getPageTechnologiesOfPage(pageId) {
|
|
342
|
+
return this.#db.getPageTechnologiesOfPage(pageId);
|
|
343
|
+
}
|
|
313
344
|
/**
|
|
314
345
|
* Retrieves pages that link to the specified page (incoming links).
|
|
315
346
|
* @param pageId - The database ID of the target page.
|
|
@@ -338,19 +369,21 @@ export class ArchiveAccessor extends EventEmitter {
|
|
|
338
369
|
}
|
|
339
370
|
/**
|
|
340
371
|
* Retrieves a flat list of all resource URLs stored in the archive.
|
|
372
|
+
* @param onProgress - Forwarded to {@link Database.getResourceUrlList} —
|
|
373
|
+
* see that method's docs.
|
|
341
374
|
* @returns An array of resource URL strings.
|
|
342
375
|
*/
|
|
343
|
-
async getResourceUrlList() {
|
|
344
|
-
return this.#db.getResourceUrlList();
|
|
376
|
+
async getResourceUrlList(onProgress) {
|
|
377
|
+
return this.#db.getResourceUrlList(onProgress);
|
|
345
378
|
}
|
|
346
379
|
/**
|
|
347
|
-
* Retrieves the
|
|
348
|
-
* from the `
|
|
380
|
+
* Retrieves the raw technology-detection signals for the given page,
|
|
381
|
+
* from the `technology_signals` table.
|
|
349
382
|
* @param pageId - The database id of the page.
|
|
350
|
-
* @returns Ordered
|
|
383
|
+
* @returns Ordered signal rows with technology, signalType, evidence, weight.
|
|
351
384
|
*/
|
|
352
|
-
async
|
|
353
|
-
return this.#db.
|
|
385
|
+
async getTechnologySignalsOfPage(pageId) {
|
|
386
|
+
return this.#db.getTechnologySignalsOfPage(pageId);
|
|
354
387
|
}
|
|
355
388
|
/**
|
|
356
389
|
* Retrieves the videos within the given page's detected main content
|
|
@@ -457,27 +490,16 @@ export class ArchiveAccessor extends EventEmitter {
|
|
|
457
490
|
await this.#db.destroy();
|
|
458
491
|
return;
|
|
459
492
|
}
|
|
460
|
-
let timer = null;
|
|
461
|
-
const timeout = new Promise((resolve) => {
|
|
462
|
-
timer = setTimeout(() => resolve('timeout'), timeoutMs);
|
|
463
|
-
});
|
|
464
493
|
// Track destroy() so we can attach an error-suppressing handler if we
|
|
465
494
|
// give up waiting — otherwise a late rejection becomes an unhandled
|
|
466
495
|
// promise rejection on the process.
|
|
467
496
|
const destroy = this.#db.destroy().then(() => 'done');
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
destroy
|
|
473
|
-
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
finally {
|
|
478
|
-
if (timer) {
|
|
479
|
-
clearTimeout(timer);
|
|
480
|
-
}
|
|
497
|
+
const { timeout } = await raceWithTimeout(() => destroy, timeoutMs);
|
|
498
|
+
if (timeout) {
|
|
499
|
+
log('ArchiveAccessor.close: db.destroy() did not settle within %dms — giving up', timeoutMs);
|
|
500
|
+
destroy.catch((error) => {
|
|
501
|
+
log('ArchiveAccessor.close: late db.destroy() rejection (post-timeout): %O', error);
|
|
502
|
+
});
|
|
481
503
|
}
|
|
482
504
|
}
|
|
483
505
|
}
|
package/lib/archive/archive.d.ts
CHANGED
|
@@ -19,16 +19,15 @@ import { ArchiveAccessor } from './archive-accessor.js';
|
|
|
19
19
|
*
|
|
20
20
|
* Use the static factory methods ({@link Archive.create}, {@link Archive.open},
|
|
21
21
|
* {@link Archive.resume}, {@link Archive.connect}) to obtain instances.
|
|
22
|
-
* The constructor is private.
|
|
22
|
+
* The constructor is private. Implements `Symbol.asyncDispose` (inherited
|
|
23
|
+
* from {@link ArchiveAccessor}) so callers can use `await using` instead of
|
|
24
|
+
* a manual `try`/`finally` around {@link close}.
|
|
23
25
|
* @example
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* // Writes the `.nitpicker` tar (if absent), removes tmpDir, releases the lock.
|
|
30
|
-
* await archive.close();
|
|
31
|
-
* }
|
|
26
|
+
* await using archive = await Archive.create({ filePath: '/path/to/site.nitpicker' });
|
|
27
|
+
* await archive.setConfig(config);
|
|
28
|
+
* const pageId = await archive.setPage(pageData);
|
|
29
|
+
* // Writes the `.nitpicker` tar (if absent), removes tmpDir, releases the
|
|
30
|
+
* // lock — all on scope exit, whether by fallthrough or thrown error.
|
|
32
31
|
*/
|
|
33
32
|
export default class Archive extends ArchiveAccessor {
|
|
34
33
|
#private;
|
|
@@ -308,17 +307,21 @@ export default class Archive extends ArchiveAccessor {
|
|
|
308
307
|
* internal pages on the next pass.
|
|
309
308
|
* @param scopes - Hostname-indexed scope map representing the new scope.
|
|
310
309
|
* @param options - URL parsing options forwarded to the scope-entry lookup.
|
|
310
|
+
* @param onProgress - Forwarded to {@link Database.repromoteExternalPages}
|
|
311
|
+
* — see that method's docs.
|
|
311
312
|
* @returns The URLs that were repromoted.
|
|
312
313
|
*/
|
|
313
|
-
repromoteExternalPages(scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions): Promise<string[]>;
|
|
314
|
+
repromoteExternalPages(scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions, onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
314
315
|
/**
|
|
315
316
|
* Reset previously-failed pages back to pending so a follow-up crawl re-fetches them.
|
|
316
317
|
*
|
|
317
318
|
* Delegates to {@link Database.resetFailedPages}. See that method for the
|
|
318
319
|
* exact failure criteria (missing status / content type, or a 5xx status).
|
|
320
|
+
* @param onProgress - Forwarded to {@link Database.resetFailedPages} —
|
|
321
|
+
* see that method's docs.
|
|
319
322
|
* @returns The URLs of the pages that were reset to pending.
|
|
320
323
|
*/
|
|
321
|
-
resetFailedPages(): Promise<string[]>;
|
|
324
|
+
resetFailedPages(onProgress?: (processed: number, total: number) => void): Promise<string[]>;
|
|
322
325
|
/**
|
|
323
326
|
* Persists the raw bytes of an `--inventory` source URL list into the
|
|
324
327
|
* archive's tar payload, at `inventory/<sha256>.txt`.
|
|
@@ -374,9 +377,12 @@ export default class Archive extends ArchiveAccessor {
|
|
|
374
377
|
* pages share a single `page_html_blobs` row.
|
|
375
378
|
* @param pageInfo - The page data to store.
|
|
376
379
|
* @param source - Provenance label for new rows. `undefined` leaves the DB DEFAULT (`'crawled'`).
|
|
380
|
+
* @param bodyHash - Precomputed body hash for the page's HTML (see
|
|
381
|
+
* `CrawlerEventTypes.page.bodyHash`). `undefined`/`null` falls back to
|
|
382
|
+
* computing it from the HTML instead.
|
|
377
383
|
* @returns The database ID of the stored page.
|
|
378
384
|
*/
|
|
379
|
-
setPage(pageInfo: PageData, source?: PageSource): Promise<number>;
|
|
385
|
+
setPage(pageInfo: PageData, source?: PageSource, bodyHash?: Buffer | null): Promise<number>;
|
|
380
386
|
/**
|
|
381
387
|
* Records a redirect edge without re-storing the destination's content.
|
|
382
388
|
*
|
|
@@ -418,8 +424,10 @@ export default class Archive extends ArchiveAccessor {
|
|
|
418
424
|
/**
|
|
419
425
|
* Assigns natural URL sort order values to all pages in the database
|
|
420
426
|
* that do not yet have an `order` field set.
|
|
427
|
+
* @param onProgress - Forwarded to {@link Database.setUrlOrder} — see that
|
|
428
|
+
* method's docs.
|
|
421
429
|
*/
|
|
422
|
-
setUrlOrder(): Promise<void>;
|
|
430
|
+
setUrlOrder(onProgress?: (processed: number, total: number) => void): Promise<void>;
|
|
423
431
|
/**
|
|
424
432
|
* Updates a subset of fields on the archive's `info` row. Used by the append
|
|
425
433
|
* flow to extend `roots` / `scope` without rewriting the entire config.
|
|
@@ -442,8 +450,26 @@ export default class Archive extends ArchiveAccessor {
|
|
|
442
450
|
* `db.sqlite`, so a re-crawl (`append` / `inventory` / `retryFailed`)
|
|
443
451
|
* opened without it would tar back a tmpDir missing those extra files,
|
|
444
452
|
* silently dropping them from the rewritten archive.
|
|
445
|
-
|
|
446
|
-
|
|
453
|
+
* @param options - Optional write settings.
|
|
454
|
+
* @param options.onTarProgress - Called as archive bytes are written
|
|
455
|
+
* during the tar step, with the bytes written so far and the estimated
|
|
456
|
+
* total (issue #294: tarring a large archive takes minutes, and
|
|
457
|
+
* without this the CLI shows nothing until `write` returns). Omit for
|
|
458
|
+
* a silent write (the default).
|
|
459
|
+
* @param options.onStep - Called once at the start of each of this
|
|
460
|
+
* method's four steps (issue #294): `checkpoint` (WAL fold-back —
|
|
461
|
+
* single synchronous PRAGMA, no countable progress) and `remove`
|
|
462
|
+
* (deleting the tarred-away tmpDir) have no progress signal of their
|
|
463
|
+
* own, so without this a large archive's write looks frozen between
|
|
464
|
+
* the `tar` step's byte updates and completion. `rename` is nearly
|
|
465
|
+
* instant (same-filesystem directory move) but included for
|
|
466
|
+
* completeness — a caller displaying phase labels shouldn't have a
|
|
467
|
+
* gap where the operation is silently between named steps.
|
|
468
|
+
*/
|
|
469
|
+
write(options?: {
|
|
470
|
+
onTarProgress?: (writtenBytes: number, totalBytes: number) => void;
|
|
471
|
+
onStep?: (step: 'checkpoint' | 'rename' | 'tar' | 'remove') => void;
|
|
472
|
+
}): Promise<void>;
|
|
447
473
|
/** The file extension for Nitpicker archive files (without the leading dot). */
|
|
448
474
|
static FILE_EXTENSION: string;
|
|
449
475
|
/** The filename of the SQLite database within the archive. */
|
|
@@ -464,27 +490,38 @@ export default class Archive extends ArchiveAccessor {
|
|
|
464
490
|
* on the user's tmpDir.
|
|
465
491
|
*
|
|
466
492
|
* `options.readOnly: false` is a narrow escape hatch for opening a
|
|
467
|
-
* second, writable connection to a `tmpDir`
|
|
468
|
-
* already
|
|
469
|
-
*
|
|
470
|
-
* read-
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
493
|
+
* second, writable connection to a `tmpDir` the caller's own process
|
|
494
|
+
* already owns and extracted itself. The one production caller is the
|
|
495
|
+
* viewer-read-model worker thread (`@nitpicker/query`'s
|
|
496
|
+
* `viewer-read-model-worker-entry.ts`, issue #294): the parent thread
|
|
497
|
+
* holds the archive via `Archive.open` (lock included — worker threads
|
|
498
|
+
* share the parent's PID, so the PID-based `acquireArchiveLock` guard
|
|
499
|
+
* stays valid), sits idle awaiting the worker, and re-tars the tmpDir
|
|
500
|
+
* afterward. What this hatch must NEVER target is a live/interrupted
|
|
501
|
+
* crawl tmpDir owned by a *different* process (the stub-mode
|
|
502
|
+
* `ArchiveManager.open` path attaches to exactly such directories, and
|
|
503
|
+
* must stay read-only): writable connects run the self-healing
|
|
504
|
+
* migrations, and mutating a directory out from under its owner is how
|
|
505
|
+
* archives corrupt. A read-only open (`Archive.openCached`/
|
|
506
|
+
* `ArchiveManager.open`) must never take this path itself — blocking or
|
|
507
|
+
* writing during what must be a read-only open is forbidden (issue
|
|
508
|
+
* #177). Any new caller is responsible for its own coordination with
|
|
509
|
+
* the tmpDir's owner (see `acquireArchiveLock` for the cross-process
|
|
510
|
+
* case) — this method does not acquire any lock itself.
|
|
476
511
|
* @param tmpDir - The path to the temporary directory containing the database.
|
|
477
512
|
* @param namespace - An optional namespace for scoping data access within the archive.
|
|
478
513
|
* @param options - Connection options.
|
|
479
514
|
* @param options.readOnly - Defaults to `true`. Pass `false` to obtain a
|
|
480
|
-
* writable accessor against
|
|
515
|
+
* writable accessor against a tmpDir the calling process itself owns.
|
|
481
516
|
* @returns An ArchiveAccessor instance for querying the archive data.
|
|
482
517
|
* @example
|
|
483
518
|
* // Default (read-only) — safe for stub mode and cache reads:
|
|
484
519
|
* const accessor = await Archive.connect(tmpDir);
|
|
485
520
|
* @example
|
|
486
|
-
* // Writable escape hatch — only against a
|
|
487
|
-
*
|
|
521
|
+
* // Writable escape hatch — only against a tmpDir this process owns
|
|
522
|
+
* // (e.g. the viewer-read-model worker thread reconnecting to the
|
|
523
|
+
* // parent's Archive.open extraction):
|
|
524
|
+
* const writable = await Archive.connect(ownTmpDir, null, { readOnly: false });
|
|
488
525
|
*/
|
|
489
526
|
static connect(tmpDir: string, namespace?: string | null, options?: {
|
|
490
527
|
readOnly?: boolean;
|
|
@@ -522,18 +559,17 @@ export default class Archive extends ArchiveAccessor {
|
|
|
522
559
|
* {@link Archive.open}.
|
|
523
560
|
* @param filePath - Absolute path to the `.nitpicker` file.
|
|
524
561
|
* @param namespace - Optional namespace forwarded to {@link ArchiveAccessor}.
|
|
562
|
+
* @param onExtractProgress - Forwarded to {@link extractArchiveToCache} —
|
|
563
|
+
* see that function's docs for the cache-hit/miss contract.
|
|
525
564
|
* @returns A read-only {@link ArchiveAccessor} backed by the cache directory.
|
|
526
565
|
* @example
|
|
527
566
|
* ```ts
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
* } finally {
|
|
532
|
-
* await accessor.close(); // tears down DB handle, cacheDir persists.
|
|
533
|
-
* }
|
|
567
|
+
* await using accessor = await Archive.openCached('/path/to/site.nitpicker');
|
|
568
|
+
* const summary = await getSummary(accessor);
|
|
569
|
+
* // tears down DB handle on scope exit; cacheDir persists.
|
|
534
570
|
* ```
|
|
535
571
|
*/
|
|
536
|
-
static openCached(filePath: string, namespace?: string | null): Promise<ArchiveAccessor>;
|
|
572
|
+
static openCached(filePath: string, namespace?: string | null, onExtractProgress?: (readBytes: number, totalBytes: number) => void): Promise<ArchiveAccessor>;
|
|
537
573
|
/**
|
|
538
574
|
* Creates a new archive at the specified file path.
|
|
539
575
|
* Initializes a temporary working directory and a fresh SQLite database.
|
|
@@ -558,10 +594,13 @@ export default class Archive extends ArchiveAccessor {
|
|
|
558
594
|
* Resumes an archive from an existing temporary directory
|
|
559
595
|
* (e.g., after an interrupted crawl session).
|
|
560
596
|
* @param targetPath - The path to the temporary directory to resume from.
|
|
597
|
+
* @param onLog - Forwarded to {@link Database.connect} — see
|
|
598
|
+
* {@link ArchiveOpenOptions.onLog}'s docs (this writable reconnect
|
|
599
|
+
* runs the same self-healing migrations `Archive.open` does).
|
|
561
600
|
* @returns An Archive instance reconnected to the existing data.
|
|
562
601
|
* @throws {Error} If the specified path is not a directory.
|
|
563
602
|
*/
|
|
564
|
-
static resume(targetPath: string): Promise<Archive>;
|
|
603
|
+
static resume(targetPath: string, onLog?: (message: string) => void): Promise<Archive>;
|
|
565
604
|
/**
|
|
566
605
|
* Generates a timestamp string in the format `YYYYMMDDHHmmssSSS`
|
|
567
606
|
* suitable for use in file names.
|
|
@@ -588,8 +627,28 @@ export default class Archive extends ArchiveAccessor {
|
|
|
588
627
|
* {@link ArchiveAccessor} (not an `Archive`), so `close()` resolves to
|
|
589
628
|
* the safe base implementation — no `write()`, no `remove()`, no lock
|
|
590
629
|
* release — leaving the tmpDir intact for the live crawler.
|
|
591
|
-
|
|
592
|
-
|
|
630
|
+
* @param options - Optional close settings. `timeoutMs` is accepted for
|
|
631
|
+
* compatibility with {@link ArchiveAccessor.close}'s signature (forwarded
|
|
632
|
+
* to the base `super.close()` call below); the rest are progress
|
|
633
|
+
* callbacks (issue #294) forwarded to {@link write} when this call ends
|
|
634
|
+
* up taking the recovery-write branch (the archive file doesn't exist
|
|
635
|
+
* yet).
|
|
636
|
+
* @param options.timeoutMs - See {@link ArchiveAccessor.close}.
|
|
637
|
+
* @param options.onRecoveryStart - Called once, only when this `close()`
|
|
638
|
+
* is about to write the archive because the file doesn't exist on
|
|
639
|
+
* disk yet — e.g. a caller's own explicit `write()` threw before
|
|
640
|
+
* finishing, or was never called at all. Without this, a listener
|
|
641
|
+
* that already tore down its display after that earlier failure has
|
|
642
|
+
* no way to know a second, recovery write is happening.
|
|
643
|
+
* @param options.onTarProgress - See {@link write}.
|
|
644
|
+
* @param options.onStep - See {@link write}.
|
|
645
|
+
*/
|
|
646
|
+
close(options?: {
|
|
647
|
+
timeoutMs?: number;
|
|
648
|
+
onRecoveryStart?: () => void;
|
|
649
|
+
onTarProgress?: (writtenBytes: number, totalBytes: number) => void;
|
|
650
|
+
onStep?: (step: 'checkpoint' | 'rename' | 'tar' | 'remove') => void;
|
|
651
|
+
}): Promise<void>;
|
|
593
652
|
/**
|
|
594
653
|
* Retrieves the crawl configuration stored in the archive database.
|
|
595
654
|
* @returns The configuration object.
|
|
@@ -623,5 +682,24 @@ type ArchiveOpenOptions = {
|
|
|
623
682
|
* dropping them from the rewritten archive.
|
|
624
683
|
*/
|
|
625
684
|
openPluginData?: boolean;
|
|
685
|
+
/**
|
|
686
|
+
* Called as archive bytes are consumed during the initial tar
|
|
687
|
+
* extraction, with the bytes read so far and the archive's total size
|
|
688
|
+
* (issue #294: a large archive takes minutes to extract, and without
|
|
689
|
+
* this the CLI shows nothing at all until `open` returns). Byte
|
|
690
|
+
* granularity is the read-stream chunk size — throttle in the callback
|
|
691
|
+
* for coarser display updates. Omit for a silent extraction (the
|
|
692
|
+
* default).
|
|
693
|
+
*/
|
|
694
|
+
onExtractProgress?: (readBytes: number, totalBytes: number) => void;
|
|
695
|
+
/**
|
|
696
|
+
* Called instead of `console.error` for self-healing schema migration
|
|
697
|
+
* notices that fire while opening a legacy archive (issue #294) —
|
|
698
|
+
* forwarded to {@link Database.connect}'s `onLog`. Without this, a
|
|
699
|
+
* migration notice can print mid-redraw of a caller's `Lanes`/`TaskList`
|
|
700
|
+
* display, corrupting its cursor tracking. Omit to fall back to
|
|
701
|
+
* `console.error` (the pre-#294 behavior).
|
|
702
|
+
*/
|
|
703
|
+
onLog?: (message: string) => void;
|
|
626
704
|
};
|
|
627
705
|
export {};
|