@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,27 @@
1
+ /**
2
+ * Loads the entire `content_type_refs` dictionary into an in-process map
3
+ * keyed by the raw wire value (issue #193).
4
+ *
5
+ * Content-type cardinality is small in practice — the reference archive
6
+ * carries ≈ 400 distinct raw values across `pages` + `resources`, so
7
+ * loading everything into a `Map` costs ≈ 40 KB and eliminates every
8
+ * per-chunk round-trip. This is the same trade-off {@link
9
+ * ../populate-ref-tables/populate-header-tables.ts} makes for `header_name_refs`.
10
+ *
11
+ * Callers should invoke this once at the start of a 0.13 populate
12
+ * step (e.g. `populateContentItems`, `populateResourceItems`) and reuse
13
+ * the map for every chunk.
14
+ * @param trx - Knex instance or transaction connected to the archive DB.
15
+ * @returns Map keyed by `content_type_refs.raw`.
16
+ * @example
17
+ * const contentTypeIds = await loadContentTypeRefs(trx);
18
+ * const id = contentTypeIds.get('text/html; charset=utf-8'); // number | undefined
19
+ */
20
+ export async function loadContentTypeRefs(trx) {
21
+ const rows = await trx('content_type_refs').select('id', 'raw');
22
+ const map = new Map();
23
+ for (const row of rows) {
24
+ map.set(row.raw, row.id);
25
+ }
26
+ return map;
27
+ }
@@ -0,0 +1,49 @@
1
+ import type { HeaderSetIdMap } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Batch-resolves `header_sets.id` for a set of raw `responseHeaders`
5
+ * JSON strings (issue #193).
6
+ *
7
+ * The lookup is a two-stage cascade:
8
+ *
9
+ * 1. **Primary lookup** by `header_sets.raw_json_hash` — SHA-256 of the
10
+ * raw JSON string exactly as stored on the source row. This hits when
11
+ * the row's JSON key ordering matches whichever ordering populated
12
+ * `header_sets` first for that stable-set equivalence class (see
13
+ * {@link ../populate-ref-tables/populate-header-tables.ts}).
14
+ * 2. **Fallback lookup** by `header_sets.raw_hash` for the misses — SHA-
15
+ * 256 of the sorted `name=value` pairs. `populate-header-tables.ts`
16
+ * stores exactly one `raw_json_hash` per `raw_hash` equivalence class
17
+ * (the first JSON variant encountered), so a second variant with
18
+ * identical decoded content but different key ordering never had its
19
+ * `raw_json_hash` persisted — but its `raw_hash` still points at the
20
+ * shared `header_sets` row. Without the fallback these rows would
21
+ * silently get `header_set_id = NULL` on `content_items` /
22
+ * `resource_items` and the count-based acceptance check would still
23
+ * pass; the fallback closes that gap.
24
+ *
25
+ * Hashes are computed in JS because SQLite has no built-in BLAKE3 /
26
+ * SHA-256 (see {@link ../populate-ref-tables/compute-content-hash.ts} for the
27
+ * algorithm choice). The `raw_hash` fallback re-decomposes each miss via
28
+ * {@link ../populate-ref-tables/decompose-header-set.ts} so the canonicalisation
29
+ * matches the one `populateHeaderTables` used at insert time.
30
+ *
31
+ * Empty / null values and the sentinel `'{}'` / `'null'` strings are
32
+ * skipped — those responses were decomposed to `null` by
33
+ * `decomposeHeaderSet` and never produced a `header_sets` row, so the
34
+ * caller writes `header_set_id = null` for them.
35
+ *
36
+ * Duplicate JSON strings are deduped internally so a page and a
37
+ * resource sharing the same raw responseHeaders JSON only contribute
38
+ * one lookup slot.
39
+ * @param trx - Knex instance or transaction connected to the archive DB.
40
+ * @param rawJsonStrings - Iterable of raw responseHeaders JSON strings.
41
+ * @returns Map keyed by the raw JSON string; missing entries indicate
42
+ * no matching `header_sets` row exists (parse failure or empty set).
43
+ * @example
44
+ * const idMap = await resolveHeaderSets(trx, [
45
+ * '{"content-type":"text/html"}',
46
+ * ]);
47
+ * const setId = idMap.get('{"content-type":"text/html"}'); // number | undefined
48
+ */
49
+ export declare function resolveHeaderSets(trx: Knex, rawJsonStrings: Iterable<string>): Promise<HeaderSetIdMap>;
@@ -0,0 +1,122 @@
1
+ import { computeContentHash } from '../populate-ref-tables/compute-content-hash.js';
2
+ import { decomposeHeaderSet } from '../populate-ref-tables/decompose-header-set.js';
3
+ /**
4
+ * Rows sent per `SELECT ... WHERE raw_json_hash IN (?, ...)` chunk. Same
5
+ * rationale as {@link ./resolve-url-refs.ts}: 500 rows × 1 param each is
6
+ * well under the SQLite variable limit and gives good round-trip
7
+ * amortisation.
8
+ */
9
+ const LOOKUP_CHUNK_SIZE = 500;
10
+ /**
11
+ * Batch-resolves `header_sets.id` for a set of raw `responseHeaders`
12
+ * JSON strings (issue #193).
13
+ *
14
+ * The lookup is a two-stage cascade:
15
+ *
16
+ * 1. **Primary lookup** by `header_sets.raw_json_hash` — SHA-256 of the
17
+ * raw JSON string exactly as stored on the source row. This hits when
18
+ * the row's JSON key ordering matches whichever ordering populated
19
+ * `header_sets` first for that stable-set equivalence class (see
20
+ * {@link ../populate-ref-tables/populate-header-tables.ts}).
21
+ * 2. **Fallback lookup** by `header_sets.raw_hash` for the misses — SHA-
22
+ * 256 of the sorted `name=value` pairs. `populate-header-tables.ts`
23
+ * stores exactly one `raw_json_hash` per `raw_hash` equivalence class
24
+ * (the first JSON variant encountered), so a second variant with
25
+ * identical decoded content but different key ordering never had its
26
+ * `raw_json_hash` persisted — but its `raw_hash` still points at the
27
+ * shared `header_sets` row. Without the fallback these rows would
28
+ * silently get `header_set_id = NULL` on `content_items` /
29
+ * `resource_items` and the count-based acceptance check would still
30
+ * pass; the fallback closes that gap.
31
+ *
32
+ * Hashes are computed in JS because SQLite has no built-in BLAKE3 /
33
+ * SHA-256 (see {@link ../populate-ref-tables/compute-content-hash.ts} for the
34
+ * algorithm choice). The `raw_hash` fallback re-decomposes each miss via
35
+ * {@link ../populate-ref-tables/decompose-header-set.ts} so the canonicalisation
36
+ * matches the one `populateHeaderTables` used at insert time.
37
+ *
38
+ * Empty / null values and the sentinel `'{}'` / `'null'` strings are
39
+ * skipped — those responses were decomposed to `null` by
40
+ * `decomposeHeaderSet` and never produced a `header_sets` row, so the
41
+ * caller writes `header_set_id = null` for them.
42
+ *
43
+ * Duplicate JSON strings are deduped internally so a page and a
44
+ * resource sharing the same raw responseHeaders JSON only contribute
45
+ * one lookup slot.
46
+ * @param trx - Knex instance or transaction connected to the archive DB.
47
+ * @param rawJsonStrings - Iterable of raw responseHeaders JSON strings.
48
+ * @returns Map keyed by the raw JSON string; missing entries indicate
49
+ * no matching `header_sets` row exists (parse failure or empty set).
50
+ * @example
51
+ * const idMap = await resolveHeaderSets(trx, [
52
+ * '{"content-type":"text/html"}',
53
+ * ]);
54
+ * const setId = idMap.get('{"content-type":"text/html"}'); // number | undefined
55
+ */
56
+ export async function resolveHeaderSets(trx, rawJsonStrings) {
57
+ const distinct = new Set();
58
+ for (const raw of rawJsonStrings) {
59
+ if (typeof raw !== 'string' || raw === '' || raw === 'null' || raw === '{}') {
60
+ continue;
61
+ }
62
+ distinct.add(raw);
63
+ }
64
+ if (distinct.size === 0) {
65
+ return new Map();
66
+ }
67
+ const values = [...distinct];
68
+ const hashByValue = new Map();
69
+ const hashHexToValue = new Map();
70
+ for (const value of values) {
71
+ const hash = computeContentHash(value);
72
+ hashByValue.set(value, hash);
73
+ hashHexToValue.set(hash.toString('hex'), value);
74
+ }
75
+ const hashes = [...hashByValue.values()];
76
+ const result = new Map();
77
+ for (let index = 0; index < hashes.length; index += LOOKUP_CHUNK_SIZE) {
78
+ const chunkHashes = hashes.slice(index, index + LOOKUP_CHUNK_SIZE);
79
+ const rows = await trx('header_sets')
80
+ .select('id', 'raw_json_hash')
81
+ .whereIn('raw_json_hash', chunkHashes);
82
+ for (const row of rows) {
83
+ const hex = Buffer.from(row.raw_json_hash).toString('hex');
84
+ const value = hashHexToValue.get(hex);
85
+ if (value !== undefined) {
86
+ result.set(value, row.id);
87
+ }
88
+ }
89
+ }
90
+ // Fallback pass: any value we could not resolve by `raw_json_hash`
91
+ // might still map to an existing `header_sets` row via `raw_hash`
92
+ // (identical decoded content, different JSON key ordering).
93
+ const misses = values.filter((value) => !result.has(value));
94
+ if (misses.length === 0) {
95
+ return result;
96
+ }
97
+ const rawHashByValue = new Map();
98
+ const rawHashHexToValue = new Map();
99
+ for (const value of misses) {
100
+ const decomposed = decomposeHeaderSet(value);
101
+ if (decomposed === null) {
102
+ continue;
103
+ }
104
+ rawHashByValue.set(value, decomposed.rawHash);
105
+ rawHashHexToValue.set(decomposed.rawHash.toString('hex'), value);
106
+ }
107
+ const rawHashes = [...rawHashByValue.values()];
108
+ for (let index = 0; index < rawHashes.length; index += LOOKUP_CHUNK_SIZE) {
109
+ const chunkHashes = rawHashes.slice(index, index + LOOKUP_CHUNK_SIZE);
110
+ const rows = await trx('header_sets')
111
+ .select('id', 'raw_hash')
112
+ .whereIn('raw_hash', chunkHashes);
113
+ for (const row of rows) {
114
+ const hex = Buffer.from(row.raw_hash).toString('hex');
115
+ const value = rawHashHexToValue.get(hex);
116
+ if (value !== undefined) {
117
+ result.set(value, row.id);
118
+ }
119
+ }
120
+ }
121
+ return result;
122
+ }
@@ -0,0 +1,25 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Batch-resolves `json_refs.id` for a set of raw `meta_extras` JSON
4
+ * strings (issue #193).
5
+ *
6
+ * The lookup goes through `json_refs.hash`, the SHA-256 hash of the raw
7
+ * JSON string stored by `populateJsonRefs` (see
8
+ * {@link ../populate-ref-tables/populate-json-refs.ts}). Hashes are computed in JS
9
+ * for the same reason as {@link ./resolve-header-sets.ts}: SQLite has no
10
+ * built-in hash function.
11
+ *
12
+ * Empty / null strings are skipped — those pages had no `meta_extras`
13
+ * and never produced a `json_refs` row, so the caller writes
14
+ * `meta_extras_json_id = null` for them.
15
+ *
16
+ * Duplicate strings in `rawJsonStrings` are deduped internally.
17
+ * @param trx - Knex instance or transaction connected to the archive DB.
18
+ * @param rawJsonStrings - Iterable of raw `meta_extras` JSON strings.
19
+ * @returns Map keyed by the raw JSON string; missing entries indicate
20
+ * no matching `json_refs` row exists.
21
+ * @example
22
+ * const idMap = await resolveJsonRefs(trx, ['{"customField":"x"}']);
23
+ * const jsonId = idMap.get('{"customField":"x"}'); // number | undefined
24
+ */
25
+ export declare function resolveJsonRefs(trx: Knex, rawJsonStrings: Iterable<string>): Promise<ReadonlyMap<string, number>>;
@@ -0,0 +1,67 @@
1
+ import { computeContentHash } from '../populate-ref-tables/compute-content-hash.js';
2
+ /**
3
+ * Rows sent per `SELECT ... WHERE hash IN (?, ...)` chunk. `json_refs`
4
+ * cardinality is bounded by the count of distinct `pages.meta_extras`
5
+ * JSON strings — typically low thousands on a reference archive — so
6
+ * 500 per chunk keeps round-trips low without straining the SQLite
7
+ * variable limit.
8
+ */
9
+ const LOOKUP_CHUNK_SIZE = 500;
10
+ /**
11
+ * Batch-resolves `json_refs.id` for a set of raw `meta_extras` JSON
12
+ * strings (issue #193).
13
+ *
14
+ * The lookup goes through `json_refs.hash`, the SHA-256 hash of the raw
15
+ * JSON string stored by `populateJsonRefs` (see
16
+ * {@link ../populate-ref-tables/populate-json-refs.ts}). Hashes are computed in JS
17
+ * for the same reason as {@link ./resolve-header-sets.ts}: SQLite has no
18
+ * built-in hash function.
19
+ *
20
+ * Empty / null strings are skipped — those pages had no `meta_extras`
21
+ * and never produced a `json_refs` row, so the caller writes
22
+ * `meta_extras_json_id = null` for them.
23
+ *
24
+ * Duplicate strings in `rawJsonStrings` are deduped internally.
25
+ * @param trx - Knex instance or transaction connected to the archive DB.
26
+ * @param rawJsonStrings - Iterable of raw `meta_extras` JSON strings.
27
+ * @returns Map keyed by the raw JSON string; missing entries indicate
28
+ * no matching `json_refs` row exists.
29
+ * @example
30
+ * const idMap = await resolveJsonRefs(trx, ['{"customField":"x"}']);
31
+ * const jsonId = idMap.get('{"customField":"x"}'); // number | undefined
32
+ */
33
+ export async function resolveJsonRefs(trx, rawJsonStrings) {
34
+ const distinct = new Set();
35
+ for (const raw of rawJsonStrings) {
36
+ if (typeof raw === 'string' && raw !== '') {
37
+ distinct.add(raw);
38
+ }
39
+ }
40
+ if (distinct.size === 0) {
41
+ return new Map();
42
+ }
43
+ const values = [...distinct];
44
+ const hashByValue = new Map();
45
+ const hashHexToValue = new Map();
46
+ for (const value of values) {
47
+ const hash = computeContentHash(value);
48
+ hashByValue.set(value, hash);
49
+ hashHexToValue.set(hash.toString('hex'), value);
50
+ }
51
+ const hashes = [...hashByValue.values()];
52
+ const result = new Map();
53
+ for (let index = 0; index < hashes.length; index += LOOKUP_CHUNK_SIZE) {
54
+ const chunkHashes = hashes.slice(index, index + LOOKUP_CHUNK_SIZE);
55
+ const rows = await trx('json_refs')
56
+ .select('id', 'hash')
57
+ .whereIn('hash', chunkHashes);
58
+ for (const row of rows) {
59
+ const hex = Buffer.from(row.hash).toString('hex');
60
+ const value = hashHexToValue.get(hex);
61
+ if (value !== undefined) {
62
+ result.set(value, row.id);
63
+ }
64
+ }
65
+ }
66
+ return result;
67
+ }
@@ -0,0 +1,30 @@
1
+ import type { TextRefIdMap } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Batch-resolves `text_refs.id` for a set of raw text strings (issue #193).
5
+ *
6
+ * The `text_refs` UNIQUE constraint is on `(hash, text)` so lookups
7
+ * prefix-seek on `hash`. Each caller-supplied text is hashed in JS via
8
+ * {@link ../populate-ref-tables/compute-content-hash.ts} (32-byte SHA-256, matching
9
+ * how `populateTextRefs` inserted the rows) before the
10
+ * SQL query; providing the hash lets SQLite's index seek in O(log n)
11
+ * without a full table scan.
12
+ *
13
+ * The `(hash, text)` UNIQUE composite makes the trailing text column
14
+ * theoretically necessary to disambiguate the astronomically improbable
15
+ * hash collision — we narrow on both by including `text` in the WHERE
16
+ * clause, then filter the map keys by the exact text on read-back.
17
+ *
18
+ * Empty / null texts are ignored. Duplicate strings in `texts` are
19
+ * deduped internally.
20
+ * @param trx - Knex instance or transaction connected to the archive DB.
21
+ * @param texts - Iterable of raw text strings to resolve.
22
+ * @returns Map keyed by the raw text; missing entries indicate the text
23
+ * is not present in `text_refs` (which should not happen after
24
+ * `populateTextRefs` completes but does happen mid-migration when a caller
25
+ * passes texts that were never inserted).
26
+ * @example
27
+ * const idMap = await resolveTextRefs(trx, ['My Page Title', 'Alt text']);
28
+ * const titleId = idMap.get('My Page Title'); // number | undefined
29
+ */
30
+ export declare function resolveTextRefs(trx: Knex, texts: Iterable<string>): Promise<TextRefIdMap>;
@@ -0,0 +1,61 @@
1
+ import { computeContentHash } from '../populate-ref-tables/compute-content-hash.js';
2
+ /**
3
+ * Rows sent per `SELECT ... WHERE hash IN (?, ...)` chunk. Same rationale
4
+ * as {@link ./resolve-url-refs.ts}: 800 rows keeps the parameter count
5
+ * well under the SQLite variable limit even in the worst case where every
6
+ * hash is bound as its own parameter.
7
+ */
8
+ const LOOKUP_CHUNK_SIZE = 800;
9
+ /**
10
+ * Batch-resolves `text_refs.id` for a set of raw text strings (issue #193).
11
+ *
12
+ * The `text_refs` UNIQUE constraint is on `(hash, text)` so lookups
13
+ * prefix-seek on `hash`. Each caller-supplied text is hashed in JS via
14
+ * {@link ../populate-ref-tables/compute-content-hash.ts} (32-byte SHA-256, matching
15
+ * how `populateTextRefs` inserted the rows) before the
16
+ * SQL query; providing the hash lets SQLite's index seek in O(log n)
17
+ * without a full table scan.
18
+ *
19
+ * The `(hash, text)` UNIQUE composite makes the trailing text column
20
+ * theoretically necessary to disambiguate the astronomically improbable
21
+ * hash collision — we narrow on both by including `text` in the WHERE
22
+ * clause, then filter the map keys by the exact text on read-back.
23
+ *
24
+ * Empty / null texts are ignored. Duplicate strings in `texts` are
25
+ * deduped internally.
26
+ * @param trx - Knex instance or transaction connected to the archive DB.
27
+ * @param texts - Iterable of raw text strings to resolve.
28
+ * @returns Map keyed by the raw text; missing entries indicate the text
29
+ * is not present in `text_refs` (which should not happen after
30
+ * `populateTextRefs` completes but does happen mid-migration when a caller
31
+ * passes texts that were never inserted).
32
+ * @example
33
+ * const idMap = await resolveTextRefs(trx, ['My Page Title', 'Alt text']);
34
+ * const titleId = idMap.get('My Page Title'); // number | undefined
35
+ */
36
+ export async function resolveTextRefs(trx, texts) {
37
+ const distinct = new Set();
38
+ for (const text of texts) {
39
+ if (typeof text === 'string' && text !== '') {
40
+ distinct.add(text);
41
+ }
42
+ }
43
+ if (distinct.size === 0) {
44
+ return new Map();
45
+ }
46
+ const values = [...distinct];
47
+ const hashes = values.map((text) => computeContentHash(text));
48
+ const result = new Map();
49
+ for (let index = 0; index < values.length; index += LOOKUP_CHUNK_SIZE) {
50
+ const chunkValues = values.slice(index, index + LOOKUP_CHUNK_SIZE);
51
+ const chunkHashes = hashes.slice(index, index + LOOKUP_CHUNK_SIZE);
52
+ const rows = await trx('text_refs')
53
+ .select('id', 'hash', 'text')
54
+ .whereIn('hash', chunkHashes)
55
+ .whereIn('text', chunkValues);
56
+ for (const row of rows) {
57
+ result.set(row.text, row.id);
58
+ }
59
+ }
60
+ return result;
61
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Routes one legacy URL-shaped value to either `url_refs` or `blob_refs`
3
+ * per the {@link isBlobRefValue} threshold rule, looking the id up in
4
+ * pre-resolved maps rather than upserting (the populate steps batch-resolve
5
+ * every distinct value across a whole chunk before this per-row lookup —
6
+ * see `resolveUrlRefs` / `resolveBlobRefs`). At most one of `url` / `blob`
7
+ * is non-null; both may be `null` when the value is null or fails to
8
+ * resolve (e.g. the `blob_refs` row is missing because the data URI was
9
+ * malformed and skipped by `populateBlobRefs`).
10
+ * @param value - Raw URL-shaped column value.
11
+ * @param urlIds - Map of URL string → `url_refs.id`.
12
+ * @param blobIds - Map of data-URI string → `blob_refs.id`.
13
+ * @returns `{ url, blob }` pair with at most one non-null field.
14
+ * @example
15
+ * const slot = resolveUrlOrBlobFromMaps(row.src, urlIds, blobIds);
16
+ * // slot.url set for a regular URL, slot.blob set for a large data: URI.
17
+ */
18
+ export declare function resolveUrlOrBlobFromMaps(value: string | null, urlIds: ReadonlyMap<string, number>, blobIds: ReadonlyMap<string, number>): {
19
+ url: number | null;
20
+ blob: number | null;
21
+ };
@@ -0,0 +1,27 @@
1
+ import { isBlobRefValue } from './is-blob-ref-value.js';
2
+ /**
3
+ * Routes one legacy URL-shaped value to either `url_refs` or `blob_refs`
4
+ * per the {@link isBlobRefValue} threshold rule, looking the id up in
5
+ * pre-resolved maps rather than upserting (the populate steps batch-resolve
6
+ * every distinct value across a whole chunk before this per-row lookup —
7
+ * see `resolveUrlRefs` / `resolveBlobRefs`). At most one of `url` / `blob`
8
+ * is non-null; both may be `null` when the value is null or fails to
9
+ * resolve (e.g. the `blob_refs` row is missing because the data URI was
10
+ * malformed and skipped by `populateBlobRefs`).
11
+ * @param value - Raw URL-shaped column value.
12
+ * @param urlIds - Map of URL string → `url_refs.id`.
13
+ * @param blobIds - Map of data-URI string → `blob_refs.id`.
14
+ * @returns `{ url, blob }` pair with at most one non-null field.
15
+ * @example
16
+ * const slot = resolveUrlOrBlobFromMaps(row.src, urlIds, blobIds);
17
+ * // slot.url set for a regular URL, slot.blob set for a large data: URI.
18
+ */
19
+ export function resolveUrlOrBlobFromMaps(value, urlIds, blobIds) {
20
+ if (typeof value !== 'string' || value === '') {
21
+ return { url: null, blob: null };
22
+ }
23
+ if (isBlobRefValue(value)) {
24
+ return { url: null, blob: blobIds.get(value) ?? null };
25
+ }
26
+ return { url: urlIds.get(value) ?? null, blob: null };
27
+ }
@@ -0,0 +1,33 @@
1
+ import type { UrlRefIdMap } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Batch-resolves `url_refs.id` for a set of URL strings (issue #193).
5
+ *
6
+ * Called by every entity populate helper that needs to translate a
7
+ * legacy URL string column into a `url_id` FK. Rather than open a
8
+ * separate SELECT per row (which multiplies migration wall-clock by
9
+ * chunk size × URL column count) the resolver collects the distinct
10
+ * strings the caller cares about, splits them into chunks of
11
+ * {@link LOOKUP_CHUNK_SIZE}, issues one `WHERE url IN (?, ...)` per
12
+ * chunk, and returns a single map.
13
+ *
14
+ * Missing entries fall out of the map — callers distinguish "URL is a
15
+ * large data URI routed to blob_refs" from "URL is absent from the
16
+ * dictionary" via a separate {@link ./resolve-blob-refs.ts} probe.
17
+ *
18
+ * Duplicates in `urls` are deduped internally so an image whose `src`
19
+ * and `currentSrc` are the same value only counts once against
20
+ * `LOOKUP_CHUNK_SIZE`.
21
+ * @param trx - Knex instance or transaction connected to the archive DB.
22
+ * @param urls - Iterable of URL strings to resolve. Empty and null-like
23
+ * values are ignored.
24
+ * @returns Map keyed by the raw URL string; missing entries indicate
25
+ * the URL is not present in `url_refs`.
26
+ * @example
27
+ * const idMap = await resolveUrlRefs(trx, [
28
+ * 'https://example.com/a',
29
+ * 'https://example.com/b',
30
+ * ]);
31
+ * const id = idMap.get('https://example.com/a'); // number | undefined
32
+ */
33
+ export declare function resolveUrlRefs(trx: Knex, urls: Iterable<string>): Promise<UrlRefIdMap>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Rows sent per `SELECT ... WHERE url IN (?, ...)` chunk. SQLite's default
3
+ * `SQLITE_MAX_VARIABLE_NUMBER` is 32766 on modern builds — 800 URLs per
4
+ * chunk stays well under that while amortising the round-trip cost across
5
+ * many rows.
6
+ */
7
+ const LOOKUP_CHUNK_SIZE = 800;
8
+ /**
9
+ * Batch-resolves `url_refs.id` for a set of URL strings (issue #193).
10
+ *
11
+ * Called by every entity populate helper that needs to translate a
12
+ * legacy URL string column into a `url_id` FK. Rather than open a
13
+ * separate SELECT per row (which multiplies migration wall-clock by
14
+ * chunk size × URL column count) the resolver collects the distinct
15
+ * strings the caller cares about, splits them into chunks of
16
+ * {@link LOOKUP_CHUNK_SIZE}, issues one `WHERE url IN (?, ...)` per
17
+ * chunk, and returns a single map.
18
+ *
19
+ * Missing entries fall out of the map — callers distinguish "URL is a
20
+ * large data URI routed to blob_refs" from "URL is absent from the
21
+ * dictionary" via a separate {@link ./resolve-blob-refs.ts} probe.
22
+ *
23
+ * Duplicates in `urls` are deduped internally so an image whose `src`
24
+ * and `currentSrc` are the same value only counts once against
25
+ * `LOOKUP_CHUNK_SIZE`.
26
+ * @param trx - Knex instance or transaction connected to the archive DB.
27
+ * @param urls - Iterable of URL strings to resolve. Empty and null-like
28
+ * values are ignored.
29
+ * @returns Map keyed by the raw URL string; missing entries indicate
30
+ * the URL is not present in `url_refs`.
31
+ * @example
32
+ * const idMap = await resolveUrlRefs(trx, [
33
+ * 'https://example.com/a',
34
+ * 'https://example.com/b',
35
+ * ]);
36
+ * const id = idMap.get('https://example.com/a'); // number | undefined
37
+ */
38
+ export async function resolveUrlRefs(trx, urls) {
39
+ const distinct = new Set();
40
+ for (const url of urls) {
41
+ if (typeof url === 'string' && url !== '') {
42
+ distinct.add(url);
43
+ }
44
+ }
45
+ if (distinct.size === 0) {
46
+ return new Map();
47
+ }
48
+ const values = [...distinct];
49
+ const result = new Map();
50
+ for (let index = 0; index < values.length; index += LOOKUP_CHUNK_SIZE) {
51
+ const chunk = values.slice(index, index + LOOKUP_CHUNK_SIZE);
52
+ const rows = await trx('url_refs')
53
+ .select('id', 'url')
54
+ .whereIn('url', chunk);
55
+ for (const row of rows) {
56
+ result.set(row.url, row.id);
57
+ }
58
+ }
59
+ return result;
60
+ }
@@ -0,0 +1,17 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Runs `SELECT count(<countColumn>) FROM <table>` and returns the count
4
+ * as a plain JS number. Kept in this package instead of importing from
5
+ * `populate-ref-tables/test-utils/count-rows.ts` because a spec in populate-entities importing
6
+ * from populate-refs test-utils would establish a cross-directory test-utils
7
+ * dependency; the populate-refs copy has the same shape but its default
8
+ * countColumn is scoped to that phase's tables. Duplication is minimal
9
+ * and keeps each phase's test scaffolding self-contained.
10
+ * @param db - Knex instance (typically a spec-local in-memory DB).
11
+ * @param table - Table name.
12
+ * @param countColumn - Column to count; defaults to `'*'` so this helper
13
+ * works uniformly across `content_items` / `page_meta` /
14
+ * `resource_ref_edges` (some of which do not have an `id` column).
15
+ * @returns Row count.
16
+ */
17
+ export declare function countRows(db: Knex, table: string, countColumn?: string): Promise<number>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Runs `SELECT count(<countColumn>) FROM <table>` and returns the count
3
+ * as a plain JS number. Kept in this package instead of importing from
4
+ * `populate-ref-tables/test-utils/count-rows.ts` because a spec in populate-entities importing
5
+ * from populate-refs test-utils would establish a cross-directory test-utils
6
+ * dependency; the populate-refs copy has the same shape but its default
7
+ * countColumn is scoped to that phase's tables. Duplication is minimal
8
+ * and keeps each phase's test scaffolding self-contained.
9
+ * @param db - Knex instance (typically a spec-local in-memory DB).
10
+ * @param table - Table name.
11
+ * @param countColumn - Column to count; defaults to `'*'` so this helper
12
+ * works uniformly across `content_items` / `page_meta` /
13
+ * `resource_ref_edges` (some of which do not have an `id` column).
14
+ * @returns Row count.
15
+ */
16
+ export async function countRows(db, table, countColumn = '*') {
17
+ const rows = await db(table).count({ n: countColumn });
18
+ const first = rows[0];
19
+ return Number(first.n);
20
+ }
@@ -0,0 +1,25 @@
1
+ import type knex from 'knex';
2
+ /**
3
+ * Inserts one row into each of `pages`, `url_refs`, and `content_items` per
4
+ * requested id so downstream inserts against tables that FK to
5
+ * `content_items(id)` (`anchor_edges`, `image_items`, `page_meta`, …) satisfy
6
+ * their foreign key without every spec re-authoring the same 20 lines of
7
+ * seed logic.
8
+ *
9
+ * Shared by 0.13 check specs (`check-anchor-edges-count.spec.ts`,
10
+ * `check-anchor-edges-sum.spec.ts`, `check-image-items-count.spec.ts`,
11
+ * `verify-migration.spec.ts`) so a schema change to
12
+ * pages/url_refs/content_items only requires editing one file. Kept in
13
+ * `populate-entity-tables/test-utils/` alongside {@link setup-entities-db.ts} because that
14
+ * module already provisions the tables this helper writes to.
15
+ *
16
+ * Rows are inserted with defaults suitable for count / structural
17
+ * invariants: `scraped=1`, `isTarget=1`, `isExternal=0`, `source='crawled'`,
18
+ * URL `https://example.com/<id>` and matching `url_refs` row on the same id
19
+ * so `url_id` binds 1:1 with `pages.id` (helpful for URL round-trip specs).
20
+ * @param db - Knex handle from {@link setupMigrationDb}.
21
+ * @param ids - Content-item ids to create. Ordering does not matter but the
22
+ * helper does not deduplicate the input list — callers should pass
23
+ * distinct ids.
24
+ */
25
+ export declare function seedContentItems(db: ReturnType<typeof knex>, ids: readonly number[]): Promise<void>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Inserts one row into each of `pages`, `url_refs`, and `content_items` per
3
+ * requested id so downstream inserts against tables that FK to
4
+ * `content_items(id)` (`anchor_edges`, `image_items`, `page_meta`, …) satisfy
5
+ * their foreign key without every spec re-authoring the same 20 lines of
6
+ * seed logic.
7
+ *
8
+ * Shared by 0.13 check specs (`check-anchor-edges-count.spec.ts`,
9
+ * `check-anchor-edges-sum.spec.ts`, `check-image-items-count.spec.ts`,
10
+ * `verify-migration.spec.ts`) so a schema change to
11
+ * pages/url_refs/content_items only requires editing one file. Kept in
12
+ * `populate-entity-tables/test-utils/` alongside {@link setup-entities-db.ts} because that
13
+ * module already provisions the tables this helper writes to.
14
+ *
15
+ * Rows are inserted with defaults suitable for count / structural
16
+ * invariants: `scraped=1`, `isTarget=1`, `isExternal=0`, `source='crawled'`,
17
+ * URL `https://example.com/<id>` and matching `url_refs` row on the same id
18
+ * so `url_id` binds 1:1 with `pages.id` (helpful for URL round-trip specs).
19
+ * @param db - Knex handle from {@link setupMigrationDb}.
20
+ * @param ids - Content-item ids to create. Ordering does not matter but the
21
+ * helper does not deduplicate the input list — callers should pass
22
+ * distinct ids.
23
+ */
24
+ export async function seedContentItems(db, ids) {
25
+ for (const id of ids) {
26
+ await db('pages').insert({
27
+ id,
28
+ url: `https://example.com/${id}`,
29
+ scraped: 1,
30
+ isTarget: 1,
31
+ });
32
+ await db('url_refs').insert({ id, url: `https://example.com/${id}` });
33
+ await db('content_items').insert({
34
+ id,
35
+ url_id: id,
36
+ is_external: 0,
37
+ scraped: 1,
38
+ is_target: 1,
39
+ source: 'crawled',
40
+ });
41
+ }
42
+ }