@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,48 @@
1
+ import { zstdCompressSync } from 'node:zlib';
2
+ import { computeContentHash } from '../../populate-ref-tables/compute-content-hash.js';
3
+ import { decodeDataUri } from '../../populate-ref-tables/decode-data-uri.js';
4
+ /**
5
+ * Resolves the `blob_refs.id` for one large `data:` URI, decoding and
6
+ * inserting the deduplicated (hash-keyed) payload row when it is new.
7
+ *
8
+ * Callers route values here per the data-URI threshold rule (`data:` URIs
9
+ * longer than `DATA_URI_URL_REFS_LIMIT` — everything else belongs in
10
+ * `url_refs`). The payload bytes are decoded from the base64 /
11
+ * percent-encoded tail, hashed, and stored zstd-compressed — the same
12
+ * storage contract as the rows produced when migrating an existing
13
+ * archive (`populate-ref-tables/populate-blob-refs.ts`).
14
+ *
15
+ * Returns `null` when the data URI fails to decode (malformed base64 /
16
+ * truncated percent escape). A single malformed image `src` must not
17
+ * abort the page write; the caller stores `*_blob_id = null` and the
18
+ * information loss is limited to that one slot.
19
+ * @param qb - Knex instance or transaction connected to the archive DB.
20
+ * @param caches - The connection's write-side id caches; mutated in place.
21
+ * @param dataUri - The raw `data:` URI value.
22
+ * @returns The `blob_refs.id`, or `null` when the URI cannot be decoded.
23
+ * @example
24
+ * const id = await upsertBlobRef(knex, caches, 'data:image/png;base64,<payload>');
25
+ */
26
+ export async function upsertBlobRef(qb, caches, dataUri) {
27
+ const decoded = decodeDataUri(dataUri);
28
+ if (decoded === null) {
29
+ return null;
30
+ }
31
+ const hash = computeContentHash(decoded.bytes);
32
+ const hex = hash.toString('hex');
33
+ const cached = caches.blobIds.get(hex);
34
+ if (cached !== undefined) {
35
+ return cached;
36
+ }
37
+ const compressed = zstdCompressSync(decoded.bytes);
38
+ const rows = await qb.raw(`INSERT INTO blob_refs (hash, body, codec, size_raw, size_stored)
39
+ VALUES (?, ?, 'zstd', ?, ?)
40
+ ON CONFLICT(hash) DO UPDATE SET hash = hash
41
+ RETURNING id`, [hash, compressed, decoded.bytes.byteLength, compressed.byteLength]);
42
+ const first = rows[0];
43
+ if (first === undefined) {
44
+ throw new Error('upsertBlobRef: RETURNING yielded no row');
45
+ }
46
+ caches.blobIds.set(hex, first.id);
47
+ return first.id;
48
+ }
@@ -0,0 +1,30 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Resolves the `content_type_refs.id` for one content-type value,
5
+ * inserting the dictionary row when the value is new.
6
+ *
7
+ * `raw` is expected to be the already-canonicalised form produced by
8
+ * `normalizeContentType` (lower-cased, trimmed) — the write path
9
+ * canonicalises before storing, exactly as the legacy `pages.contentType`
10
+ * column did, so the dictionary's natural key never forks on case or
11
+ * whitespace variants of the same MIME. `normalized` is derived via the
12
+ * shared {@link ../../populate-ref-tables/normalize-mime.ts} (the same
13
+ * derivation the archive-migration populate uses, so live-crawl rows and
14
+ * migrated rows never fork on the same raw value); `category` via
15
+ * {@link ../../populate-ref-tables/classify-content-type.ts}.
16
+ *
17
+ * The content-type space is tiny (well under 1 000 distinct values on
18
+ * even the largest archives) so the cache converges after the first few
19
+ * pages and steady-state calls issue no SQL.
20
+ * @param qb - Knex instance or transaction connected to the archive DB.
21
+ * @param caches - The connection's write-side id caches; mutated in place.
22
+ * @param raw - Canonicalised content-type value. Pass the output of
23
+ * `normalizeContentType`; `null` / empty input must be short-circuited
24
+ * by the caller (a row without a content type stores `content_type_id
25
+ * = null`, not a dictionary entry).
26
+ * @returns The `content_type_refs.id` of the existing or new row.
27
+ * @example
28
+ * const id = await upsertContentTypeRef(knex, caches, 'text/html');
29
+ */
30
+ export declare function upsertContentTypeRef(qb: Knex | Knex.Transaction, caches: WriteRefCaches, raw: string): Promise<number>;
@@ -0,0 +1,45 @@
1
+ import { classifyContentType } from '../../populate-ref-tables/classify-content-type.js';
2
+ import { normalizeMime } from '../../populate-ref-tables/normalize-mime.js';
3
+ /**
4
+ * Resolves the `content_type_refs.id` for one content-type value,
5
+ * inserting the dictionary row when the value is new.
6
+ *
7
+ * `raw` is expected to be the already-canonicalised form produced by
8
+ * `normalizeContentType` (lower-cased, trimmed) — the write path
9
+ * canonicalises before storing, exactly as the legacy `pages.contentType`
10
+ * column did, so the dictionary's natural key never forks on case or
11
+ * whitespace variants of the same MIME. `normalized` is derived via the
12
+ * shared {@link ../../populate-ref-tables/normalize-mime.ts} (the same
13
+ * derivation the archive-migration populate uses, so live-crawl rows and
14
+ * migrated rows never fork on the same raw value); `category` via
15
+ * {@link ../../populate-ref-tables/classify-content-type.ts}.
16
+ *
17
+ * The content-type space is tiny (well under 1 000 distinct values on
18
+ * even the largest archives) so the cache converges after the first few
19
+ * pages and steady-state calls issue no SQL.
20
+ * @param qb - Knex instance or transaction connected to the archive DB.
21
+ * @param caches - The connection's write-side id caches; mutated in place.
22
+ * @param raw - Canonicalised content-type value. Pass the output of
23
+ * `normalizeContentType`; `null` / empty input must be short-circuited
24
+ * by the caller (a row without a content type stores `content_type_id
25
+ * = null`, not a dictionary entry).
26
+ * @returns The `content_type_refs.id` of the existing or new row.
27
+ * @example
28
+ * const id = await upsertContentTypeRef(knex, caches, 'text/html');
29
+ */
30
+ export async function upsertContentTypeRef(qb, caches, raw) {
31
+ const cached = caches.contentTypeIds.get(raw);
32
+ if (cached !== undefined) {
33
+ return cached;
34
+ }
35
+ const rows = await qb.raw(`INSERT INTO content_type_refs (raw, normalized, category)
36
+ VALUES (?, ?, ?)
37
+ ON CONFLICT(raw) DO UPDATE SET raw = raw
38
+ RETURNING id`, [raw, normalizeMime(raw), classifyContentType(raw)]);
39
+ const first = rows[0];
40
+ if (first === undefined) {
41
+ throw new Error(`upsertContentTypeRef: RETURNING yielded no row for ${raw}`);
42
+ }
43
+ caches.contentTypeIds.set(raw, first.id);
44
+ return first.id;
45
+ }
@@ -0,0 +1,22 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Resolves the `json_refs.id` for one JSON payload string, inserting the
5
+ * deduplicated (hash-keyed) row when the payload is new.
6
+ *
7
+ * The payload is hashed over its raw UTF-8 bytes and stored
8
+ * zstd-compressed (`codec = 'zstd'`) with `size_raw` / `size_stored`
9
+ * bookkeeping — the same storage contract as `page_html_blobs` and the
10
+ * dictionary rows produced when migrating an existing archive
11
+ * (`populate-ref-tables/populate-json-refs.ts`). Identical payloads
12
+ * (e.g. the same `meta_extras` JSON across templated pages) produce one
13
+ * row; the compression cost is paid only on a cache-and-DB miss.
14
+ * @param qb - Knex instance or transaction connected to the archive DB.
15
+ * @param caches - The connection's write-side id caches; mutated in place.
16
+ * @param jsonText - Raw JSON string. Empty / null input must be
17
+ * short-circuited by the caller (`*_json_id = null`).
18
+ * @returns The `json_refs.id` of the existing or new row.
19
+ * @example
20
+ * const id = await upsertJsonRef(knex, caches, JSON.stringify(extras));
21
+ */
22
+ export declare function upsertJsonRef(qb: Knex | Knex.Transaction, caches: WriteRefCaches, jsonText: string): Promise<number>;
@@ -0,0 +1,41 @@
1
+ import { zstdCompressSync } from 'node:zlib';
2
+ import { computeContentHash } from '../../populate-ref-tables/compute-content-hash.js';
3
+ /**
4
+ * Resolves the `json_refs.id` for one JSON payload string, inserting the
5
+ * deduplicated (hash-keyed) row when the payload is new.
6
+ *
7
+ * The payload is hashed over its raw UTF-8 bytes and stored
8
+ * zstd-compressed (`codec = 'zstd'`) with `size_raw` / `size_stored`
9
+ * bookkeeping — the same storage contract as `page_html_blobs` and the
10
+ * dictionary rows produced when migrating an existing archive
11
+ * (`populate-ref-tables/populate-json-refs.ts`). Identical payloads
12
+ * (e.g. the same `meta_extras` JSON across templated pages) produce one
13
+ * row; the compression cost is paid only on a cache-and-DB miss.
14
+ * @param qb - Knex instance or transaction connected to the archive DB.
15
+ * @param caches - The connection's write-side id caches; mutated in place.
16
+ * @param jsonText - Raw JSON string. Empty / null input must be
17
+ * short-circuited by the caller (`*_json_id = null`).
18
+ * @returns The `json_refs.id` of the existing or new row.
19
+ * @example
20
+ * const id = await upsertJsonRef(knex, caches, JSON.stringify(extras));
21
+ */
22
+ export async function upsertJsonRef(qb, caches, jsonText) {
23
+ const rawBytes = Buffer.from(jsonText, 'utf8');
24
+ const hash = computeContentHash(rawBytes);
25
+ const hex = hash.toString('hex');
26
+ const cached = caches.jsonIds.get(hex);
27
+ if (cached !== undefined) {
28
+ return cached;
29
+ }
30
+ const compressed = zstdCompressSync(rawBytes);
31
+ const rows = await qb.raw(`INSERT INTO json_refs (hash, json_text, codec, size_raw, size_stored)
32
+ VALUES (?, ?, 'zstd', ?, ?)
33
+ ON CONFLICT(hash) DO UPDATE SET hash = hash
34
+ RETURNING id`, [hash, compressed, rawBytes.byteLength, compressed.byteLength]);
35
+ const first = rows[0];
36
+ if (first === undefined) {
37
+ throw new Error('upsertJsonRef: RETURNING yielded no row');
38
+ }
39
+ caches.jsonIds.set(hex, first.id);
40
+ return first.id;
41
+ }
@@ -0,0 +1,35 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Decomposes one response's headers and upserts them into the header
5
+ * dictionary tables (`header_name_refs` / `header_value_refs` /
6
+ * `header_sets` / `header_set_entries` / `header_flags`), returning the
7
+ * `header_sets.id` to store on the referring `content_items` /
8
+ * `resource_items` row.
9
+ *
10
+ * Runs per response (incrementally during the crawl), not deferred to
11
+ * crawl-end — the header dictionaries grow as pages arrive and the
12
+ * entity row can reference its `header_set_id` in the same write.
13
+ * Dedup makes the steady-state cost small: CDN-served sites reuse a
14
+ * handful of header shapes, so after the first few pages most calls
15
+ * resolve entirely from the in-process caches (warmed lazily from the DB
16
+ * on the first call, so a crawl that never sees headers pays nothing).
17
+ *
18
+ * Returns `null` for responses with no headers (`null` / `{}` /
19
+ * unparsable) — the referring row stores `header_set_id = null`, which
20
+ * is exactly how such rows read back through `header_flags` joins
21
+ * (absent flags row = no headers recorded).
22
+ * @param qb - Knex instance or transaction connected to the archive DB.
23
+ * @param caches - The connection's write-side id caches; the header
24
+ * sub-caches are created and attached on first use.
25
+ * @param responseHeaders - The response's header record as delivered by
26
+ * the scraper (`Record<name, string | string[]>`), or `null` /
27
+ * `undefined` when the response carried none.
28
+ * @returns The `header_sets.id`, or `null` when there is no set to store.
29
+ * @example
30
+ * const headerSetId = await upsertResponseHeaders(knex, caches, {
31
+ * 'content-type': 'text/html',
32
+ * 'cache-control': 'no-store',
33
+ * });
34
+ */
35
+ export declare function upsertResponseHeaders(qb: Knex | Knex.Transaction, caches: WriteRefCaches, responseHeaders: Record<string, string | string[] | undefined> | null | undefined): Promise<number | null>;
@@ -0,0 +1,49 @@
1
+ import { createHeaderTableCaches } from '../../populate-ref-tables/create-header-table-caches.js';
2
+ import { decomposeHeaderSet } from '../../populate-ref-tables/decompose-header-set.js';
3
+ import { upsertOneHeaderSet } from '../../populate-ref-tables/upsert-one-header-set.js';
4
+ /**
5
+ * Decomposes one response's headers and upserts them into the header
6
+ * dictionary tables (`header_name_refs` / `header_value_refs` /
7
+ * `header_sets` / `header_set_entries` / `header_flags`), returning the
8
+ * `header_sets.id` to store on the referring `content_items` /
9
+ * `resource_items` row.
10
+ *
11
+ * Runs per response (incrementally during the crawl), not deferred to
12
+ * crawl-end — the header dictionaries grow as pages arrive and the
13
+ * entity row can reference its `header_set_id` in the same write.
14
+ * Dedup makes the steady-state cost small: CDN-served sites reuse a
15
+ * handful of header shapes, so after the first few pages most calls
16
+ * resolve entirely from the in-process caches (warmed lazily from the DB
17
+ * on the first call, so a crawl that never sees headers pays nothing).
18
+ *
19
+ * Returns `null` for responses with no headers (`null` / `{}` /
20
+ * unparsable) — the referring row stores `header_set_id = null`, which
21
+ * is exactly how such rows read back through `header_flags` joins
22
+ * (absent flags row = no headers recorded).
23
+ * @param qb - Knex instance or transaction connected to the archive DB.
24
+ * @param caches - The connection's write-side id caches; the header
25
+ * sub-caches are created and attached on first use.
26
+ * @param responseHeaders - The response's header record as delivered by
27
+ * the scraper (`Record<name, string | string[]>`), or `null` /
28
+ * `undefined` when the response carried none.
29
+ * @returns The `header_sets.id`, or `null` when there is no set to store.
30
+ * @example
31
+ * const headerSetId = await upsertResponseHeaders(knex, caches, {
32
+ * 'content-type': 'text/html',
33
+ * 'cache-control': 'no-store',
34
+ * });
35
+ */
36
+ export async function upsertResponseHeaders(qb, caches, responseHeaders) {
37
+ // Serialise exactly like the scraper's own JSON storage form so the
38
+ // decomposition (and its raw_json_hash dedup key) matches what a bulk
39
+ // archive populate would compute for the same response.
40
+ const rawJson = responseHeaders == null ? null : JSON.stringify(responseHeaders);
41
+ const decomposed = decomposeHeaderSet(rawJson);
42
+ if (decomposed === null) {
43
+ return null;
44
+ }
45
+ if (caches.headers === null) {
46
+ caches.headers = await createHeaderTableCaches(qb);
47
+ }
48
+ return await upsertOneHeaderSet(qb, decomposed, caches.headers);
49
+ }
@@ -0,0 +1,39 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Resolves the `url_refs.id` for one URL string, inserting the row (with
5
+ * its decomposed `scheme` / `host` / `port` / `path` / `query_hash` /
6
+ * `fragment` columns) when the URL is not yet in the dictionary.
7
+ *
8
+ * The write path calls this for every URL it touches — page identities,
9
+ * anchor hrefs, image `src` / `currentSrc`, resource URLs, and URL-shaped
10
+ * meta columns — so the result is cached in `caches.urlIds` and a cache
11
+ * hit issues no SQL at all. Anchor hrefs and CDN-hosted image URLs repeat
12
+ * across thousands of pages, so the hit rate dominates after the first
13
+ * few pages.
14
+ *
15
+ * The cache-miss upsert is a single statement:
16
+ *
17
+ * ```sql
18
+ * INSERT INTO url_refs (...) VALUES (...)
19
+ * ON CONFLICT(url) DO UPDATE SET url = url
20
+ * RETURNING id
21
+ * ```
22
+ *
23
+ * `DO UPDATE SET url = url` is a deliberate no-op update whose only
24
+ * purpose is to make `RETURNING id` yield the existing row's id on
25
+ * conflict. `INSERT OR IGNORE ... RETURNING id` must NOT be used here:
26
+ * `RETURNING` returns zero rows when the insert is ignored, which would
27
+ * leave the caller with no id and a `null` FK on the next entity INSERT.
28
+ * @param qb - Knex instance or transaction connected to the archive DB.
29
+ * Callers inside a transaction MUST pass the `trx` so the read sees
30
+ * uncommitted rows from the same transaction.
31
+ * @param caches - The connection's write-side id caches; mutated in place.
32
+ * @param url - The URL string to resolve. Stored verbatim as the natural key.
33
+ * @returns The `url_refs.id` of the existing or newly inserted row.
34
+ * @example
35
+ * const id = await upsertUrlRef(knex, caches, 'https://example.com/a');
36
+ * const again = await upsertUrlRef(knex, caches, 'https://example.com/a');
37
+ * // id === again, second call hits the cache and issues no SQL
38
+ */
39
+ export declare function upsertUrlRef(qb: Knex | Knex.Transaction, caches: WriteRefCaches, url: string): Promise<number>;
@@ -0,0 +1,62 @@
1
+ import { decomposeUrl } from '../../populate-ref-tables/decompose-url.js';
2
+ /**
3
+ * Resolves the `url_refs.id` for one URL string, inserting the row (with
4
+ * its decomposed `scheme` / `host` / `port` / `path` / `query_hash` /
5
+ * `fragment` columns) when the URL is not yet in the dictionary.
6
+ *
7
+ * The write path calls this for every URL it touches — page identities,
8
+ * anchor hrefs, image `src` / `currentSrc`, resource URLs, and URL-shaped
9
+ * meta columns — so the result is cached in `caches.urlIds` and a cache
10
+ * hit issues no SQL at all. Anchor hrefs and CDN-hosted image URLs repeat
11
+ * across thousands of pages, so the hit rate dominates after the first
12
+ * few pages.
13
+ *
14
+ * The cache-miss upsert is a single statement:
15
+ *
16
+ * ```sql
17
+ * INSERT INTO url_refs (...) VALUES (...)
18
+ * ON CONFLICT(url) DO UPDATE SET url = url
19
+ * RETURNING id
20
+ * ```
21
+ *
22
+ * `DO UPDATE SET url = url` is a deliberate no-op update whose only
23
+ * purpose is to make `RETURNING id` yield the existing row's id on
24
+ * conflict. `INSERT OR IGNORE ... RETURNING id` must NOT be used here:
25
+ * `RETURNING` returns zero rows when the insert is ignored, which would
26
+ * leave the caller with no id and a `null` FK on the next entity INSERT.
27
+ * @param qb - Knex instance or transaction connected to the archive DB.
28
+ * Callers inside a transaction MUST pass the `trx` so the read sees
29
+ * uncommitted rows from the same transaction.
30
+ * @param caches - The connection's write-side id caches; mutated in place.
31
+ * @param url - The URL string to resolve. Stored verbatim as the natural key.
32
+ * @returns The `url_refs.id` of the existing or newly inserted row.
33
+ * @example
34
+ * const id = await upsertUrlRef(knex, caches, 'https://example.com/a');
35
+ * const again = await upsertUrlRef(knex, caches, 'https://example.com/a');
36
+ * // id === again, second call hits the cache and issues no SQL
37
+ */
38
+ export async function upsertUrlRef(qb, caches, url) {
39
+ const cached = caches.urlIds.get(url);
40
+ if (cached !== undefined) {
41
+ return cached;
42
+ }
43
+ const decomposed = decomposeUrl(url);
44
+ const rows = await qb.raw(`INSERT INTO url_refs (url, scheme, host, port, path, query_hash, fragment)
45
+ VALUES (?, ?, ?, ?, ?, ?, ?)
46
+ ON CONFLICT(url) DO UPDATE SET url = url
47
+ RETURNING id`, [
48
+ url,
49
+ decomposed.scheme,
50
+ decomposed.host,
51
+ decomposed.port,
52
+ decomposed.path,
53
+ decomposed.query_hash,
54
+ decomposed.fragment,
55
+ ]);
56
+ const first = rows[0];
57
+ if (first === undefined) {
58
+ throw new Error(`upsertUrlRef: RETURNING yielded no row for ${url}`);
59
+ }
60
+ caches.urlIds.set(url, first.id);
61
+ return first.id;
62
+ }
@@ -0,0 +1,28 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Replaces the stored analysis violations with a freshly generated set.
4
+ *
5
+ * The function resolves every violation URL to a `content_items.id` (via
6
+ * the `url_refs` dictionary), deduplicates message/code text through
7
+ * `analysis_text_refs`, and rewrites `analysis_violations` in one
8
+ * transaction. This is the storage-side counterpart of the query-layer
9
+ * `getViolations` read path.
10
+ *
11
+ * Violations whose `url` still carries the pre-#225 corrupted position
12
+ * suffix (and that do not already carry explicit `line`/`col`) are repaired
13
+ * in place: {@link parseLegacyCorruptedUrl} splits the suffix off before URL
14
+ * resolution, so both freshly-analyzed and legacy-JSON-backfilled data end
15
+ * up in the same clean shape.
16
+ * @param knex - Knex query builder connected to the archive DB.
17
+ * @param violations - Flat violation list from the analyze phase.
18
+ */
19
+ export declare function replaceAnalysisViolations(knex: Knex, violations: readonly {
20
+ validator: string;
21
+ severity: string;
22
+ rule: string;
23
+ code?: string | null;
24
+ message: string;
25
+ url: string;
26
+ line?: number | null;
27
+ col?: number | null;
28
+ }[]): Promise<void>;
@@ -0,0 +1,152 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { eachSplitted } from '../../../utils/array/each-splitted.js';
3
+ /**
4
+ * Matches the position suffix that `@nitpicker/analyze-markuplint` and
5
+ * `@nitpicker/analyze-textlint` used to append to `Violation.url` before
6
+ * issue #225 (e.g. `https://example.com/page (5:10)`). Anchored to the end
7
+ * of the string so a URL that legitimately contains a similar-looking
8
+ * substring earlier on is not mistaken for the suffix.
9
+ */
10
+ const LEGACY_CORRUPTED_URL_PATTERN = /^(.+) \((\d+):(\d+)\)$/;
11
+ /**
12
+ * Splits a legacy corrupted `Violation.url` (page URL + trailing
13
+ * `" (line:col)"`) back into a clean URL and its position, or returns
14
+ * `null` when `url` does not match the corrupted shape (e.g. a clean axe
15
+ * URL, or a URL that already carries explicit `line`/`col`).
16
+ * @param url - The `Violation.url` value as read from storage or input.
17
+ * @returns The recovered `{ url, line, col }`, or `null` when `url` is not corrupted.
18
+ */
19
+ function parseLegacyCorruptedUrl(url) {
20
+ const match = LEGACY_CORRUPTED_URL_PATTERN.exec(url);
21
+ if (!match) {
22
+ return null;
23
+ }
24
+ const [, cleanUrl, line, col] = match;
25
+ return { url: cleanUrl, line: Number(line), col: Number(col) };
26
+ }
27
+ /**
28
+ * Adds the `line`/`col` columns to `analysis_violations` when they are
29
+ * missing, i.e. when the archive's table was provisioned before issue #225.
30
+ *
31
+ * `create-adjunct-tables.ts` never mutates a table it finds already
32
+ * present — catching up an *existing* table normally requires a
33
+ * version-gated migration script (see `scripts/migrate-to-0.13.mjs`). This
34
+ * function relies on the exception documented in ARCHITECTURE.md's
35
+ * invariants list: a nullable, additive column on a table with a single
36
+ * write path may self-heal here without a version bump, because
37
+ * `replaceAnalysisViolations` is that single write path for
38
+ * `analysis_violations`. Runs before the transaction below so the DDL is
39
+ * not mixed with the DML rewrite.
40
+ * @param knex - Knex query builder connected to the archive DB.
41
+ */
42
+ async function ensureLineColColumns(knex) {
43
+ if (!(await knex.schema.hasColumn('analysis_violations', 'line'))) {
44
+ await knex.schema.alterTable('analysis_violations', (table) => {
45
+ table.integer('line').nullable();
46
+ });
47
+ }
48
+ if (!(await knex.schema.hasColumn('analysis_violations', 'col'))) {
49
+ await knex.schema.alterTable('analysis_violations', (table) => {
50
+ table.integer('col').nullable();
51
+ });
52
+ }
53
+ }
54
+ /**
55
+ * Replaces the stored analysis violations with a freshly generated set.
56
+ *
57
+ * The function resolves every violation URL to a `content_items.id` (via
58
+ * the `url_refs` dictionary), deduplicates message/code text through
59
+ * `analysis_text_refs`, and rewrites `analysis_violations` in one
60
+ * transaction. This is the storage-side counterpart of the query-layer
61
+ * `getViolations` read path.
62
+ *
63
+ * Violations whose `url` still carries the pre-#225 corrupted position
64
+ * suffix (and that do not already carry explicit `line`/`col`) are repaired
65
+ * in place: {@link parseLegacyCorruptedUrl} splits the suffix off before URL
66
+ * resolution, so both freshly-analyzed and legacy-JSON-backfilled data end
67
+ * up in the same clean shape.
68
+ * @param knex - Knex query builder connected to the archive DB.
69
+ * @param violations - Flat violation list from the analyze phase.
70
+ */
71
+ export async function replaceAnalysisViolations(knex, violations) {
72
+ await ensureLineColColumns(knex);
73
+ const repaired = violations.map((violation) => {
74
+ if (violation.line != null || violation.col != null) {
75
+ return violation;
76
+ }
77
+ const parsed = parseLegacyCorruptedUrl(violation.url);
78
+ return parsed
79
+ ? { ...violation, url: parsed.url, line: parsed.line, col: parsed.col }
80
+ : violation;
81
+ });
82
+ await knex.transaction(async (trx) => {
83
+ await trx('analysis_violations').delete();
84
+ await trx('analysis_text_refs').delete();
85
+ if (repaired.length === 0) {
86
+ return;
87
+ }
88
+ const urls = [...new Set(repaired.map((v) => v.url))];
89
+ const pageIdByUrl = new Map();
90
+ await eachSplitted(urls, 500, async (chunk) => {
91
+ const pageRows = await trx('content_items')
92
+ .join('url_refs', 'url_refs.id', 'content_items.url_id')
93
+ .select('content_items.id as id', 'url_refs.url as url')
94
+ .whereIn('url_refs.url', chunk);
95
+ for (const row of pageRows) {
96
+ pageIdByUrl.set(row.url, row.id);
97
+ }
98
+ });
99
+ if (pageIdByUrl.size !== urls.length) {
100
+ const missing = urls.filter((url) => !pageIdByUrl.has(url));
101
+ throw new Error(`replaceAnalysisViolations: could not resolve ${missing.length} page URL(s): ${missing[0]}`);
102
+ }
103
+ const textByValue = new Map();
104
+ const resolveTextId = async (text) => {
105
+ const cached = textByValue.get(text);
106
+ if (cached != null) {
107
+ return cached;
108
+ }
109
+ const sha256 = createHash('sha256').update(text).digest('hex');
110
+ await trx('analysis_text_refs')
111
+ .insert({ text, sha256 })
112
+ .onConflict(['sha256', 'text'])
113
+ .ignore();
114
+ const [existing] = await trx
115
+ .select('id')
116
+ .from('analysis_text_refs')
117
+ .where('sha256', sha256)
118
+ .where('text', text);
119
+ if (!existing) {
120
+ throw new Error(`replaceAnalysisViolations: failed to resolve text ref for ${text}`);
121
+ }
122
+ textByValue.set(text, existing.id);
123
+ return existing.id;
124
+ };
125
+ const rows = [];
126
+ for (const violation of repaired) {
127
+ const pageId = pageIdByUrl.get(violation.url);
128
+ if (!pageId) {
129
+ throw new Error(`replaceAnalysisViolations: could not resolve page URL: ${violation.url}`);
130
+ }
131
+ const messageTextId = await resolveTextId(violation.message);
132
+ const codeValue = violation.code ?? '';
133
+ const codeTextId = codeValue === '' ? null : await resolveTextId(codeValue);
134
+ rows.push({
135
+ page_id: pageId,
136
+ validator: violation.validator,
137
+ severity: violation.severity,
138
+ rule: violation.rule,
139
+ message_text_id: messageTextId,
140
+ line: violation.line ?? null,
141
+ col: violation.col ?? null,
142
+ code_text_id: codeTextId,
143
+ page_url_sort_key: violation.url,
144
+ message_sort_key: violation.message,
145
+ code_sort_key: codeValue,
146
+ });
147
+ }
148
+ await eachSplitted(rows, 500, async (chunk) => {
149
+ await trx('analysis_violations').insert(chunk);
150
+ });
151
+ });
152
+ }
@@ -0,0 +1,10 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Retrieves all anchors (outgoing links) on a specific page.
4
+ * Joins `anchor_edges` with `content_items` (+ `page_meta` for title) to
5
+ * resolve link destinations.
6
+ * @param knex - Knex query builder connected to the archive DB.
7
+ * @param pageId - The database ID of the page whose anchors to retrieve.
8
+ * @returns An array of anchor records with resolved URL, title, status, and content type.
9
+ */
10
+ export declare function getAnchorsOnPage(knex: Knex, pageId: number): Promise<any[]>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Retrieves all anchors (outgoing links) on a specific page.
3
+ * Joins `anchor_edges` with `content_items` (+ `page_meta` for title) to
4
+ * resolve link destinations.
5
+ * @param knex - Knex query builder connected to the archive DB.
6
+ * @param pageId - The database ID of the page whose anchors to retrieve.
7
+ * @returns An array of anchor records with resolved URL, title, status, and content type.
8
+ */
9
+ export async function getAnchorsOnPage(knex, pageId) {
10
+ const res = await knex
11
+ .select('dest_url.url as url', 'title_ref.text as title', 'dest.status as status', 'dest.status_text as statusText', 'ctr.raw as contentType', 'anchor_edges.first_hash as hash', 'first_text.text as textContent')
12
+ .from('anchor_edges')
13
+ .join('content_items as dest', 'anchor_edges.href_page_id', '=', 'dest.id')
14
+ .join('url_refs as dest_url', 'dest.url_id', '=', 'dest_url.id')
15
+ .leftJoin('content_type_refs as ctr', 'dest.content_type_id', '=', 'ctr.id')
16
+ .leftJoin('page_meta as pm', 'dest.id', '=', 'pm.page_id')
17
+ .leftJoin('text_refs as title_ref', 'pm.title_text_id', '=', 'title_ref.id')
18
+ .leftJoin('text_refs as first_text', 'anchor_edges.first_text_id', '=', 'first_text.id')
19
+ .where('anchor_edges.page_id', pageId);
20
+ return res;
21
+ }
@@ -0,0 +1,8 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Retrieves the base URL of the crawl session from the `info` table.
4
+ * @param knex - Knex query builder connected to the archive DB.
5
+ * @returns The base URL string.
6
+ * @throws {Error} If no base URL is found in the database.
7
+ */
8
+ export declare function getBaseUrl(knex: Knex): Promise<string>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Retrieves the base URL of the crawl session from the `info` table.
3
+ * @param knex - Knex query builder connected to the archive DB.
4
+ * @returns The base URL string.
5
+ * @throws {Error} If no base URL is found in the database.
6
+ */
7
+ export async function getBaseUrl(knex) {
8
+ const selected = await knex.select('baseUrl').from('info');
9
+ if (!selected[0]) {
10
+ throw new Error('No baseUrl');
11
+ }
12
+ const [{ baseUrl }] = selected;
13
+ return baseUrl || '';
14
+ }
@@ -0,0 +1,10 @@
1
+ import type { Config } from '../../types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Retrieves the full crawl configuration from the `info` table.
5
+ * Deserializes JSON-encoded fields (`roots`, `excludes`, `excludeKeywords`, `excludeUrls`).
6
+ * @param knex - Knex query builder connected to the archive DB.
7
+ * @returns The parsed {@link Config} object.
8
+ * @throws {Error} If no configuration is found in the database.
9
+ */
10
+ export declare function getConfig(knex: Knex): Promise<Config>;
@@ -0,0 +1,27 @@
1
+ import { dbLog } from '../../debug.js';
2
+ import { getJSON } from '../../get-json.js';
3
+ /**
4
+ * Retrieves the full crawl configuration from the `info` table.
5
+ * Deserializes JSON-encoded fields (`roots`, `excludes`, `excludeKeywords`, `excludeUrls`).
6
+ * @param knex - Knex query builder connected to the archive DB.
7
+ * @returns The parsed {@link Config} object.
8
+ * @throws {Error} If no configuration is found in the database.
9
+ */
10
+ export async function getConfig(knex) {
11
+ const [config] = await knex.select('*').from('info');
12
+ if (!config) {
13
+ throw new Error('No config');
14
+ }
15
+ const opt = {
16
+ ...config,
17
+ excludes: getJSON(config.excludes, []),
18
+ excludeKeywords: getJSON(config.excludeKeywords, []),
19
+ excludeUrls: getJSON(config.excludeUrls, []),
20
+ roots: getJSON(config.roots, []),
21
+ retry: config.retry ?? 3,
22
+ };
23
+ // @ts-expect-error — `id` is the primary key, not part of the public Config shape
24
+ delete opt.id;
25
+ dbLog('Table `info`: %O => %O', config, opt);
26
+ return opt;
27
+ }