@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,13 @@
1
+ /**
2
+ * Retrieves a flat list of all resource URLs from the `resource_items`
3
+ * table. URL text is normalised into `url_refs`, so the read joins the
4
+ * two tables and returns the resolved strings.
5
+ * @param knex - Knex query builder connected to the archive DB.
6
+ * @returns An array of resource URL strings.
7
+ */
8
+ export async function getResourceUrlList(knex) {
9
+ const res = await knex('resource_items')
10
+ .join('url_refs', 'url_refs.id', 'resource_items.url_id')
11
+ .select('url_refs.url as url');
12
+ return res.map((r) => r.url);
13
+ }
@@ -0,0 +1,8 @@
1
+ import type { DB_Resource } from '../../types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Retrieves all sub-resources.
5
+ * @param knex - Knex query builder connected to the archive DB.
6
+ * @returns An array of reconstructed {@link DB_Resource} rows.
7
+ */
8
+ export declare function getResources(knex: Knex): Promise<DB_Resource[]>;
@@ -0,0 +1,11 @@
1
+ import { buildResourceQuery } from './build-resource-query.js';
2
+ import { reconstructResourceRows } from './reconstruct-resource-rows.js';
3
+ /**
4
+ * Retrieves all sub-resources.
5
+ * @param knex - Knex query builder connected to the archive DB.
6
+ * @returns An array of reconstructed {@link DB_Resource} rows.
7
+ */
8
+ export async function getResources(knex) {
9
+ const rows = await buildResourceQuery(knex);
10
+ return reconstructResourceRows(knex, rows);
11
+ }
@@ -0,0 +1,24 @@
1
+ import type { WriteRefCaches } from '../_shared/types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Pre-insert inventory non-HTML URLs into `resource_items` as placeholder
5
+ * rows with `source = 'inventory-seed'` and all metadata columns NULL —
6
+ * the non-HTML counterpart of `insertInventorySeeds`. Used by
7
+ * `CrawlerOrchestrator.inventory` so the ingestion phase commits all of
8
+ * its non-HTML URLs in one chunked round-trip per 500 instead of N
9
+ * sequential `insertResource` awaits — a per-URL loop would spend
10
+ * minutes inside the `.bak`-protected window on a 50k-URL inventory
11
+ * list, where the bulk path finishes in seconds.
12
+ *
13
+ * Idempotent: both the `url_refs` and `resource_items` inserts are
14
+ * `ON CONFLICT ... IGNORE`, leaving existing rows untouched (the
15
+ * orchestrator's `getExistingResourceUrls` filter is what keeps a
16
+ * crawled-lineage `resource_items` row from being downgraded to the
17
+ * inventory label here).
18
+ *
19
+ * Chunked at 500 to stay well under SQLite's `SQLITE_MAX_VARIABLE_NUMBER`.
20
+ * @param knex - Knex query builder connected to the archive DB.
21
+ * @param caches
22
+ * @param urls - URL strings (already in `withoutHashAndAuth` form).
23
+ */
24
+ export declare function insertInventoryResources(knex: Knex, caches: WriteRefCaches, urls: readonly string[]): Promise<void>;
@@ -0,0 +1,64 @@
1
+ import { eachSplitted } from '../../../utils/array/each-splitted.js';
2
+ import { resolveUrlRefs } from '../../populate-entity-tables/resolve-url-refs.js';
3
+ import { decomposeUrl } from '../../populate-ref-tables/decompose-url.js';
4
+ /**
5
+ * Pre-insert inventory non-HTML URLs into `resource_items` as placeholder
6
+ * rows with `source = 'inventory-seed'` and all metadata columns NULL —
7
+ * the non-HTML counterpart of `insertInventorySeeds`. Used by
8
+ * `CrawlerOrchestrator.inventory` so the ingestion phase commits all of
9
+ * its non-HTML URLs in one chunked round-trip per 500 instead of N
10
+ * sequential `insertResource` awaits — a per-URL loop would spend
11
+ * minutes inside the `.bak`-protected window on a 50k-URL inventory
12
+ * list, where the bulk path finishes in seconds.
13
+ *
14
+ * Idempotent: both the `url_refs` and `resource_items` inserts are
15
+ * `ON CONFLICT ... IGNORE`, leaving existing rows untouched (the
16
+ * orchestrator's `getExistingResourceUrls` filter is what keeps a
17
+ * crawled-lineage `resource_items` row from being downgraded to the
18
+ * inventory label here).
19
+ *
20
+ * Chunked at 500 to stay well under SQLite's `SQLITE_MAX_VARIABLE_NUMBER`.
21
+ * @param knex - Knex query builder connected to the archive DB.
22
+ * @param caches
23
+ * @param urls - URL strings (already in `withoutHashAndAuth` form).
24
+ */
25
+ export async function insertInventoryResources(knex, caches, urls) {
26
+ if (urls.length === 0) {
27
+ return;
28
+ }
29
+ await eachSplitted([...urls], 500, async (chunk) => {
30
+ await knex('url_refs')
31
+ .insert(chunk.map((url) => ({ url, ...decomposeUrl(url) })))
32
+ .onConflict('url')
33
+ .ignore();
34
+ const urlIds = await resolveUrlRefs(knex, chunk);
35
+ const rows = chunk.map((url) => {
36
+ const urlId = urlIds.get(url);
37
+ if (urlId === undefined) {
38
+ throw new Error(`insertInventoryResources: url_refs.id not resolved for ${url}`);
39
+ }
40
+ caches.urlIds.set(url, urlId);
41
+ return {
42
+ url_id: urlId,
43
+ is_external: 0,
44
+ status: null,
45
+ status_text: null,
46
+ content_type_id: null,
47
+ content_length: null,
48
+ header_set_id: null,
49
+ compress: 0,
50
+ cdn: 0,
51
+ source: 'inventory-seed',
52
+ };
53
+ });
54
+ await knex('resource_items').insert(rows).onConflict('url_id').ignore();
55
+ const inserted = (await knex
56
+ .select('ri.id', 'ur.url')
57
+ .from('resource_items as ri')
58
+ .join('url_refs as ur', 'ur.id', 'ri.url_id')
59
+ .whereIn('ur.url', chunk));
60
+ for (const row of inserted) {
61
+ caches.resourceIds.set(row.url, row.id);
62
+ }
63
+ });
64
+ }
@@ -0,0 +1,15 @@
1
+ import type { WriteRefCaches } from '../_shared/types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Inserts a referrer relationship between a resource and a page into the
5
+ * `resource_ref_edges` table. Silently skips if the resource is not found.
6
+ *
7
+ * A repeat observation of the same `(resource, page)` pair is ignored
8
+ * (`ON CONFLICT ... IGNORE`), leaving `count` at its DEFAULT `1` — one
9
+ * row per pair, matching the legacy uniqueness contract.
10
+ * @param knex - Knex query builder connected to the archive DB.
11
+ * @param caches - The connection's write-side id caches.
12
+ * @param src - The URL of the resource.
13
+ * @param pageUrl - The URL of the page that references the resource.
14
+ */
15
+ export declare function insertResourceReferrers(knex: Knex, caches: WriteRefCaches, src: string, pageUrl: string): Promise<void>;
@@ -0,0 +1,54 @@
1
+ import { resolveContentItemId } from '../_shared/resolve-content-item-id.js';
2
+ /**
3
+ * Inserts a referrer relationship between a resource and a page into the
4
+ * `resource_ref_edges` table. Silently skips if the resource is not found.
5
+ *
6
+ * A repeat observation of the same `(resource, page)` pair is ignored
7
+ * (`ON CONFLICT ... IGNORE`), leaving `count` at its DEFAULT `1` — one
8
+ * row per pair, matching the legacy uniqueness contract.
9
+ * @param knex - Knex query builder connected to the archive DB.
10
+ * @param caches - The connection's write-side id caches.
11
+ * @param src - The URL of the resource.
12
+ * @param pageUrl - The URL of the page that references the resource.
13
+ */
14
+ export async function insertResourceReferrers(knex, caches, src, pageUrl) {
15
+ const resourceId = await findResourceIdByUrl(knex, caches, src);
16
+ if (resourceId === undefined) {
17
+ // Ignore when the resource is not found
18
+ return;
19
+ }
20
+ const pageId = await resolveContentItemId(knex, caches, pageUrl);
21
+ await knex('resource_ref_edges')
22
+ .insert({
23
+ resource_id: resourceId,
24
+ page_id: pageId,
25
+ })
26
+ .onConflict(['resource_id', 'page_id'])
27
+ .ignore();
28
+ }
29
+ /**
30
+ * Cache-first lookup of `resource_items.id` by the resource URL. The
31
+ * cache only ever gains entries for URLs that exist in the table
32
+ * (resource identities are never deleted during a crawl), so a hit is
33
+ * always valid; a miss falls through to a `url_refs` join.
34
+ * @param knex - Knex query builder.
35
+ * @param caches - The connection's write-side id caches.
36
+ * @param url - The resource URL, verbatim as stored.
37
+ * @returns The resource id, or `undefined` when the URL is unknown.
38
+ */
39
+ async function findResourceIdByUrl(knex, caches, url) {
40
+ const cached = caches.resourceIds.get(url);
41
+ if (cached !== undefined) {
42
+ return cached;
43
+ }
44
+ const [row] = (await knex
45
+ .select('ri.id')
46
+ .from('resource_items as ri')
47
+ .join('url_refs as ur', 'ur.id', 'ri.url_id')
48
+ .where('ur.url', url));
49
+ if (row === undefined) {
50
+ return undefined;
51
+ }
52
+ caches.resourceIds.set(url, row.id);
53
+ return row.id;
54
+ }
@@ -0,0 +1,34 @@
1
+ import type { Resource } from '../../../utils/types/types.js';
2
+ import type { PageSource } from '../../types.js';
3
+ import type { WriteRefCaches } from '../_shared/types.js';
4
+ import type { Knex } from 'knex';
5
+ /**
6
+ * Inserts a sub-resource into the `resource_items` table, interning its
7
+ * URL / content type / response headers into the ref tables first. A
8
+ * resource's identity URL routes through {@link resolveUrlOrBlob} like an
9
+ * image `src` — a large inline `data:` URI (e.g. a CSS
10
+ * `background-image` sub-resource) lands in `blob_refs` instead of
11
+ * `url_refs`.
12
+ *
13
+ * A conflict on the identity column (`url_id` or `url_blob_id`) is an
14
+ * **upgrade-only merge**, not first-write-wins: the existing row's
15
+ * response fields (`status` / `status_text` / `content_type_id` /
16
+ * `content_length` / `header_set_id` / `compress` / `cdn`) are replaced
17
+ * only when the existing row currently has `status IS NULL` (beholder's
18
+ * signal for "the request never got a response" — see the `Resource`
19
+ * type) AND the new observation has a non-null `status`. A resource that
20
+ * failed on first sight and later succeeds (the exact case
21
+ * `--retry-failed` re-renders a page for) is therefore healed instead of
22
+ * staying frozen at its failure; a resource that already has a real
23
+ * response never gets overwritten by a later transient failure, and two
24
+ * failures in a row are a no-op — neither carries new information.
25
+ *
26
+ * The `source` provenance label is written ONLY on insert; a conflict
27
+ * (upgrade or no-op alike) leaves an existing row's source untouched
28
+ * (this is what makes a second `crawl --inventory` non-destructive).
29
+ * @param knex - Knex query builder connected to the archive DB.
30
+ * @param caches - The connection's write-side id caches.
31
+ * @param resource - The resource data to insert.
32
+ * @param source - Provenance label for new rows. `undefined` leaves the DB DEFAULT (`'crawled'`).
33
+ */
34
+ export declare function insertResource(knex: Knex, caches: WriteRefCaches, resource: Resource, source?: PageSource): Promise<void>;
@@ -0,0 +1,73 @@
1
+ import { normalizeContentType } from '../../../crawler/normalize-content-type.js';
2
+ import { resolveUrlOrBlob } from '../_shared/resolve-url-or-blob.js';
3
+ import { upsertContentTypeRef } from '../_shared/upsert-content-type-ref.js';
4
+ import { upsertResponseHeaders } from '../_shared/upsert-response-headers.js';
5
+ /**
6
+ * Inserts a sub-resource into the `resource_items` table, interning its
7
+ * URL / content type / response headers into the ref tables first. A
8
+ * resource's identity URL routes through {@link resolveUrlOrBlob} like an
9
+ * image `src` — a large inline `data:` URI (e.g. a CSS
10
+ * `background-image` sub-resource) lands in `blob_refs` instead of
11
+ * `url_refs`.
12
+ *
13
+ * A conflict on the identity column (`url_id` or `url_blob_id`) is an
14
+ * **upgrade-only merge**, not first-write-wins: the existing row's
15
+ * response fields (`status` / `status_text` / `content_type_id` /
16
+ * `content_length` / `header_set_id` / `compress` / `cdn`) are replaced
17
+ * only when the existing row currently has `status IS NULL` (beholder's
18
+ * signal for "the request never got a response" — see the `Resource`
19
+ * type) AND the new observation has a non-null `status`. A resource that
20
+ * failed on first sight and later succeeds (the exact case
21
+ * `--retry-failed` re-renders a page for) is therefore healed instead of
22
+ * staying frozen at its failure; a resource that already has a real
23
+ * response never gets overwritten by a later transient failure, and two
24
+ * failures in a row are a no-op — neither carries new information.
25
+ *
26
+ * The `source` provenance label is written ONLY on insert; a conflict
27
+ * (upgrade or no-op alike) leaves an existing row's source untouched
28
+ * (this is what makes a second `crawl --inventory` non-destructive).
29
+ * @param knex - Knex query builder connected to the archive DB.
30
+ * @param caches - The connection's write-side id caches.
31
+ * @param resource - The resource data to insert.
32
+ * @param source - Provenance label for new rows. `undefined` leaves the DB DEFAULT (`'crawled'`).
33
+ */
34
+ export async function insertResource(knex, caches, resource, source) {
35
+ const slot = await resolveUrlOrBlob(knex, caches, resource.url.href);
36
+ // Canonicalize like the page path (see `insertPage`) so resource
37
+ // content-type filters / dedupe keys are case- and whitespace-stable.
38
+ const contentType = normalizeContentType(resource.contentType);
39
+ const contentTypeId = contentType == null || contentType === ''
40
+ ? null
41
+ : await upsertContentTypeRef(knex, caches, contentType);
42
+ const headerSetId = await upsertResponseHeaders(knex, caches, resource.headers);
43
+ const identityColumn = slot.blob === null ? 'url_id' : 'url_blob_id';
44
+ const identityValue = slot.blob === null ? slot.url : slot.blob;
45
+ const responseFields = {
46
+ is_external: resource.isExternal ? 1 : 0,
47
+ status: resource.status,
48
+ status_text: resource.statusText,
49
+ content_type_id: contentTypeId,
50
+ content_length: resource.contentLength,
51
+ header_set_id: headerSetId,
52
+ compress: resource.compress || 0,
53
+ cdn: resource.cdn || 0,
54
+ };
55
+ const existing = await knex('resource_items')
56
+ .select('id', 'status')
57
+ .where(identityColumn, identityValue)
58
+ .first();
59
+ if (existing === undefined) {
60
+ await knex('resource_items').insert({
61
+ url_id: slot.url,
62
+ url_blob_id: slot.blob,
63
+ ...responseFields,
64
+ ...(source === undefined ? {} : { source }),
65
+ });
66
+ return;
67
+ }
68
+ if (existing.status === null && resource.status !== null) {
69
+ await knex('resource_items')
70
+ .where('id', existing.id)
71
+ .update(responseFields);
72
+ }
73
+ }
@@ -0,0 +1,26 @@
1
+ import type { DB_Resource } from '../../types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Raw row shape produced by {@link ../resources/build-resource-query.js}
5
+ * before `responseHeaders` reconstruction.
6
+ */
7
+ interface RawResourceRow extends Omit<DB_Resource, 'responseHeaders'> {
8
+ /** `resource_items.header_set_id`, or null when no headers were recorded. */
9
+ headerSetId: number | null;
10
+ }
11
+ /**
12
+ * Reconstructs `responseHeaders` (JSON string) for a batch of raw resource
13
+ * rows. Headers load through
14
+ * {@link ../_shared/load-response-headers-by-set-ids.js} (chunked batch
15
+ * lookup, never N+1) — the same reconstruction the page read path uses,
16
+ * so pages and resources always agree on how a given `header_set_id`
17
+ * reads back.
18
+ * @param knex - Knex query builder connected to the archive DB.
19
+ * @param rows - Raw rows from {@link ../resources/build-resource-query.js}.
20
+ * @returns Fully reconstructed `DB_Resource` rows, in the same order as `rows`.
21
+ * @example
22
+ * const raw = await buildResourceQuery(knex).whereIn('ur.url', urls);
23
+ * const resources = await reconstructResourceRows(knex, raw);
24
+ */
25
+ export declare function reconstructResourceRows(knex: Knex, rows: readonly RawResourceRow[]): Promise<DB_Resource[]>;
26
+ export {};
@@ -0,0 +1,30 @@
1
+ import { loadResponseHeadersBySetIds } from '../_shared/load-response-headers-by-set-ids.js';
2
+ /**
3
+ * Reconstructs `responseHeaders` (JSON string) for a batch of raw resource
4
+ * rows. Headers load through
5
+ * {@link ../_shared/load-response-headers-by-set-ids.js} (chunked batch
6
+ * lookup, never N+1) — the same reconstruction the page read path uses,
7
+ * so pages and resources always agree on how a given `header_set_id`
8
+ * reads back.
9
+ * @param knex - Knex query builder connected to the archive DB.
10
+ * @param rows - Raw rows from {@link ../resources/build-resource-query.js}.
11
+ * @returns Fully reconstructed `DB_Resource` rows, in the same order as `rows`.
12
+ * @example
13
+ * const raw = await buildResourceQuery(knex).whereIn('ur.url', urls);
14
+ * const resources = await reconstructResourceRows(knex, raw);
15
+ */
16
+ export async function reconstructResourceRows(knex, rows) {
17
+ const headerSetIds = [
18
+ ...new Set(rows.map((r) => r.headerSetId).filter((id) => id != null)),
19
+ ];
20
+ const headersBySetId = await loadResponseHeadersBySetIds(knex, headerSetIds);
21
+ return rows.map((row) => {
22
+ const { headerSetId, ...rest } = row;
23
+ return {
24
+ ...rest,
25
+ responseHeaders: headerSetId == null
26
+ ? null
27
+ : JSON.stringify(headersBySetId.get(headerSetId) ?? {}),
28
+ };
29
+ });
30
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Decodes a stored HTML body BLOB according to its codec marker. The codec
3
+ * column on `page_html_blobs` records which encoding was applied at write
4
+ * time so individual rows can be migrated to a different compression without
5
+ * a table-wide rewrite.
6
+ *
7
+ * Shared by {@link ./database.ts#Database.getHtmlOfPageById} (writer read
8
+ * path) and {@link ./populate-entity-tables/populate-image-items.ts} (0.13
9
+ * migration, which reads HTML *inside* its writer transaction and therefore
10
+ * cannot go through `Database.getHtmlOfPageById` — that method uses the
11
+ * non-transactional Knex instance, and re-entering the pool from within an
12
+ * outer writer trx serialises on libsql's single writer connection).
13
+ * @param body - Raw bytes as stored in `page_html_blobs.body`.
14
+ * @param codec - The `codec` column value (e.g. `'zstd'`, `'none'`).
15
+ * @returns The decoded HTML string.
16
+ * @throws {Error} If the codec is not recognised.
17
+ */
18
+ export declare function decodeStoredBlob(body: Uint8Array, codec: string): string;
@@ -0,0 +1,31 @@
1
+ import { zstdDecompressSync } from 'node:zlib';
2
+ /**
3
+ * Decodes a stored HTML body BLOB according to its codec marker. The codec
4
+ * column on `page_html_blobs` records which encoding was applied at write
5
+ * time so individual rows can be migrated to a different compression without
6
+ * a table-wide rewrite.
7
+ *
8
+ * Shared by {@link ./database.ts#Database.getHtmlOfPageById} (writer read
9
+ * path) and {@link ./populate-entity-tables/populate-image-items.ts} (0.13
10
+ * migration, which reads HTML *inside* its writer transaction and therefore
11
+ * cannot go through `Database.getHtmlOfPageById` — that method uses the
12
+ * non-transactional Knex instance, and re-entering the pool from within an
13
+ * outer writer trx serialises on libsql's single writer connection).
14
+ * @param body - Raw bytes as stored in `page_html_blobs.body`.
15
+ * @param codec - The `codec` column value (e.g. `'zstd'`, `'none'`).
16
+ * @returns The decoded HTML string.
17
+ * @throws {Error} If the codec is not recognised.
18
+ */
19
+ export function decodeStoredBlob(body, codec) {
20
+ // `Buffer.from(buffer)` accepts Uint8Array, Buffer, and array-like
21
+ // shapes uniformly; libsql may hand back any of these for a BLOB
22
+ // column depending on the row encoding.
23
+ const buffer = Buffer.from(body);
24
+ if (codec === 'zstd') {
25
+ return zstdDecompressSync(buffer).toString('utf8');
26
+ }
27
+ if (codec === 'none') {
28
+ return buffer.toString('utf8');
29
+ }
30
+ throw new Error(`Unknown page_html_blobs.codec: ${codec}`);
31
+ }
@@ -0,0 +1,37 @@
1
+ import type { PageSource } from './types.js';
2
+ /**
3
+ * Decide which {@link PageSource} label a child row reached through the
4
+ * crawl graph (anchor placeholder, sub-resource, redirect chain
5
+ * intermediate) should inherit from its parent.
6
+ *
7
+ * Two simple rules, expressed once so anchor / redirect / sub-resource
8
+ * call sites stay in lockstep:
9
+ *
10
+ * 1. If the parent is in the inventory chain
11
+ * ({@link isInventorySource}) → propagate
12
+ * `'inventory-discovered'`. The child is itself a transitively
13
+ * reached node in the inventory chain; it is NOT a new seed (the
14
+ * seed label is reserved for URLs the operator listed in
15
+ * `--inventory ./list.txt`).
16
+ *
17
+ * 2. Otherwise → return `fallback`. The two production fallbacks differ
18
+ * by call site:
19
+ *
20
+ * - Anchor lineage passes `'crawled'` explicitly so the crawled-wins
21
+ * downgrade inside `resolveContentItemId` fires when the anchor reaches an
22
+ * existing `'inventory-*'` row.
23
+ * - Sub-resource emit passes `undefined` so the DB DEFAULT
24
+ * `'crawled'` lands on the freshly INSERTed `resources` row (the
25
+ * `setResources` path is INSERT-only with `onConflict.ignore()`, so
26
+ * no downgrade is needed).
27
+ * - Redirect chain intermediate uses `'crawled'` (same reason as
28
+ * anchor): an existing inventory-* intermediate reached by a
29
+ * crawled redirect chain must be downgraded.
30
+ *
31
+ * Pure function — keeps the lineage decision testable in isolation from
32
+ * the database transaction / event-emitter wiring that consumes it.
33
+ * @param parentSource - The parent page's stored `source` column (or `undefined` when no parent row exists).
34
+ * @param fallback - The label to return when the parent is NOT in the inventory chain. Pass `'crawled'` to enable the crawled-wins downgrade, or `undefined` to let the DB DEFAULT apply.
35
+ * @returns The lineage label to attach to the child row.
36
+ */
37
+ export declare function deriveLineageFromParent(parentSource: PageSource | undefined, fallback: PageSource | undefined): PageSource | undefined;
@@ -0,0 +1,42 @@
1
+ import { isInventorySource } from './is-inventory-source.js';
2
+ /**
3
+ * Decide which {@link PageSource} label a child row reached through the
4
+ * crawl graph (anchor placeholder, sub-resource, redirect chain
5
+ * intermediate) should inherit from its parent.
6
+ *
7
+ * Two simple rules, expressed once so anchor / redirect / sub-resource
8
+ * call sites stay in lockstep:
9
+ *
10
+ * 1. If the parent is in the inventory chain
11
+ * ({@link isInventorySource}) → propagate
12
+ * `'inventory-discovered'`. The child is itself a transitively
13
+ * reached node in the inventory chain; it is NOT a new seed (the
14
+ * seed label is reserved for URLs the operator listed in
15
+ * `--inventory ./list.txt`).
16
+ *
17
+ * 2. Otherwise → return `fallback`. The two production fallbacks differ
18
+ * by call site:
19
+ *
20
+ * - Anchor lineage passes `'crawled'` explicitly so the crawled-wins
21
+ * downgrade inside `resolveContentItemId` fires when the anchor reaches an
22
+ * existing `'inventory-*'` row.
23
+ * - Sub-resource emit passes `undefined` so the DB DEFAULT
24
+ * `'crawled'` lands on the freshly INSERTed `resources` row (the
25
+ * `setResources` path is INSERT-only with `onConflict.ignore()`, so
26
+ * no downgrade is needed).
27
+ * - Redirect chain intermediate uses `'crawled'` (same reason as
28
+ * anchor): an existing inventory-* intermediate reached by a
29
+ * crawled redirect chain must be downgraded.
30
+ *
31
+ * Pure function — keeps the lineage decision testable in isolation from
32
+ * the database transaction / event-emitter wiring that consumes it.
33
+ * @param parentSource - The parent page's stored `source` column (or `undefined` when no parent row exists).
34
+ * @param fallback - The label to return when the parent is NOT in the inventory chain. Pass `'crawled'` to enable the crawled-wins downgrade, or `undefined` to let the DB DEFAULT apply.
35
+ * @returns The lineage label to attach to the child row.
36
+ */
37
+ export function deriveLineageFromParent(parentSource, fallback) {
38
+ if (isInventorySource(parentSource)) {
39
+ return 'inventory-discovered';
40
+ }
41
+ return fallback;
42
+ }
@@ -0,0 +1,45 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Drops the five legacy write-model tables from a migrated archive. Reader
4
+ * and writer both operate exclusively on the entity / ref tables, so once
5
+ * the 0.13 populate has copied everything across (and
6
+ * `retargetLegacyFkTables` has repointed the adjunct FKs at
7
+ * `content_items`), the legacy tables are pure dead weight — dropping them
8
+ * reclaims the duplicated storage and removes the last consumers of the
9
+ * `pages(id)` id-space.
10
+ *
11
+ * **Requires `PRAGMA foreign_keys = OFF`** (asserted at runtime, because a
12
+ * caller that forgets the toggle would fail nondeterministically instead
13
+ * of loudly). Two reasons for the requirement:
14
+ *
15
+ * 1. `pages.redirectDestId` is a self-referential FK. With enforcement ON,
16
+ * `DROP TABLE` performs an implicit `DELETE FROM`, and deleting a
17
+ * redirect-destination row before its source row trips the constraint —
18
+ * whether the drop survives depends on row order, so archives with
19
+ * redirect chains can fail nondeterministically.
20
+ * 2. With enforcement OFF the implicit `DELETE FROM` is skipped entirely
21
+ * and SQLite reclaims each table as a whole b-tree — O(pages) row
22
+ * deletions become O(1) truncations, which matters at the several
23
+ * hundred-thousand-row scale real archives reach.
24
+ *
25
+ * The drops still run children-first (`resources-referrers` / `anchors` /
26
+ * `images` reference `pages` / `resources`) as defence in depth. Missing
27
+ * tables are skipped so the function is idempotent.
28
+ *
29
+ * Data-integrity of the surviving tables is asserted separately by
30
+ * `checkForeignKeyIntegrity` (`PRAGMA foreign_key_check` ignores the
31
+ * `foreign_keys` setting), which the migration script runs right after
32
+ * this function.
33
+ * @param trx - Knex transaction on the archive being migrated
34
+ * (`scripts/migrate-to-0.13.mjs` is the caller).
35
+ * @throws {Error} when the connection still has `PRAGMA foreign_keys = ON`
36
+ * (`foreign_keys` cannot change inside a transaction, so the caller must
37
+ * toggle it off before opening the transaction).
38
+ * @example
39
+ * await db.raw('PRAGMA foreign_keys = OFF');
40
+ * await db.transaction(async (trx) => {
41
+ * await dropLegacyTables(trx);
42
+ * });
43
+ * await db.raw('PRAGMA foreign_keys = ON');
44
+ */
45
+ export declare function dropLegacyTables(trx: Knex): Promise<void>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Drops the five legacy write-model tables from a migrated archive. Reader
3
+ * and writer both operate exclusively on the entity / ref tables, so once
4
+ * the 0.13 populate has copied everything across (and
5
+ * `retargetLegacyFkTables` has repointed the adjunct FKs at
6
+ * `content_items`), the legacy tables are pure dead weight — dropping them
7
+ * reclaims the duplicated storage and removes the last consumers of the
8
+ * `pages(id)` id-space.
9
+ *
10
+ * **Requires `PRAGMA foreign_keys = OFF`** (asserted at runtime, because a
11
+ * caller that forgets the toggle would fail nondeterministically instead
12
+ * of loudly). Two reasons for the requirement:
13
+ *
14
+ * 1. `pages.redirectDestId` is a self-referential FK. With enforcement ON,
15
+ * `DROP TABLE` performs an implicit `DELETE FROM`, and deleting a
16
+ * redirect-destination row before its source row trips the constraint —
17
+ * whether the drop survives depends on row order, so archives with
18
+ * redirect chains can fail nondeterministically.
19
+ * 2. With enforcement OFF the implicit `DELETE FROM` is skipped entirely
20
+ * and SQLite reclaims each table as a whole b-tree — O(pages) row
21
+ * deletions become O(1) truncations, which matters at the several
22
+ * hundred-thousand-row scale real archives reach.
23
+ *
24
+ * The drops still run children-first (`resources-referrers` / `anchors` /
25
+ * `images` reference `pages` / `resources`) as defence in depth. Missing
26
+ * tables are skipped so the function is idempotent.
27
+ *
28
+ * Data-integrity of the surviving tables is asserted separately by
29
+ * `checkForeignKeyIntegrity` (`PRAGMA foreign_key_check` ignores the
30
+ * `foreign_keys` setting), which the migration script runs right after
31
+ * this function.
32
+ * @param trx - Knex transaction on the archive being migrated
33
+ * (`scripts/migrate-to-0.13.mjs` is the caller).
34
+ * @throws {Error} when the connection still has `PRAGMA foreign_keys = ON`
35
+ * (`foreign_keys` cannot change inside a transaction, so the caller must
36
+ * toggle it off before opening the transaction).
37
+ * @example
38
+ * await db.raw('PRAGMA foreign_keys = OFF');
39
+ * await db.transaction(async (trx) => {
40
+ * await dropLegacyTables(trx);
41
+ * });
42
+ * await db.raw('PRAGMA foreign_keys = ON');
43
+ */
44
+ export async function dropLegacyTables(trx) {
45
+ const pragma = await trx.raw('PRAGMA foreign_keys');
46
+ if (pragma[0]?.foreign_keys === 1) {
47
+ throw new Error('dropLegacyTables: PRAGMA foreign_keys is ON — turn it OFF before the transaction. ' +
48
+ 'With enforcement ON, dropping `pages` runs an implicit DELETE whose row order ' +
49
+ 'can trip the redirectDestId self-FK nondeterministically.');
50
+ }
51
+ await trx.raw('DROP TABLE IF EXISTS "resources-referrers"');
52
+ await trx.raw('DROP TABLE IF EXISTS "anchors"');
53
+ await trx.raw('DROP TABLE IF EXISTS "images"');
54
+ await trx.raw('DROP TABLE IF EXISTS "resources"');
55
+ await trx.raw('DROP TABLE IF EXISTS "pages"');
56
+ }
@@ -1,7 +1,7 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import { remove } from './remove.js';
3
3
  /**
4
- * Checks whether a value is a {@link NodeJS.ErrnoException}.
4
+ * Checks whether a value is a `NodeJS.ErrnoException`.
5
5
  * @param error - The value to check.
6
6
  * @returns `true` if the value has a `code` property of type `string`.
7
7
  */
@@ -0,0 +1,44 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Bulk-resolve a raw error message for each given page id, using only sources
4
+ * reachable from a {@link Knex} handle. Read order: `page_errors` (keyed by
5
+ * `pageId`, the most direct signal a scrape attempt recorded), then
6
+ * `crawl_errors` (keyed by `url`, the crawler-channel record for failures
7
+ * that happened before a page row was scraped).
8
+ *
9
+ * **Known limitation — pre-`crawl_errors` archives**: This helper does NOT
10
+ * read `error.log`. The `crawl_errors` table is created empty (by
11
+ * `initSchema` on fresh archives, by `scripts/migrate-to-0.13.mjs` on
12
+ * migrated ones) and historical lines from `error.log` are never
13
+ * back-filled, so an archive whose failures live only in `error.log`
14
+ * will resolve every id to "no message" here. The
15
+ * downstream `Database.resetFailedPages` treats absence as `unknown` (still
16
+ * retryable), so legacy archives lose the permanent-kind exclusion until a
17
+ * fresh crawl run populates `crawl_errors` / `page_errors`. The trade-off
18
+ * (no error.log parsing in the writer path) keeps the writer dependency
19
+ * surface narrow and avoids re-implementing the parser already living in
20
+ * `@nitpicker/query`'s `resolveFailedPageMessages` — which the crawler
21
+ * package cannot import (reverse-direction dependency). When this matters
22
+ * in practice, run the archive through one fresh `crawl --retry-failed`
23
+ * pass first to populate the structured tables.
24
+ *
25
+ * Pages with no message in any consulted source are simply absent from the
26
+ * returned map; callers treat the absence as "unclassifiable, keep retrying"
27
+ * (i.e. `unknown`).
28
+ * @param instance - The {@link Knex} handle.
29
+ * @param ids - Candidate `pages.id` values.
30
+ * @param urls - The corresponding `pages.url` values, in the same order as
31
+ * `ids`. Length and indexing MUST match `ids` so the page → url join can be
32
+ * reconstructed without a second `pages` round-trip.
33
+ * @returns `Map<pageId, message>` populated only for ids whose message was
34
+ * found in one of the consulted tables.
35
+ * @example
36
+ * ```ts
37
+ * const messages = await getFailedPageMessages(
38
+ * instance,
39
+ * candidates.map(c => c.id),
40
+ * candidates.map(c => c.url),
41
+ * );
42
+ * ```
43
+ */
44
+ export declare function getFailedPageMessages(instance: Knex, ids: readonly number[], urls: readonly string[]): Promise<Map<number, string>>;