@nitpicker/crawler 0.14.0 → 0.15.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 (120) hide show
  1. package/lib/archive/archive.d.ts +98 -10
  2. package/lib/archive/archive.js +98 -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 +11 -0
  22. package/lib/archive/create-adjunct-tables.js +127 -0
  23. package/lib/archive/create-entity-tables.js +21 -1
  24. package/lib/archive/database.d.ts +37 -2
  25. package/lib/archive/database.js +50 -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/errors/list-dns-burned-host-candidates.d.ts +11 -0
  47. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +21 -4
  48. package/lib/archive/db-ops/inventory/record-inventory-run.js +1 -0
  49. package/lib/archive/db-ops/lifecycle/init.d.ts +11 -2
  50. package/lib/archive/db-ops/lifecycle/init.js +21 -2
  51. package/lib/archive/db-ops/outages/close-network-outage.d.ts +12 -0
  52. package/lib/archive/db-ops/outages/close-network-outage.js +15 -0
  53. package/lib/archive/db-ops/outages/close-stale-open-network-outages.d.ts +26 -0
  54. package/lib/archive/db-ops/outages/close-stale-open-network-outages.js +42 -0
  55. package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.d.ts +26 -0
  56. package/lib/archive/db-ops/outages/compute-outage-clamp-timestamp.js +31 -0
  57. package/lib/archive/db-ops/outages/insert-network-outage.d.ts +14 -0
  58. package/lib/archive/db-ops/outages/insert-network-outage.js +29 -0
  59. package/lib/archive/db-ops/outages/list-network-outages.d.ts +28 -0
  60. package/lib/archive/db-ops/outages/list-network-outages.js +42 -0
  61. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +13 -0
  62. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +35 -7
  63. package/lib/archive/db-ops/pages/write/update-page.js +12 -0
  64. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +6 -6
  65. package/lib/archive/filesystem/output-binary.d.ts +12 -0
  66. package/lib/archive/filesystem/output-binary.js +17 -0
  67. package/lib/archive/get-failed-page-messages.d.ts +20 -8
  68. package/lib/archive/get-failed-page-messages.js +29 -15
  69. package/lib/archive/migrate-content-items-alias-of-id.d.ts +47 -0
  70. package/lib/archive/migrate-content-items-alias-of-id.js +58 -0
  71. package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +11 -0
  72. package/lib/archive/migrate-inventory-runs-invalid-skipped.js +24 -0
  73. package/lib/archive/migrate-page-meta-body-hash.d.ts +39 -0
  74. package/lib/archive/migrate-page-meta-body-hash.js +52 -0
  75. package/lib/archive/migrate-page-meta-console-error-count.d.ts +22 -0
  76. package/lib/archive/migrate-page-meta-console-error-count.js +34 -0
  77. package/lib/archive/page.d.ts +1 -1
  78. package/lib/archive/types.d.ts +47 -2
  79. package/lib/archive/url-alias/compute-tier-a-alias-key.d.ts +31 -0
  80. package/lib/archive/url-alias/compute-tier-a-alias-key.js +36 -0
  81. package/lib/archive/url-alias/compute-tier-b-alias-key.d.ts +19 -0
  82. package/lib/archive/url-alias/compute-tier-b-alias-key.js +30 -0
  83. package/lib/archive/url-alias/format-alias-key.d.ts +13 -0
  84. package/lib/archive/url-alias/format-alias-key.js +14 -0
  85. package/lib/archive/url-alias/parse-alias-key-parts.d.ts +15 -0
  86. package/lib/archive/url-alias/parse-alias-key-parts.js +40 -0
  87. package/lib/archive/url-alias/types.d.ts +7 -0
  88. package/lib/archive/url-alias/types.js +1 -0
  89. package/lib/crawler/choose-probe-host.d.ts +24 -0
  90. package/lib/crawler/choose-probe-host.js +38 -0
  91. package/lib/crawler/clear-dns-burned-host-cache.js +2 -0
  92. package/lib/crawler/crawler.js +257 -0
  93. package/lib/crawler/dns-burned-host-burn-timestamps.d.ts +17 -0
  94. package/lib/crawler/dns-burned-host-burn-timestamps.js +17 -0
  95. package/lib/crawler/evict-network-classified-destination-cache-entries.d.ts +26 -0
  96. package/lib/crawler/evict-network-classified-destination-cache-entries.js +34 -0
  97. package/lib/crawler/evict-outage-tainted-dns-burns.d.ts +23 -0
  98. package/lib/crawler/evict-outage-tainted-dns-burns.js +26 -0
  99. package/lib/crawler/network-gate.d.ts +49 -0
  100. package/lib/crawler/network-gate.js +78 -0
  101. package/lib/crawler/network-outage-detector.d.ts +51 -0
  102. package/lib/crawler/network-outage-detector.js +81 -0
  103. package/lib/crawler/network-outage-summary-counter.d.ts +21 -0
  104. package/lib/crawler/network-outage-summary-counter.js +18 -0
  105. package/lib/crawler/probe-network.d.ts +26 -0
  106. package/lib/crawler/probe-network.js +21 -0
  107. package/lib/crawler/types.d.ts +147 -1
  108. package/lib/crawler-orchestrator.d.ts +62 -18
  109. package/lib/crawler-orchestrator.js +161 -26
  110. package/lib/crawler.d.ts +21 -0
  111. package/lib/crawler.js +17 -0
  112. package/lib/is-within-outage-window.d.ts +49 -0
  113. package/lib/is-within-outage-window.js +33 -0
  114. package/lib/network-related-error-kinds.d.ts +23 -0
  115. package/lib/network-related-error-kinds.js +28 -0
  116. package/lib/types.d.ts +9 -1
  117. package/lib/utils/array/each-splitted.d.ts +1 -1
  118. package/lib/utils/compute-file-sha256.d.ts +17 -17
  119. package/lib/utils/compute-file-sha256.js +18 -47
  120. package/package.json +3 -3
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `ErrorKind`s that plausibly indicate trouble with the operator's own
3
+ * network path rather than a genuinely dead or misconfigured target site.
4
+ *
5
+ * Single source of truth shared by two consumers that must agree on
6
+ * exactly the same set:
7
+ *
8
+ * - `NetworkOutageDetector` — only these kinds contribute to the sliding
9
+ * window that detects a suspect outage.
10
+ * - `evict-network-classified-destination-cache-entries.ts` — on recovery,
11
+ * only `destinationCache` entries whose cached `Error` classifies into
12
+ * one of these kinds are evicted (a cached `tls` or `client-blocked`
13
+ * failure is a site-specific fact and must survive an outage recovery
14
+ * unrelated to it).
15
+ *
16
+ * `dns` is included despite `permanent-error-kinds.ts` treating it as a
17
+ * permanent, site-specific verdict in isolation — that classification is
18
+ * exactly what outage detection exists to override when the surrounding
19
+ * evidence (many hosts, tight time window) points to the local network
20
+ * instead.
21
+ */
22
+ export const NETWORK_RELATED_ERROR_KINDS = new Set([
23
+ 'dns',
24
+ 'dns-transient',
25
+ 'local-network',
26
+ 'connection-timeout',
27
+ 'connection-reset',
28
+ ]);
package/lib/types.d.ts CHANGED
@@ -10,7 +10,7 @@ import type { CrawlerError, PageData } from './utils/types/types.js';
10
10
  * documented per-property rather than scattered across the two emit sites.
