@nitpicker/crawler 0.11.0 → 0.13.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 (387) hide show
  1. package/README.md +6 -4
  2. package/lib/archive/archive-accessor.d.ts +2 -2
  3. package/lib/archive/archive-accessor.js +2 -2
  4. package/lib/archive/archive-lock.d.ts +7 -0
  5. package/lib/archive/archive-lock.js +7 -0
  6. package/lib/archive/archive.d.ts +175 -13
  7. package/lib/archive/archive.js +198 -14
  8. package/lib/archive/cache/compute-archive-cache-key.d.ts +39 -0
  9. package/lib/archive/cache/compute-archive-cache-key.js +95 -0
  10. package/lib/archive/cache/extract-archive-to-cache.d.ts +43 -0
  11. package/lib/archive/cache/extract-archive-to-cache.js +309 -0
  12. package/lib/archive/cache/get-archive-cache-root.d.ts +20 -0
  13. package/lib/archive/cache/get-archive-cache-root.js +53 -0
  14. package/lib/archive/cache/is-archive-cache-disabled.d.ts +24 -0
  15. package/lib/archive/cache/is-archive-cache-disabled.js +34 -0
  16. package/lib/archive/cache/resolve-archive-cache-dir.d.ts +26 -0
  17. package/lib/archive/cache/resolve-archive-cache-dir.js +32 -0
  18. package/lib/archive/create-adjunct-tables.d.ts +43 -0
  19. package/lib/archive/create-adjunct-tables.js +213 -0
  20. package/lib/archive/create-entity-tables.d.ts +173 -0
  21. package/lib/archive/create-entity-tables.js +318 -0
  22. package/lib/archive/create-progress-reporter.d.ts +30 -0
  23. package/lib/archive/create-progress-reporter.js +38 -0
  24. package/lib/archive/create-ref-tables.d.ts +35 -0
  25. package/lib/archive/create-ref-tables.js +188 -0
  26. package/lib/archive/database.d.ts +130 -182
  27. package/lib/archive/database.js +524 -1777
  28. package/lib/archive/db-ops/_shared/clear-write-ref-caches.d.ts +27 -0
  29. package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +34 -0
  30. package/lib/archive/db-ops/_shared/create-write-ref-caches.d.ts +17 -0
  31. package/lib/archive/db-ops/_shared/create-write-ref-caches.js +26 -0
  32. package/lib/archive/db-ops/_shared/decode-json-ref.d.ts +17 -0
  33. package/lib/archive/db-ops/_shared/decode-json-ref.js +31 -0
  34. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.d.ts +20 -0
  35. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.js +53 -0
  36. package/lib/archive/db-ops/_shared/resolve-content-item-id.d.ts +61 -0
  37. package/lib/archive/db-ops/_shared/resolve-content-item-id.js +111 -0
  38. package/lib/archive/db-ops/_shared/resolve-url-or-blob.d.ts +23 -0
  39. package/lib/archive/db-ops/_shared/resolve-url-or-blob.js +29 -0
  40. package/lib/archive/db-ops/_shared/retry-setting.d.ts +16 -0
  41. package/lib/archive/db-ops/_shared/retry-setting.js +18 -0
  42. package/lib/archive/db-ops/_shared/safe-parse-json.d.ts +11 -0
  43. package/lib/archive/db-ops/_shared/safe-parse-json.js +18 -0
  44. package/lib/archive/db-ops/_shared/types.d.ts +53 -0
  45. package/lib/archive/db-ops/_shared/types.js +1 -0
  46. package/lib/archive/db-ops/_shared/upsert-blob-ref.d.ts +25 -0
  47. package/lib/archive/db-ops/_shared/upsert-blob-ref.js +48 -0
  48. package/lib/archive/db-ops/_shared/upsert-content-type-ref.d.ts +30 -0
  49. package/lib/archive/db-ops/_shared/upsert-content-type-ref.js +45 -0
  50. package/lib/archive/db-ops/_shared/upsert-json-ref.d.ts +22 -0
  51. package/lib/archive/db-ops/_shared/upsert-json-ref.js +41 -0
  52. package/lib/archive/db-ops/_shared/upsert-response-headers.d.ts +35 -0
  53. package/lib/archive/db-ops/_shared/upsert-response-headers.js +49 -0
  54. package/lib/archive/db-ops/_shared/upsert-url-ref.d.ts +39 -0
  55. package/lib/archive/db-ops/_shared/upsert-url-ref.js +62 -0
  56. package/lib/archive/db-ops/analysis/replace-analysis-violations.d.ts +28 -0
  57. package/lib/archive/db-ops/analysis/replace-analysis-violations.js +152 -0
  58. package/lib/archive/db-ops/anchors/get-anchors-on-page.d.ts +10 -0
  59. package/lib/archive/db-ops/anchors/get-anchors-on-page.js +21 -0
  60. package/lib/archive/db-ops/config/get-base-url.d.ts +8 -0
  61. package/lib/archive/db-ops/config/get-base-url.js +14 -0
  62. package/lib/archive/db-ops/config/get-config.d.ts +10 -0
  63. package/lib/archive/db-ops/config/get-config.js +27 -0
  64. package/lib/archive/db-ops/config/get-name.d.ts +8 -0
  65. package/lib/archive/db-ops/config/get-name.js +14 -0
  66. package/lib/archive/db-ops/config/info-column-allowlist.d.ts +7 -0
  67. package/lib/archive/db-ops/config/info-column-allowlist.js +26 -0
  68. package/lib/archive/db-ops/config/info-json-columns.d.ts +5 -0
  69. package/lib/archive/db-ops/config/info-json-columns.js +10 -0
  70. package/lib/archive/db-ops/config/set-config.d.ts +12 -0
  71. package/lib/archive/db-ops/config/set-config.js +21 -0
  72. package/lib/archive/db-ops/config/update-config.d.ts +17 -0
  73. package/lib/archive/db-ops/config/update-config.js +36 -0
  74. package/lib/archive/db-ops/errors/insert-crawl-error.d.ts +15 -0
  75. package/lib/archive/db-ops/errors/insert-crawl-error.js +21 -0
  76. package/lib/archive/db-ops/errors/insert-page-error.d.ts +21 -0
  77. package/lib/archive/db-ops/errors/insert-page-error.js +28 -0
  78. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +22 -0
  79. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +141 -0
  80. package/lib/archive/db-ops/html/get-html-of-page-by-id.d.ts +18 -0
  81. package/lib/archive/db-ops/html/get-html-of-page-by-id.js +29 -0
  82. package/lib/archive/db-ops/inventory/record-inventory-run.d.ts +21 -0
  83. package/lib/archive/db-ops/inventory/record-inventory-run.js +38 -0
  84. package/lib/archive/db-ops/lifecycle/checkpoint.d.ts +8 -0
  85. package/lib/archive/db-ops/lifecycle/checkpoint.js +9 -0
  86. package/lib/archive/db-ops/lifecycle/destroy.d.ts +6 -0
  87. package/lib/archive/db-ops/lifecycle/destroy.js +7 -0
  88. package/lib/archive/db-ops/lifecycle/init.d.ts +22 -0
  89. package/lib/archive/db-ops/lifecycle/init.js +42 -0
  90. package/lib/archive/db-ops/meta/get-jsonld-of-page.d.ts +13 -0
  91. package/lib/archive/db-ops/meta/get-jsonld-of-page.js +27 -0
  92. package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +12 -0
  93. package/lib/archive/db-ops/meta/get-tags-of-page.js +28 -0
  94. package/lib/archive/db-ops/pages/order/set-url-order.d.ts +8 -0
  95. package/lib/archive/db-ops/pages/order/set-url-order.js +32 -0
  96. package/lib/archive/db-ops/pages/read/build-page-query.d.ts +18 -0
  97. package/lib/archive/db-ops/pages/read/build-page-query.js +40 -0
  98. package/lib/archive/db-ops/pages/read/get-crawling-state.d.ts +70 -0
  99. package/lib/archive/db-ops/pages/read/get-crawling-state.js +98 -0
  100. package/lib/archive/db-ops/pages/read/get-existing-page-urls.d.ts +15 -0
  101. package/lib/archive/db-ops/pages/read/get-existing-page-urls.js +30 -0
  102. package/lib/archive/db-ops/pages/read/get-page-count.d.ts +12 -0
  103. package/lib/archive/db-ops/pages/read/get-page-count.js +21 -0
  104. package/lib/archive/db-ops/pages/read/get-page-source-by-url.d.ts +24 -0
  105. package/lib/archive/db-ops/pages/read/get-page-source-by-url.js +28 -0
  106. package/lib/archive/db-ops/pages/read/get-pages-with-rels.d.ts +38 -0
  107. package/lib/archive/db-ops/pages/read/get-pages-with-rels.js +107 -0
  108. package/lib/archive/db-ops/pages/read/get-pages.d.ts +11 -0
  109. package/lib/archive/db-ops/pages/read/get-pages.js +51 -0
  110. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.d.ts +18 -0
  111. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.js +25 -0
  112. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.d.ts +31 -0
  113. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.js +32 -0
  114. package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +24 -0
  115. package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +93 -0
  116. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +47 -0
  117. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +124 -0
  118. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.d.ts +37 -0
  119. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.js +72 -0
  120. package/lib/archive/db-ops/pages/write/insert-jsonld.d.ts +17 -0
  121. package/lib/archive/db-ops/pages/write/insert-jsonld.js +49 -0
  122. package/lib/archive/db-ops/pages/write/insert-page.d.ts +36 -0
  123. package/lib/archive/db-ops/pages/write/insert-page.js +208 -0
  124. package/lib/archive/db-ops/pages/write/insert-tags.d.ts +16 -0
  125. package/lib/archive/db-ops/pages/write/insert-tags.js +34 -0
  126. package/lib/archive/db-ops/pages/write/link-redirect-sources.d.ts +36 -0
  127. package/lib/archive/db-ops/pages/write/link-redirect-sources.js +93 -0
  128. package/lib/archive/db-ops/pages/write/record-redirect.d.ts +35 -0
  129. package/lib/archive/db-ops/pages/write/record-redirect.js +100 -0
  130. package/lib/archive/db-ops/pages/write/set-skipped-page.d.ts +13 -0
  131. package/lib/archive/db-ops/pages/write/set-skipped-page.js +22 -0
  132. package/lib/archive/db-ops/pages/write/update-page.d.ts +29 -0
  133. package/lib/archive/db-ops/pages/write/update-page.js +334 -0
  134. package/lib/archive/db-ops/pages/write/write-page-html-blob.d.ts +19 -0
  135. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +41 -0
  136. package/lib/archive/db-ops/referrers/get-redirects-for-pages.d.ts +9 -0
  137. package/lib/archive/db-ops/referrers/get-redirects-for-pages.js +15 -0
  138. package/lib/archive/db-ops/referrers/get-referrers-of-page.d.ts +17 -0
  139. package/lib/archive/db-ops/referrers/get-referrers-of-page.js +32 -0
  140. package/lib/archive/db-ops/referrers/get-referrers-of-resource.d.ts +8 -0
  141. package/lib/archive/db-ops/referrers/get-referrers-of-resource.js +15 -0
  142. package/lib/archive/db-ops/resources/build-resource-query.d.ts +25 -0
  143. package/lib/archive/db-ops/resources/build-resource-query.js +29 -0
  144. package/lib/archive/db-ops/resources/get-existing-resource-urls.d.ts +9 -0
  145. package/lib/archive/db-ops/resources/get-existing-resource-urls.js +24 -0
  146. package/lib/archive/db-ops/resources/get-resource-by-url.d.ts +13 -0
  147. package/lib/archive/db-ops/resources/get-resource-by-url.js +22 -0
  148. package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +9 -0
  149. package/lib/archive/db-ops/resources/get-resource-url-list.js +13 -0
  150. package/lib/archive/db-ops/resources/get-resources.d.ts +8 -0
  151. package/lib/archive/db-ops/resources/get-resources.js +11 -0
  152. package/lib/archive/db-ops/resources/insert-inventory-resources.d.ts +24 -0
  153. package/lib/archive/db-ops/resources/insert-inventory-resources.js +64 -0
  154. package/lib/archive/db-ops/resources/insert-resource-referrers.d.ts +15 -0
  155. package/lib/archive/db-ops/resources/insert-resource-referrers.js +54 -0
  156. package/lib/archive/db-ops/resources/insert-resource.d.ts +34 -0
  157. package/lib/archive/db-ops/resources/insert-resource.js +73 -0
  158. package/lib/archive/db-ops/resources/reconstruct-resource-rows.d.ts +26 -0
  159. package/lib/archive/db-ops/resources/reconstruct-resource-rows.js +30 -0
  160. package/lib/archive/decode-html-blob.d.ts +18 -0
  161. package/lib/archive/decode-html-blob.js +31 -0
  162. package/lib/archive/derive-lineage-from-parent.d.ts +37 -0
  163. package/lib/archive/derive-lineage-from-parent.js +42 -0
  164. package/lib/archive/drop-legacy-tables.d.ts +45 -0
  165. package/lib/archive/drop-legacy-tables.js +56 -0
  166. package/lib/archive/filesystem/rename.js +1 -1
  167. package/lib/archive/get-failed-page-messages.d.ts +44 -0
  168. package/lib/archive/get-failed-page-messages.js +132 -0
  169. package/lib/archive/init-schema.d.ts +35 -39
  170. package/lib/archive/init-schema.js +111 -320
  171. package/lib/archive/is-inventory-source.d.ts +21 -0
  172. package/lib/archive/is-inventory-source.js +22 -0
  173. package/lib/archive/limited-page-ids.d.ts +2 -1
  174. package/lib/archive/limited-page-ids.js +5 -4
  175. package/lib/archive/meta/assert-compatible-version.d.ts +24 -3
  176. package/lib/archive/meta/assert-compatible-version.js +24 -3
  177. package/lib/archive/meta/types.d.ts +87 -1
  178. package/lib/archive/meta/types.js +34 -2
  179. package/lib/archive/migrate-entity-tables.d.ts +45 -0
  180. package/lib/archive/migrate-entity-tables.js +56 -0
  181. package/lib/archive/migrate-ref-tables.d.ts +25 -0
  182. package/lib/archive/migrate-ref-tables.js +38 -0
  183. package/lib/archive/page-meta-column-maps.d.ts +32 -0
  184. package/lib/archive/page-meta-column-maps.js +43 -0
  185. package/lib/archive/page.d.ts +6 -6
  186. package/lib/archive/page.js +5 -5
  187. package/lib/archive/peek-archive-lock.d.ts +2 -2
  188. package/lib/archive/peek-archive-lock.js +2 -2
  189. package/lib/archive/populate-entity-tables/collapse-anchor-rows.d.ts +41 -0
  190. package/lib/archive/populate-entity-tables/collapse-anchor-rows.js +87 -0
  191. package/lib/archive/populate-entity-tables/derive-dom-path.d.ts +35 -0
  192. package/lib/archive/populate-entity-tables/derive-dom-path.js +72 -0
  193. package/lib/archive/populate-entity-tables/is-blob-ref-value.d.ts +16 -0
  194. package/lib/archive/populate-entity-tables/is-blob-ref-value.js +19 -0
  195. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.d.ts +66 -0
  196. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.js +96 -0
  197. package/lib/archive/populate-entity-tables/populate-anchor-edges.d.ts +33 -0
  198. package/lib/archive/populate-entity-tables/populate-anchor-edges.js +153 -0
  199. package/lib/archive/populate-entity-tables/populate-content-items.d.ts +40 -0
  200. package/lib/archive/populate-entity-tables/populate-content-items.js +141 -0
  201. package/lib/archive/populate-entity-tables/populate-entities.d.ts +81 -0
  202. package/lib/archive/populate-entity-tables/populate-entities.js +111 -0
  203. package/lib/archive/populate-entity-tables/populate-image-items.d.ts +91 -0
  204. package/lib/archive/populate-entity-tables/populate-image-items.js +223 -0
  205. package/lib/archive/populate-entity-tables/populate-page-meta.d.ts +33 -0
  206. package/lib/archive/populate-entity-tables/populate-page-meta.js +267 -0
  207. package/lib/archive/populate-entity-tables/populate-resource-items.d.ts +22 -0
  208. package/lib/archive/populate-entity-tables/populate-resource-items.js +114 -0
  209. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.d.ts +31 -0
  210. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.js +33 -0
  211. package/lib/archive/populate-entity-tables/resolve-blob-refs.d.ts +31 -0
  212. package/lib/archive/populate-entity-tables/resolve-blob-refs.js +100 -0
  213. package/lib/archive/populate-entity-tables/resolve-content-type-refs.d.ts +22 -0
  214. package/lib/archive/populate-entity-tables/resolve-content-type-refs.js +27 -0
  215. package/lib/archive/populate-entity-tables/resolve-header-sets.d.ts +49 -0
  216. package/lib/archive/populate-entity-tables/resolve-header-sets.js +122 -0
  217. package/lib/archive/populate-entity-tables/resolve-json-refs.d.ts +25 -0
  218. package/lib/archive/populate-entity-tables/resolve-json-refs.js +67 -0
  219. package/lib/archive/populate-entity-tables/resolve-text-refs.d.ts +30 -0
  220. package/lib/archive/populate-entity-tables/resolve-text-refs.js +61 -0
  221. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.d.ts +21 -0
  222. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.js +27 -0
  223. package/lib/archive/populate-entity-tables/resolve-url-refs.d.ts +33 -0
  224. package/lib/archive/populate-entity-tables/resolve-url-refs.js +60 -0
  225. package/lib/archive/populate-entity-tables/test-utils/count-rows.d.ts +17 -0
  226. package/lib/archive/populate-entity-tables/test-utils/count-rows.js +20 -0
  227. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.d.ts +25 -0
  228. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.js +42 -0
  229. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.d.ts +23 -0
  230. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.js +178 -0
  231. package/lib/archive/populate-entity-tables/types.d.ts +157 -0
  232. package/lib/archive/populate-entity-tables/types.js +12 -0
  233. package/lib/archive/populate-entity-tables/upsert-text-refs.d.ts +38 -0
  234. package/lib/archive/populate-entity-tables/upsert-text-refs.js +78 -0
  235. package/lib/archive/populate-ref-tables/classify-content-type.d.ts +16 -0
  236. package/lib/archive/populate-ref-tables/classify-content-type.js +52 -0
  237. package/lib/archive/populate-ref-tables/compute-content-hash.d.ts +22 -0
  238. package/lib/archive/populate-ref-tables/compute-content-hash.js +26 -0
  239. package/lib/archive/populate-ref-tables/compute-header-flags.d.ts +16 -0
  240. package/lib/archive/populate-ref-tables/compute-header-flags.js +70 -0
  241. package/lib/archive/populate-ref-tables/content-type-rules.d.ts +38 -0
  242. package/lib/archive/populate-ref-tables/content-type-rules.js +133 -0
  243. package/lib/archive/populate-ref-tables/create-header-table-caches.d.ts +25 -0
  244. package/lib/archive/populate-ref-tables/create-header-table-caches.js +49 -0
  245. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.d.ts +15 -0
  246. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.js +15 -0
  247. package/lib/archive/populate-ref-tables/decode-data-uri.d.ts +21 -0
  248. package/lib/archive/populate-ref-tables/decode-data-uri.js +126 -0
  249. package/lib/archive/populate-ref-tables/decompose-header-set.d.ts +29 -0
  250. package/lib/archive/populate-ref-tables/decompose-header-set.js +157 -0
  251. package/lib/archive/populate-ref-tables/decompose-url.d.ts +25 -0
  252. package/lib/archive/populate-ref-tables/decompose-url.js +70 -0
  253. package/lib/archive/populate-ref-tables/header-stability.d.ts +19 -0
  254. package/lib/archive/populate-ref-tables/header-stability.js +22 -0
  255. package/lib/archive/populate-ref-tables/header-value-cache-key.d.ts +17 -0
  256. package/lib/archive/populate-ref-tables/header-value-cache-key.js +19 -0
  257. package/lib/archive/populate-ref-tables/normalize-mime.d.ts +24 -0
  258. package/lib/archive/populate-ref-tables/normalize-mime.js +36 -0
  259. package/lib/archive/populate-ref-tables/populate-blob-refs.d.ts +38 -0
  260. package/lib/archive/populate-ref-tables/populate-blob-refs.js +134 -0
  261. package/lib/archive/populate-ref-tables/populate-content-type-refs.d.ts +27 -0
  262. package/lib/archive/populate-ref-tables/populate-content-type-refs.js +70 -0
  263. package/lib/archive/populate-ref-tables/populate-header-tables.d.ts +35 -0
  264. package/lib/archive/populate-ref-tables/populate-header-tables.js +80 -0
  265. package/lib/archive/populate-ref-tables/populate-json-refs.d.ts +29 -0
  266. package/lib/archive/populate-ref-tables/populate-json-refs.js +101 -0
  267. package/lib/archive/populate-ref-tables/populate-refs.d.ts +51 -0
  268. package/lib/archive/populate-ref-tables/populate-refs.js +62 -0
  269. package/lib/archive/populate-ref-tables/populate-text-refs.d.ts +32 -0
  270. package/lib/archive/populate-ref-tables/populate-text-refs.js +133 -0
  271. package/lib/archive/populate-ref-tables/populate-url-refs.d.ts +28 -0
  272. package/lib/archive/populate-ref-tables/populate-url-refs.js +148 -0
  273. package/lib/archive/populate-ref-tables/test-utils/count-rows.d.ts +15 -0
  274. package/lib/archive/populate-ref-tables/test-utils/count-rows.js +17 -0
  275. package/lib/archive/populate-ref-tables/types.d.ts +197 -0
  276. package/lib/archive/populate-ref-tables/types.js +7 -0
  277. package/lib/archive/populate-ref-tables/upsert-one-header-set.d.ts +34 -0
  278. package/lib/archive/populate-ref-tables/upsert-one-header-set.js +208 -0
  279. package/lib/archive/populate-ref-tables/volatile-header-names.d.ts +20 -0
  280. package/lib/archive/populate-ref-tables/volatile-header-names.js +33 -0
  281. package/lib/archive/redirect-table.d.ts +4 -2
  282. package/lib/archive/redirect-table.js +15 -10
  283. package/lib/archive/resolve-redirect-chain.d.ts +3 -3
  284. package/lib/archive/resolve-redirect-chain.js +2 -2
  285. package/lib/archive/resource.d.ts +1 -1
  286. package/lib/archive/retarget-legacy-fk-tables.d.ts +47 -0
  287. package/lib/archive/retarget-legacy-fk-tables.js +107 -0
  288. package/lib/archive/test-utils/fk-parent-tables.d.ts +15 -0
  289. package/lib/archive/test-utils/fk-parent-tables.js +19 -0
  290. package/lib/archive/test-utils/seed-content-item.d.ts +35 -0
  291. package/lib/archive/test-utils/seed-content-item.js +42 -0
  292. package/lib/archive/test-utils/setup-legacy-fk-db.d.ts +33 -0
  293. package/lib/archive/test-utils/setup-legacy-fk-db.js +270 -0
  294. package/lib/archive/types.d.ts +151 -15
  295. package/lib/archive/verify-migration/capture-rejection.d.ts +24 -0
  296. package/lib/archive/verify-migration/capture-rejection.js +31 -0
  297. package/lib/archive/verify-migration/check-anchor-edges-count.d.ts +34 -0
  298. package/lib/archive/verify-migration/check-anchor-edges-count.js +72 -0
  299. package/lib/archive/verify-migration/check-anchor-edges-sum.d.ts +13 -0
  300. package/lib/archive/verify-migration/check-anchor-edges-sum.js +27 -0
  301. package/lib/archive/verify-migration/check-content-items-count.d.ts +16 -0
  302. package/lib/archive/verify-migration/check-content-items-count.js +30 -0
  303. package/lib/archive/verify-migration/check-content-type-preservation.d.ts +22 -0
  304. package/lib/archive/verify-migration/check-content-type-preservation.js +40 -0
  305. package/lib/archive/verify-migration/check-foreign-key-integrity.d.ts +31 -0
  306. package/lib/archive/verify-migration/check-foreign-key-integrity.js +47 -0
  307. package/lib/archive/verify-migration/check-image-items-count.d.ts +12 -0
  308. package/lib/archive/verify-migration/check-image-items-count.js +26 -0
  309. package/lib/archive/verify-migration/check-page-meta-count.d.ts +15 -0
  310. package/lib/archive/verify-migration/check-page-meta-count.js +31 -0
  311. package/lib/archive/verify-migration/check-reader-parity.d.ts +23 -0
  312. package/lib/archive/verify-migration/check-reader-parity.js +211 -0
  313. package/lib/archive/verify-migration/check-resource-items-count.d.ts +17 -0
  314. package/lib/archive/verify-migration/check-resource-items-count.js +33 -0
  315. package/lib/archive/verify-migration/check-url-round-trip.d.ts +43 -0
  316. package/lib/archive/verify-migration/check-url-round-trip.js +112 -0
  317. package/lib/archive/verify-migration/types.d.ts +70 -0
  318. package/lib/archive/verify-migration/types.js +63 -0
  319. package/lib/archive/verify-migration/verify-migration.d.ts +41 -0
  320. package/lib/archive/verify-migration/verify-migration.js +120 -0
  321. package/lib/classify-error-kind.d.ts +19 -0
  322. package/lib/classify-error-kind.js +122 -0
  323. package/lib/crawler/build-js-redirect-edge.d.ts +68 -0
  324. package/lib/crawler/build-js-redirect-edge.js +57 -0
  325. package/lib/crawler/build-redirect-event.d.ts +24 -0
  326. package/lib/crawler/build-redirect-event.js +28 -0
  327. package/lib/crawler/capture-image-dom-paths.d.ts +33 -0
  328. package/lib/crawler/capture-image-dom-paths.js +39 -0
  329. package/lib/crawler/clear-dns-burned-host-cache.d.ts +6 -0
  330. package/lib/crawler/clear-dns-burned-host-cache.js +11 -0
  331. package/lib/crawler/collect-image-dom-paths.d.ts +23 -0
  332. package/lib/crawler/collect-image-dom-paths.js +64 -0
  333. package/lib/crawler/crawler.d.ts +22 -1
  334. package/lib/crawler/crawler.js +679 -117
  335. package/lib/crawler/derive-js-redirect-target.d.ts +68 -0
  336. package/lib/crawler/derive-js-redirect-target.js +129 -0
  337. package/lib/crawler/derive-resource-source.d.ts +25 -15
  338. package/lib/crawler/derive-resource-source.js +28 -17
  339. package/lib/crawler/dns-burned-host-cache.d.ts +26 -0
  340. package/lib/crawler/dns-burned-host-cache.js +25 -0
  341. package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +13 -0
  342. package/lib/crawler/dns-burned-host-short-circuit-counter.js +11 -0
  343. package/lib/crawler/fetch-destination.d.ts +12 -4
  344. package/lib/crawler/fetch-destination.js +94 -16
  345. package/lib/crawler/inject-scope-auth.d.ts +1 -1
  346. package/lib/crawler/inject-scope-auth.js +1 -1
  347. package/lib/crawler/is-js-redirect-error-shape.d.ts +40 -0
  348. package/lib/crawler/is-js-redirect-error-shape.js +53 -0
  349. package/lib/crawler/is-puppeteer-fallback-candidate.d.ts +16 -0
  350. package/lib/crawler/is-puppeteer-fallback-candidate.js +63 -0
  351. package/lib/crawler/link-list.d.ts +21 -1
  352. package/lib/crawler/link-list.js +23 -3
  353. package/lib/crawler/normalize-content-type.d.ts +1 -1
  354. package/lib/crawler/normalize-content-type.js +1 -1
  355. package/lib/crawler/plan-sub-resource-emits.d.ts +63 -0
  356. package/lib/crawler/plan-sub-resource-emits.js +44 -0
  357. package/lib/crawler/preload-short-circuit-error.d.ts +22 -0
  358. package/lib/crawler/preload-short-circuit-error.js +25 -0
  359. package/lib/crawler/should-burn-host.d.ts +78 -0
  360. package/lib/crawler/should-burn-host.js +61 -0
  361. package/lib/crawler/should-get-fallback-on-head-failure.d.ts +38 -0
  362. package/lib/crawler/should-get-fallback-on-head-failure.js +46 -0
  363. package/lib/crawler/types.d.ts +108 -1
  364. package/lib/crawler-orchestrator.d.ts +22 -3
  365. package/lib/crawler-orchestrator.js +312 -73
  366. package/lib/crawler.d.ts +15 -2
  367. package/lib/crawler.js +24 -1
  368. package/lib/permanent-error-kinds.d.ts +43 -0
  369. package/lib/permanent-error-kinds.js +48 -0
  370. package/lib/types.d.ts +84 -0
  371. package/lib/utils/compute-file-sha256.d.ts +24 -0
  372. package/lib/utils/compute-file-sha256.js +56 -0
  373. package/lib/utils/error/emit-error-with-retry.d.ts +40 -0
  374. package/lib/utils/error/emit-error-with-retry.js +44 -0
  375. package/lib/utils/error/emit-error.d.ts +39 -0
  376. package/lib/utils/error/emit-error.js +41 -0
  377. package/package.json +12 -12
  378. package/lib/archive/migrate-crawl-errors.d.ts +0 -20
  379. package/lib/archive/migrate-crawl-errors.js +0 -38
  380. package/lib/archive/migrate-html-blob-tables.d.ts +0 -24
  381. package/lib/archive/migrate-html-blob-tables.js +0 -53
  382. package/lib/archive/migrate-page-errors.d.ts +0 -16
  383. package/lib/archive/migrate-page-errors.js +0 -35
  384. package/lib/archive/migrate-pages-resources-source.d.ts +0 -16
  385. package/lib/archive/migrate-pages-resources-source.js +0 -46
  386. package/lib/utils/error/error-emitter.d.ts +0 -18
  387. package/lib/utils/error/error-emitter.js +0 -29
