@nitpicker/crawler 0.14.0 → 0.16.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.
Files changed (155) hide show
  1. package/lib/archive/archive.d.ts +136 -10
  2. package/lib/archive/archive.js +147 -9
  3. package/lib/archive/body-hash/compute-body-hash.d.ts +22 -0
  4. package/lib/archive/body-hash/compute-body-hash.js +31 -0
  5. package/lib/archive/body-hash/extract-body.d.ts +18 -0
  6. package/lib/archive/body-hash/extract-body.js +29 -0
  7. package/lib/archive/body-hash/mask-dynamic-ids.d.ts +33 -0
  8. package/lib/archive/body-hash/mask-dynamic-ids.js +44 -0
  9. package/lib/archive/body-hash/normalize-url-like-strings.d.ts +16 -0
  10. package/lib/archive/body-hash/normalize-url-like-strings.js +19 -0
  11. package/lib/archive/cache/clear-archive-cache-entry.d.ts +19 -0
  12. package/lib/archive/cache/clear-archive-cache-entry.js +25 -0
  13. package/lib/archive/cache/clear-archive-cache-root.d.ts +26 -0
  14. package/lib/archive/cache/clear-archive-cache-root.js +32 -0
  15. package/lib/archive/cache/list-archive-cache-entries.d.ts +23 -0
  16. package/lib/archive/cache/list-archive-cache-entries.js +125 -0
  17. package/lib/archive/cache/path-exists.d.ts +9 -0
  18. package/lib/archive/cache/path-exists.js +18 -0
  19. package/lib/archive/cache/types.d.ts +33 -0
  20. package/lib/archive/cache/types.js +1 -0
  21. package/lib/archive/create-adjunct-tables.d.ts +14 -0
  22. package/lib/archive/create-adjunct-tables.js +169 -0
  23. package/lib/archive/create-entity-tables.js +21 -1
  24. package/lib/archive/database.d.ts +67 -2
  25. package/lib/archive/database.js +92 -3
  26. package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +1 -0
  27. package/lib/archive/db-ops/_shared/compress-payload.d.ts +26 -0
  28. package/lib/archive/db-ops/_shared/compress-payload.js +30 -0
  29. package/lib/archive/db-ops/_shared/create-write-ref-caches.js +1 -0
  30. package/lib/archive/db-ops/_shared/types.d.ts +2 -0
  31. package/lib/archive/db-ops/_shared/upsert-json-ref.js +3 -3
  32. package/lib/archive/db-ops/analysis/replace-page-templates.d.ts +12 -3
  33. package/lib/archive/db-ops/analysis/replace-page-templates.js +35 -3
  34. package/lib/archive/db-ops/analysis/types.d.ts +61 -0
  35. package/lib/archive/db-ops/analysis/types.js +1 -0
  36. package/lib/archive/db-ops/console-logs/compute-console-log-hash.d.ts +19 -0
  37. package/lib/archive/db-ops/console-logs/compute-console-log-hash.js +30 -0
  38. package/lib/archive/db-ops/console-logs/replace-console-logs.d.ts +34 -0
  39. package/lib/archive/db-ops/console-logs/replace-console-logs.js +123 -0
  40. package/lib/archive/db-ops/console-logs/stringify-console-log-args.d.ts +16 -0
  41. package/lib/archive/db-ops/console-logs/stringify-console-log-args.js +26 -0
  42. package/lib/archive/db-ops/console-logs/types.d.ts +38 -0
  43. package/lib/archive/db-ops/console-logs/types.js +1 -0
  44. package/lib/archive/db-ops/console-logs/upsert-console-log-item.d.ts +29 -0
  45. package/lib/archive/db-ops/console-logs/upsert-console-log-item.js +52 -0
  46. package/lib/archive/db-ops/dedupe-cap/accumulate-dedupe-cap-rejected-count.d.ts +18 -0
  47. package/lib/archive/db-ops/dedupe-cap/accumulate-dedupe-cap-rejected-count.js +23 -0
  48. package/lib/archive/db-ops/dedupe-cap/finalize-dedupe-cap-event.d.ts +12 -0
  49. package/lib/archive/db-ops/dedupe-cap/finalize-dedupe-cap-event.js +15 -0
  50. package/lib/archive/db-ops/dedupe-cap/insert-dedupe-cap-event.d.ts +14 -0
  51. package/lib/archive/db-ops/dedupe-cap/insert-dedupe-cap-event.js +30 -0
  52. package/lib/archive/db-ops/dedupe-cap/list-dedupe-cap-shape-keys.d.ts +21 -0
  53. package/lib/archive/db-ops/dedupe-cap/list-dedupe-cap-shape-keys.js +27 -0
  54. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +11 -0
  55. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +21 -4
  56. package/lib/archive/db-ops/inventory/record-inventory-run.js +1 -0
  57. package/lib/archive/db-ops/lifecycle/init.d.ts +11 -2
  58. package/lib/archive/db-ops/lifecycle/init.js +21 -2
  59. package/lib/archive/db-ops/outages/close-network-outage.d.ts +12 -0
  60. package/lib/archive/db-ops/outages/close-network-outage.js +15 -0
  61. package/lib/archive/db-ops/outages/close-stale-open-network-outages.d.ts +26 -0
  62. package/lib/archive/db-ops/outages/close-stale-open-network-outages.js +42 -0
  63. package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.d.ts +26 -0
  64. package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.js +31 -0
  65. package/lib/archive/db-ops/outages/insert-network-outage.d.ts +14 -0
  66. package/lib/archive/db-ops/outages/insert-network-outage.js +29 -0
  67. package/lib/archive/db-ops/outages/list-network-outages.d.ts +28 -0
  68. package/lib/archive/db-ops/outages/list-network-outages.js +42 -0
  69. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +13 -0
  70. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +35 -7
  71. package/lib/archive/db-ops/pages/write/update-page.js +12 -0
  72. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +6 -6
  73. package/lib/archive/filesystem/output-binary.d.ts +12 -0
  74. package/lib/archive/filesystem/output-binary.js +17 -0
  75. package/lib/archive/get-failed-page-messages.d.ts +20 -8
  76. package/lib/archive/get-failed-page-messages.js +29 -15
  77. package/lib/archive/migrate-content-items-alias-of-id.d.ts +47 -0
  78. package/lib/archive/migrate-content-items-alias-of-id.js +58 -0
  79. package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +11 -0
  80. package/lib/archive/migrate-inventory-runs-invalid-skipped.js +24 -0
  81. package/lib/archive/migrate-page-meta-body-hash.d.ts +39 -0
  82. package/lib/archive/migrate-page-meta-body-hash.js +52 -0
  83. package/lib/archive/migrate-page-meta-console-error-count.d.ts +22 -0
  84. package/lib/archive/migrate-page-meta-console-error-count.js +34 -0
  85. package/lib/archive/page.d.ts +1 -1
  86. package/lib/archive/types.d.ts +60 -2
  87. package/lib/archive/url-alias/compute-tier-a-alias-key.d.ts +31 -0
  88. package/lib/archive/url-alias/compute-tier-a-alias-key.js +36 -0
  89. package/lib/archive/url-alias/compute-tier-b-alias-key.d.ts +19 -0
  90. package/lib/archive/url-alias/compute-tier-b-alias-key.js +30 -0
  91. package/lib/archive/url-alias/format-alias-key.d.ts +13 -0
  92. package/lib/archive/url-alias/format-alias-key.js +14 -0
  93. package/lib/archive/url-alias/parse-alias-key-parts.d.ts +15 -0
  94. package/lib/archive/url-alias/parse-alias-key-parts.js +40 -0
  95. package/lib/archive/url-alias/types.d.ts +7 -0
  96. package/lib/archive/url-alias/types.js +1 -0
  97. package/lib/classify-error-kind.d.ts +1 -0
  98. package/lib/classify-error-kind.js +14 -0
  99. package/lib/crawler/assert-chrome-installed.d.ts +24 -0
  100. package/lib/crawler/assert-chrome-installed.js +43 -0
  101. package/lib/crawler/choose-probe-host.d.ts +24 -0
  102. package/lib/crawler/choose-probe-host.js +38 -0
  103. package/lib/crawler/clear-dns-burned-host-cache.js +2 -0
  104. package/lib/crawler/crawler.d.ts +12 -0
  105. package/lib/crawler/crawler.js +496 -29
  106. package/lib/crawler/decode-auth-credential.d.ts +29 -0
  107. package/lib/crawler/decode-auth-credential.js +39 -0
  108. package/lib/crawler/dedupe/compute-meta-signature.d.ts +30 -0
  109. package/lib/crawler/dedupe/compute-meta-signature.js +0 -0
  110. package/lib/crawler/dedupe/compute-shape-key.d.ts +37 -0
  111. package/lib/crawler/dedupe/compute-shape-key.js +56 -0
  112. package/lib/crawler/dedupe/dedupe-cap-tracker.d.ts +84 -0
  113. package/lib/crawler/dedupe/dedupe-cap-tracker.js +185 -0
  114. package/lib/crawler/dedupe/is-predicted-content-duplicate.d.ts +24 -0
  115. package/lib/crawler/dedupe/is-predicted-content-duplicate.js +26 -0
  116. package/lib/crawler/dedupe/is-shape-capped.d.ts +10 -0
  117. package/lib/crawler/dedupe/is-shape-capped.js +12 -0
  118. package/lib/crawler/dedupe/resolve-og-url-mismatch.d.ts +31 -0
  119. package/lib/crawler/dedupe/resolve-og-url-mismatch.js +40 -0
  120. package/lib/crawler/dedupe/types.d.ts +42 -0
  121. package/lib/crawler/dedupe/types.js +1 -0
  122. package/lib/crawler/dns-burned-host-burn-timestamps.d.ts +17 -0
  123. package/lib/crawler/dns-burned-host-burn-timestamps.js +17 -0
  124. package/lib/crawler/evict-network-classified-destination-cache-entries.d.ts +26 -0
  125. package/lib/crawler/evict-network-classified-destination-cache-entries.js +34 -0
  126. package/lib/crawler/evict-outage-tainted-dns-burns.d.ts +23 -0
  127. package/lib/crawler/evict-outage-tainted-dns-burns.js +26 -0
  128. package/lib/crawler/fetch-destination.js +14 -2
  129. package/lib/crawler/generate-predicted-urls.d.ts +12 -0
  130. package/lib/crawler/generate-predicted-urls.js +33 -2
  131. package/lib/crawler/is-puppeteer-fallback-candidate.js +3 -0
  132. package/lib/crawler/network-gate.d.ts +49 -0
  133. package/lib/crawler/network-gate.js +78 -0
  134. package/lib/crawler/network-outage-detector.d.ts +51 -0
  135. package/lib/crawler/network-outage-detector.js +81 -0
  136. package/lib/crawler/network-outage-summary-counter.d.ts +21 -0
  137. package/lib/crawler/network-outage-summary-counter.js +18 -0
  138. package/lib/crawler/probe-network.d.ts +26 -0
  139. package/lib/crawler/probe-network.js +21 -0
  140. package/lib/crawler/types.d.ts +185 -1
  141. package/lib/crawler-orchestrator.d.ts +74 -18
  142. package/lib/crawler-orchestrator.js +267 -27
  143. package/lib/crawler.d.ts +22 -0
  144. package/lib/crawler.js +18 -0
  145. package/lib/is-within-outage-window.d.ts +49 -0
  146. package/lib/is-within-outage-window.js +33 -0
  147. package/lib/network-related-error-kinds.d.ts +23 -0
  148. package/lib/network-related-error-kinds.js +28 -0
  149. package/lib/permanent-error-kinds.d.ts +9 -4
  150. package/lib/permanent-error-kinds.js +10 -4
  151. package/lib/types.d.ts +11 -2
  152. package/lib/utils/array/each-splitted.d.ts +1 -1
  153. package/lib/utils/compute-file-sha256.d.ts +17 -17
  154. package/lib/utils/compute-file-sha256.js +18 -47
  155. package/package.json +3 -3
