@nitpicker/crawler 0.12.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 (349) 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 +63 -16
  7. package/lib/archive/archive.js +56 -17
  8. package/lib/archive/create-adjunct-tables.d.ts +43 -0
  9. package/lib/archive/create-adjunct-tables.js +213 -0
  10. package/lib/archive/create-entity-tables.d.ts +173 -0
  11. package/lib/archive/create-entity-tables.js +318 -0
  12. package/lib/archive/create-progress-reporter.d.ts +30 -0
  13. package/lib/archive/create-progress-reporter.js +38 -0
  14. package/lib/archive/create-ref-tables.d.ts +35 -0
  15. package/lib/archive/create-ref-tables.js +188 -0
  16. package/lib/archive/database.d.ts +92 -345
  17. package/lib/archive/database.js +168 -1942
  18. package/lib/archive/db-ops/_shared/clear-write-ref-caches.d.ts +27 -0
  19. package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +34 -0
  20. package/lib/archive/db-ops/_shared/create-write-ref-caches.d.ts +17 -0
  21. package/lib/archive/db-ops/_shared/create-write-ref-caches.js +26 -0
  22. package/lib/archive/db-ops/_shared/decode-json-ref.d.ts +17 -0
  23. package/lib/archive/db-ops/_shared/decode-json-ref.js +31 -0
  24. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.d.ts +20 -0
  25. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.js +53 -0
  26. package/lib/archive/db-ops/_shared/resolve-content-item-id.d.ts +61 -0
  27. package/lib/archive/db-ops/_shared/resolve-content-item-id.js +111 -0
  28. package/lib/archive/db-ops/_shared/resolve-url-or-blob.d.ts +23 -0
  29. package/lib/archive/db-ops/_shared/resolve-url-or-blob.js +29 -0
  30. package/lib/archive/db-ops/_shared/retry-setting.d.ts +16 -0
  31. package/lib/archive/db-ops/_shared/retry-setting.js +18 -0
  32. package/lib/archive/db-ops/_shared/safe-parse-json.d.ts +11 -0
  33. package/lib/archive/db-ops/_shared/safe-parse-json.js +18 -0
  34. package/lib/archive/db-ops/_shared/types.d.ts +53 -0
  35. package/lib/archive/db-ops/_shared/types.js +1 -0
  36. package/lib/archive/db-ops/_shared/upsert-blob-ref.d.ts +25 -0
  37. package/lib/archive/db-ops/_shared/upsert-blob-ref.js +48 -0
  38. package/lib/archive/db-ops/_shared/upsert-content-type-ref.d.ts +30 -0
  39. package/lib/archive/db-ops/_shared/upsert-content-type-ref.js +45 -0
  40. package/lib/archive/db-ops/_shared/upsert-json-ref.d.ts +22 -0
  41. package/lib/archive/db-ops/_shared/upsert-json-ref.js +41 -0
  42. package/lib/archive/db-ops/_shared/upsert-response-headers.d.ts +35 -0
  43. package/lib/archive/db-ops/_shared/upsert-response-headers.js +49 -0
  44. package/lib/archive/db-ops/_shared/upsert-url-ref.d.ts +39 -0
  45. package/lib/archive/db-ops/_shared/upsert-url-ref.js +62 -0
  46. package/lib/archive/db-ops/analysis/replace-analysis-violations.d.ts +28 -0
  47. package/lib/archive/db-ops/analysis/replace-analysis-violations.js +152 -0
  48. package/lib/archive/db-ops/anchors/get-anchors-on-page.d.ts +10 -0
  49. package/lib/archive/db-ops/anchors/get-anchors-on-page.js +21 -0
  50. package/lib/archive/db-ops/config/get-base-url.d.ts +8 -0
  51. package/lib/archive/db-ops/config/get-base-url.js +14 -0
  52. package/lib/archive/db-ops/config/get-config.d.ts +10 -0
  53. package/lib/archive/db-ops/config/get-config.js +27 -0
  54. package/lib/archive/db-ops/config/get-name.d.ts +8 -0
  55. package/lib/archive/db-ops/config/get-name.js +14 -0
  56. package/lib/archive/db-ops/config/info-column-allowlist.d.ts +7 -0
  57. package/lib/archive/db-ops/config/info-column-allowlist.js +26 -0
  58. package/lib/archive/db-ops/config/info-json-columns.d.ts +5 -0
  59. package/lib/archive/db-ops/config/info-json-columns.js +10 -0
  60. package/lib/archive/db-ops/config/set-config.d.ts +12 -0
  61. package/lib/archive/db-ops/config/set-config.js +21 -0
  62. package/lib/archive/db-ops/config/update-config.d.ts +17 -0
  63. package/lib/archive/db-ops/config/update-config.js +36 -0
  64. package/lib/archive/db-ops/errors/insert-crawl-error.d.ts +15 -0
  65. package/lib/archive/db-ops/errors/insert-crawl-error.js +21 -0
  66. package/lib/archive/db-ops/errors/insert-page-error.d.ts +21 -0
  67. package/lib/archive/db-ops/errors/insert-page-error.js +28 -0
  68. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +22 -0
  69. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +141 -0
  70. package/lib/archive/db-ops/html/get-html-of-page-by-id.d.ts +18 -0
  71. package/lib/archive/db-ops/html/get-html-of-page-by-id.js +29 -0
  72. package/lib/archive/db-ops/inventory/record-inventory-run.d.ts +21 -0
  73. package/lib/archive/db-ops/inventory/record-inventory-run.js +38 -0
  74. package/lib/archive/db-ops/lifecycle/checkpoint.d.ts +8 -0
  75. package/lib/archive/db-ops/lifecycle/checkpoint.js +9 -0
  76. package/lib/archive/db-ops/lifecycle/destroy.d.ts +6 -0
  77. package/lib/archive/db-ops/lifecycle/destroy.js +7 -0
  78. package/lib/archive/db-ops/lifecycle/init.d.ts +22 -0
  79. package/lib/archive/db-ops/lifecycle/init.js +42 -0
  80. package/lib/archive/db-ops/meta/get-jsonld-of-page.d.ts +13 -0
  81. package/lib/archive/db-ops/meta/get-jsonld-of-page.js +27 -0
  82. package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +12 -0
  83. package/lib/archive/db-ops/meta/get-tags-of-page.js +28 -0
  84. package/lib/archive/db-ops/pages/order/set-url-order.d.ts +8 -0
  85. package/lib/archive/db-ops/pages/order/set-url-order.js +32 -0
  86. package/lib/archive/db-ops/pages/read/build-page-query.d.ts +18 -0
  87. package/lib/archive/db-ops/pages/read/build-page-query.js +40 -0
  88. package/lib/archive/db-ops/pages/read/get-crawling-state.d.ts +70 -0
  89. package/lib/archive/db-ops/pages/read/get-crawling-state.js +98 -0
  90. package/lib/archive/db-ops/pages/read/get-existing-page-urls.d.ts +15 -0
  91. package/lib/archive/db-ops/pages/read/get-existing-page-urls.js +30 -0
  92. package/lib/archive/db-ops/pages/read/get-page-count.d.ts +12 -0
  93. package/lib/archive/db-ops/pages/read/get-page-count.js +21 -0
  94. package/lib/archive/db-ops/pages/read/get-page-source-by-url.d.ts +24 -0
  95. package/lib/archive/db-ops/pages/read/get-page-source-by-url.js +28 -0
  96. package/lib/archive/db-ops/pages/read/get-pages-with-rels.d.ts +38 -0
  97. package/lib/archive/db-ops/pages/read/get-pages-with-rels.js +107 -0
  98. package/lib/archive/db-ops/pages/read/get-pages.d.ts +11 -0
  99. package/lib/archive/db-ops/pages/read/get-pages.js +51 -0
  100. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.d.ts +18 -0
  101. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.js +25 -0
  102. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.d.ts +31 -0
  103. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.js +32 -0
  104. package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +24 -0
  105. package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +93 -0
  106. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +47 -0
  107. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +124 -0
  108. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.d.ts +37 -0
  109. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.js +72 -0
  110. package/lib/archive/db-ops/pages/write/insert-jsonld.d.ts +17 -0
  111. package/lib/archive/db-ops/pages/write/insert-jsonld.js +49 -0
  112. package/lib/archive/db-ops/pages/write/insert-page.d.ts +36 -0
  113. package/lib/archive/db-ops/pages/write/insert-page.js +208 -0
  114. package/lib/archive/db-ops/pages/write/insert-tags.d.ts +16 -0
  115. package/lib/archive/db-ops/pages/write/insert-tags.js +34 -0
  116. package/lib/archive/db-ops/pages/write/link-redirect-sources.d.ts +36 -0
  117. package/lib/archive/db-ops/pages/write/link-redirect-sources.js +93 -0
  118. package/lib/archive/db-ops/pages/write/record-redirect.d.ts +35 -0
  119. package/lib/archive/db-ops/pages/write/record-redirect.js +100 -0
  120. package/lib/archive/db-ops/pages/write/set-skipped-page.d.ts +13 -0
  121. package/lib/archive/db-ops/pages/write/set-skipped-page.js +22 -0
  122. package/lib/archive/db-ops/pages/write/update-page.d.ts +29 -0
  123. package/lib/archive/db-ops/pages/write/update-page.js +334 -0
  124. package/lib/archive/db-ops/pages/write/write-page-html-blob.d.ts +19 -0
  125. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +41 -0
  126. package/lib/archive/db-ops/referrers/get-redirects-for-pages.d.ts +9 -0
  127. package/lib/archive/db-ops/referrers/get-redirects-for-pages.js +15 -0
  128. package/lib/archive/db-ops/referrers/get-referrers-of-page.d.ts +17 -0
  129. package/lib/archive/db-ops/referrers/get-referrers-of-page.js +32 -0
  130. package/lib/archive/db-ops/referrers/get-referrers-of-resource.d.ts +8 -0
  131. package/lib/archive/db-ops/referrers/get-referrers-of-resource.js +15 -0
  132. package/lib/archive/db-ops/resources/build-resource-query.d.ts +25 -0
  133. package/lib/archive/db-ops/resources/build-resource-query.js +29 -0
  134. package/lib/archive/db-ops/resources/get-existing-resource-urls.d.ts +9 -0
  135. package/lib/archive/db-ops/resources/get-existing-resource-urls.js +24 -0
  136. package/lib/archive/db-ops/resources/get-resource-by-url.d.ts +13 -0
  137. package/lib/archive/db-ops/resources/get-resource-by-url.js +22 -0
  138. package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +9 -0
  139. package/lib/archive/db-ops/resources/get-resource-url-list.js +13 -0
  140. package/lib/archive/db-ops/resources/get-resources.d.ts +8 -0
  141. package/lib/archive/db-ops/resources/get-resources.js +11 -0
  142. package/lib/archive/db-ops/resources/insert-inventory-resources.d.ts +24 -0
  143. package/lib/archive/db-ops/resources/insert-inventory-resources.js +64 -0
  144. package/lib/archive/db-ops/resources/insert-resource-referrers.d.ts +15 -0
  145. package/lib/archive/db-ops/resources/insert-resource-referrers.js +54 -0
  146. package/lib/archive/db-ops/resources/insert-resource.d.ts +34 -0
  147. package/lib/archive/db-ops/resources/insert-resource.js +73 -0
  148. package/lib/archive/db-ops/resources/reconstruct-resource-rows.d.ts +26 -0
  149. package/lib/archive/db-ops/resources/reconstruct-resource-rows.js +30 -0
  150. package/lib/archive/decode-html-blob.d.ts +18 -0
  151. package/lib/archive/decode-html-blob.js +31 -0
  152. package/lib/archive/derive-lineage-from-parent.d.ts +1 -1
  153. package/lib/archive/derive-lineage-from-parent.js +1 -1
  154. package/lib/archive/drop-legacy-tables.d.ts +45 -0
  155. package/lib/archive/drop-legacy-tables.js +56 -0
  156. package/lib/archive/filesystem/rename.js +1 -1
  157. package/lib/archive/get-failed-page-messages.d.ts +5 -4
  158. package/lib/archive/get-failed-page-messages.js +5 -4
  159. package/lib/archive/init-schema.d.ts +35 -39
  160. package/lib/archive/init-schema.js +99 -460
  161. package/lib/archive/limited-page-ids.d.ts +2 -1
  162. package/lib/archive/limited-page-ids.js +5 -4
  163. package/lib/archive/meta/assert-compatible-version.d.ts +24 -3
  164. package/lib/archive/meta/assert-compatible-version.js +24 -3
  165. package/lib/archive/meta/types.d.ts +87 -1
  166. package/lib/archive/meta/types.js +34 -2
  167. package/lib/archive/migrate-entity-tables.d.ts +45 -0
  168. package/lib/archive/migrate-entity-tables.js +56 -0
  169. package/lib/archive/migrate-ref-tables.d.ts +25 -0
  170. package/lib/archive/migrate-ref-tables.js +38 -0
  171. package/lib/archive/page-meta-column-maps.d.ts +32 -0
  172. package/lib/archive/page-meta-column-maps.js +43 -0
  173. package/lib/archive/page.d.ts +6 -6
  174. package/lib/archive/page.js +5 -5
  175. package/lib/archive/peek-archive-lock.d.ts +2 -2
  176. package/lib/archive/peek-archive-lock.js +2 -2
  177. package/lib/archive/populate-entity-tables/collapse-anchor-rows.d.ts +41 -0
  178. package/lib/archive/populate-entity-tables/collapse-anchor-rows.js +87 -0
  179. package/lib/archive/populate-entity-tables/derive-dom-path.d.ts +35 -0
  180. package/lib/archive/populate-entity-tables/derive-dom-path.js +72 -0
  181. package/lib/archive/populate-entity-tables/is-blob-ref-value.d.ts +16 -0
  182. package/lib/archive/populate-entity-tables/is-blob-ref-value.js +19 -0
  183. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.d.ts +66 -0
  184. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.js +96 -0
  185. package/lib/archive/populate-entity-tables/populate-anchor-edges.d.ts +33 -0
  186. package/lib/archive/populate-entity-tables/populate-anchor-edges.js +153 -0
  187. package/lib/archive/populate-entity-tables/populate-content-items.d.ts +40 -0
  188. package/lib/archive/populate-entity-tables/populate-content-items.js +141 -0
  189. package/lib/archive/populate-entity-tables/populate-entities.d.ts +81 -0
  190. package/lib/archive/populate-entity-tables/populate-entities.js +111 -0
  191. package/lib/archive/populate-entity-tables/populate-image-items.d.ts +91 -0
  192. package/lib/archive/populate-entity-tables/populate-image-items.js +223 -0
  193. package/lib/archive/populate-entity-tables/populate-page-meta.d.ts +33 -0
  194. package/lib/archive/populate-entity-tables/populate-page-meta.js +267 -0
  195. package/lib/archive/populate-entity-tables/populate-resource-items.d.ts +22 -0
  196. package/lib/archive/populate-entity-tables/populate-resource-items.js +114 -0
  197. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.d.ts +31 -0
  198. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.js +33 -0
  199. package/lib/archive/populate-entity-tables/resolve-blob-refs.d.ts +31 -0
  200. package/lib/archive/populate-entity-tables/resolve-blob-refs.js +100 -0
  201. package/lib/archive/populate-entity-tables/resolve-content-type-refs.d.ts +22 -0
  202. package/lib/archive/populate-entity-tables/resolve-content-type-refs.js +27 -0
  203. package/lib/archive/populate-entity-tables/resolve-header-sets.d.ts +49 -0
  204. package/lib/archive/populate-entity-tables/resolve-header-sets.js +122 -0
  205. package/lib/archive/populate-entity-tables/resolve-json-refs.d.ts +25 -0
  206. package/lib/archive/populate-entity-tables/resolve-json-refs.js +67 -0
  207. package/lib/archive/populate-entity-tables/resolve-text-refs.d.ts +30 -0
  208. package/lib/archive/populate-entity-tables/resolve-text-refs.js +61 -0
  209. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.d.ts +21 -0
  210. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.js +27 -0
  211. package/lib/archive/populate-entity-tables/resolve-url-refs.d.ts +33 -0
  212. package/lib/archive/populate-entity-tables/resolve-url-refs.js +60 -0
  213. package/lib/archive/populate-entity-tables/test-utils/count-rows.d.ts +17 -0
  214. package/lib/archive/populate-entity-tables/test-utils/count-rows.js +20 -0
  215. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.d.ts +25 -0
  216. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.js +42 -0
  217. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.d.ts +23 -0
  218. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.js +178 -0
  219. package/lib/archive/populate-entity-tables/types.d.ts +157 -0
  220. package/lib/archive/populate-entity-tables/types.js +12 -0
  221. package/lib/archive/populate-entity-tables/upsert-text-refs.d.ts +38 -0
  222. package/lib/archive/populate-entity-tables/upsert-text-refs.js +78 -0
  223. package/lib/archive/populate-ref-tables/classify-content-type.d.ts +16 -0
  224. package/lib/archive/populate-ref-tables/classify-content-type.js +52 -0
  225. package/lib/archive/populate-ref-tables/compute-content-hash.d.ts +22 -0
  226. package/lib/archive/populate-ref-tables/compute-content-hash.js +26 -0
  227. package/lib/archive/populate-ref-tables/compute-header-flags.d.ts +16 -0
  228. package/lib/archive/populate-ref-tables/compute-header-flags.js +70 -0
  229. package/lib/archive/populate-ref-tables/content-type-rules.d.ts +38 -0
  230. package/lib/archive/populate-ref-tables/content-type-rules.js +133 -0
  231. package/lib/archive/populate-ref-tables/create-header-table-caches.d.ts +25 -0
  232. package/lib/archive/populate-ref-tables/create-header-table-caches.js +49 -0
  233. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.d.ts +15 -0
  234. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.js +15 -0
  235. package/lib/archive/populate-ref-tables/decode-data-uri.d.ts +21 -0
  236. package/lib/archive/populate-ref-tables/decode-data-uri.js +126 -0
  237. package/lib/archive/populate-ref-tables/decompose-header-set.d.ts +29 -0
  238. package/lib/archive/populate-ref-tables/decompose-header-set.js +157 -0
  239. package/lib/archive/populate-ref-tables/decompose-url.d.ts +25 -0
  240. package/lib/archive/populate-ref-tables/decompose-url.js +70 -0
  241. package/lib/archive/populate-ref-tables/header-stability.d.ts +19 -0
  242. package/lib/archive/populate-ref-tables/header-stability.js +22 -0
  243. package/lib/archive/populate-ref-tables/header-value-cache-key.d.ts +17 -0
  244. package/lib/archive/populate-ref-tables/header-value-cache-key.js +19 -0
  245. package/lib/archive/populate-ref-tables/normalize-mime.d.ts +24 -0
  246. package/lib/archive/populate-ref-tables/normalize-mime.js +36 -0
  247. package/lib/archive/populate-ref-tables/populate-blob-refs.d.ts +38 -0
  248. package/lib/archive/populate-ref-tables/populate-blob-refs.js +134 -0
  249. package/lib/archive/populate-ref-tables/populate-content-type-refs.d.ts +27 -0
  250. package/lib/archive/populate-ref-tables/populate-content-type-refs.js +70 -0
  251. package/lib/archive/populate-ref-tables/populate-header-tables.d.ts +35 -0
  252. package/lib/archive/populate-ref-tables/populate-header-tables.js +80 -0
  253. package/lib/archive/populate-ref-tables/populate-json-refs.d.ts +29 -0
  254. package/lib/archive/populate-ref-tables/populate-json-refs.js +101 -0
  255. package/lib/archive/populate-ref-tables/populate-refs.d.ts +51 -0
  256. package/lib/archive/populate-ref-tables/populate-refs.js +62 -0
  257. package/lib/archive/populate-ref-tables/populate-text-refs.d.ts +32 -0
  258. package/lib/archive/populate-ref-tables/populate-text-refs.js +133 -0
  259. package/lib/archive/populate-ref-tables/populate-url-refs.d.ts +28 -0
  260. package/lib/archive/populate-ref-tables/populate-url-refs.js +148 -0
  261. package/lib/archive/populate-ref-tables/test-utils/count-rows.d.ts +15 -0
  262. package/lib/archive/populate-ref-tables/test-utils/count-rows.js +17 -0
  263. package/lib/archive/populate-ref-tables/types.d.ts +197 -0
  264. package/lib/archive/populate-ref-tables/types.js +7 -0
  265. package/lib/archive/populate-ref-tables/upsert-one-header-set.d.ts +34 -0
  266. package/lib/archive/populate-ref-tables/upsert-one-header-set.js +208 -0
  267. package/lib/archive/populate-ref-tables/volatile-header-names.d.ts +20 -0
  268. package/lib/archive/populate-ref-tables/volatile-header-names.js +33 -0
  269. package/lib/archive/redirect-table.d.ts +4 -2
  270. package/lib/archive/redirect-table.js +15 -10
  271. package/lib/archive/resolve-redirect-chain.d.ts +3 -3
  272. package/lib/archive/resolve-redirect-chain.js +2 -2
  273. package/lib/archive/resource.d.ts +1 -1
  274. package/lib/archive/retarget-legacy-fk-tables.d.ts +47 -0
  275. package/lib/archive/retarget-legacy-fk-tables.js +107 -0
  276. package/lib/archive/test-utils/fk-parent-tables.d.ts +15 -0
  277. package/lib/archive/test-utils/fk-parent-tables.js +19 -0
  278. package/lib/archive/test-utils/seed-content-item.d.ts +35 -0
  279. package/lib/archive/test-utils/seed-content-item.js +42 -0
  280. package/lib/archive/test-utils/setup-legacy-fk-db.d.ts +33 -0
  281. package/lib/archive/test-utils/setup-legacy-fk-db.js +270 -0
  282. package/lib/archive/types.d.ts +127 -24
  283. package/lib/archive/verify-migration/capture-rejection.d.ts +24 -0
  284. package/lib/archive/verify-migration/capture-rejection.js +31 -0
  285. package/lib/archive/verify-migration/check-anchor-edges-count.d.ts +34 -0
  286. package/lib/archive/verify-migration/check-anchor-edges-count.js +72 -0
  287. package/lib/archive/verify-migration/check-anchor-edges-sum.d.ts +13 -0
  288. package/lib/archive/verify-migration/check-anchor-edges-sum.js +27 -0
  289. package/lib/archive/verify-migration/check-content-items-count.d.ts +16 -0
  290. package/lib/archive/verify-migration/check-content-items-count.js +30 -0
  291. package/lib/archive/verify-migration/check-content-type-preservation.d.ts +22 -0
  292. package/lib/archive/verify-migration/check-content-type-preservation.js +40 -0
  293. package/lib/archive/verify-migration/check-foreign-key-integrity.d.ts +31 -0
  294. package/lib/archive/verify-migration/check-foreign-key-integrity.js +47 -0
  295. package/lib/archive/verify-migration/check-image-items-count.d.ts +12 -0
  296. package/lib/archive/verify-migration/check-image-items-count.js +26 -0
  297. package/lib/archive/verify-migration/check-page-meta-count.d.ts +15 -0
  298. package/lib/archive/verify-migration/check-page-meta-count.js +31 -0
  299. package/lib/archive/verify-migration/check-reader-parity.d.ts +23 -0
  300. package/lib/archive/verify-migration/check-reader-parity.js +211 -0
  301. package/lib/archive/verify-migration/check-resource-items-count.d.ts +17 -0
  302. package/lib/archive/verify-migration/check-resource-items-count.js +33 -0
  303. package/lib/archive/verify-migration/check-url-round-trip.d.ts +43 -0
  304. package/lib/archive/verify-migration/check-url-round-trip.js +112 -0
  305. package/lib/archive/verify-migration/types.d.ts +70 -0
  306. package/lib/archive/verify-migration/types.js +63 -0
  307. package/lib/archive/verify-migration/verify-migration.d.ts +41 -0
  308. package/lib/archive/verify-migration/verify-migration.js +120 -0
  309. package/lib/crawler/build-redirect-event.d.ts +1 -1
  310. package/lib/crawler/build-redirect-event.js +1 -1
  311. package/lib/crawler/capture-image-dom-paths.d.ts +33 -0
  312. package/lib/crawler/capture-image-dom-paths.js +39 -0
  313. package/lib/crawler/clear-dns-burned-host-cache.d.ts +1 -1
  314. package/lib/crawler/clear-dns-burned-host-cache.js +1 -1
  315. package/lib/crawler/collect-image-dom-paths.d.ts +23 -0
  316. package/lib/crawler/collect-image-dom-paths.js +64 -0
  317. package/lib/crawler/crawler.d.ts +19 -0
  318. package/lib/crawler/crawler.js +40 -26
  319. package/lib/crawler/dns-burned-host-cache.d.ts +3 -3
  320. package/lib/crawler/dns-burned-host-cache.js +3 -3
  321. package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +2 -2
  322. package/lib/crawler/dns-burned-host-short-circuit-counter.js +2 -2
  323. package/lib/crawler/inject-scope-auth.d.ts +1 -1
  324. package/lib/crawler/inject-scope-auth.js +1 -1
  325. package/lib/crawler/normalize-content-type.d.ts +1 -1
  326. package/lib/crawler/normalize-content-type.js +1 -1
  327. package/lib/crawler/types.d.ts +3 -3
  328. package/lib/crawler-orchestrator.d.ts +9 -0
  329. package/lib/crawler-orchestrator.js +44 -28
  330. package/lib/crawler.d.ts +12 -0
  331. package/lib/crawler.js +21 -0
  332. package/lib/permanent-error-kinds.d.ts +1 -1
  333. package/lib/permanent-error-kinds.js +1 -1
  334. package/lib/types.d.ts +1 -1
  335. package/lib/utils/compute-file-sha256.d.ts +5 -4
  336. package/lib/utils/compute-file-sha256.js +5 -4
  337. package/lib/utils/error/emit-error-with-retry.d.ts +1 -1
  338. package/lib/utils/error/emit-error-with-retry.js +1 -1
  339. package/package.json +10 -10
  340. package/lib/archive/migrate-crawl-errors.d.ts +0 -20
  341. package/lib/archive/migrate-crawl-errors.js +0 -38
  342. package/lib/archive/migrate-html-blob-tables.d.ts +0 -24
  343. package/lib/archive/migrate-html-blob-tables.js +0 -53
  344. package/lib/archive/migrate-inventory-runs.d.ts +0 -29
  345. package/lib/archive/migrate-inventory-runs.js +0 -52
  346. package/lib/archive/migrate-page-errors.d.ts +0 -16
  347. package/lib/archive/migrate-page-errors.js +0 -35
  348. package/lib/archive/migrate-pages-resources-source.d.ts +0 -16
  349. package/lib/archive/migrate-pages-resources-source.js +0 -46
