@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,87 @@
1
+ /**
2
+ * Collapses consecutive `anchors`-shaped rows into `anchor_edges` rows by
3
+ * `(pageId, hrefId)` (issue #193).
4
+ *
5
+ * **Input contract**: `rows` MUST be sorted by `(pageId, hrefId, id)`
6
+ * ascending. The caller (`populate-anchor-edges.ts`) achieves this via a
7
+ * single keyset scan `ORDER BY pageId, hrefId, id` over `anchors`.
8
+ *
9
+ * The `id`-then-key sort matters because the dedup rule is
10
+ * "first instance wins": the smallest `anchors.id` for a given
11
+ * `(pageId, hrefId)` pair contributes its `hash` and `textContent` to
12
+ * the edge; every subsequent duplicate is counted but its body is
13
+ * discarded. A naive `min(hash) GROUP BY pageId, hrefId` would pick the
14
+ * lexicographically smallest hex string (uniformly distributed) rather
15
+ * than the earliest occurrence, so the pass has to happen in JS.
16
+ *
17
+ * The generator yields one {@link AnchorEdgeRowInProgress} per distinct
18
+ * pair as soon as the next pair boundary is observed. Emitting eagerly
19
+ * keeps peak memory bounded to O(1) — the collapser holds at most one
20
+ * open pair state at a time.
21
+ *
22
+ * `first_text_id` is intentionally left `null` here; the caller resolves
23
+ * it in a second pass after all edges are known (see
24
+ * `populate-anchor-edges.ts`). The `first_hash` field is set to the
25
+ * first row's `hash` verbatim; a first row with `hash === null` results
26
+ * in `first_hash = null` on the edge (rare but possible on legacy
27
+ * archives that failed to compute a hash).
28
+ * @param rows - Anchor input rows sorted by `(pageId, hrefId, id)`.
29
+ * @yields {AnchorEdgeRowInProgress} One entry per distinct
30
+ * `(pageId, hrefId)` pair in the input.
31
+ * @example
32
+ * const edges = [...collapseAnchorRows([
33
+ * { id: 1, pageId: 10, hrefId: 20, hash: 'a', textContent: 'first' },
34
+ * { id: 2, pageId: 10, hrefId: 20, hash: 'b', textContent: 'second' },
35
+ * { id: 3, pageId: 10, hrefId: 30, hash: 'c', textContent: 'x' },
36
+ * ])];
37
+ * // edges[0] = { page_id: 10, href_page_id: 20, count: 2, first_hash: 'a', ... }
38
+ * // edges[1] = { page_id: 10, href_page_id: 30, count: 1, first_hash: 'c', ... }
39
+ */
40
+ export function* collapseAnchorRows(rows) {
41
+ let openPageId = null;
42
+ let openHrefId = null;
43
+ let openCount = 0;
44
+ let openFirstHash = null;
45
+ let openFirstTextContent = null;
46
+ let openLastId = null;
47
+ for (const row of rows) {
48
+ if (openPageId === row.pageId && openHrefId === row.hrefId) {
49
+ // Same pair — assert monotonically-increasing `id` so callers
50
+ // that regress the `ORDER BY id` fail loudly instead of
51
+ // silently producing the wrong "first instance". The count-
52
+ // based acceptance check does not catch this (it only sums
53
+ // counts, not first_hash values), so the collapser defends
54
+ // its own precondition.
55
+ if (openLastId !== null && row.id <= openLastId) {
56
+ throw new Error(`collapseAnchorRows: input not sorted by id within (pageId=${row.pageId}, hrefId=${row.hrefId}) — saw id=${row.id} after id=${openLastId}. Caller must ORDER BY pageId, hrefId, id ASC.`);
57
+ }
58
+ openLastId = row.id;
59
+ openCount += 1;
60
+ continue;
61
+ }
62
+ if (openPageId !== null && openHrefId !== null) {
63
+ yield {
64
+ page_id: openPageId,
65
+ href_page_id: openHrefId,
66
+ count: openCount,
67
+ first_hash: openFirstHash,
68
+ first_textContent: openFirstTextContent,
69
+ };
70
+ }
71
+ openPageId = row.pageId;
72
+ openHrefId = row.hrefId;
73
+ openCount = 1;
74
+ openFirstHash = row.hash;
75
+ openFirstTextContent = row.textContent;
76
+ openLastId = row.id;
77
+ }
78
+ if (openPageId !== null && openHrefId !== null) {
79
+ yield {
80
+ page_id: openPageId,
81
+ href_page_id: openHrefId,
82
+ count: openCount,
83
+ first_hash: openFirstHash,
84
+ first_textContent: openFirstTextContent,
85
+ };
86
+ }
87
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Computes the `dom_path` string for one DOM element as required by
3
+ * `image_items.dom_path_text_id` (issue #193).
4
+ *
5
+ * Format: slash-joined ancestor tags starting at `<html>`, each tag
6
+ * followed by a 1-based sibling ordinal counting **same-tag** siblings
7
+ * only (e.g.
8
+ * `html/body[1]/main[1]/section[2]/picture[1]/img[1]`). The `<html>`
9
+ * root has no bracketed ordinal because there is only one document
10
+ * root per page.
11
+ *
12
+ * The walk uses `parentElement` + `previousElementSibling` — no
13
+ * document mutation, no reliance on `id` / `class` — so a re-render
14
+ * that only reshuffles class names or ids still produces the same path.
15
+ * The chain of tag names alone is stable for a given DOM shape.
16
+ *
17
+ * The function operates on the generic `Element` interface from DOM lib
18
+ * (available in the crawler's TS lib config) so it does not force
19
+ * jsdom into the crawler runtime — the migration script and unit tests
20
+ * inject jsdom-backed elements, while a future crawler-side wrapper
21
+ * can pass puppeteer's DOM handles through the same code.
22
+ *
23
+ * Detached elements (`element.parentElement === null` before reaching
24
+ * `<html>`) return the tag chain from wherever the walk terminates —
25
+ * a legally detached image (rare, but
26
+ * technically possible during scripted mutations) yields e.g.
27
+ * `img[1]` alone instead of throwing.
28
+ * @param element - The DOM element to derive the path for. Typically an
29
+ * `<img>` in the archived HTML, but the function is not tag-specific.
30
+ * @returns The dom_path string.
31
+ * @example
32
+ * // In an HTML snapshot `<html><body><main><img></main></body></html>`
33
+ * deriveDomPath(imgElement); // 'html/body[1]/main[1]/img[1]'
34
+ */
35
+ export declare function deriveDomPath(element: Element): string;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Computes the `dom_path` string for one DOM element as required by
3
+ * `image_items.dom_path_text_id` (issue #193).
4
+ *
5
+ * Format: slash-joined ancestor tags starting at `<html>`, each tag
6
+ * followed by a 1-based sibling ordinal counting **same-tag** siblings
7
+ * only (e.g.
8
+ * `html/body[1]/main[1]/section[2]/picture[1]/img[1]`). The `<html>`
9
+ * root has no bracketed ordinal because there is only one document
10
+ * root per page.
11
+ *
12
+ * The walk uses `parentElement` + `previousElementSibling` — no
13
+ * document mutation, no reliance on `id` / `class` — so a re-render
14
+ * that only reshuffles class names or ids still produces the same path.
15
+ * The chain of tag names alone is stable for a given DOM shape.
16
+ *
17
+ * The function operates on the generic `Element` interface from DOM lib
18
+ * (available in the crawler's TS lib config) so it does not force
19
+ * jsdom into the crawler runtime — the migration script and unit tests
20
+ * inject jsdom-backed elements, while a future crawler-side wrapper
21
+ * can pass puppeteer's DOM handles through the same code.
22
+ *
23
+ * Detached elements (`element.parentElement === null` before reaching
24
+ * `<html>`) return the tag chain from wherever the walk terminates —
25
+ * a legally detached image (rare, but
26
+ * technically possible during scripted mutations) yields e.g.
27
+ * `img[1]` alone instead of throwing.
28
+ * @param element - The DOM element to derive the path for. Typically an
29
+ * `<img>` in the archived HTML, but the function is not tag-specific.
30
+ * @returns The dom_path string.
31
+ * @example
32
+ * // In an HTML snapshot `<html><body><main><img></main></body></html>`
33
+ * deriveDomPath(imgElement); // 'html/body[1]/main[1]/img[1]'
34
+ */
35
+ export function deriveDomPath(element) {
36
+ const segments = [];
37
+ let current = element;
38
+ while (current !== null) {
39
+ const tag = current.tagName.toLowerCase();
40
+ if (tag === 'html') {
41
+ segments.unshift('html');
42
+ break;
43
+ }
44
+ segments.unshift(`${tag}[${computeSiblingOrdinal(current, tag)}]`);
45
+ current = current.parentElement;
46
+ }
47
+ return segments.join('/');
48
+ }
49
+ /**
50
+ * Counts how many earlier siblings share the same tag name as `element`,
51
+ * returning `count + 1` as the 1-based ordinal.
52
+ *
53
+ * Iterating `previousElementSibling` is O(k) where k is the sibling
54
+ * position; for typical DOM depth and sibling counts this is negligible
55
+ * next to the jsdom parse cost of the enclosing page.
56
+ * @param element - The element whose ordinal is being computed.
57
+ * @param tag - The lower-cased tag name to match on. Passed in by the
58
+ * caller so `element.tagName.toLowerCase()` is not recomputed inside
59
+ * the loop for every sibling comparison.
60
+ * @returns 1-based ordinal among same-tag siblings.
61
+ */
62
+ function computeSiblingOrdinal(element, tag) {
63
+ let count = 1;
64
+ let sibling = element.previousElementSibling;
65
+ while (sibling !== null) {
66
+ if (sibling.tagName.toLowerCase() === tag) {
67
+ count += 1;
68
+ }
69
+ sibling = sibling.previousElementSibling;
70
+ }
71
+ return count;
72
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Predicate matching the ref-tables populate's routing rule
3
+ * (`populate-ref-tables/populate-url-refs.ts` /
4
+ * `populate-ref-tables/populate-blob-refs.ts`): a `data:` URI whose
5
+ * length exceeds {@link DATA_URI_URL_REFS_LIMIT} lands in `blob_refs`;
6
+ * anything else (regular URL or short data URI) lands in `url_refs`.
7
+ * Shared by every entity populate that partitions a URL-shaped column
8
+ * into the two dictionaries (`populate-image-items.ts`,
9
+ * `populate-resource-items.ts`).
10
+ * @param value - Raw URL-shaped column value.
11
+ * @returns `true` when the value belongs in `blob_refs`.
12
+ * @example
13
+ * isBlobRefValue('data:image/png;base64,' + 'x'.repeat(600)); // true
14
+ * isBlobRefValue('https://example.com/a.png'); // false
15
+ */
16
+ export declare function isBlobRefValue(value: string): boolean;
@@ -0,0 +1,19 @@
1
+ import { DATA_URI_URL_REFS_LIMIT } from '../populate-ref-tables/data-uri-url-refs-limit.js';
2
+ /**
3
+ * Predicate matching the ref-tables populate's routing rule
4
+ * (`populate-ref-tables/populate-url-refs.ts` /
5
+ * `populate-ref-tables/populate-blob-refs.ts`): a `data:` URI whose
6
+ * length exceeds {@link DATA_URI_URL_REFS_LIMIT} lands in `blob_refs`;
7
+ * anything else (regular URL or short data URI) lands in `url_refs`.
8
+ * Shared by every entity populate that partitions a URL-shaped column
9
+ * into the two dictionaries (`populate-image-items.ts`,
10
+ * `populate-resource-items.ts`).
11
+ * @param value - Raw URL-shaped column value.
12
+ * @returns `true` when the value belongs in `blob_refs`.
13
+ * @example
14
+ * isBlobRefValue('data:image/png;base64,' + 'x'.repeat(600)); // true
15
+ * isBlobRefValue('https://example.com/a.png'); // false
16
+ */
17
+ export function isBlobRefValue(value) {
18
+ return value.startsWith('data:') && value.length > DATA_URI_URL_REFS_LIMIT;
19
+ }
@@ -0,0 +1,66 @@
1
+ import type { DomPathCandidate, DomPathResult } from './types.js';
2
+ /**
3
+ * One image record projected onto the fields the matcher needs:
4
+ * `id` (used to build the returned Map) and `sourceCode` (the outerHTML
5
+ * string to match against document candidates). `id` may be a real
6
+ * `images.id` (migration) or a synthetic per-page index (crawler write
7
+ * path) — the matcher only requires ids to be unique within one call and
8
+ * ordered in crawler insertion order.
9
+ */
10
+ interface ImageRowForMatching {
11
+ /** Unique-within-call id, in crawler insertion order. */
12
+ id: number;
13
+ /**
14
+ * The `<img>` element's `outerHTML` as captured by the crawler.
15
+ * `null` when the crawler failed to capture the source (a null blob
16
+ * or a puppeteer serialisation edge case).
17
+ */
18
+ sourceCode: string | null;
19
+ }
20
+ /**
21
+ * Matches image records to their DOM-path strings using a 3-case
22
+ * algorithm (issue #193).
23
+ *
24
+ * The three cases:
25
+ *
26
+ * 1. **Single match** — `image.sourceCode` matches exactly one `<img>`
27
+ * outerHTML in the document. Assign that candidate's `dom_path`.
28
+ * 2. **Ordinal match** — multiple `<img>` elements share the same
29
+ * outerHTML (identical `src` / `alt` / attributes on the same page).
30
+ * Assign paths in `id` order, matching the crawler's insertion order
31
+ * which is expected to correspond to DOM order.
32
+ * 3. **Unknown** — `sourceCode` is null OR no matching `<img>` exists
33
+ * in the document (crawler-side rewriting drift). Fall back to the
34
+ * synthetic `unknown/<id>` marker so the `dom_path_text_id NOT NULL`
35
+ * constraint is still satisfied.
36
+ *
37
+ * The function is pure — no DB, no DOM. Callers produce the candidate
38
+ * list from whatever DOM they have: jsdom over an archived HTML snapshot
39
+ * (migration script) or an in-browser walk over the live page (crawler
40
+ * write path). See {@link ./types.ts}'s `DomPathCandidate`.
41
+ *
42
+ * `candidatesInDocumentOrder` MUST already be in document order — the
43
+ * matcher does not sort. `document.querySelectorAll('img')` satisfies
44
+ * this contract; ordering is the caller's responsibility.
45
+ *
46
+ * `images` MUST already be sorted by `id` ascending for the ordinal-
47
+ * match case to reproduce the crawler's insertion order deterministically.
48
+ * A minor deviation from this ordering only affects the ordinal-match
49
+ * branch — single-match and unknown branches are order-independent.
50
+ * @param images - The page's image records in `id` order.
51
+ * @param candidatesInDocumentOrder - `<img>` outerHTML + dom_path pairs
52
+ * from the document, in document order.
53
+ * @returns Map keyed by `images[].id`; every input row gets one entry.
54
+ * @example
55
+ * const jsdom = new JSDOM(html);
56
+ * const candidates = [...jsdom.window.document.querySelectorAll('img')].map(
57
+ * (img) => ({ outerHTML: img.outerHTML, path: deriveDomPath(img) }),
58
+ * );
59
+ * const result = matchImagesToDomPaths(
60
+ * [{ id: 1, sourceCode: '<img src="a.png">' }],
61
+ * candidates,
62
+ * );
63
+ * result.get(1); // { path: 'html/body[1]/img[1]', case: 'single-match' }
64
+ */
65
+ export declare function matchImagesToDomPaths(images: readonly ImageRowForMatching[], candidatesInDocumentOrder: readonly DomPathCandidate[]): ReadonlyMap<number, DomPathResult>;
66
+ export {};
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Matches image records to their DOM-path strings using a 3-case
3
+ * algorithm (issue #193).
4
+ *
5
+ * The three cases:
6
+ *
7
+ * 1. **Single match** — `image.sourceCode` matches exactly one `<img>`
8
+ * outerHTML in the document. Assign that candidate's `dom_path`.
9
+ * 2. **Ordinal match** — multiple `<img>` elements share the same
10
+ * outerHTML (identical `src` / `alt` / attributes on the same page).
11
+ * Assign paths in `id` order, matching the crawler's insertion order
12
+ * which is expected to correspond to DOM order.
13
+ * 3. **Unknown** — `sourceCode` is null OR no matching `<img>` exists
14
+ * in the document (crawler-side rewriting drift). Fall back to the
15
+ * synthetic `unknown/<id>` marker so the `dom_path_text_id NOT NULL`
16
+ * constraint is still satisfied.
17
+ *
18
+ * The function is pure — no DB, no DOM. Callers produce the candidate
19
+ * list from whatever DOM they have: jsdom over an archived HTML snapshot
20
+ * (migration script) or an in-browser walk over the live page (crawler
21
+ * write path). See {@link ./types.ts}'s `DomPathCandidate`.
22
+ *
23
+ * `candidatesInDocumentOrder` MUST already be in document order — the
24
+ * matcher does not sort. `document.querySelectorAll('img')` satisfies
25
+ * this contract; ordering is the caller's responsibility.
26
+ *
27
+ * `images` MUST already be sorted by `id` ascending for the ordinal-
28
+ * match case to reproduce the crawler's insertion order deterministically.
29
+ * A minor deviation from this ordering only affects the ordinal-match
30
+ * branch — single-match and unknown branches are order-independent.
31
+ * @param images - The page's image records in `id` order.
32
+ * @param candidatesInDocumentOrder - `<img>` outerHTML + dom_path pairs
33
+ * from the document, in document order.
34
+ * @returns Map keyed by `images[].id`; every input row gets one entry.
35
+ * @example
36
+ * const jsdom = new JSDOM(html);
37
+ * const candidates = [...jsdom.window.document.querySelectorAll('img')].map(
38
+ * (img) => ({ outerHTML: img.outerHTML, path: deriveDomPath(img) }),
39
+ * );
40
+ * const result = matchImagesToDomPaths(
41
+ * [{ id: 1, sourceCode: '<img src="a.png">' }],
42
+ * candidates,
43
+ * );
44
+ * result.get(1); // { path: 'html/body[1]/img[1]', case: 'single-match' }
45
+ */
46
+ export function matchImagesToDomPaths(images, candidatesInDocumentOrder) {
47
+ const byOuterHtml = new Map();
48
+ for (const candidate of candidatesInDocumentOrder) {
49
+ const bucket = byOuterHtml.get(candidate.outerHTML);
50
+ if (bucket === undefined) {
51
+ byOuterHtml.set(candidate.outerHTML, [candidate]);
52
+ }
53
+ else {
54
+ bucket.push(candidate);
55
+ }
56
+ }
57
+ const cursorByOuterHtml = new Map();
58
+ const result = new Map();
59
+ for (const image of images) {
60
+ if (image.sourceCode === null || image.sourceCode === '') {
61
+ result.set(image.id, { path: `unknown/${image.id}`, case: 'unknown' });
62
+ continue;
63
+ }
64
+ const candidates = byOuterHtml.get(image.sourceCode);
65
+ if (candidates === undefined || candidates.length === 0) {
66
+ result.set(image.id, { path: `unknown/${image.id}`, case: 'unknown' });
67
+ continue;
68
+ }
69
+ if (candidates.length === 1) {
70
+ result.set(image.id, {
71
+ path: candidates[0].path,
72
+ case: 'single-match',
73
+ });
74
+ continue;
75
+ }
76
+ const cursor = cursorByOuterHtml.get(image.sourceCode) ?? 0;
77
+ cursorByOuterHtml.set(image.sourceCode, cursor + 1);
78
+ const chosen = candidates[cursor];
79
+ if (chosen === undefined) {
80
+ // More image records share this outerHTML than the document
81
+ // has matching `<img>` elements. Reusing the last candidate
82
+ // (`candidates.at(-1)`) would silently map every overflow row
83
+ // to the same element, producing multiple `image_items` rows
84
+ // with the same `dom_path_text_id`. Falling back to
85
+ // `unknown/<id>` keeps overflow rows individually
86
+ // distinguishable in the archive.
87
+ result.set(image.id, { path: `unknown/${image.id}`, case: 'unknown' });
88
+ continue;
89
+ }
90
+ result.set(image.id, {
91
+ path: chosen.path,
92
+ case: 'ordinal-match',
93
+ });
94
+ }
95
+ return result;
96
+ }
@@ -0,0 +1,33 @@
1
+ import type { ProgressCallback } from '../create-progress-reporter.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Populates `anchor_edges` from `anchors` (issue #193).
5
+ *
6
+ * The algorithm is a **single keyset-paginated scan** over `anchors`
7
+ * ordered by `(pageId, hrefId, id)`. Each chunk feeds
8
+ * {@link ./collapse-anchor-rows.ts}, which yields one edge per distinct
9
+ * `(pageId, hrefId)` pair as soon as the pair boundary is observed.
10
+ *
11
+ * A boundary can straddle two chunks (the last row of chunk N shares a
12
+ * pair with the first row of chunk N+1), so the outer loop keeps an
13
+ * "open edge" state that flushes only when the *next* pair actually
14
+ * starts. On end-of-stream the last open edge is emitted.
15
+ *
16
+ * `first_text_id` is left unresolved during the streaming pass — the
17
+ * text is buffered on each pending edge and resolved in bulk by
18
+ * {@link ./resolve-text-refs.ts} at INSERT time. Every INSERT batch
19
+ * therefore issues one text-refs lookup + one INSERT, keeping DB
20
+ * round-trips per edge low.
21
+ *
22
+ * `INSERT OR IGNORE` on the `(page_id, href_page_id)` UNIQUE composite
23
+ * makes the step idempotent: a re-run after partial failure re-emits the
24
+ * same edges but the writes no-op.
25
+ * @param trx - Knex instance or transaction connected to the archive DB.
26
+ * @param onProgress - Optional sink for periodic progress lines (one per
27
+ * ~5% of `anchors` scanned); see {@link ../create-progress-reporter.ts}.
28
+ * @example
29
+ * await knex.transaction(async (trx) => {
30
+ * await populateAnchorEdges(trx);
31
+ * });
32
+ */
33
+ export declare function populateAnchorEdges(trx: Knex, onProgress?: ProgressCallback): Promise<void>;
@@ -0,0 +1,153 @@
1
+ import { createProgressReporter } from '../create-progress-reporter.js';
2
+ import { collapseAnchorRows } from './collapse-anchor-rows.js';
3
+ import { resolveTextRefs } from './resolve-text-refs.js';
4
+ /**
5
+ * Rows scanned per keyset-paginated `SELECT` chunk against `anchors`.
6
+ * `anchors` is the largest table in the archive (≈ 13 M rows on the
7
+ * reference archive) so the chunk size trades off memory against
8
+ * round-trip overhead. 5 000 rows per SELECT keeps peak chunk memory
9
+ * ≈ 5 MB (per-row ≈ 1 KB with URL / textContent stored elsewhere) and
10
+ * amortises the per-query round-trip cost across many collapses.
11
+ */
12
+ const READ_CHUNK_SIZE = 5000;
13
+ /**
14
+ * Edges buffered before a bulk `.insert(rows).onConflict(...).ignore()`.
15
+ * The real constraint here is NOT the SQLite bound-parameter limit —
16
+ * knex's sqlite3-family dialect compiles any multi-row `.insert()` (with
17
+ * or without `onConflict`) into `INSERT INTO ... SELECT ... UNION ALL
18
+ * SELECT ...`, which is capped by SQLite's `SQLITE_LIMIT_COMPOUND_SELECT`
19
+ * (default 500). A chunk size above 500 fails with "too many terms in
20
+ * compound SELECT" — confirmed against a real archive whose anchor count
21
+ * routinely produces 500+ distinct `(page_id, href_page_id)` pairs per
22
+ * page. Kept at exactly 500 (not lower) to match the established
23
+ * convention already used by `upsert-one-header-set.ts` and the
24
+ * ref-tables populate steps.
25
+ */
26
+ const INSERT_CHUNK_SIZE = 500;
27
+ /**
28
+ * Populates `anchor_edges` from `anchors` (issue #193).
29
+ *
30
+ * The algorithm is a **single keyset-paginated scan** over `anchors`
31
+ * ordered by `(pageId, hrefId, id)`. Each chunk feeds
32
+ * {@link ./collapse-anchor-rows.ts}, which yields one edge per distinct
33
+ * `(pageId, hrefId)` pair as soon as the pair boundary is observed.
34
+ *
35
+ * A boundary can straddle two chunks (the last row of chunk N shares a
36
+ * pair with the first row of chunk N+1), so the outer loop keeps an
37
+ * "open edge" state that flushes only when the *next* pair actually
38
+ * starts. On end-of-stream the last open edge is emitted.
39
+ *
40
+ * `first_text_id` is left unresolved during the streaming pass — the
41
+ * text is buffered on each pending edge and resolved in bulk by
42
+ * {@link ./resolve-text-refs.ts} at INSERT time. Every INSERT batch
43
+ * therefore issues one text-refs lookup + one INSERT, keeping DB
44
+ * round-trips per edge low.
45
+ *
46
+ * `INSERT OR IGNORE` on the `(page_id, href_page_id)` UNIQUE composite
47
+ * makes the step idempotent: a re-run after partial failure re-emits the
48
+ * same edges but the writes no-op.
49
+ * @param trx - Knex instance or transaction connected to the archive DB.
50
+ * @param onProgress - Optional sink for periodic progress lines (one per
51
+ * ~5% of `anchors` scanned); see {@link ../create-progress-reporter.ts}.
52
+ * @example
53
+ * await knex.transaction(async (trx) => {
54
+ * await populateAnchorEdges(trx);
55
+ * });
56
+ */
57
+ export async function populateAnchorEdges(trx, onProgress) {
58
+ let cursorPageId = 0;
59
+ let cursorHrefId = 0;
60
+ let cursorId = 0;
61
+ let carryOver = null;
62
+ const pending = [];
63
+ const countRows = await trx('anchors').count({ n: '*' });
64
+ const total = Number(countRows[0]?.n ?? 0);
65
+ const report = createProgressReporter('anchor_edges (anchors)', total, onProgress);
66
+ let processed = 0;
67
+ while (true) {
68
+ const rows = await trx('anchors')
69
+ .select('id', 'pageId', 'hrefId', 'hash', 'textContent')
70
+ .where(function () {
71
+ this.where('pageId', '>', cursorPageId)
72
+ .orWhere(function () {
73
+ this.where('pageId', cursorPageId).andWhere('hrefId', '>', cursorHrefId);
74
+ })
75
+ .orWhere(function () {
76
+ this.where('pageId', cursorPageId)
77
+ .andWhere('hrefId', cursorHrefId)
78
+ .andWhere('id', '>', cursorId);
79
+ });
80
+ })
81
+ .orderBy([
82
+ { column: 'pageId', order: 'asc' },
83
+ { column: 'hrefId', order: 'asc' },
84
+ { column: 'id', order: 'asc' },
85
+ ])
86
+ .limit(READ_CHUNK_SIZE);
87
+ if (rows.length === 0) {
88
+ break;
89
+ }
90
+ const lastRow = rows.at(-1);
91
+ cursorPageId = lastRow.pageId;
92
+ cursorHrefId = lastRow.hrefId;
93
+ cursorId = lastRow.id;
94
+ processed += rows.length;
95
+ report(processed);
96
+ const chunkEdges = [...collapseAnchorRows(rows)];
97
+ for (const edge of chunkEdges) {
98
+ if (carryOver !== null &&
99
+ carryOver.page_id === edge.page_id &&
100
+ carryOver.href_page_id === edge.href_page_id) {
101
+ carryOver.count += edge.count;
102
+ continue;
103
+ }
104
+ if (carryOver !== null) {
105
+ pending.push(carryOver);
106
+ }
107
+ carryOver = edge;
108
+ if (pending.length >= INSERT_CHUNK_SIZE) {
109
+ await flush(trx, pending);
110
+ }
111
+ }
112
+ }
113
+ if (carryOver !== null) {
114
+ pending.push(carryOver);
115
+ }
116
+ if (pending.length > 0) {
117
+ await flush(trx, pending);
118
+ }
119
+ }
120
+ /**
121
+ * Resolves `first_text_id` for every buffered edge in `pending`, then
122
+ * bulk-inserts the batch into `anchor_edges` and clears the buffer.
123
+ *
124
+ * Extracted from the main loop so the "resolve + insert" pair happens in
125
+ * exactly one place — the streaming loop, the carry-over flush at
126
+ * end-of-stream, and any future re-order boundary all land here.
127
+ * @param trx - Knex instance or transaction.
128
+ * @param pending - Buffered edges awaiting `first_text_id` resolution
129
+ * and INSERT; mutated in place (cleared on return).
130
+ */
131
+ async function flush(trx, pending) {
132
+ const texts = new Set();
133
+ for (const edge of pending) {
134
+ if (edge.first_textContent != null && edge.first_textContent !== '') {
135
+ texts.add(edge.first_textContent);
136
+ }
137
+ }
138
+ const textIds = await resolveTextRefs(trx, texts);
139
+ const inserts = pending.map((edge) => ({
140
+ page_id: edge.page_id,
141
+ href_page_id: edge.href_page_id,
142
+ count: edge.count,
143
+ first_hash: edge.first_hash,
144
+ first_text_id: edge.first_textContent != null && edge.first_textContent !== ''
145
+ ? (textIds.get(edge.first_textContent) ?? null)
146
+ : null,
147
+ }));
148
+ await trx('anchor_edges')
149
+ .insert(inserts)
150
+ .onConflict(['page_id', 'href_page_id'])
151
+ .ignore();
152
+ pending.length = 0;
153
+ }
@@ -0,0 +1,40 @@
1
+ import type { ProgressCallback } from '../create-progress-reporter.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Populates `content_items` from `pages` (issue #193).
5
+ *
6
+ * Strategy per chunk:
7
+ *
8
+ * 1. **Keyset-paginate** `pages` in id order so partial-failure re-runs
9
+ * resume from the last committed row without reading the full table.
10
+ * Same pattern as {@link ../populate-ref-tables/populate-url-refs.ts}.
11
+ * 2. **Batch-resolve** `url_refs.id`, `content_type_refs.id`, and
12
+ * `header_sets.id` for every distinct value in the chunk — one round
13
+ * trip per ref table instead of N round trips per row.
14
+ * 3. **Bulk INSERT** with explicit `id = pages.id`. Reusing the legacy
15
+ * PK preserves every FK reference in `page_errors` / `page_tags` /
16
+ * `page_jsonld` / `page_html_ref` without a per-row UPDATE.
17
+ *
18
+ * `redirect_dest_id` is copied verbatim from `redirectDestId` — the FK
19
+ * self-reference is `DEFERRABLE INITIALLY DEFERRED` (see
20
+ * {@link ../create-entity-tables.ts}) so a redirect source
21
+ * inserted before its destination is validated only at COMMIT time.
22
+ *
23
+ * `INSERT OR IGNORE` on the natural PK makes the step idempotent — a
24
+ * re-run after a partial failure only inserts the rows that are still
25
+ * missing.
26
+ *
27
+ * `content_type_refs` is preloaded once at the start because its
28
+ * cardinality is small (see {@link ./resolve-content-type-refs.ts}); the
29
+ * URL and header-set resolvers are called per chunk because their
30
+ * per-archive cardinality can be very large (one row per distinct URL /
31
+ * response JSON).
32
+ * @param trx - Knex instance or transaction connected to the archive DB.
33
+ * @param onProgress - Optional sink for periodic progress lines (one per
34
+ * ~5% of `pages` scanned); see {@link ../create-progress-reporter.ts}.
35
+ * @example
36
+ * await knex.transaction(async (trx) => {
37
+ * await populateContentItems(trx);
38
+ * });
39
+ */
40
+ export declare function populateContentItems(trx: Knex, onProgress?: ProgressCallback): Promise<void>;