@@ -0,0 +1,43 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import pkg from '../../package.json' with { type: 'json' };
4
+ /**
5
+ * Verifies that Puppeteer can resolve an installed Chrome/Chromium executable
6
+ * before a crawl starts.
7
+ *
8
+ * A crawl otherwise only discovers a missing browser deep inside the
9
+ * per-URL scrape loop (`Crawler#_launchBrowserAndScrape`), where it surfaces
10
+ * as one more scrape error among many — the CLI still prints "Crawl
11
+ * completed" and writes an archive, so a missing Chrome (a fatal
12
+ * precondition, not a per-page failure) is easy to miss. Calling this once,
13
+ * before any archive I/O begins, turns it into an immediate, actionable
14
+ * failure instead.
15
+ * @param executablePath - Explicit override, matching
16
+ * {@link CrawlerOptions.executablePath}. Pass `null` (or omit) to check
17
+ * Puppeteer's own pinned Chrome resolution instead.
18
+ * @throws {Error} When the resolved executable path does not exist on disk.
19
+ * @example
20
+ * ```ts
21
+ * import { assertChromeIsInstalled } from '@nitpicker/crawler';
22
+ *
23
+ * // Throws with install instructions before any crawl work starts.
24
+ * await assertChromeIsInstalled();
25
+ * ```
26
+ */
27
+ export async function assertChromeIsInstalled(executablePath) {
28
+ if (executablePath) {
29
+ const execPath = path.resolve(executablePath);
30
+ if (existsSync(execPath)) {
31
+ return;
32
+ }
33
+ throw new Error(`Executable path does not exist: ${execPath}`);
34
+ }
35
+ const puppeteer = await import('puppeteer');
36
+ const resolvedPath = await puppeteer.executablePath();
37
+ if (existsSync(resolvedPath)) {
38
+ return;
39
+ }
40
+ const puppeteerVersion = pkg.dependencies.puppeteer;
41
+ throw new Error(`Chrome executable not found at: ${resolvedPath}\n` +
42
+ `Run \`npx puppeteer@${puppeteerVersion} browsers install chrome\` to install the Chrome build Puppeteer expects, then retry.`);
43
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Pick the hostname a network-outage probe should target.
3
+ *
4
+ * Prefers a host that has already answered successfully during this crawl
5
+ * session (`Crawler.#successfulHosts` — the same "proven alive" evidence
6
+ * `shouldBurnHost` uses) over a root URL's hostname, and never falls back to
7
+ * a hardcoded external address (e.g. `1.1.1.1`): the probe should depend
8
+ * only on infrastructure the crawl is already touching. `ReadonlySet`
9
+ * iteration order in JS is insertion order, so this deterministically picks
10
+ * the first host to have succeeded this session.
11
+ * @param successfulHosts - Hostnames observed to respond in this session.
12
+ * @param roots - The crawl's configured root URLs (`CrawlerOptions.roots`),
13
+ * used as a fallback before any host has succeeded yet.
14
+ * @returns A probe target hostname, or `null` if neither source yields one
15
+ * (e.g. a fresh session with a malformed/empty roots list and no
16
+ * successes yet — the caller should treat this as "cannot probe").
17
+ * @example
18
+ * ```ts
19
+ * chooseProbeHost(new Set(['a.example']), ['https://b.example/']); // 'a.example'
20
+ * chooseProbeHost(new Set(), ['https://b.example/']); // 'b.example'
21
+ * chooseProbeHost(new Set(), []); // null
22
+ * ```
23
+ */
24
+ export declare function chooseProbeHost(successfulHosts: ReadonlySet<string>, roots: readonly string[]): string | null;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Pick the hostname a network-outage probe should target.
3
+ *
4
+ * Prefers a host that has already answered successfully during this crawl
5
+ * session (`Crawler.#successfulHosts` — the same "proven alive" evidence
6
+ * `shouldBurnHost` uses) over a root URL's hostname, and never falls back to
7
+ * a hardcoded external address (e.g. `1.1.1.1`): the probe should depend
8
+ * only on infrastructure the crawl is already touching. `ReadonlySet`
9
+ * iteration order in JS is insertion order, so this deterministically picks
10
+ * the first host to have succeeded this session.
11
+ * @param successfulHosts - Hostnames observed to respond in this session.
12
+ * @param roots - The crawl's configured root URLs (`CrawlerOptions.roots`),
13
+ * used as a fallback before any host has succeeded yet.
14
+ * @returns A probe target hostname, or `null` if neither source yields one
15
+ * (e.g. a fresh session with a malformed/empty roots list and no
16
+ * successes yet — the caller should treat this as "cannot probe").
17
+ * @example
18
+ * ```ts
19
+ * chooseProbeHost(new Set(['a.example']), ['https://b.example/']); // 'a.example'
20
+ * chooseProbeHost(new Set(), ['https://b.example/']); // 'b.example'
21
+ * chooseProbeHost(new Set(), []); // null
22
+ * ```
23
+ */
24
+ export function chooseProbeHost(successfulHosts, roots) {
25
+ const [firstSuccessfulHost] = successfulHosts;
26
+ if (firstSuccessfulHost !== undefined) {
27
+ return firstSuccessfulHost;
28
+ }
29
+ for (const root of roots) {
30
+ try {
31
+ return new URL(root).hostname;
32
+ }
33
+ catch {
34
+ continue;
35
+ }
36
+ }
37
+ return null;
38
+ }
@@ -1,3 +1,4 @@
1
+ import { dnsBurnedHostBurnTimestamps } from './dns-burned-host-burn-timestamps.js';
1
2
  import { dnsBurnedHostCache } from './dns-burned-host-cache.js';
2
3
  import { dnsBurnedHostShortCircuitCounter } from './dns-burned-host-short-circuit-counter.js';
3
4
  /**
@@ -8,4 +9,5 @@ import { dnsBurnedHostShortCircuitCounter } from './dns-burned-host-short-circui
8
9
  export function clearDnsBurnedHostCache() {
9
10
  dnsBurnedHostCache.clear();
10
11
  dnsBurnedHostShortCircuitCounter.count = 0;
12
+ dnsBurnedHostBurnTimestamps.clear();
11
13
  }
@@ -37,6 +37,18 @@ export default class Crawler extends EventEmitter<CrawlerEventTypes> {
37
37
  * {@link #runDeal}.
38
38
  */
39
39
  abort(): void;
40
+ /**
41
+ * Per-shape count of anchors the dedupe-cap enqueue gates rejected after
42
+ * that shape capped (opt-in `--dedupe-cap`). Read by
43
+ * `CrawlerOrchestrator` at `crawlEnd` to finalize each
44
+ * `dedupe_cap_events.rejected_count` exactly once — rejections are
45
+ * accumulated in memory rather than written to the archive per-rejection
46
+ * to avoid write amplification (a capped trap can generate an unbounded
47
+ * number of rejected anchors).
48
+ * @returns A snapshot of the per-shape rejection counts. Empty when
49
+ * `--dedupe-cap` was not enabled or no shape has capped yet.
50
+ */
51
+ getDedupeCapRejections(): ReadonlyMap<string, number>;
40
52
  /**
41
53
  * Retrieve the list of Chromium process IDs that are still running.
42
54
  *