@@ -0,0 +1,33 @@
1
+ import { MigrationVerificationError } from './types.js';
2
+ /**
3
+ * Verifies 0.13 invariant #6: every legacy `resources` row is mirrored
4
+ * by one row in `resource_items`.
5
+ *
6
+ * `populateResourceItems` fills `resource_items` with the same PK as
7
+ * `resources.id`
8
+ * (see `populate-resource-items.ts`); the invariant is broken only if the
9
+ * populate loop skipped rows during URL / header-set / content-type
10
+ * resolution. The paired invariant `resource_ref_edges = "resources-referrers"`
11
+ * is left unchecked here because issue #194's spec list stops at the
12
+ * six enumerated checks, and `resource_ref_edges` populate is a straight
13
+ * `INSERT … SELECT` (no dedup, no resolution) with negligible failure surface.
14
+ * @param trx - Knex instance or transaction connected to the populated archive.
15
+ * @throws {MigrationVerificationError} when the row counts diverge.
16
+ */
17
+ export async function checkResourceItemsCount(trx) {
18
+ const resourceItemsRows = await trx('resource_items').count({
19
+ n: '*',
20
+ });
21
+ const resourcesRows = await trx('resources').count({ n: '*' });
22
+ const resourceItemsCount = Number(resourceItemsRows[0].n);
23
+ const resourcesCount = Number(resourcesRows[0].n);
24
+ if (resourceItemsCount !== resourcesCount) {
25
+ throw new MigrationVerificationError({
26
+ check: '#6 resource_items row count',
27
+ context: {
28
+ resource_items: resourceItemsCount,
29
+ resources: resourcesCount,
30
+ },
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,43 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Verifies 0.13 invariant #8: `content_items.url_id → url_refs.url`
4
+ * round-trips back to the original `pages.url` on a deterministic
5
+ * ≤ {@link SAMPLE_SIZE} sample.
6
+ *
7
+ * A mismatch means one of two things:
8
+ *
9
+ * - `populate-url-refs.ts` collapsed two distinct URLs into the same
10
+ * `url_refs.id` (BINARY collation bug, whitespace normalisation, …).
11
+ * - `populate-content-items.ts` associated a page id with the wrong
12
+ * `url_ref.id` (row-order dependency in the batch resolver).
13
+ *
14
+ * Additionally an FK gap (`content_items.url_id` pointing at a
15
+ * non-existent `url_refs.id`) or a missing `pages` row surfaces as a
16
+ * `roundTripUrl` / `sourceUrl` of `null`; the check treats either as a
17
+ * round-trip failure. LEFT JOINs are used deliberately so orphan rows
18
+ * are observable — an INNER JOIN would silently drop them and let the
19
+ * bug the invariant is meant to catch slip through.
20
+ *
21
+ * Sampling is **deterministic**: stride = ⌈count(content_items) / N⌉
22
+ * gives approximately {@link SAMPLE_SIZE} rows spread across the id
23
+ * range for large archives, and the full table for archives with
24
+ * ≤ {@link SAMPLE_SIZE} rows. Deterministic sampling means the same
25
+ * archive always produces the same verdict; a random sample would let a
26
+ * failing archive pass on retry if the offending rows happen to fall
27
+ * outside the second draw.
28
+ *
29
+ * The check throws when the sampled row count is smaller than expected
30
+ * (LEFT JOINs may return `null` sides but never fewer rows than the
31
+ * driving `content_items` set — a shortfall means SQLite silently
32
+ * dropped rows we intended to inspect). "Expected" is a separate
33
+ * `COUNT(*)` against the same `(id % stride) = 0` predicate, not a
34
+ * `floor(total / stride)` estimate: `content_items.id` is copied
35
+ * verbatim from the legacy `pages.id`, which accumulates gaps over an
36
+ * archive's crawl history (redirect consolidation, deleted pages, …).
37
+ * A density estimate over-counts whenever the id range is sparser than
38
+ * `[1, total]`, which produced false-positive failures on real
39
+ * multi-thousand-page archives.
40
+ * @param trx - Knex instance or transaction connected to the populated archive.
41
+ * @throws {MigrationVerificationError} when at least one sampled URL does not round-trip.
42
+ */
43
+ export declare function checkUrlRoundTrip(trx: Knex): Promise<void>;
@@ -0,0 +1,112 @@
1
+ import { MigrationVerificationError } from './types.js';
2
+ /**
3
+ * Sample size for the URL round-trip smoke test — a 1,000-row sample per
4
+ * issue #194.
5
+ * Not statistical coverage — URL normalisation bugs tend to be systematic
6
+ * (all rows with a particular scheme or host), so a modest evenly-spread
7
+ * sample catches them reliably.
8
+ */
9
+ const SAMPLE_SIZE = 1000;
10
+ /**
11
+ * Verifies 0.13 invariant #8: `content_items.url_id → url_refs.url`
12
+ * round-trips back to the original `pages.url` on a deterministic
13
+ * ≤ {@link SAMPLE_SIZE} sample.
14
+ *
15
+ * A mismatch means one of two things:
16
+ *
17
+ * - `populate-url-refs.ts` collapsed two distinct URLs into the same
18
+ * `url_refs.id` (BINARY collation bug, whitespace normalisation, …).
19
+ * - `populate-content-items.ts` associated a page id with the wrong
20
+ * `url_ref.id` (row-order dependency in the batch resolver).
21
+ *
22
+ * Additionally an FK gap (`content_items.url_id` pointing at a
23
+ * non-existent `url_refs.id`) or a missing `pages` row surfaces as a
24
+ * `roundTripUrl` / `sourceUrl` of `null`; the check treats either as a
25
+ * round-trip failure. LEFT JOINs are used deliberately so orphan rows
26
+ * are observable — an INNER JOIN would silently drop them and let the
27
+ * bug the invariant is meant to catch slip through.
28
+ *
29
+ * Sampling is **deterministic**: stride = ⌈count(content_items) / N⌉
30
+ * gives approximately {@link SAMPLE_SIZE} rows spread across the id
31
+ * range for large archives, and the full table for archives with
32
+ * ≤ {@link SAMPLE_SIZE} rows. Deterministic sampling means the same
33
+ * archive always produces the same verdict; a random sample would let a
34
+ * failing archive pass on retry if the offending rows happen to fall
35
+ * outside the second draw.
36
+ *
37
+ * The check throws when the sampled row count is smaller than expected
38
+ * (LEFT JOINs may return `null` sides but never fewer rows than the
39
+ * driving `content_items` set — a shortfall means SQLite silently
40
+ * dropped rows we intended to inspect). "Expected" is a separate
41
+ * `COUNT(*)` against the same `(id % stride) = 0` predicate, not a
42
+ * `floor(total / stride)` estimate: `content_items.id` is copied
43
+ * verbatim from the legacy `pages.id`, which accumulates gaps over an
44
+ * archive's crawl history (redirect consolidation, deleted pages, …).
45
+ * A density estimate over-counts whenever the id range is sparser than
46
+ * `[1, total]`, which produced false-positive failures on real
47
+ * multi-thousand-page archives.
48
+ * @param trx - Knex instance or transaction connected to the populated archive.
49
+ * @throws {MigrationVerificationError} when at least one sampled URL does not round-trip.
50
+ */
51
+ export async function checkUrlRoundTrip(trx) {
52
+ const totalRows = await trx('content_items').count({ n: '*' });
53
+ const total = Number(totalRows[0].n);
54
+ if (total === 0) {
55
+ return;
56
+ }
57
+ // stride = ⌈total / SAMPLE_SIZE⌉ so `id % stride = 0` picks approximately
58
+ // SAMPLE_SIZE rows evenly across the id range for large archives, and the
59
+ // full table for archives ≤ SAMPLE_SIZE.
60
+ const stride = Math.max(1, Math.ceil(total / SAMPLE_SIZE));
61
+ const matchingRows = await trx('content_items')
62
+ .whereRaw('(id % ?) = 0', [stride])
63
+ .count({ n: '*' });
64
+ const expectedSize = Math.min(SAMPLE_SIZE, Number(matchingRows[0].n));
65
+ const sample = await trx('content_items as ci')
66
+ .leftJoin('pages as p', 'p.id', 'ci.id')
67
+ .leftJoin('url_refs as ur', 'ur.id', 'ci.url_id')
68
+ .select('ci.id as id', 'p.url as sourceUrl', 'ur.url as roundTripUrl')
69
+ .whereRaw('(ci.id % ?) = 0', [stride])
70
+ .orderBy('ci.id')
71
+ .limit(SAMPLE_SIZE);
72
+ if (sample.length < expectedSize) {
73
+ // LEFT JOINs preserve driving-table row counts (`p.id` / `url_refs.id`
74
+ // are PKs → 1:1 or 0:1 match). A shortfall means SQLite silently
75
+ // dropped rows the WHERE clause matched — a real integrity signal.
76
+ throw new MigrationVerificationError({
77
+ check: '#8 URL round-trip',
78
+ context: {
79
+ content_items_total: total,
80
+ stride,
81
+ expected_sample_size: expectedSize,
82
+ actual_sample_size: sample.length,
83
+ reason: 'sample query returned fewer rows than expected',
84
+ },
85
+ });
86
+ }
87
+ for (const row of sample) {
88
+ if (row.sourceUrl === null || row.roundTripUrl === null) {
89
+ throw new MigrationVerificationError({
90
+ check: '#8 URL round-trip',
91
+ context: {
92
+ page_id: row.id,
93
+ source_url: row.sourceUrl,
94
+ round_trip_url: row.roundTripUrl,
95
+ sample_size: sample.length,
96
+ reason: 'orphan row — pages or url_refs join returned null (FK gap)',
97
+ },
98
+ });
99
+ }
100
+ if (row.sourceUrl !== row.roundTripUrl) {
101
+ throw new MigrationVerificationError({
102
+ check: '#8 URL round-trip',
103
+ context: {
104
+ page_id: row.id,
105
+ source_url: row.sourceUrl,
106
+ round_trip_url: row.roundTripUrl,
107
+ sample_size: sample.length,
108
+ },
109
+ });
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Shared type definitions for 0.13 migration verification.
3
+ *
4
+ * These are the acceptance-invariant checks that `scripts/migrate-to-0.13.mjs`
5
+ * runs at the end of the migration to confirm that the 0.13 entity /
6
+ * edge populates produced a consistent archive. Each check is one file
7
+ * under `verify-migration/` and throws {@link MigrationVerificationError} on mismatch;
8
+ * the orchestrator {@link ./verify-migration.ts} chains them in the
9
+ * order defined by issue #194.
10
+ * @module
11
+ */
12
+ /**
13
+ * Row-count snapshot collected by every successful verify run and returned
14
+ * by {@link import('./verify-migration.js').verifyMigration}.
15
+ * The migration script logs these on the successful path so operators can
16
+ * eyeball migration outcomes from stdout without re-opening the archive.
17
+ */
18
+ export interface MigrationVerificationSummary {
19
+ /** `count(*) FROM content_items` — matches `count(pages)`. */
20
+ readonly contentItems: number;
21
+ /** `count(*) FROM page_meta` — matches `count(pages WHERE scraped=1)`. */
22
+ readonly pageMeta: number;
23
+ /** `count(*) FROM anchor_edges` — always ≤ `count(anchors)`. */
24
+ readonly anchorEdges: number;
25
+ /** `SUM(count) FROM anchor_edges` — matches `count(anchors)`. */
26
+ readonly anchorEdgesSum: number;
27
+ /** `count(*) FROM image_items` — matches `count(images)`. */
28
+ readonly imageItems: number;
29
+ /** `count(*) FROM resource_items` — matches `count(resources)`. */
30
+ readonly resourceItems: number;
31
+ }
32
+ /**
33
+ * Structured details of a single migration invariant that failed. Attached to
34
+ * {@link MigrationVerificationError} so operators can see which check tripped
35
+ * without parsing the error message.
36
+ */
37
+ export interface MigrationVerificationErrorDetails {
38
+ /**
39
+ * Human-readable check identifier matching issue #194's numbered list
40
+ * (e.g. `'#3'` for the anchor-edge dedup range check). Included in the
41
+ * error message so migration logs point at the exact clause.
42
+ */
43
+ readonly check: string;
44
+ /**
45
+ * Optional structured payload — typically `expected` / `actual` counts or
46
+ * the offending page id from the URL round-trip sample. Rendered into the
47
+ * error message so operators can diagnose without re-running the check.
48
+ */
49
+ readonly context?: Readonly<Record<string, string | number | null>>;
50
+ }
51
+ /**
52
+ * Error thrown by any 0.13 check when an invariant does not hold. The
53
+ * migration script catches this at the top level and aborts with a non-zero
54
+ * exit code; the enclosing transaction rolls back so the archive returns to
55
+ * its pre-populate state (ref tables from ref populate stay populated but are
56
+ * additive).
57
+ *
58
+ * Prefer a single error class with a structured {@link details} payload over
59
+ * one subclass per check: eight subclasses would create eight nearly
60
+ * identical constructors, and callers (script + integration test) only need
61
+ * to distinguish "verification failed" from other error kinds.
62
+ */
63
+ export declare class MigrationVerificationError extends Error {
64
+ readonly details: MigrationVerificationErrorDetails;
65
+ /**
66
+ * @param details - Structured description of which check failed and why.
67
+ * Rendered into the error message on construction.
68
+ */
69
+ constructor(details: MigrationVerificationErrorDetails);
70
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shared type definitions for 0.13 migration verification.
3
+ *
4
+ * These are the acceptance-invariant checks that `scripts/migrate-to-0.13.mjs`
5
+ * runs at the end of the migration to confirm that the 0.13 entity /
6
+ * edge populates produced a consistent archive. Each check is one file
7
+ * under `verify-migration/` and throws {@link MigrationVerificationError} on mismatch;
8
+ * the orchestrator {@link ./verify-migration.ts} chains them in the
9
+ * order defined by issue #194.
10
+ * @module
11
+ */
12
+ /**
13
+ * Error thrown by any 0.13 check when an invariant does not hold. The
14
+ * migration script catches this at the top level and aborts with a non-zero
15
+ * exit code; the enclosing transaction rolls back so the archive returns to
16
+ * its pre-populate state (ref tables from ref populate stay populated but are
17
+ * additive).
18
+ *
19
+ * Prefer a single error class with a structured {@link details} payload over
20
+ * one subclass per check: eight subclasses would create eight nearly
21
+ * identical constructors, and callers (script + integration test) only need
22
+ * to distinguish "verification failed" from other error kinds.
23
+ */
24
+ export class MigrationVerificationError extends Error {
25
+ details;
26
+ /**
27
+ * @param details - Structured description of which check failed and why.
28
+ * Rendered into the error message on construction.
29
+ */
30
+ constructor(details) {
31
+ const contextText = details.context === undefined
32
+ ? ''
33
+ : ' — ' +
34
+ Object.entries(details.context)
35
+ .map(([key, value]) =>
36
+ // Render null as the parenthesised literal `(null)` so
37
+ // operators reading the log can distinguish "the sample
38
+ // query returned no rows" from a caller that passed the
39
+ // four-character string `"null"`. Strings get quoted for
40
+ // the same reason.
41
+ `${key}=${formatContextValue(value)}`)
42
+ .join(', ');
43
+ super(`migration verification failed ${details.check}${contextText}`);
44
+ this.details = details;
45
+ this.name = 'MigrationVerificationError';
46
+ }
47
+ }
48
+ /**
49
+ * Renders one context value for {@link MigrationVerificationError}'s message.
50
+ * `null` becomes the literal `(null)`; strings are wrapped in double quotes;
51
+ * numbers pass through as their decimal representation. The intent is to
52
+ * make missing-vs-present distinguishable in operator logs.
53
+ * @param value - Value from `MigrationVerificationErrorDetails.context`.
54
+ */
55
+ function formatContextValue(value) {
56
+ if (value === null) {
57
+ return '(null)';
58
+ }
59
+ if (typeof value === 'string') {
60
+ return JSON.stringify(value);
61
+ }
62
+ return String(value);
63
+ }
@@ -0,0 +1,41 @@
1
+ import type { MigrationVerificationSummary } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Runs every 0.13 migration acceptance invariant against a populated archive and
5
+ * returns the verified row-count summary on success. The migration script
6
+ * (`scripts/migrate-to-0.13.mjs`) calls this **inside** the same
7
+ * `knex.transaction()` block that ran `populateEntityTables` so a thrown
8
+ * {@link MigrationVerificationError} rolls back the entire entity populate — ref
9
+ * tables from ref populate stay committed because they live in a separate, additive
10
+ * transaction and never lose facts on re-run.
11
+ *
12
+ * The returned {@link MigrationVerificationSummary} is echoed to stdout by the
13
+ * migration script so operators can eyeball migration outcomes (`did we lose
14
+ * rows silently within the invariant window?`) from batch pipeline logs
15
+ * without re-opening the archive. Callers must run this inside a `db.transaction()`
16
+ * for the counts and the JOIN-based checks (#7 / #8) to see a consistent
17
+ * snapshot of the archive.
18
+ *
19
+ * Checks run sequentially in issue #194's numbered order so error messages
20
+ * pinpoint the earliest broken invariant. Sequential (rather than parallel)
21
+ * execution keeps the failure diagnostics focused: fanning eight COUNTs out
22
+ * at once would return the first-completed rejection rather than the
23
+ * lowest-numbered one, which surprises operators reading migration logs.
24
+ * The individual COUNT queries are fast enough at any real archive scale
25
+ * for sequential wall-clock cost to be irrelevant.
26
+ *
27
+ * Non-{@link MigrationVerificationError} exceptions from a check (e.g. a
28
+ * transient libsql error or a schema-drift SqliteError) are wrapped in a
29
+ * {@link MigrationVerificationError} so the migration script's `catch` sees a
30
+ * uniform failure surface — otherwise operators grepping stderr for
31
+ * `migration verification failed` would miss driver-side errors.
32
+ * @param trx - Knex instance or transaction connected to the archive
33
+ * **after** every 0.13 migration populate has run. Must be a transaction
34
+ * in production so ref counts, JOIN samples, and error rollback see the
35
+ * same snapshot; non-transactional callers (unit tests) work but do not
36
+ * get snapshot isolation.
37
+ * @returns Row-count summary of the verified archive.
38
+ * @throws {MigrationVerificationError} on the first invariant that does not
39
+ * hold; subsequent checks are skipped.
40
+ */
41
+ export declare function verifyMigration(trx: Knex): Promise<MigrationVerificationSummary>;
@@ -0,0 +1,120 @@
1
+ import { checkAnchorEdgesCount } from './check-anchor-edges-count.js';
2
+ import { checkAnchorEdgesSum } from './check-anchor-edges-sum.js';
3
+ import { checkContentItemsCount } from './check-content-items-count.js';
4
+ import { checkContentTypePreservation } from './check-content-type-preservation.js';
5
+ import { checkImageItemsCount } from './check-image-items-count.js';
6
+ import { checkPageMetaCount } from './check-page-meta-count.js';
7
+ import { checkReaderParity } from './check-reader-parity.js';
8
+ import { checkResourceItemsCount } from './check-resource-items-count.js';
9
+ import { checkUrlRoundTrip } from './check-url-round-trip.js';
10
+ import { MigrationVerificationError } from './types.js';
11
+ /**
12
+ * Runs every 0.13 migration acceptance invariant against a populated archive and
13
+ * returns the verified row-count summary on success. The migration script
14
+ * (`scripts/migrate-to-0.13.mjs`) calls this **inside** the same
15
+ * `knex.transaction()` block that ran `populateEntityTables` so a thrown
16
+ * {@link MigrationVerificationError} rolls back the entire entity populate — ref
17
+ * tables from ref populate stay committed because they live in a separate, additive
18
+ * transaction and never lose facts on re-run.
19
+ *
20
+ * The returned {@link MigrationVerificationSummary} is echoed to stdout by the
21
+ * migration script so operators can eyeball migration outcomes (`did we lose
22
+ * rows silently within the invariant window?`) from batch pipeline logs
23
+ * without re-opening the archive. Callers must run this inside a `db.transaction()`
24
+ * for the counts and the JOIN-based checks (#7 / #8) to see a consistent
25
+ * snapshot of the archive.
26
+ *
27
+ * Checks run sequentially in issue #194's numbered order so error messages
28
+ * pinpoint the earliest broken invariant. Sequential (rather than parallel)
29
+ * execution keeps the failure diagnostics focused: fanning eight COUNTs out
30
+ * at once would return the first-completed rejection rather than the
31
+ * lowest-numbered one, which surprises operators reading migration logs.
32
+ * The individual COUNT queries are fast enough at any real archive scale
33
+ * for sequential wall-clock cost to be irrelevant.
34
+ *
35
+ * Non-{@link MigrationVerificationError} exceptions from a check (e.g. a
36
+ * transient libsql error or a schema-drift SqliteError) are wrapped in a
37
+ * {@link MigrationVerificationError} so the migration script's `catch` sees a
38
+ * uniform failure surface — otherwise operators grepping stderr for
39
+ * `migration verification failed` would miss driver-side errors.
40
+ * @param trx - Knex instance or transaction connected to the archive
41
+ * **after** every 0.13 migration populate has run. Must be a transaction
42
+ * in production so ref counts, JOIN samples, and error rollback see the
43
+ * same snapshot; non-transactional callers (unit tests) work but do not
44
+ * get snapshot isolation.
45
+ * @returns Row-count summary of the verified archive.
46
+ * @throws {MigrationVerificationError} on the first invariant that does not
47
+ * hold; subsequent checks are skipped.
48
+ */
49
+ export async function verifyMigration(trx) {
50
+ try {
51
+ await checkContentItemsCount(trx);
52
+ await checkPageMetaCount(trx);
53
+ await checkAnchorEdgesCount(trx);
54
+ await checkAnchorEdgesSum(trx);
55
+ await checkImageItemsCount(trx);
56
+ await checkResourceItemsCount(trx);
57
+ await checkContentTypePreservation(trx);
58
+ await checkUrlRoundTrip(trx);
59
+ // Reader-level parity between the legacy tables and the
60
+ // new entity tables (see `checkReaderParity` for the eight totals
61
+ // this catches that the row-count checks above miss).
62
+ await checkReaderParity(trx);
63
+ }
64
+ catch (error) {
65
+ if (error instanceof MigrationVerificationError) {
66
+ throw error;
67
+ }
68
+ throw new MigrationVerificationError({
69
+ check: 'runtime',
70
+ context: {
71
+ underlying_error: error instanceof Error ? error.message : String(error),
72
+ },
73
+ });
74
+ }
75
+ return collectSummary(trx);
76
+ }
77
+ /**
78
+ * Runs the six row counts that populate {@link MigrationVerificationSummary}.
79
+ * Separate from the invariant checks so those stay side-effect-free asserts,
80
+ * and this runs once at the end when all invariants have already held.
81
+ * @param trx - Same Knex handle passed to {@link verifyMigration}.
82
+ */
83
+ async function collectSummary(trx) {
84
+ const [contentItems, pageMeta, anchorEdges, anchorEdgesSum, imageItems, resourceItems] = await Promise.all([
85
+ countOf(trx, 'content_items'),
86
+ countOf(trx, 'page_meta'),
87
+ countOf(trx, 'anchor_edges'),
88
+ sumOf(trx, 'anchor_edges', 'count'),
89
+ countOf(trx, 'image_items'),
90
+ countOf(trx, 'resource_items'),
91
+ ]);
92
+ return {
93
+ contentItems,
94
+ pageMeta,
95
+ anchorEdges,
96
+ anchorEdgesSum,
97
+ imageItems,
98
+ resourceItems,
99
+ };
100
+ }
101
+ /**
102
+ * `SELECT count(*) FROM <table>` returning a plain number.
103
+ * @param trx - Knex handle.
104
+ * @param table - Table to count.
105
+ */
106
+ async function countOf(trx, table) {
107
+ const rows = await trx(table).count({ n: '*' });
108
+ return Number(rows[0].n);
109
+ }
110
+ /**
111
+ * `SELECT SUM(<column>) FROM <table>` returning a plain number (`null` sum
112
+ * on an empty table collapses to zero).
113
+ * @param trx - Knex handle.
114
+ * @param table - Table to sum over.
115
+ * @param column - Column to sum.
116
+ */
117
+ async function sumOf(trx, table, column) {
118
+ const rows = await trx(table).sum({ n: column });
119
+ return Number(rows[0].n ?? 0);
120
+ }
@@ -14,7 +14,7 @@ import type { PageData } from '../utils/types/types.js';
14
14
  *
15
15
  * The originating URL passed in `pageUrlWithoutHashAndAuth` must be the
16
16
  * URL the redirect chain STARTS from (the page being scraped), not the
17
- * destination — see {@link Database.recordRedirect} JSDoc for why
17
+ * destination — see `Database.recordRedirect` JSDoc for why
18
18
  * lineage propagates from origin, not from the destination.
19
19
  * @param pageData - HEAD-resolved page data carrying the redirect chain.
20
20
  * @param inventoryMode - Inventory-mode config from `CrawlerOptions.inventoryMode`, or `null` outside `--inventory`.
@@ -13,7 +13,7 @@ import { derivePageSource } from './derive-page-source.js';
13
13
  *
14
14
  * The originating URL passed in `pageUrlWithoutHashAndAuth` must be the
15
15
  * URL the redirect chain STARTS from (the page being scraped), not the
16
- * destination — see {@link Database.recordRedirect} JSDoc for why
16
+ * destination — see `Database.recordRedirect` JSDoc for why
17
17
  * lineage propagates from origin, not from the destination.
18
18
  * @param pageData - HEAD-resolved page data carrying the redirect chain.
19
19
  * @param inventoryMode - Inventory-mode config from `CrawlerOptions.inventoryMode`, or `null` outside `--inventory`.
@@ -0,0 +1,33 @@
1
+ import type { DomPathCandidate } from '../archive/populate-entity-tables/types.js';
2
+ import type { Page as PuppeteerPage } from 'puppeteer';
3
+ /**
4
+ * Captures every `<img>` in the rendered document — its `outerHTML` and
5
+ * its `dom_path` string — in document order, for `image_items.dom_path_text_id`
6
+ * resolution at write time.
7
+ *
8
+ * `@d-zero/beholder`'s image metadata carries the element's `outerHTML`
9
+ * (`sourceCode`) but no positional information, so the crawler runs this
10
+ * one extra `page.evaluate` after `scrapeStart` returns, while the page
11
+ * is still alive. The in-browser walk is
12
+ * {@link ./collect-image-dom-paths.ts} — a self-contained function whose
13
+ * source puppeteer serialises into the page, and whose spec pins its
14
+ * output against the Node-side
15
+ * {@link ../archive/populate-entity-tables/derive-dom-path.ts} so a
16
+ * live-crawled archive and one whose dom paths were reconstructed from
17
+ * HTML snapshots by the migration script produce identical strings for
18
+ * identical DOM shapes.
19
+ *
20
+ * Returns `undefined` on any evaluation failure (page context died,
21
+ * navigation raced the call) — dom paths are best-effort enrichment and
22
+ * a capture failure must not fail the scrape. Callers fall back to the
23
+ * `unknown/<id>` synthetic marker per image via
24
+ * {@link ../archive/populate-entity-tables/match-images-to-dom-paths.ts}.
25
+ * @param page - The live puppeteer page, after the scrape completed and
26
+ * before the browser closes.
27
+ * @returns Candidates in document order, or `undefined` when the page
28
+ * could not be evaluated.
29
+ * @example
30
+ * const result = await scraper.scrapeStart(page, url, options);
31
+ * const imageDomPaths = await captureImageDomPaths(page);
32
+ */
33
+ export declare function captureImageDomPaths(page: PuppeteerPage): Promise<DomPathCandidate[] | undefined>;
@@ -0,0 +1,39 @@
1
+ import { collectImageDomPaths } from './collect-image-dom-paths.js';
2
+ /**
3
+ * Captures every `<img>` in the rendered document — its `outerHTML` and
4
+ * its `dom_path` string — in document order, for `image_items.dom_path_text_id`
5
+ * resolution at write time.
6
+ *
7
+ * `@d-zero/beholder`'s image metadata carries the element's `outerHTML`
8
+ * (`sourceCode`) but no positional information, so the crawler runs this
9
+ * one extra `page.evaluate` after `scrapeStart` returns, while the page
10
+ * is still alive. The in-browser walk is
11
+ * {@link ./collect-image-dom-paths.ts} — a self-contained function whose
12
+ * source puppeteer serialises into the page, and whose spec pins its
13
+ * output against the Node-side
14
+ * {@link ../archive/populate-entity-tables/derive-dom-path.ts} so a
15
+ * live-crawled archive and one whose dom paths were reconstructed from
16
+ * HTML snapshots by the migration script produce identical strings for
17
+ * identical DOM shapes.
18
+ *
19
+ * Returns `undefined` on any evaluation failure (page context died,
20
+ * navigation raced the call) — dom paths are best-effort enrichment and
21
+ * a capture failure must not fail the scrape. Callers fall back to the
22
+ * `unknown/<id>` synthetic marker per image via
23
+ * {@link ../archive/populate-entity-tables/match-images-to-dom-paths.ts}.
24
+ * @param page - The live puppeteer page, after the scrape completed and
25
+ * before the browser closes.
26
+ * @returns Candidates in document order, or `undefined` when the page
27
+ * could not be evaluated.
28
+ * @example
29
+ * const result = await scraper.scrapeStart(page, url, options);
30
+ * const imageDomPaths = await captureImageDomPaths(page);
31
+ */
32
+ export async function captureImageDomPaths(page) {
33
+ try {
34
+ return await page.evaluate(collectImageDomPaths);
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Clears the DNS-burned host cache and resets the short-circuit counter.
3
- * Called between crawl sessions, alongside {@link clearDestinationCache}, to
3
+ * Called between crawl sessions, alongside `clearDestinationCache`, to
4
4
  * avoid leaking state from a previous session into a fresh one.
5
5
  */
6
6
  export declare function clearDnsBurnedHostCache(): void;
@@ -2,7 +2,7 @@ import { dnsBurnedHostCache } from './dns-burned-host-cache.js';
2
2
  import { dnsBurnedHostShortCircuitCounter } from './dns-burned-host-short-circuit-counter.js';
3
3
  /**
4
4
  * Clears the DNS-burned host cache and resets the short-circuit counter.
5
- * Called between crawl sessions, alongside {@link clearDestinationCache}, to
5
+ * Called between crawl sessions, alongside `clearDestinationCache`, to
6
6
  * avoid leaking state from a previous session into a fresh one.
7
7
  */
8
8
  export function clearDnsBurnedHostCache() {
@@ -0,0 +1,23 @@
1
+ import type { DomPathCandidate } from '../archive/populate-entity-tables/types.js';
2
+ /**
3
+ * Collects every `<img>` in a document — its `outerHTML` and its
4
+ * `dom_path` string — in document order.
5
+ *
6
+ * **Self-contained by contract.** This function is passed verbatim to
7
+ * puppeteer's `page.evaluate`, which serialises the function source and
8
+ * executes it inside the browser: it must not reference imports,
9
+ * module-scope bindings, or any closure state. That constraint is also
10
+ * what makes single-sourcing possible — the exact same function body
11
+ * runs in the browser during a live crawl AND in Node (against a jsdom
12
+ * document) in its spec, so the dom-path derivation cannot drift between
13
+ * the two runtimes. The spec additionally pins its output against
14
+ * {@link ../archive/populate-entity-tables/derive-dom-path.ts} (the
15
+ * Node-side derivation the migration script uses) element-for-element.
16
+ * @param doc - The document to walk. Defaults to the global `document`,
17
+ * which is how the in-browser `page.evaluate(collectImageDomPaths)`
18
+ * call resolves it; Node callers (specs) pass a jsdom document.
19
+ * @returns Candidates in document order.
20
+ * @example
21
+ * const candidates = await page.evaluate(collectImageDomPaths);
22
+ */
23
+ export declare function collectImageDomPaths(doc?: Document): DomPathCandidate[];