@@ -0,0 +1,43 @@
1
+ import type { ErrorKind } from './types.js';
2
+ /**
3
+ * Error kinds whose failure mode is **persistent** — re-fetching the same URL
4
+ * (without changing the network, the certificate, the destination host, or the
5
+ * browser's block-list state) will almost certainly produce the same failure.
6
+ *
7
+ * Used by `resetFailedPages` to exclude pages whose latest recorded error
8
+ * falls in this set, so `--retry-failed` actually converges: without the
9
+ * exclusion, NXDOMAIN / TLS mismatch / `ERR_BLOCKED_BY_CLIENT` /
10
+ * `ECONNREFUSED` / HTTP parse-error pages would be reset to pending on every
11
+ * iteration, the crawler would re-attempt them, they would fail again the
12
+ * same way, and the retry-target count would stay constant forever.
13
+ *
14
+ * Why these five and not others:
15
+ * - **dns** — `ENOTFOUND` / `ERR_NAME_NOT_RESOLVED` are authoritative DNS
16
+ * answers; the host is gone (or never existed). EAI_AGAIN is split out as
17
+ * `dns-transient` precisely so it is NOT in this set.
18
+ * - **tls** — cert expiry / SAN mismatch / SSL protocol errors require the
19
+ * server operator to fix something; retrying within the same archive run
20
+ * cannot succeed.
21
+ * - **client-blocked** — Chromium's `ERR_BLOCKED_*` family is a deliberate
22
+ * browser-side rejection; the request will be blocked identically every
23
+ * time the browser sees the same URL.
24
+ * - **parse-error** — the server's HTTP response is malformed (`Expected
25
+ * HTTP/, RTSP/ or ICE/`, `Unexpected end of stream`). Retrying the same
26
+ * request hits the same parser failure.
27
+ * - **connection-refused** — `ECONNREFUSED` is an authoritative TCP RST from
28
+ * the listener; either no process is listening on the port or its accept
29
+ * queue rejected the connection. Either way the answer is final until the
30
+ * server operator intervenes.
31
+ *
32
+ * Notably absent (intentionally retryable):
33
+ * - `connection-reset` / `connection-timeout` — could be middlebox or
34
+ * transient overload
35
+ * - `dns-transient` (EAI_AGAIN / EREFUSED) — local resolver hiccup
36
+ * - `local-network` — operator-side connectivity loss
37
+ * - `timeout` — slow but reachable server (HEAD-timeout escalation gives
38
+ * these a real chance)
39
+ * - `protocol` — puppeteer lifecycle race, often recovers on retry
40
+ * - `unknown` — by definition we don't know it's permanent, so we keep
41
+ * retrying (errs on the side of investigating)
42
+ */
43
+ export declare const PERMANENT_ERROR_KINDS: ReadonlySet<ErrorKind>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Error kinds whose failure mode is **persistent** — re-fetching the same URL
3
+ * (without changing the network, the certificate, the destination host, or the
4
+ * browser's block-list state) will almost certainly produce the same failure.
5
+ *
6
+ * Used by `resetFailedPages` to exclude pages whose latest recorded error
7
+ * falls in this set, so `--retry-failed` actually converges: without the
8
+ * exclusion, NXDOMAIN / TLS mismatch / `ERR_BLOCKED_BY_CLIENT` /
9
+ * `ECONNREFUSED` / HTTP parse-error pages would be reset to pending on every
10
+ * iteration, the crawler would re-attempt them, they would fail again the
11
+ * same way, and the retry-target count would stay constant forever.
12
+ *
13
+ * Why these five and not others:
14
+ * - **dns** — `ENOTFOUND` / `ERR_NAME_NOT_RESOLVED` are authoritative DNS
15
+ * answers; the host is gone (or never existed). EAI_AGAIN is split out as
16
+ * `dns-transient` precisely so it is NOT in this set.
17
+ * - **tls** — cert expiry / SAN mismatch / SSL protocol errors require the
18
+ * server operator to fix something; retrying within the same archive run
19
+ * cannot succeed.
20
+ * - **client-blocked** — Chromium's `ERR_BLOCKED_*` family is a deliberate
21
+ * browser-side rejection; the request will be blocked identically every
22
+ * time the browser sees the same URL.
23
+ * - **parse-error** — the server's HTTP response is malformed (`Expected
24
+ * HTTP/, RTSP/ or ICE/`, `Unexpected end of stream`). Retrying the same
25
+ * request hits the same parser failure.
26
+ * - **connection-refused** — `ECONNREFUSED` is an authoritative TCP RST from
27
+ * the listener; either no process is listening on the port or its accept
28
+ * queue rejected the connection. Either way the answer is final until the
29
+ * server operator intervenes.
30
+ *
31
+ * Notably absent (intentionally retryable):
32
+ * - `connection-reset` / `connection-timeout` — could be middlebox or
33
+ * transient overload
34
+ * - `dns-transient` (EAI_AGAIN / EREFUSED) — local resolver hiccup
35
+ * - `local-network` — operator-side connectivity loss
36
+ * - `timeout` — slow but reachable server (HEAD-timeout escalation gives
37
+ * these a real chance)
38
+ * - `protocol` — puppeteer lifecycle race, often recovers on retry
39
+ * - `unknown` — by definition we don't know it's permanent, so we keep
40
+ * retrying (errs on the side of investigating)
41
+ */
42
+ export const PERMANENT_ERROR_KINDS = new Set([
43
+ 'dns',
44
+ 'tls',
45
+ 'client-blocked',
46
+ 'parse-error',
47
+ 'connection-refused',
48
+ ]);
package/lib/types.d.ts CHANGED
@@ -1,4 +1,88 @@
1
1
  import type { CrawlerError, PageData } from './utils/types/types.js';
2
+ /**
3
+ * Aggregate counts captured during a `--inventory` invocation, forwarded to
4
+ * `#writeInventoryRunRow` so the audit log row is consistent between the
5
+ * HTML-seed branch and the non-HTML-only branch of
6
+ * `CrawlerOrchestrator.inventory`.
7
+ *
8
+ * Spelled out here (not inlined at the call site) so a new field added to
9
+ * the audit row has a single edit point and so each field's semantics are
10
+ * documented per-property rather than scattered across the two emit sites.
11
+ */
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`. */
14
+ inventoryUrlsCount: number;
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
+ htmlSeedsCount: number;
17
+ /** Number of novel URLs classified as non-HTML and written directly into `resources`. Stored as `new_resources`. */
18
+ nonHtmlCount: number;
19
+ /** URLs dropped because they fell outside the archived scope. Stored as `scope_skipped`. */
20
+ outOfScope: number;
21
+ /**
22
+ * SHA-256 hex digest of the source `.txt`, **pre-computed by the caller**
23
+ * (typically the CLI's `inventoryCrawl`). Stored verbatim as
24
+ * `inventory_runs.source_file_sha256`.
25
+ *
26
+ * Pre-computation lifts the absolute path off the orchestrator
27
+ * boundary entirely — the path is privacy-sensitive (leaks
28
+ * user-home / OS structure when archives are shared), and the
29
+ * orchestrator has no business handling it after the audit-row
30
+ * column was dropped. Pass `null` for programmatic callers that
31
+ * built `inventoryUrls` in-memory; the audit row's
32
+ * `source_file_sha256` will be `NULL`.
33
+ */
34
+ sourceFileSha256: string | null;
35
+ }
36
+ /**
37
+ * Coarse cause of a crawl/scrape failure.
38
+ *
39
+ * The crawler stores only the raw error message (in `crawl_errors`,
40
+ * `page_errors`, or `error.log`); the cause is derived on read by
41
+ * `classifyErrorKind`, so existing archives gain classification without a
42
+ * re-crawl.
43
+ *
44
+ * Owned by the crawler package because both the crawler (for DNS-burned host
45
+ * caching) and `@nitpicker/query` (for `getErrorKinds` / `getSummary`) need to
46
+ * classify error messages, and crawler cannot depend on query.
47
+ *
48
+ * ### transient vs persistent
49
+ *
50
+ * | kind | transient? | DNS-burn? | notes |
51
+ * | --- | --- | --- | --- |
52
+ * | `dns` | no | yes | NXDOMAIN; the host does not resolve at all |
53
+ * | `dns-transient` | **yes** | no | `EAI_AGAIN`; local resolver hiccup, retry often recovers |
54
+ * | `tls` | no | no | certificate issue, usually persistent until cert rotates |
55
+ * | `connection-refused` | mostly persistent | no | server actively rejecting on this port |
56
+ * | `connection-reset` | yes | no | TCP reset mid-stream, often transient |
57
+ * | `connection-timeout` | yes | no | TCP-level timeout (`ETIMEDOUT`); slow but reachable |
58
+ * | `local-network` | **yes** | no | local machine's network is unreachable / changed (WiFi, sleep, ICMP-unreachable, …) |
59
+ * | `parse-error` | mostly persistent | no | HTTP response could not be parsed (proxy, garbage, MITM) |
60
+ * | `client-blocked` | persistent (per browser) | no | Chromium-side `ERR_BLOCKED_BY_*` family — the browser actively refused the request (ad/tracker heuristics, CSP, CORP, administrator block list, …) |
61
+ * | `protocol` | yes | no | puppeteer protocol layer (frame detached, target closed, …) |
62
+ * | `timeout` | yes | no | puppeteer navigation timeout or HEAD pre-flight race timeout (`Timeout: <url>`) |
63
+ * | `unknown` | unknown | no | catch-all for messages no matcher recognised |
64
+ *
65
+ * Only `dns` is mark-target for the DNS-burned host cache; everything else is
66
+ * either too transient to burn (network glitch / browser hiccup) or too
67
+ * server-specific to extrapolate to "this whole host is dead."
68
+ *
69
+ * ### Derived constants that MUST be reviewed when this union changes
70
+ *
71
+ * - `PERMANENT_ERROR_KINDS` (`permanent-error-kinds.ts`) — the set of kinds
72
+ * excluded from `--retry-failed` so retry iterations actually converge.
73
+ * A new kind that is deterministically permanent (server-state, cert,
74
+ * browser-block, …) likely belongs here.
75
+ * - `PUPPETEER_FALLBACK_KINDS` (`crawler/is-puppeteer-fallback-candidate.ts`)
76
+ * — the set of kinds where one puppeteer attempt has a realistic chance
77
+ * of succeeding after HEAD+GET pre-flight exhausted retries. A new kind
78
+ * modelling a middlebox / WAF / slow-server quirk likely belongs here.
79
+ *
80
+ * Adding a kind without reviewing both sets risks a silent regression:
81
+ * `--retry-failed` re-trying a permanent failure forever (no PERMANENT
82
+ * entry), or a recoverable URL never reaching the puppeteer fallback (no
83
+ * PUPPETEER_FALLBACK entry).
84
+ */
85
+ export type ErrorKind = 'dns' | 'dns-transient' | 'connection-refused' | 'connection-reset' | 'connection-timeout' | 'tls' | 'local-network' | 'parse-error' | 'client-blocked' | 'timeout' | 'protocol' | 'unknown';
2
86
  /**
3
87
  * Event map for the `CrawlerOrchestrator` class.
4
88
  *
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Compute the SHA-256 digest of a file's bytes, streaming so memory stays
3
+ * O(1) regardless of file size.
4
+ *
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.
10
+ *
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.
18
+ * @example
19
+ * ```ts
20
+ * const sha = await computeFileSha256('/tmp/list.txt');
21
+ * if (sha) console.log(sha); // 64-char hex string
22
+ * ```
23
+ */
24
+ export declare function computeFileSha256(filePath: string): Promise<string | null>;
@@ -0,0 +1,56 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { createReadStream } from 'node:fs';
3
+ import { crawlerLog } from '../debug.js';
4
+ /**
5
+ * Compute the SHA-256 digest of a file's bytes, streaming so memory stays
6
+ * O(1) regardless of file size.
7
+ *
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.
13
+ *
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.
21
+ * @example
22
+ * ```ts
23
+ * const sha = await computeFileSha256('/tmp/list.txt');
24
+ * if (sha) console.log(sha); // 64-char hex string
25
+ * ```
26
+ */
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
+ });
56
+ }
@@ -0,0 +1,40 @@
1
+ import type { RetryCallOptions } from '@d-zero/shared/retry';
2
+ /**
3
+ * Minimal object shape required by {@link emitErrorAndRetry}: an emitter
4
+ * able to publish an `'error'` event carrying an `Error` payload. Same
5
+ * contract as `emitError` — see {@link ./emit-error.js} for details.
6
+ */
7
+ interface Emitter {
8
+ /**
9
+ * Emits the `'error'` event with the given `Error` payload.
10
+ * @param event - Event name; always `'error'` here.
11
+ * @param payload - The error instance to publish.
12
+ * @returns Anything; the caller ignores it.
13
+ */
14
+ emit(event: 'error', payload: Error): unknown;
15
+ }
16
+ /**
17
+ * Retries `fn` per `retryOptions`, then — if retries are exhausted with a
18
+ * final `Error` — logs and re-emits it as an `'error'` event on `emitter`
19
+ * before re-throwing. `label` is forwarded into `retryCall` so its own
20
+ * onWait / onGiveUp / timeout messages identify the call site (previously
21
+ * derived automatically by the `@retry` decorator via
22
+ * `ClassMethodDecoratorContext.name`).
23
+ *
24
+ * Inlined try/catch rather than composing `emitError` with a
25
+ * `() => retryCall(...)` closure to keep the per-invocation closure count
26
+ * at 1 — this method is called on the DB write hot path (`updatePage`,
27
+ * `insertResource`), where wrapping decorators previously ran with zero
28
+ * per-call allocation.
29
+ * @template T - The return type of `fn`.
30
+ * @param emitter - The event emitter that receives the `'error'` event.
31
+ * @param label - Human-readable label; forwarded into `retryCall`
32
+ * (visible in retry timeouts / onWait / onGiveUp) and used
33
+ * as the debug log prefix on final failure.
34
+ * @param fn - The async operation to retry.
35
+ * @param retryOptions - Retry configuration; `label` is overwritten
36
+ * with the `label` argument here.
37
+ * @returns The resolved value of `fn`.
38
+ */
39
+ export declare function emitErrorAndRetry<T>(emitter: Emitter, label: string, fn: () => Promise<T>, retryOptions: RetryCallOptions): Promise<T>;
40
+ export {};
@@ -0,0 +1,44 @@
1
+ import { retryCall } from '@d-zero/shared/retry';
2
+ import { log } from '../debug.js';
3
+ /**
4
+ * Debug logger for the ErrorEmitter helper family.
5
+ * Namespace: `Nitpicker:Utils:ErrorEmitter`. Kept identical to the namespace
6
+ * used by the deleted `@ErrorEmitter()` decorator so existing `DEBUG` env
7
+ * filters keep working after the decorator-to-HOF migration.
8
+ */
9
+ const errorLog = log.extend('ErrorEmitter');
10
+ /**
11
+ * Retries `fn` per `retryOptions`, then — if retries are exhausted with a
12
+ * final `Error` — logs and re-emits it as an `'error'` event on `emitter`
13
+ * before re-throwing. `label` is forwarded into `retryCall` so its own
14
+ * onWait / onGiveUp / timeout messages identify the call site (previously
15
+ * derived automatically by the `@retry` decorator via
16
+ * `ClassMethodDecoratorContext.name`).
17
+ *
18
+ * Inlined try/catch rather than composing `emitError` with a
19
+ * `() => retryCall(...)` closure to keep the per-invocation closure count
20
+ * at 1 — this method is called on the DB write hot path (`updatePage`,
21
+ * `insertResource`), where wrapping decorators previously ran with zero
22
+ * per-call allocation.
23
+ * @template T - The return type of `fn`.
24
+ * @param emitter - The event emitter that receives the `'error'` event.
25
+ * @param label - Human-readable label; forwarded into `retryCall`
26
+ * (visible in retry timeouts / onWait / onGiveUp) and used
27
+ * as the debug log prefix on final failure.
28
+ * @param fn - The async operation to retry.
29
+ * @param retryOptions - Retry configuration; `label` is overwritten
30
+ * with the `label` argument here.
31
+ * @returns The resolved value of `fn`.
32
+ */
33
+ export async function emitErrorAndRetry(emitter, label, fn, retryOptions) {
34
+ try {
35
+ return await retryCall(fn, { ...retryOptions, label });
36
+ }
37
+ catch (error) {
38
+ if (error instanceof Error) {
39
+ errorLog('%s: %O', label, error);
40
+ void emitter.emit('error', error);
41
+ }
42
+ throw error;
43
+ }
44
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Minimal object shape required by {@link emitError}: an emitter able to
3
+ * publish an `'error'` event carrying an `Error` payload. Any class that
4
+ * extends `TypedAwaitEventEmitter<{ error: Error, ... }>` satisfies this,
5
+ * and so does Node's built-in `EventEmitter`.
6
+ */
7
+ interface Emitter {
8
+ /**
9
+ * Emits the `'error'` event with the given `Error` payload.
10
+ * The return value is intentionally unspecified — callers ignore it.
11
+ * @param event - Event name; always `'error'` here.
12
+ * @param payload - The error instance to publish.
13
+ * @returns Anything; the caller ignores it.
14
+ */
15
+ emit(event: 'error', payload: Error): unknown;
16
+ }
17
+ /**
18
+ * Wraps `fn` so that any thrown `Error` is logged with `label` and
19
+ * re-emitted as an `'error'` event on `emitter` before being re-thrown.
20
+ * Non-`Error` throws pass through unchanged — no log, no emit — matching
21
+ * the behaviour of the deleted `@ErrorEmitter()` decorator so that
22
+ * downstream `'error'` listeners (e.g. the crawler orchestrator that
23
+ * aborts on any emitted error) are only invoked with real `Error`
24
+ * instances.
25
+ *
26
+ * Kept as a standalone HOF (instead of a decorator) because Vite 8 /
27
+ * Vitest 4.1 switched from esbuild to Rolldown + oxc, which emit
28
+ * TC39 Stage 3 decorator syntax as-is and let it reach Node, causing
29
+ * `SyntaxError: Invalid or unexpected token` at test runtime.
30
+ * @template T - The return type of `fn`.
31
+ * @param emitter - The event emitter that receives the `'error'` event.
32
+ * @param label - Human-readable label included in the debug log line
33
+ * (typically `'ClassName.methodName'`). Also useful as the
34
+ * grep target when triaging failed crawls.
35
+ * @param fn - The async operation to wrap.
36
+ * @returns The resolved value of `fn`.
37
+ */
38
+ export declare function emitError<T>(emitter: Emitter, label: string, fn: () => Promise<T>): Promise<T>;
39
+ export {};
@@ -0,0 +1,41 @@
1
+ import { log } from '../debug.js';
2
+ /**
3
+ * Debug logger for the ErrorEmitter helper family.
4
+ * Namespace: `Nitpicker:Utils:ErrorEmitter`. Kept identical to the namespace
5
+ * used by the deleted `@ErrorEmitter()` decorator so existing `DEBUG` env
6
+ * filters keep working after the decorator-to-HOF migration.
7
+ */
8
+ const errorLog = log.extend('ErrorEmitter');
9
+ /**
10
+ * Wraps `fn` so that any thrown `Error` is logged with `label` and
11
+ * re-emitted as an `'error'` event on `emitter` before being re-thrown.
12
+ * Non-`Error` throws pass through unchanged — no log, no emit — matching
13
+ * the behaviour of the deleted `@ErrorEmitter()` decorator so that
14
+ * downstream `'error'` listeners (e.g. the crawler orchestrator that
15
+ * aborts on any emitted error) are only invoked with real `Error`
16
+ * instances.
17
+ *
18
+ * Kept as a standalone HOF (instead of a decorator) because Vite 8 /
19
+ * Vitest 4.1 switched from esbuild to Rolldown + oxc, which emit
20
+ * TC39 Stage 3 decorator syntax as-is and let it reach Node, causing
21
+ * `SyntaxError: Invalid or unexpected token` at test runtime.
22
+ * @template T - The return type of `fn`.
23
+ * @param emitter - The event emitter that receives the `'error'` event.
24
+ * @param label - Human-readable label included in the debug log line
25
+ * (typically `'ClassName.methodName'`). Also useful as the
26
+ * grep target when triaging failed crawls.
27
+ * @param fn - The async operation to wrap.
28
+ * @returns The resolved value of `fn`.
29
+ */
30
+ export async function emitError(emitter, label, fn) {
31
+ try {
32
+ return await fn();
33
+ }
34
+ catch (error) {
35
+ if (error instanceof Error) {
36
+ errorLog('%s: %O', label, error);
37
+ void emitter.emit('error', error);
38
+ }
39
+ throw error;
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitpicker/crawler",
3
- "version": "0.11.0",
3
+ "version": "0.13.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,26 +27,26 @@
27
27
  "clean": "tsc --build --clean"
28
28
  },
29
29
  "dependencies": {
30
- "@d-zero/beholder": "3.1.1",
31
- "@d-zero/dealer": "1.9.0",
32
- "@d-zero/fs": "0.2.2",
33
- "@d-zero/shared": "0.20.1",
30
+ "@d-zero/beholder": "3.1.5",
31
+ "@d-zero/dealer": "1.9.4",
32
+ "@d-zero/fs": "0.2.6",
33
+ "@d-zero/shared": "0.22.2",
34
34
  "ansi-colors": "4.1.3",
35
35
  "debug": "4.4.3",
36
- "follow-redirects": "1.15.11",
37
- "fs-extra": "11.3.3",
38
- "knex": "3.1.0",
36
+ "follow-redirects": "1.16.0",
37
+ "fs-extra": "11.3.6",
38
+ "knex": "3.3.0",
39
39
  "libsql": "0.5.29",
40
- "puppeteer": "24.37.5",
40
+ "puppeteer": "25.3.0",
41
41
  "robots-parser": "3.0.1",
42
- "tar": "7.5.9"
42
+ "tar": "7.5.20"
43
43
  },
44
44
  "devDependencies": {
45
- "@types/debug": "4.1.12",
45
+ "@types/debug": "4.1.13",
46
46
  "@types/follow-redirects": "1.14.4",
47
47
  "@types/fs-extra": "11.0.4",
48
48
  "@types/tar": "7.0.87",
49
49
  "@types/unzipper": "0.10.11"
50
50
  },
51
- "gitHead": "b11dbe3691746b3cb980476808d1f745bc6d0e98"
51
+ "gitHead": "2cd462ea6b183f4462b7caba27b4db04aaba7c70"
52
52
  }
@@ -1,20 +0,0 @@
1
- import type { Knex } from 'knex';
2
- /**
3
- * Adds the `crawl_errors` table to archives created before structured capture
4
- * of the crawler-level `error` channel landed.
5
- *
6
- * `crawl_errors` records errors that previously only reached `error.log` (DNS
7
- * failures, connection resets, TLS problems, process-level errors), in a
8
- * queryable form so the `error-kinds` analysis can classify them without
9
- * parsing the text log. Older `.nitpicker` files predate this table; this
10
- * migration creates it idempotently. Existing archives stay empty until they
11
- * are crawled again (resume / append / retry), at which point new errors are
12
- * written here — analysis of pre-existing data falls back to parsing
13
- * `error.log`.
14
- *
15
- * Idempotent: when the table already exists, the function exits without
16
- * touching the schema or writing to stderr. It also skips empty archives (no
17
- * `pages` table), which the regular initSchema path will fully provision.
18
- * @param instance - The Knex query builder instance connected to the database.
19
- */
20
- export declare function migrateCrawlErrors(instance: Knex): Promise<void>;
@@ -1,38 +0,0 @@
1
- /**
2
- * Adds the `crawl_errors` table to archives created before structured capture
3
- * of the crawler-level `error` channel landed.
4
- *
5
- * `crawl_errors` records errors that previously only reached `error.log` (DNS
6
- * failures, connection resets, TLS problems, process-level errors), in a
7
- * queryable form so the `error-kinds` analysis can classify them without
8
- * parsing the text log. Older `.nitpicker` files predate this table; this
9
- * migration creates it idempotently. Existing archives stay empty until they
10
- * are crawled again (resume / append / retry), at which point new errors are
11
- * written here — analysis of pre-existing data falls back to parsing
12
- * `error.log`.
13
- *
14
- * Idempotent: when the table already exists, the function exits without
15
- * touching the schema or writing to stderr. It also skips empty archives (no
16
- * `pages` table), which the regular initSchema path will fully provision.
17
- * @param instance - The Knex query builder instance connected to the database.
18
- */
19
- export async function migrateCrawlErrors(instance) {
20
- const hasTable = await instance.schema.hasTable('crawl_errors');
21
- if (hasTable) {
22
- return;
23
- }
24
- const hasPages = await instance.schema.hasTable('pages');
25
- if (!hasPages) {
26
- // Empty archive; the regular initSchema path will create the table.
27
- return;
28
- }
29
- await instance.schema.createTable('crawl_errors', (t) => {
30
- t.increments('id');
31
- t.string('url', 8190).nullable();
32
- t.boolean('isExternal');
33
- t.text('message').notNullable();
34
- t.integer('createdAt').notNullable();
35
- });
36
- // eslint-disable-next-line no-console
37
- console.error('[migrate] crawl_errors table created');
38
- }
@@ -1,24 +0,0 @@
1
- import type { Knex } from 'knex';
2
- /**
3
- * Adds the BLOB-backed HTML snapshot tables (`page_html_blobs` +
4
- * `page_html_ref`) to archives created before #75.
5
- *
6
- * A pre-#75 `.nitpicker` already has the `info` table, so `initSchema`'s
7
- * early-return path skips the freshly-introduced `CREATE TABLE`s. This
8
- * migration brings the schema forward idempotently so the read API stops
9
- * raising raw "no such table" errors against legacy archives. It does NOT
10
- * touch the `pages.html` column or backfill the new tables from the
11
- * archive's `snapshot-html.zip`: that data migration belongs to the
12
- * `scripts/migrate-to-0.10.mjs` one-shot, which can run whenever the
13
- * user is ready to commit the (multi-hour) CPU cost.
14
- *
15
- * Outcome on a legacy archive that has NOT yet been data-migrated:
16
- * - new tables exist but are empty,
17
- * - `getHtmlOfPageById` returns `null` for every page (no row in `page_html_ref`),
18
- * - viewer / MCP / analyze plugins see "snapshot unavailable" instead of crashing.
19
- *
20
- * The migration prints a one-line notice when it runs so the user knows
21
- * the archive was upgraded in place.
22
- * @param instance - The Knex query builder instance connected to the database.
23
- */
24
- export declare function migrateHtmlBlobTables(instance: Knex): Promise<void>;
@@ -1,53 +0,0 @@
1
- /**
2
- * Adds the BLOB-backed HTML snapshot tables (`page_html_blobs` +
3
- * `page_html_ref`) to archives created before #75.
4
- *
5
- * A pre-#75 `.nitpicker` already has the `info` table, so `initSchema`'s
6
- * early-return path skips the freshly-introduced `CREATE TABLE`s. This
7
- * migration brings the schema forward idempotently so the read API stops
8
- * raising raw "no such table" errors against legacy archives. It does NOT
9
- * touch the `pages.html` column or backfill the new tables from the
10
- * archive's `snapshot-html.zip`: that data migration belongs to the
11
- * `scripts/migrate-to-0.10.mjs` one-shot, which can run whenever the
12
- * user is ready to commit the (multi-hour) CPU cost.
13
- *
14
- * Outcome on a legacy archive that has NOT yet been data-migrated:
15
- * - new tables exist but are empty,
16
- * - `getHtmlOfPageById` returns `null` for every page (no row in `page_html_ref`),
17
- * - viewer / MCP / analyze plugins see "snapshot unavailable" instead of crashing.
18
- *
19
- * The migration prints a one-line notice when it runs so the user knows
20
- * the archive was upgraded in place.
21
- * @param instance - The Knex query builder instance connected to the database.
22
- */
23
- export async function migrateHtmlBlobTables(instance) {
24
- const hasBlobs = await instance.schema.hasTable('page_html_blobs');
25
- if (hasBlobs) {
26
- return;
27
- }
28
- const hasPages = await instance.schema.hasTable('pages');
29
- if (!hasPages) {
30
- // Empty archive; initSchema will create both tables on the
31
- // fresh-DB path. Nothing to migrate.
32
- return;
33
- }
34
- await instance.raw(`
35
- CREATE TABLE page_html_blobs (
36
- hash BLOB PRIMARY KEY,
37
- body BLOB NOT NULL,
38
- codec TEXT NOT NULL CHECK(codec IN ('zstd', 'none')),
39
- size_raw INTEGER NOT NULL,
40
- size_stored INTEGER NOT NULL
41
- ) WITHOUT ROWID
42
- `);
43
- await instance.raw(`
44
- CREATE TABLE page_html_ref (
45
- page_id INTEGER PRIMARY KEY REFERENCES pages(id) ON DELETE CASCADE,
46
- hash BLOB NOT NULL REFERENCES page_html_blobs(hash)
47
- ) WITHOUT ROWID
48
- `);
49
- await instance.raw('CREATE INDEX idx_page_html_ref_hash ON page_html_ref(hash)');
50
- // eslint-disable-next-line no-console
51
- console.error('[migrate] page_html_blobs / page_html_ref tables created. ' +
52
- 'HTML snapshots are empty until `node scripts/migrate-to-0.10.mjs` is run on the original archive.');
53
- }
@@ -1,16 +0,0 @@
1
- import type { Knex } from 'knex';
2
- /**
3
- * Adds the `page_errors` table to archives created before partial-failure
4
- * recording landed.
5
- *
6
- * `page_errors` captures secondary scrape failures (e.g. a viewport switch
7
- * that detaches the frame and surfaces `retryExhausted`) so they show up in
8
- * the archive instead of being lost to stdout logs. Older `.nitpicker` files
9
- * predate this table; this migration creates it idempotently.
10
- *
11
- * Idempotent: when the table already exists, the function exits without
12
- * touching the schema or writing to stderr. When it has to run, a single
13
- * notice is written so the user knows the file was upgraded.
14
- * @param instance - The Knex query builder instance connected to the database.
15
- */
16
- export declare function migratePageErrors(instance: Knex): Promise<void>;