11
11
  */
12
12
  export interface InventoryRunAggregates {
13
- /** Total non-empty lines in the input list (= `inventoryUrls.length` before any filtering). Stored verbatim as `inventory_runs.total_lines`. */
13
+ /** `inventoryUrls.length` as received by `CrawlerOrchestrator.inventory` — the CLI (`inventoryCrawl`) has already warned-and-dropped unparseable-URL lines before this point, so this counts valid URLs, not raw source-file lines. Stored verbatim as `inventory_runs.total_lines`. */
14
14
  inventoryUrlsCount: number;
15
15
  /** Number of novel URLs classified as HTML and queued for render. Stored as `new_pages` (excludes anchor-discovered descendants — those add later via the crawler graph and are NOT counted here). */
16
16
  htmlSeedsCount: number;
@@ -32,6 +32,14 @@ export interface InventoryRunAggregates {
32
32
  * `source_file_sha256` will be `NULL`.
33
33
  */
34
34
  sourceFileSha256: string | null;
35
+ /**
36
+ * Number of source-file lines the CLI warned-and-dropped for failing
37
+ * URL validation, before `inventoryUrlsCount` was ever counted. Stored
38
+ * verbatim as `inventory_runs.invalid_skipped`. `null` for programmatic
39
+ * callers that built `inventoryUrls` in-memory — there is no source
40
+ * file, so no line was ever dropped as invalid.
41
+ */
42
+ invalidSkipped: number | null;
35
43
  }
36
44
  /**
37
45
  * Coarse cause of a crawl/scrape failure.
@@ -7,4 +7,4 @@
7
7
  * @param callback - A function to invoke on each chunk. May be synchronous or asynchronous.
8
8
  * @returns A promise that resolves when all chunk callbacks have completed.
9
9
  */
10
- export declare function eachSplitted<T>(a: T[], count: number, callback: (items: T[]) => void | Promise<void>): Promise<void>;
10
+ export declare function eachSplitted<T>(a: readonly T[], count: number, callback: (items: T[]) => void | Promise<void>): Promise<void>;
@@ -1,24 +1,24 @@
1
1
  /**
2
- * Compute the SHA-256 digest of a file's bytes, streaming so memory stays
3
- * O(1) regardless of file size.
2
+ * Compute the SHA-256 digest of an in-memory byte buffer.
4
3
  *
5
- * Used by `CrawlerOrchestrator.inventory` to fingerprint the source
6
- * URL list `.txt` and store the digest on the `inventory_runs` row. The
7
- * digest is the content-identity key for "was this exact list applied
8
- * before" recorded for operator audit; nothing dedupes on it at write
9
- * time.
4
+ * Used by `CrawlerOrchestrator.inventory` to fingerprint the source URL
5
+ * list the digest is both recorded on the `inventory_runs` audit row
6
+ * (the content-identity key for "was this exact list applied before") and
7
+ * used as the file name under which the raw list is archived
8
+ * (`Archive.saveInventorySourceList`).
10
9
  *
11
- * Returns `null` instead of throwing when the file cannot be read (e.g.
12
- * vanished mid-run, permissions issue) so the inventory run's audit row
13
- * can still be written with a NULL digest. Hashing failure is an audit
14
- * loss, not a correctness failure the actual ingestion has already
15
- * succeeded by the time this is called.
16
- * @param filePath - Absolute or relative path to the file to hash.
17
- * @returns Lower-case hex digest (64 chars), or `null` if reading failed.
10
+ * Takes an already-read `Buffer` rather than a file path: the CLI reads the
11
+ * source file exactly once (`inventoryCrawl`) and derives the digest, the
12
+ * parsed URL list, and the archived copy all from that single buffer. A
13
+ * separate read-then-hash pass would let the file change between the two
14
+ * reads and desync the archived bytes from the hash naming them.
15
+ * @param bytes - The exact bytes to hash.
16
+ * @returns Lower-case hex digest (64 chars).
18
17
  * @example
19
18
  * ```ts
20
- * const sha = await computeFileSha256('/tmp/list.txt');
21
- * if (sha) console.log(sha); // 64-char hex string
19
+ * const bytes = await fs.readFile('/tmp/list.txt');
20
+ * const sha = computeFileSha256(bytes);
21
+ * console.log(sha); // 64-char hex string
22
22
  * ```
23
23
  */
24
- export declare function computeFileSha256(filePath: string): Promise<string | null>;
24
+ export declare function computeFileSha256(bytes: Buffer): string;
@@ -1,56 +1,27 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { createReadStream } from 'node:fs';
3
- import { crawlerLog } from '../debug.js';
4
2
  /**
5
- * Compute the SHA-256 digest of a file's bytes, streaming so memory stays
6
- * O(1) regardless of file size.
3
+ * Compute the SHA-256 digest of an in-memory byte buffer.
7
4
  *
8
- * Used by `CrawlerOrchestrator.inventory` to fingerprint the source
9
- * URL list `.txt` and store the digest on the `inventory_runs` row. The
10
- * digest is the content-identity key for "was this exact list applied
11
- * before" recorded for operator audit; nothing dedupes on it at write
12
- * time.
5
+ * Used by `CrawlerOrchestrator.inventory` to fingerprint the source URL
6
+ * list the digest is both recorded on the `inventory_runs` audit row
7
+ * (the content-identity key for "was this exact list applied before") and
8
+ * used as the file name under which the raw list is archived
9
+ * (`Archive.saveInventorySourceList`).
13
10
  *
14
- * Returns `null` instead of throwing when the file cannot be read (e.g.
15
- * vanished mid-run, permissions issue) so the inventory run's audit row
16
- * can still be written with a NULL digest. Hashing failure is an audit
17
- * loss, not a correctness failure the actual ingestion has already
18
- * succeeded by the time this is called.
19
- * @param filePath - Absolute or relative path to the file to hash.
20
- * @returns Lower-case hex digest (64 chars), or `null` if reading failed.
11
+ * Takes an already-read `Buffer` rather than a file path: the CLI reads the
12
+ * source file exactly once (`inventoryCrawl`) and derives the digest, the
13
+ * parsed URL list, and the archived copy all from that single buffer. A
14
+ * separate read-then-hash pass would let the file change between the two
15
+ * reads and desync the archived bytes from the hash naming them.
16
+ * @param bytes - The exact bytes to hash.
17
+ * @returns Lower-case hex digest (64 chars).
21
18
  * @example
22
19
  * ```ts
23
- * const sha = await computeFileSha256('/tmp/list.txt');
24
- * if (sha) console.log(sha); // 64-char hex string
20
+ * const bytes = await fs.readFile('/tmp/list.txt');
21
+ * const sha = computeFileSha256(bytes);
22
+ * console.log(sha); // 64-char hex string
25
23
  * ```
26
24
  */
27
- export async function computeFileSha256(filePath) {
28
- return await new Promise((resolve) => {
29
- const hash = createHash('sha256');
30
- const stream = createReadStream(filePath);
31
- let settled = false;
32
- const settle = (value) => {
33
- if (settled) {
34
- return;
35
- }
36
- settled = true;
37
- resolve(value);
38
- };
39
- stream.on('data', (chunk) => hash.update(chunk));
40
- stream.on('end', () => settle(hash.digest('hex')));
41
- stream.on('error', (error) => {
42
- // Trace the failure so operators inspecting `--inventory`
43
- // audit rows with `source_file_sha256 = NULL` can recover
44
- // the underlying cause via `DEBUG=Nitpicker:Crawler:*`.
45
- crawlerLog('compute-file-sha256 failed for %s: %s', filePath, error.message);
46
- settle(null);
47
- });
48
- // `'close'` is the last-resort settler. If the stream is
49
- // destroyed externally between `'data'` and `'end'` (e.g. a
50
- // test or signal handler), neither `'end'` nor `'error'` may
51
- // fire — leaving the promise pending forever. Falling back to
52
- // `null` matches the documented contract ("hashing failure
53
- // yields null").
54
- stream.on('close', () => settle(null));
55
- });
25
+ export function computeFileSha256(bytes) {
26
+ return createHash('sha256').update(bytes).digest('hex');
56
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitpicker/crawler",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Web crawler engine with headless browser rendering and archive storage",
5
5
  "author": "D-ZERO",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "clean": "tsc --build --clean"
28
28
  },
29
29
  "dependencies": {
30
- "@d-zero/beholder": "4.0.0",
30
+ "@d-zero/beholder": "4.1.0",
31
31
  "@d-zero/dealer": "1.9.4",
32
32
  "@d-zero/fs": "0.2.6",
33
33
  "@d-zero/shared": "0.22.2",
@@ -48,5 +48,5 @@
48
48
  "@types/tar": "7.0.87",
49
49
  "@types/unzipper": "0.10.11"
50
50
  },
51
- "gitHead": "98222c2bbce55ab6d253787a251f040b51c7a9a0"
51
+ "gitHead": "d1485df6e43375c6a44edfe07a0293fd39d7810d"
52
52
  }