@nitpicker/crawler 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/README.md +6 -4
  2. package/lib/archive/archive-accessor.d.ts +2 -2
  3. package/lib/archive/archive-accessor.js +2 -2
  4. package/lib/archive/archive-lock.d.ts +7 -0
  5. package/lib/archive/archive-lock.js +7 -0
  6. package/lib/archive/archive.d.ts +63 -16
  7. package/lib/archive/archive.js +56 -17
  8. package/lib/archive/create-adjunct-tables.d.ts +43 -0
  9. package/lib/archive/create-adjunct-tables.js +213 -0
  10. package/lib/archive/create-entity-tables.d.ts +173 -0
  11. package/lib/archive/create-entity-tables.js +318 -0
  12. package/lib/archive/create-progress-reporter.d.ts +30 -0
  13. package/lib/archive/create-progress-reporter.js +38 -0
  14. package/lib/archive/create-ref-tables.d.ts +35 -0
  15. package/lib/archive/create-ref-tables.js +188 -0
  16. package/lib/archive/database.d.ts +92 -345
  17. package/lib/archive/database.js +168 -1942
  18. package/lib/archive/db-ops/_shared/clear-write-ref-caches.d.ts +27 -0
  19. package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +34 -0
  20. package/lib/archive/db-ops/_shared/create-write-ref-caches.d.ts +17 -0
  21. package/lib/archive/db-ops/_shared/create-write-ref-caches.js +26 -0
  22. package/lib/archive/db-ops/_shared/decode-json-ref.d.ts +17 -0
  23. package/lib/archive/db-ops/_shared/decode-json-ref.js +31 -0
  24. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.d.ts +20 -0
  25. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.js +53 -0
  26. package/lib/archive/db-ops/_shared/resolve-content-item-id.d.ts +61 -0
  27. package/lib/archive/db-ops/_shared/resolve-content-item-id.js +111 -0
  28. package/lib/archive/db-ops/_shared/resolve-url-or-blob.d.ts +23 -0
  29. package/lib/archive/db-ops/_shared/resolve-url-or-blob.js +29 -0
  30. package/lib/archive/db-ops/_shared/retry-setting.d.ts +16 -0
  31. package/lib/archive/db-ops/_shared/retry-setting.js +18 -0
  32. package/lib/archive/db-ops/_shared/safe-parse-json.d.ts +11 -0
  33. package/lib/archive/db-ops/_shared/safe-parse-json.js +18 -0
  34. package/lib/archive/db-ops/_shared/types.d.ts +53 -0
  35. package/lib/archive/db-ops/_shared/types.js +1 -0
  36. package/lib/archive/db-ops/_shared/upsert-blob-ref.d.ts +25 -0
  37. package/lib/archive/db-ops/_shared/upsert-blob-ref.js +48 -0
  38. package/lib/archive/db-ops/_shared/upsert-content-type-ref.d.ts +30 -0
  39. package/lib/archive/db-ops/_shared/upsert-content-type-ref.js +45 -0
  40. package/lib/archive/db-ops/_shared/upsert-json-ref.d.ts +22 -0
  41. package/lib/archive/db-ops/_shared/upsert-json-ref.js +41 -0
  42. package/lib/archive/db-ops/_shared/upsert-response-headers.d.ts +35 -0
  43. package/lib/archive/db-ops/_shared/upsert-response-headers.js +49 -0
  44. package/lib/archive/db-ops/_shared/upsert-url-ref.d.ts +39 -0
  45. package/lib/archive/db-ops/_shared/upsert-url-ref.js +62 -0
  46. package/lib/archive/db-ops/analysis/replace-analysis-violations.d.ts +28 -0
  47. package/lib/archive/db-ops/analysis/replace-analysis-violations.js +152 -0
  48. package/lib/archive/db-ops/anchors/get-anchors-on-page.d.ts +10 -0
  49. package/lib/archive/db-ops/anchors/get-anchors-on-page.js +21 -0
  50. package/lib/archive/db-ops/config/get-base-url.d.ts +8 -0
  51. package/lib/archive/db-ops/config/get-base-url.js +14 -0
  52. package/lib/archive/db-ops/config/get-config.d.ts +10 -0
  53. package/lib/archive/db-ops/config/get-config.js +27 -0
  54. package/lib/archive/db-ops/config/get-name.d.ts +8 -0
  55. package/lib/archive/db-ops/config/get-name.js +14 -0
  56. package/lib/archive/db-ops/config/info-column-allowlist.d.ts +7 -0
  57. package/lib/archive/db-ops/config/info-column-allowlist.js +26 -0
  58. package/lib/archive/db-ops/config/info-json-columns.d.ts +5 -0
  59. package/lib/archive/db-ops/config/info-json-columns.js +10 -0
  60. package/lib/archive/db-ops/config/set-config.d.ts +12 -0
  61. package/lib/archive/db-ops/config/set-config.js +21 -0
  62. package/lib/archive/db-ops/config/update-config.d.ts +17 -0
  63. package/lib/archive/db-ops/config/update-config.js +36 -0
  64. package/lib/archive/db-ops/errors/insert-crawl-error.d.ts +15 -0
  65. package/lib/archive/db-ops/errors/insert-crawl-error.js +21 -0
  66. package/lib/archive/db-ops/errors/insert-page-error.d.ts +21 -0
  67. package/lib/archive/db-ops/errors/insert-page-error.js +28 -0
  68. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +22 -0
  69. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +141 -0
  70. package/lib/archive/db-ops/html/get-html-of-page-by-id.d.ts +18 -0
  71. package/lib/archive/db-ops/html/get-html-of-page-by-id.js +29 -0
  72. package/lib/archive/db-ops/inventory/record-inventory-run.d.ts +21 -0
  73. package/lib/archive/db-ops/inventory/record-inventory-run.js +38 -0
  74. package/lib/archive/db-ops/lifecycle/checkpoint.d.ts +8 -0
  75. package/lib/archive/db-ops/lifecycle/checkpoint.js +9 -0
  76. package/lib/archive/db-ops/lifecycle/destroy.d.ts +6 -0
  77. package/lib/archive/db-ops/lifecycle/destroy.js +7 -0
  78. package/lib/archive/db-ops/lifecycle/init.d.ts +22 -0
  79. package/lib/archive/db-ops/lifecycle/init.js +42 -0
  80. package/lib/archive/db-ops/meta/get-jsonld-of-page.d.ts +13 -0
  81. package/lib/archive/db-ops/meta/get-jsonld-of-page.js +27 -0
  82. package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +12 -0
  83. package/lib/archive/db-ops/meta/get-tags-of-page.js +28 -0
  84. package/lib/archive/db-ops/pages/order/set-url-order.d.ts +8 -0
  85. package/lib/archive/db-ops/pages/order/set-url-order.js +32 -0
  86. package/lib/archive/db-ops/pages/read/build-page-query.d.ts +18 -0
  87. package/lib/archive/db-ops/pages/read/build-page-query.js +40 -0
  88. package/lib/archive/db-ops/pages/read/get-crawling-state.d.ts +70 -0
  89. package/lib/archive/db-ops/pages/read/get-crawling-state.js +98 -0
  90. package/lib/archive/db-ops/pages/read/get-existing-page-urls.d.ts +15 -0
  91. package/lib/archive/db-ops/pages/read/get-existing-page-urls.js +30 -0
  92. package/lib/archive/db-ops/pages/read/get-page-count.d.ts +12 -0
  93. package/lib/archive/db-ops/pages/read/get-page-count.js +21 -0
  94. package/lib/archive/db-ops/pages/read/get-page-source-by-url.d.ts +24 -0
  95. package/lib/archive/db-ops/pages/read/get-page-source-by-url.js +28 -0
  96. package/lib/archive/db-ops/pages/read/get-pages-with-rels.d.ts +38 -0
  97. package/lib/archive/db-ops/pages/read/get-pages-with-rels.js +107 -0
  98. package/lib/archive/db-ops/pages/read/get-pages.d.ts +11 -0
  99. package/lib/archive/db-ops/pages/read/get-pages.js +51 -0
  100. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.d.ts +18 -0
  101. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.js +25 -0
  102. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.d.ts +31 -0
  103. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.js +32 -0
  104. package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +24 -0
  105. package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +93 -0
  106. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +47 -0
  107. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +124 -0
  108. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.d.ts +37 -0
  109. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.js +72 -0
  110. package/lib/archive/db-ops/pages/write/insert-jsonld.d.ts +17 -0
  111. package/lib/archive/db-ops/pages/write/insert-jsonld.js +49 -0
  112. package/lib/archive/db-ops/pages/write/insert-page.d.ts +36 -0
  113. package/lib/archive/db-ops/pages/write/insert-page.js +208 -0
  114. package/lib/archive/db-ops/pages/write/insert-tags.d.ts +16 -0
  115. package/lib/archive/db-ops/pages/write/insert-tags.js +34 -0
  116. package/lib/archive/db-ops/pages/write/link-redirect-sources.d.ts +36 -0
  117. package/lib/archive/db-ops/pages/write/link-redirect-sources.js +93 -0
  118. package/lib/archive/db-ops/pages/write/record-redirect.d.ts +35 -0
  119. package/lib/archive/db-ops/pages/write/record-redirect.js +100 -0
  120. package/lib/archive/db-ops/pages/write/set-skipped-page.d.ts +13 -0
  121. package/lib/archive/db-ops/pages/write/set-skipped-page.js +22 -0
  122. package/lib/archive/db-ops/pages/write/update-page.d.ts +29 -0
  123. package/lib/archive/db-ops/pages/write/update-page.js +334 -0
  124. package/lib/archive/db-ops/pages/write/write-page-html-blob.d.ts +19 -0
  125. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +41 -0
  126. package/lib/archive/db-ops/referrers/get-redirects-for-pages.d.ts +9 -0
  127. package/lib/archive/db-ops/referrers/get-redirects-for-pages.js +15 -0
  128. package/lib/archive/db-ops/referrers/get-referrers-of-page.d.ts +17 -0
  129. package/lib/archive/db-ops/referrers/get-referrers-of-page.js +32 -0
  130. package/lib/archive/db-ops/referrers/get-referrers-of-resource.d.ts +8 -0
  131. package/lib/archive/db-ops/referrers/get-referrers-of-resource.js +15 -0
  132. package/lib/archive/db-ops/resources/build-resource-query.d.ts +25 -0
  133. package/lib/archive/db-ops/resources/build-resource-query.js +29 -0
  134. package/lib/archive/db-ops/resources/get-existing-resource-urls.d.ts +9 -0
  135. package/lib/archive/db-ops/resources/get-existing-resource-urls.js +24 -0
  136. package/lib/archive/db-ops/resources/get-resource-by-url.d.ts +13 -0
  137. package/lib/archive/db-ops/resources/get-resource-by-url.js +22 -0
  138. package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +9 -0
  139. package/lib/archive/db-ops/resources/get-resource-url-list.js +13 -0
  140. package/lib/archive/db-ops/resources/get-resources.d.ts +8 -0
  141. package/lib/archive/db-ops/resources/get-resources.js +11 -0
  142. package/lib/archive/db-ops/resources/insert-inventory-resources.d.ts +24 -0
  143. package/lib/archive/db-ops/resources/insert-inventory-resources.js +64 -0
  144. package/lib/archive/db-ops/resources/insert-resource-referrers.d.ts +15 -0
  145. package/lib/archive/db-ops/resources/insert-resource-referrers.js +54 -0
  146. package/lib/archive/db-ops/resources/insert-resource.d.ts +34 -0
  147. package/lib/archive/db-ops/resources/insert-resource.js +73 -0
  148. package/lib/archive/db-ops/resources/reconstruct-resource-rows.d.ts +26 -0
  149. package/lib/archive/db-ops/resources/reconstruct-resource-rows.js +30 -0
  150. package/lib/archive/decode-html-blob.d.ts +18 -0
  151. package/lib/archive/decode-html-blob.js +31 -0
  152. package/lib/archive/derive-lineage-from-parent.d.ts +1 -1
  153. package/lib/archive/derive-lineage-from-parent.js +1 -1
  154. package/lib/archive/drop-legacy-tables.d.ts +45 -0
  155. package/lib/archive/drop-legacy-tables.js +56 -0
  156. package/lib/archive/filesystem/rename.js +1 -1
  157. package/lib/archive/get-failed-page-messages.d.ts +5 -4
  158. package/lib/archive/get-failed-page-messages.js +5 -4
  159. package/lib/archive/init-schema.d.ts +35 -39
  160. package/lib/archive/init-schema.js +99 -460
  161. package/lib/archive/limited-page-ids.d.ts +2 -1
  162. package/lib/archive/limited-page-ids.js +5 -4
  163. package/lib/archive/meta/assert-compatible-version.d.ts +24 -3
  164. package/lib/archive/meta/assert-compatible-version.js +24 -3
  165. package/lib/archive/meta/types.d.ts +87 -1
  166. package/lib/archive/meta/types.js +34 -2
  167. package/lib/archive/migrate-entity-tables.d.ts +45 -0
  168. package/lib/archive/migrate-entity-tables.js +56 -0
  169. package/lib/archive/migrate-ref-tables.d.ts +25 -0
  170. package/lib/archive/migrate-ref-tables.js +38 -0
  171. package/lib/archive/page-meta-column-maps.d.ts +32 -0
  172. package/lib/archive/page-meta-column-maps.js +43 -0
  173. package/lib/archive/page.d.ts +6 -6
  174. package/lib/archive/page.js +5 -5
  175. package/lib/archive/peek-archive-lock.d.ts +2 -2
  176. package/lib/archive/peek-archive-lock.js +2 -2
  177. package/lib/archive/populate-entity-tables/collapse-anchor-rows.d.ts +41 -0
  178. package/lib/archive/populate-entity-tables/collapse-anchor-rows.js +87 -0
  179. package/lib/archive/populate-entity-tables/derive-dom-path.d.ts +35 -0
  180. package/lib/archive/populate-entity-tables/derive-dom-path.js +72 -0
  181. package/lib/archive/populate-entity-tables/is-blob-ref-value.d.ts +16 -0
  182. package/lib/archive/populate-entity-tables/is-blob-ref-value.js +19 -0
  183. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.d.ts +66 -0
  184. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.js +96 -0
  185. package/lib/archive/populate-entity-tables/populate-anchor-edges.d.ts +33 -0
  186. package/lib/archive/populate-entity-tables/populate-anchor-edges.js +153 -0
  187. package/lib/archive/populate-entity-tables/populate-content-items.d.ts +40 -0
  188. package/lib/archive/populate-entity-tables/populate-content-items.js +141 -0
  189. package/lib/archive/populate-entity-tables/populate-entities.d.ts +81 -0
  190. package/lib/archive/populate-entity-tables/populate-entities.js +111 -0
  191. package/lib/archive/populate-entity-tables/populate-image-items.d.ts +91 -0
  192. package/lib/archive/populate-entity-tables/populate-image-items.js +223 -0
  193. package/lib/archive/populate-entity-tables/populate-page-meta.d.ts +33 -0
  194. package/lib/archive/populate-entity-tables/populate-page-meta.js +267 -0
  195. package/lib/archive/populate-entity-tables/populate-resource-items.d.ts +22 -0
  196. package/lib/archive/populate-entity-tables/populate-resource-items.js +114 -0
  197. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.d.ts +31 -0
  198. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.js +33 -0
  199. package/lib/archive/populate-entity-tables/resolve-blob-refs.d.ts +31 -0
  200. package/lib/archive/populate-entity-tables/resolve-blob-refs.js +100 -0
  201. package/lib/archive/populate-entity-tables/resolve-content-type-refs.d.ts +22 -0
  202. package/lib/archive/populate-entity-tables/resolve-content-type-refs.js +27 -0
  203. package/lib/archive/populate-entity-tables/resolve-header-sets.d.ts +49 -0
  204. package/lib/archive/populate-entity-tables/resolve-header-sets.js +122 -0
  205. package/lib/archive/populate-entity-tables/resolve-json-refs.d.ts +25 -0
  206. package/lib/archive/populate-entity-tables/resolve-json-refs.js +67 -0
  207. package/lib/archive/populate-entity-tables/resolve-text-refs.d.ts +30 -0
  208. package/lib/archive/populate-entity-tables/resolve-text-refs.js +61 -0
  209. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.d.ts +21 -0
  210. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.js +27 -0
  211. package/lib/archive/populate-entity-tables/resolve-url-refs.d.ts +33 -0
  212. package/lib/archive/populate-entity-tables/resolve-url-refs.js +60 -0
  213. package/lib/archive/populate-entity-tables/test-utils/count-rows.d.ts +17 -0
  214. package/lib/archive/populate-entity-tables/test-utils/count-rows.js +20 -0
  215. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.d.ts +25 -0
  216. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.js +42 -0
  217. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.d.ts +23 -0
  218. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.js +178 -0
  219. package/lib/archive/populate-entity-tables/types.d.ts +157 -0
  220. package/lib/archive/populate-entity-tables/types.js +12 -0
  221. package/lib/archive/populate-entity-tables/upsert-text-refs.d.ts +38 -0
  222. package/lib/archive/populate-entity-tables/upsert-text-refs.js +78 -0
  223. package/lib/archive/populate-ref-tables/classify-content-type.d.ts +16 -0
  224. package/lib/archive/populate-ref-tables/classify-content-type.js +52 -0
  225. package/lib/archive/populate-ref-tables/compute-content-hash.d.ts +22 -0
  226. package/lib/archive/populate-ref-tables/compute-content-hash.js +26 -0
  227. package/lib/archive/populate-ref-tables/compute-header-flags.d.ts +16 -0
  228. package/lib/archive/populate-ref-tables/compute-header-flags.js +70 -0
  229. package/lib/archive/populate-ref-tables/content-type-rules.d.ts +38 -0
  230. package/lib/archive/populate-ref-tables/content-type-rules.js +133 -0
  231. package/lib/archive/populate-ref-tables/create-header-table-caches.d.ts +25 -0
  232. package/lib/archive/populate-ref-tables/create-header-table-caches.js +49 -0
  233. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.d.ts +15 -0
  234. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.js +15 -0
  235. package/lib/archive/populate-ref-tables/decode-data-uri.d.ts +21 -0
  236. package/lib/archive/populate-ref-tables/decode-data-uri.js +126 -0
  237. package/lib/archive/populate-ref-tables/decompose-header-set.d.ts +29 -0
  238. package/lib/archive/populate-ref-tables/decompose-header-set.js +157 -0
  239. package/lib/archive/populate-ref-tables/decompose-url.d.ts +25 -0
  240. package/lib/archive/populate-ref-tables/decompose-url.js +70 -0
  241. package/lib/archive/populate-ref-tables/header-stability.d.ts +19 -0
  242. package/lib/archive/populate-ref-tables/header-stability.js +22 -0
  243. package/lib/archive/populate-ref-tables/header-value-cache-key.d.ts +17 -0
  244. package/lib/archive/populate-ref-tables/header-value-cache-key.js +19 -0
  245. package/lib/archive/populate-ref-tables/normalize-mime.d.ts +24 -0
  246. package/lib/archive/populate-ref-tables/normalize-mime.js +36 -0
  247. package/lib/archive/populate-ref-tables/populate-blob-refs.d.ts +38 -0
  248. package/lib/archive/populate-ref-tables/populate-blob-refs.js +134 -0
  249. package/lib/archive/populate-ref-tables/populate-content-type-refs.d.ts +27 -0
  250. package/lib/archive/populate-ref-tables/populate-content-type-refs.js +70 -0
  251. package/lib/archive/populate-ref-tables/populate-header-tables.d.ts +35 -0
  252. package/lib/archive/populate-ref-tables/populate-header-tables.js +80 -0
  253. package/lib/archive/populate-ref-tables/populate-json-refs.d.ts +29 -0
  254. package/lib/archive/populate-ref-tables/populate-json-refs.js +101 -0
  255. package/lib/archive/populate-ref-tables/populate-refs.d.ts +51 -0
  256. package/lib/archive/populate-ref-tables/populate-refs.js +62 -0
  257. package/lib/archive/populate-ref-tables/populate-text-refs.d.ts +32 -0
  258. package/lib/archive/populate-ref-tables/populate-text-refs.js +133 -0
  259. package/lib/archive/populate-ref-tables/populate-url-refs.d.ts +28 -0
  260. package/lib/archive/populate-ref-tables/populate-url-refs.js +148 -0
  261. package/lib/archive/populate-ref-tables/test-utils/count-rows.d.ts +15 -0
  262. package/lib/archive/populate-ref-tables/test-utils/count-rows.js +17 -0
  263. package/lib/archive/populate-ref-tables/types.d.ts +197 -0
  264. package/lib/archive/populate-ref-tables/types.js +7 -0
  265. package/lib/archive/populate-ref-tables/upsert-one-header-set.d.ts +34 -0
  266. package/lib/archive/populate-ref-tables/upsert-one-header-set.js +208 -0
  267. package/lib/archive/populate-ref-tables/volatile-header-names.d.ts +20 -0
  268. package/lib/archive/populate-ref-tables/volatile-header-names.js +33 -0
  269. package/lib/archive/redirect-table.d.ts +4 -2
  270. package/lib/archive/redirect-table.js +15 -10
  271. package/lib/archive/resolve-redirect-chain.d.ts +3 -3
  272. package/lib/archive/resolve-redirect-chain.js +2 -2
  273. package/lib/archive/resource.d.ts +1 -1
  274. package/lib/archive/retarget-legacy-fk-tables.d.ts +47 -0
  275. package/lib/archive/retarget-legacy-fk-tables.js +107 -0
  276. package/lib/archive/test-utils/fk-parent-tables.d.ts +15 -0
  277. package/lib/archive/test-utils/fk-parent-tables.js +19 -0
  278. package/lib/archive/test-utils/seed-content-item.d.ts +35 -0
  279. package/lib/archive/test-utils/seed-content-item.js +42 -0
  280. package/lib/archive/test-utils/setup-legacy-fk-db.d.ts +33 -0
  281. package/lib/archive/test-utils/setup-legacy-fk-db.js +270 -0
  282. package/lib/archive/types.d.ts +127 -24
  283. package/lib/archive/verify-migration/capture-rejection.d.ts +24 -0
  284. package/lib/archive/verify-migration/capture-rejection.js +31 -0
  285. package/lib/archive/verify-migration/check-anchor-edges-count.d.ts +34 -0
  286. package/lib/archive/verify-migration/check-anchor-edges-count.js +72 -0
  287. package/lib/archive/verify-migration/check-anchor-edges-sum.d.ts +13 -0
  288. package/lib/archive/verify-migration/check-anchor-edges-sum.js +27 -0
  289. package/lib/archive/verify-migration/check-content-items-count.d.ts +16 -0
  290. package/lib/archive/verify-migration/check-content-items-count.js +30 -0
  291. package/lib/archive/verify-migration/check-content-type-preservation.d.ts +22 -0
  292. package/lib/archive/verify-migration/check-content-type-preservation.js +40 -0
  293. package/lib/archive/verify-migration/check-foreign-key-integrity.d.ts +31 -0
  294. package/lib/archive/verify-migration/check-foreign-key-integrity.js +47 -0
  295. package/lib/archive/verify-migration/check-image-items-count.d.ts +12 -0
  296. package/lib/archive/verify-migration/check-image-items-count.js +26 -0
  297. package/lib/archive/verify-migration/check-page-meta-count.d.ts +15 -0
  298. package/lib/archive/verify-migration/check-page-meta-count.js +31 -0
  299. package/lib/archive/verify-migration/check-reader-parity.d.ts +23 -0
  300. package/lib/archive/verify-migration/check-reader-parity.js +211 -0
  301. package/lib/archive/verify-migration/check-resource-items-count.d.ts +17 -0
  302. package/lib/archive/verify-migration/check-resource-items-count.js +33 -0
  303. package/lib/archive/verify-migration/check-url-round-trip.d.ts +43 -0
  304. package/lib/archive/verify-migration/check-url-round-trip.js +112 -0
  305. package/lib/archive/verify-migration/types.d.ts +70 -0
  306. package/lib/archive/verify-migration/types.js +63 -0
  307. package/lib/archive/verify-migration/verify-migration.d.ts +41 -0
  308. package/lib/archive/verify-migration/verify-migration.js +120 -0
  309. package/lib/crawler/build-redirect-event.d.ts +1 -1
  310. package/lib/crawler/build-redirect-event.js +1 -1
  311. package/lib/crawler/capture-image-dom-paths.d.ts +33 -0
  312. package/lib/crawler/capture-image-dom-paths.js +39 -0
  313. package/lib/crawler/clear-dns-burned-host-cache.d.ts +1 -1
  314. package/lib/crawler/clear-dns-burned-host-cache.js +1 -1
  315. package/lib/crawler/collect-image-dom-paths.d.ts +23 -0
  316. package/lib/crawler/collect-image-dom-paths.js +64 -0
  317. package/lib/crawler/crawler.d.ts +19 -0
  318. package/lib/crawler/crawler.js +40 -26
  319. package/lib/crawler/dns-burned-host-cache.d.ts +3 -3
  320. package/lib/crawler/dns-burned-host-cache.js +3 -3
  321. package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +2 -2
  322. package/lib/crawler/dns-burned-host-short-circuit-counter.js +2 -2
  323. package/lib/crawler/inject-scope-auth.d.ts +1 -1
  324. package/lib/crawler/inject-scope-auth.js +1 -1
  325. package/lib/crawler/normalize-content-type.d.ts +1 -1
  326. package/lib/crawler/normalize-content-type.js +1 -1
  327. package/lib/crawler/types.d.ts +3 -3
  328. package/lib/crawler-orchestrator.d.ts +9 -0
  329. package/lib/crawler-orchestrator.js +44 -28
  330. package/lib/crawler.d.ts +12 -0
  331. package/lib/crawler.js +21 -0
  332. package/lib/permanent-error-kinds.d.ts +1 -1
  333. package/lib/permanent-error-kinds.js +1 -1
  334. package/lib/types.d.ts +1 -1
  335. package/lib/utils/compute-file-sha256.d.ts +5 -4
  336. package/lib/utils/compute-file-sha256.js +5 -4
  337. package/lib/utils/error/emit-error-with-retry.d.ts +1 -1
  338. package/lib/utils/error/emit-error-with-retry.js +1 -1
  339. package/package.json +10 -10
  340. package/lib/archive/migrate-crawl-errors.d.ts +0 -20
  341. package/lib/archive/migrate-crawl-errors.js +0 -38
  342. package/lib/archive/migrate-html-blob-tables.d.ts +0 -24
  343. package/lib/archive/migrate-html-blob-tables.js +0 -53
  344. package/lib/archive/migrate-inventory-runs.d.ts +0 -29
  345. package/lib/archive/migrate-inventory-runs.js +0 -52
  346. package/lib/archive/migrate-page-errors.d.ts +0 -16
  347. package/lib/archive/migrate-page-errors.js +0 -35
  348. package/lib/archive/migrate-pages-resources-source.d.ts +0 -16
  349. package/lib/archive/migrate-pages-resources-source.js +0 -46
@@ -0,0 +1,27 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ /**
3
+ * Empties every map in a {@link WriteRefCaches} bundle in place.
4
+ *
5
+ * Called when a write transaction that used the caches rolls back. SQLite
6
+ * `ROLLBACK` undoes every row the transaction inserted, but `AUTOINCREMENT`
7
+ * counters never rewind — so an id cached optimistically during the failed
8
+ * attempt would survive the rollback and no longer correspond to any row.
9
+ * A retried attempt reusing that id would issue `UPDATE ... WHERE id = ?`
10
+ * against a nonexistent row and silently affect zero rows instead of
11
+ * failing loudly.
12
+ *
13
+ * A full clear (rather than tracking which entries the failed transaction
14
+ * touched) is the simplest correct fix: transient write failures are rare
15
+ * (retry exists precisely because they are), so paying a one-time
16
+ * DB-again warm-up after a rollback is cheap next to the correctness risk
17
+ * of a partially-poisoned cache.
18
+ * @param caches - The cache bundle to clear.
19
+ * @example
20
+ * try {
21
+ * await knex.transaction(async (trx) => { ... });
22
+ * } catch (error) {
23
+ * clearWriteRefCaches(caches);
24
+ * throw error;
25
+ * }
26
+ */
27
+ export declare function clearWriteRefCaches(caches: WriteRefCaches): void;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Empties every map in a {@link WriteRefCaches} bundle in place.
3
+ *
4
+ * Called when a write transaction that used the caches rolls back. SQLite
5
+ * `ROLLBACK` undoes every row the transaction inserted, but `AUTOINCREMENT`
6
+ * counters never rewind — so an id cached optimistically during the failed
7
+ * attempt would survive the rollback and no longer correspond to any row.
8
+ * A retried attempt reusing that id would issue `UPDATE ... WHERE id = ?`
9
+ * against a nonexistent row and silently affect zero rows instead of
10
+ * failing loudly.
11
+ *
12
+ * A full clear (rather than tracking which entries the failed transaction
13
+ * touched) is the simplest correct fix: transient write failures are rare
14
+ * (retry exists precisely because they are), so paying a one-time
15
+ * DB-again warm-up after a rollback is cheap next to the correctness risk
16
+ * of a partially-poisoned cache.
17
+ * @param caches - The cache bundle to clear.
18
+ * @example
19
+ * try {
20
+ * await knex.transaction(async (trx) => { ... });
21
+ * } catch (error) {
22
+ * clearWriteRefCaches(caches);
23
+ * throw error;
24
+ * }
25
+ */
26
+ export function clearWriteRefCaches(caches) {
27
+ caches.urlIds.clear();
28
+ caches.contentItems.clear();
29
+ caches.resourceIds.clear();
30
+ caches.contentTypeIds.clear();
31
+ caches.jsonIds.clear();
32
+ caches.blobIds.clear();
33
+ caches.headers = null;
34
+ }
@@ -0,0 +1,17 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ /**
3
+ * Creates an empty {@link WriteRefCaches} bundle for one archive writer
4
+ * connection.
5
+ *
6
+ * All maps start empty and fill lazily as the write path touches URLs,
7
+ * content types, JSON payloads, and data-URI blobs. The header caches
8
+ * (`headers`) start as `null` and are warmed from the DB by the first
9
+ * header-set upsert — see {@link ./types.ts} for the cache-correctness
10
+ * argument (append-only ref tables + single writer connection).
11
+ * @returns A fresh cache bundle. One per `Database` instance; never share
12
+ * across connections.
13
+ * @example
14
+ * const caches = createWriteRefCaches();
15
+ * const urlId = await upsertUrlRef(knex, caches, 'https://example.com/');
16
+ */
17
+ export declare function createWriteRefCaches(): WriteRefCaches;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Creates an empty {@link WriteRefCaches} bundle for one archive writer
3
+ * connection.
4
+ *
5
+ * All maps start empty and fill lazily as the write path touches URLs,
6
+ * content types, JSON payloads, and data-URI blobs. The header caches
7
+ * (`headers`) start as `null` and are warmed from the DB by the first
8
+ * header-set upsert — see {@link ./types.ts} for the cache-correctness
9
+ * argument (append-only ref tables + single writer connection).
10
+ * @returns A fresh cache bundle. One per `Database` instance; never share
11
+ * across connections.
12
+ * @example
13
+ * const caches = createWriteRefCaches();
14
+ * const urlId = await upsertUrlRef(knex, caches, 'https://example.com/');
15
+ */
16
+ export function createWriteRefCaches() {
17
+ return {
18
+ urlIds: new Map(),
19
+ contentItems: new Map(),
20
+ resourceIds: new Map(),
21
+ contentTypeIds: new Map(),
22
+ jsonIds: new Map(),
23
+ blobIds: new Map(),
24
+ headers: null,
25
+ };
26
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Decodes one `json_refs` payload back into its JSON string.
3
+ *
4
+ * The write path (`upsert-json-ref.ts`) and the archive-migration
5
+ * populate (`populate-json-refs.ts`) both store bodies zstd-compressed;
6
+ * the `'none'` codec is accepted because the `json_refs.codec` column
7
+ * permits it, not because either writer currently produces it. Corrupt
8
+ * bodies fail closed to `null` rather than throwing — readers treat an
9
+ * undecodable payload the same as an absent one.
10
+ * @param body - The raw `json_refs.json_text` body, or null when absent.
11
+ * @param codec - The `json_refs.codec` value, or null when absent.
12
+ * @returns The decoded JSON string, or null when there is no body or the
13
+ * body cannot be decoded.
14
+ * @example
15
+ * const metaExtras = decodeJsonRef(row.extras_body, row.extras_codec);
16
+ */
17
+ export declare function decodeJsonRef(body: Buffer | string | null, codec: 'zstd' | 'none' | null): string | null;
@@ -0,0 +1,31 @@
1
+ import { zstdDecompressSync } from 'node:zlib';
2
+ /**
3
+ * Decodes one `json_refs` payload back into its JSON string.
4
+ *
5
+ * The write path (`upsert-json-ref.ts`) and the archive-migration
6
+ * populate (`populate-json-refs.ts`) both store bodies zstd-compressed;
7
+ * the `'none'` codec is accepted because the `json_refs.codec` column
8
+ * permits it, not because either writer currently produces it. Corrupt
9
+ * bodies fail closed to `null` rather than throwing — readers treat an
10
+ * undecodable payload the same as an absent one.
11
+ * @param body - The raw `json_refs.json_text` body, or null when absent.
12
+ * @param codec - The `json_refs.codec` value, or null when absent.
13
+ * @returns The decoded JSON string, or null when there is no body or the
14
+ * body cannot be decoded.
15
+ * @example
16
+ * const metaExtras = decodeJsonRef(row.extras_body, row.extras_codec);
17
+ */
18
+ export function decodeJsonRef(body, codec) {
19
+ if (body == null) {
20
+ return null;
21
+ }
22
+ try {
23
+ if (codec === 'zstd') {
24
+ return zstdDecompressSync(body).toString('utf8');
25
+ }
26
+ return typeof body === 'string' ? body : body.toString('utf8');
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
@@ -0,0 +1,20 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Loads and merges the response headers for every given `header_sets.id`
4
+ * back into flat `Record<name, value>` objects, in one chunked batch
5
+ * (never one query per row).
6
+ *
7
+ * Multi-value headers (repeated `Set-Cookie` etc.) are joined with
8
+ * `', '` in `occurrence` order within each name — the flat-record shape
9
+ * the pre-0.13 `responseHeaders` JSON column stored, which every reader
10
+ * of the reconstructed rows still expects.
11
+ * @param knex - Knex query builder connected to the archive DB.
12
+ * @param headerSetIds - Distinct `header_sets.id` values to load. Empty
13
+ * input returns an empty map without touching the DB.
14
+ * @returns Map from `header_sets.id` to the merged header record. Ids
15
+ * with no entries are absent from the map.
16
+ * @example
17
+ * const headersBySetId = await loadResponseHeadersBySetIds(knex, [1, 2]);
18
+ * const record = headersBySetId.get(1) ?? {};
19
+ */
20
+ export declare function loadResponseHeadersBySetIds(knex: Knex, headerSetIds: readonly number[]): Promise<Map<number, Record<string, string>>>;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Ids per `WHERE header_set_id IN (?, ...)` chunk. Modern SQLite builds
3
+ * (including the vendored libsql) reject statements binding more than
4
+ * 32,766 parameters, and header sets dedupe poorly on real sites (Date /
5
+ * ETag / Set-Cookie make most sets unique), so an IN-list sized by the
6
+ * page batch without chunking would exceed the limit on large archives.
7
+ * 800 matches
8
+ * the `LOOKUP_CHUNK_SIZE` convention the populate/upsert paths already
9
+ * use (`resolve-text-refs.ts` and siblings).
10
+ */
11
+ const LOOKUP_CHUNK_SIZE = 800;
12
+ /**
13
+ * Loads and merges the response headers for every given `header_sets.id`
14
+ * back into flat `Record<name, value>` objects, in one chunked batch
15
+ * (never one query per row).
16
+ *
17
+ * Multi-value headers (repeated `Set-Cookie` etc.) are joined with
18
+ * `', '` in `occurrence` order within each name — the flat-record shape
19
+ * the pre-0.13 `responseHeaders` JSON column stored, which every reader
20
+ * of the reconstructed rows still expects.
21
+ * @param knex - Knex query builder connected to the archive DB.
22
+ * @param headerSetIds - Distinct `header_sets.id` values to load. Empty
23
+ * input returns an empty map without touching the DB.
24
+ * @returns Map from `header_sets.id` to the merged header record. Ids
25
+ * with no entries are absent from the map.
26
+ * @example
27
+ * const headersBySetId = await loadResponseHeadersBySetIds(knex, [1, 2]);
28
+ * const record = headersBySetId.get(1) ?? {};
29
+ */
30
+ export async function loadResponseHeadersBySetIds(knex, headerSetIds) {
31
+ const merged = new Map();
32
+ for (let index = 0; index < headerSetIds.length; index += LOOKUP_CHUNK_SIZE) {
33
+ const chunk = headerSetIds.slice(index, index + LOOKUP_CHUNK_SIZE);
34
+ const headerRows = (await knex('header_set_entries as hse')
35
+ .join('header_name_refs as hnr', 'hnr.id', 'hse.name_id')
36
+ .join('header_value_refs as hvr', 'hvr.id', 'hse.value_id')
37
+ .whereIn('hse.header_set_id', chunk)
38
+ .orderBy(['hse.header_set_id', 'hnr.name', 'hse.occurrence'])
39
+ .select('hse.header_set_id as headerSetId', 'hnr.name as name', 'hvr.value as value'));
40
+ for (const row of headerRows) {
41
+ const bySet = merged.get(row.headerSetId) ?? new Map();
42
+ const values = bySet.get(row.name) ?? [];
43
+ values.push(row.value);
44
+ bySet.set(row.name, values);
45
+ merged.set(row.headerSetId, bySet);
46
+ }
47
+ }
48
+ const result = new Map();
49
+ for (const [setId, bySet] of merged) {
50
+ result.set(setId, Object.fromEntries([...bySet.entries()].map(([k, v]) => [k, v.join(', ')])));
51
+ }
52
+ return result;
53
+ }
@@ -0,0 +1,61 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { PageSource } from '../../types.js';
3
+ import type { Knex } from 'knex';
4
+ /**
5
+ * Returns the `content_items.id` for `url`, inserting a placeholder row
6
+ * (via `url_refs`) when the URL is not yet known. Foundational identity
7
+ * primitive shared by every op that needs to record an edge or lookup to
8
+ * a URL — the `content_items` successor of the legacy `pages.id`
9
+ * resolver.
10
+ *
11
+ * **Source semantics.** `source` is written ONLY on the INSERT path —
12
+ * when the row already exists, the INSERT is never reached and the
13
+ * existing row's `source` stays untouched. This is what keeps a second
14
+ * `crawl --inventory` from "demoting" a page that was first labelled
15
+ * `'inventory-seed'` back to `'inventory-discovered'` on later passes.
16
+ *
17
+ * **Crawled-wins downgrade.** When a row that was previously labelled
18
+ * `'inventory-seed'` or `'inventory-discovered'` is re-encountered via a
19
+ * `'crawled'`-lineage anchor (the parent page is part of the graph
20
+ * reachable from the original crawl roots), the row is downgraded to
21
+ * `'crawled'`. The inventory goal is finding orphans — anything reachable
22
+ * from the crawled chain is NOT an orphan and should not retain an
23
+ * inventory label. The cached `source` is updated in the same step so a
24
+ * later hit does not re-issue the UPDATE.
25
+ *
26
+ * **Cache poisoning on rollback.** Entries cached inside a transaction
27
+ * that later rolls back would point at ids that no longer exist. Every
28
+ * write op that opens a multi-statement transaction around this function
29
+ * must discard the whole cache bundle when that transaction fails,
30
+ * before any retry — see the try/catch + `clearWriteRefCaches` wrappers
31
+ * in `update-page.ts` and `record-redirect.ts`.
32
+ *
33
+ * **Race-condition safety.** The INSERT uses `ON CONFLICT(url_id) DO
34
+ * UPDATE SET url_id = url_id RETURNING id, source` — the same no-op
35
+ * -update idiom as `upsertUrlRef` — so when a concurrent transaction
36
+ * wins the insert race, the statement still returns the existing row's
37
+ * id and source in one round trip. `onConflict().ignore()` without
38
+ * `RETURNING` must NOT be used here: knex's sqlite-family dialects
39
+ * report the connection's stale `lastInsertRowid` for a conflict-ignored
40
+ * insert, which reads as a valid id belonging to an unrelated row.
41
+ * @param qb - Knex instance OR a transaction, used verbatim for every
42
+ * statement this function issues. Pass the `trx` when running inside a
43
+ * transaction so reads see uncommitted writes from that transaction.
44
+ * @param caches - The connection's write-side id caches; mutated in place.
45
+ * @param url - The URL to look up or insert (normalised
46
+ * `withoutHashAndAuth` form, matching the legacy identity contract).
47
+ * @param isExternal - Optional; recorded on new inserts only. `1` marks
48
+ * the row as an external URL that will never be scraped as a target.
49
+ * Defaults to `0` (in-scope) on insert, mirroring the legacy column
50
+ * default.
51
+ * @param source - Optional provenance label put on a newly-inserted row.
52
+ * Omit to let the `content_items.source` DEFAULT (`'crawled'`) apply.
53
+ * Pass `'crawled'` to arm the crawled-wins downgrade on existing
54
+ * inventory-labelled rows.
55
+ * @returns The `content_items.id` of the existing or newly inserted row.
56
+ * @throws {Error} When the upsert's `RETURNING` yields no row — should
57
+ * not happen, so it surfaces as a hard error.
58
+ * @example
59
+ * const pageId = await resolveContentItemId(trx, caches, anchor.href, 1, 'crawled');
60
+ */
61
+ export declare function resolveContentItemId(qb: Knex | Knex.Transaction, caches: WriteRefCaches, url: string, isExternal?: 0 | 1, source?: PageSource): Promise<number>;
@@ -0,0 +1,111 @@
1
+ import { upsertUrlRef } from './upsert-url-ref.js';
2
+ /**
3
+ * Returns the `content_items.id` for `url`, inserting a placeholder row
4
+ * (via `url_refs`) when the URL is not yet known. Foundational identity
5
+ * primitive shared by every op that needs to record an edge or lookup to
6
+ * a URL — the `content_items` successor of the legacy `pages.id`
7
+ * resolver.
8
+ *
9
+ * **Source semantics.** `source` is written ONLY on the INSERT path —
10
+ * when the row already exists, the INSERT is never reached and the
11
+ * existing row's `source` stays untouched. This is what keeps a second
12
+ * `crawl --inventory` from "demoting" a page that was first labelled
13
+ * `'inventory-seed'` back to `'inventory-discovered'` on later passes.
14
+ *
15
+ * **Crawled-wins downgrade.** When a row that was previously labelled
16
+ * `'inventory-seed'` or `'inventory-discovered'` is re-encountered via a
17
+ * `'crawled'`-lineage anchor (the parent page is part of the graph
18
+ * reachable from the original crawl roots), the row is downgraded to
19
+ * `'crawled'`. The inventory goal is finding orphans — anything reachable
20
+ * from the crawled chain is NOT an orphan and should not retain an
21
+ * inventory label. The cached `source` is updated in the same step so a
22
+ * later hit does not re-issue the UPDATE.
23
+ *
24
+ * **Cache poisoning on rollback.** Entries cached inside a transaction
25
+ * that later rolls back would point at ids that no longer exist. Every
26
+ * write op that opens a multi-statement transaction around this function
27
+ * must discard the whole cache bundle when that transaction fails,
28
+ * before any retry — see the try/catch + `clearWriteRefCaches` wrappers
29
+ * in `update-page.ts` and `record-redirect.ts`.
30
+ *
31
+ * **Race-condition safety.** The INSERT uses `ON CONFLICT(url_id) DO
32
+ * UPDATE SET url_id = url_id RETURNING id, source` — the same no-op
33
+ * -update idiom as `upsertUrlRef` — so when a concurrent transaction
34
+ * wins the insert race, the statement still returns the existing row's
35
+ * id and source in one round trip. `onConflict().ignore()` without
36
+ * `RETURNING` must NOT be used here: knex's sqlite-family dialects
37
+ * report the connection's stale `lastInsertRowid` for a conflict-ignored
38
+ * insert, which reads as a valid id belonging to an unrelated row.
39
+ * @param qb - Knex instance OR a transaction, used verbatim for every
40
+ * statement this function issues. Pass the `trx` when running inside a
41
+ * transaction so reads see uncommitted writes from that transaction.
42
+ * @param caches - The connection's write-side id caches; mutated in place.
43
+ * @param url - The URL to look up or insert (normalised
44
+ * `withoutHashAndAuth` form, matching the legacy identity contract).
45
+ * @param isExternal - Optional; recorded on new inserts only. `1` marks
46
+ * the row as an external URL that will never be scraped as a target.
47
+ * Defaults to `0` (in-scope) on insert, mirroring the legacy column
48
+ * default.
49
+ * @param source - Optional provenance label put on a newly-inserted row.
50
+ * Omit to let the `content_items.source` DEFAULT (`'crawled'`) apply.
51
+ * Pass `'crawled'` to arm the crawled-wins downgrade on existing
52
+ * inventory-labelled rows.
53
+ * @returns The `content_items.id` of the existing or newly inserted row.
54
+ * @throws {Error} When the upsert's `RETURNING` yields no row — should
55
+ * not happen, so it surfaces as a hard error.
56
+ * @example
57
+ * const pageId = await resolveContentItemId(trx, caches, anchor.href, 1, 'crawled');
58
+ */
59
+ export async function resolveContentItemId(qb, caches, url, isExternal, source) {
60
+ const cached = caches.contentItems.get(url);
61
+ if (cached !== undefined) {
62
+ await applyCrawledWinsDowngrade(qb, cached, source);
63
+ return cached.id;
64
+ }
65
+ const urlId = await upsertUrlRef(qb, caches, url);
66
+ const [record] = (await qb
67
+ .select('id', 'source')
68
+ .from('content_items')
69
+ .where('url_id', urlId));
70
+ if (record !== undefined) {
71
+ const entry = { id: record.id, source: record.source };
72
+ await applyCrawledWinsDowngrade(qb, entry, source);
73
+ caches.contentItems.set(url, entry);
74
+ return entry.id;
75
+ }
76
+ const insertedRows = await qb.raw(`INSERT INTO content_items (url_id, scraped, is_target, is_external${source === undefined ? '' : ', source'})
77
+ VALUES (?, 0, 0, ?${source === undefined ? '' : ', ?'})
78
+ ON CONFLICT(url_id) DO UPDATE SET url_id = url_id
79
+ RETURNING id, source`, source === undefined ? [urlId, isExternal ?? 0] : [urlId, isExternal ?? 0, source]);
80
+ const inserted = insertedRows[0];
81
+ if (inserted === undefined) {
82
+ throw new Error(`Failed to insert a new content item: ${url}`);
83
+ }
84
+ const insertedEntry = {
85
+ id: inserted.id,
86
+ source: inserted.source,
87
+ };
88
+ // A conflict means a concurrent writer created the row between this
89
+ // function's SELECT miss and the INSERT — the returned `source` is that
90
+ // row's value, so the downgrade must be evaluated exactly as on the
91
+ // SELECT-hit path.
92
+ await applyCrawledWinsDowngrade(qb, insertedEntry, source);
93
+ caches.contentItems.set(url, insertedEntry);
94
+ return insertedEntry.id;
95
+ }
96
+ /**
97
+ * Fires the crawled-wins downgrade when a `'crawled'`-lineage resolution
98
+ * lands on a row whose last-known `source` is an inventory label, and
99
+ * keeps the cache entry in sync so the UPDATE runs at most once per
100
+ * (connection, row).
101
+ * @param qb - Knex instance or transaction.
102
+ * @param entry - The cached identity to check and mutate.
103
+ * @param source - The resolution's lineage label.
104
+ */
105
+ async function applyCrawledWinsDowngrade(qb, entry, source) {
106
+ if (source !== 'crawled' || entry.source === 'crawled') {
107
+ return;
108
+ }
109
+ await qb('content_items').where('id', entry.id).update({ source: 'crawled' });
110
+ entry.source = 'crawled';
111
+ }
@@ -0,0 +1,23 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Routes one URL-shaped value to either `url_refs` or `blob_refs` per the
5
+ * data-URI threshold rule (large data URIs live in `blob_refs`, everything
6
+ * else in `url_refs`), upserting into whichever dictionary applies.
7
+ *
8
+ * Shared by every writer that can receive a `data:` URI in a URL-shaped
9
+ * column: `image_items.src` / `current_src` and `resource_items`' own
10
+ * identity URL. At most one of `url` / `blob` is non-null; both are `null`
11
+ * when the value is empty or a malformed data URI that fails to decode.
12
+ * @param qb - Knex instance or transaction connected to the archive DB.
13
+ * @param caches - The connection's write-side id caches.
14
+ * @param value - Raw URL-shaped value.
15
+ * @returns `{ url, blob }` pair with at most one non-null field.
16
+ * @example
17
+ * const slot = await resolveUrlOrBlob(trx, caches, image.src);
18
+ * // slot.url set for a regular URL, slot.blob set for a large data: URI.
19
+ */
20
+ export declare function resolveUrlOrBlob(qb: Knex | Knex.Transaction, caches: WriteRefCaches, value: string | null | undefined): Promise<{
21
+ url: number | null;
22
+ blob: number | null;
23
+ }>;
@@ -0,0 +1,29 @@
1
+ import { DATA_URI_URL_REFS_LIMIT } from '../../populate-ref-tables/data-uri-url-refs-limit.js';
2
+ import { upsertBlobRef } from './upsert-blob-ref.js';
3
+ import { upsertUrlRef } from './upsert-url-ref.js';
4
+ /**
5
+ * Routes one URL-shaped value to either `url_refs` or `blob_refs` per the
6
+ * data-URI threshold rule (large data URIs live in `blob_refs`, everything
7
+ * else in `url_refs`), upserting into whichever dictionary applies.
8
+ *
9
+ * Shared by every writer that can receive a `data:` URI in a URL-shaped
10
+ * column: `image_items.src` / `current_src` and `resource_items`' own
11
+ * identity URL. At most one of `url` / `blob` is non-null; both are `null`
12
+ * when the value is empty or a malformed data URI that fails to decode.
13
+ * @param qb - Knex instance or transaction connected to the archive DB.
14
+ * @param caches - The connection's write-side id caches.
15
+ * @param value - Raw URL-shaped value.
16
+ * @returns `{ url, blob }` pair with at most one non-null field.
17
+ * @example
18
+ * const slot = await resolveUrlOrBlob(trx, caches, image.src);
19
+ * // slot.url set for a regular URL, slot.blob set for a large data: URI.
20
+ */
21
+ export async function resolveUrlOrBlob(qb, caches, value) {
22
+ if (typeof value !== 'string' || value === '') {
23
+ return { url: null, blob: null };
24
+ }
25
+ if (value.startsWith('data:') && value.length > DATA_URI_URL_REFS_LIMIT) {
26
+ return { url: null, blob: await upsertBlobRef(qb, caches, value) };
27
+ }
28
+ return { url: await upsertUrlRef(qb, caches, value), blob: null };
29
+ }
@@ -0,0 +1,16 @@
1
+ import type { RetryCallOptions } from '@d-zero/shared/retry';
2
+ /**
3
+ * Default retry options shared by every `Database.*` op that wraps its
4
+ * knex work in {@link ../../../utils/error/emit-error-with-retry.ts}
5
+ * (`emitErrorAndRetry`) or {@link ../../../utils/error/emit-error.ts}
6
+ * (`emitError`, no retry). Three retries at 300 ms intervals covers the
7
+ * transient libsql failure modes (WAL contention, brief lock waits)
8
+ * without letting a genuinely broken query hang the crawler for long.
9
+ *
10
+ * Two ops deliberately do NOT use these settings and instead wrap
11
+ * `retryCall` themselves so they can override `label` (`getResourceByUrl`)
12
+ * or opt out entirely (`checkpoint`, `destroy`, `getKnex`,
13
+ * `setUrlOrder`, the `constructor`, and `Database.connect`). See the
14
+ * corresponding op files for the reasoning at each site.
15
+ */
16
+ export declare const retrySetting: RetryCallOptions;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Default retry options shared by every `Database.*` op that wraps its
3
+ * knex work in {@link ../../../utils/error/emit-error-with-retry.ts}
4
+ * (`emitErrorAndRetry`) or {@link ../../../utils/error/emit-error.ts}
5
+ * (`emitError`, no retry). Three retries at 300 ms intervals covers the
6
+ * transient libsql failure modes (WAL contention, brief lock waits)
7
+ * without letting a genuinely broken query hang the crawler for long.
8
+ *
9
+ * Two ops deliberately do NOT use these settings and instead wrap
10
+ * `retryCall` themselves so they can override `label` (`getResourceByUrl`)
11
+ * or opt out entirely (`checkpoint`, `destroy`, `getKnex`,
12
+ * `setUrlOrder`, the `constructor`, and `Database.connect`). See the
13
+ * corresponding op files for the reasoning at each site.
14
+ */
15
+ export const retrySetting = {
16
+ interval: 300,
17
+ retries: 3,
18
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Parses a JSON column value, returning `null` on parse failure rather
3
+ * than throwing. JSON columns in `page_jsonld` (`parsed`) and `page_tags`
4
+ * (`categories`, `sources`) are written by `JSON.stringify` and round-trip
5
+ * cleanly under normal conditions; a hand-edited archive that has
6
+ * malformed JSON in those columns should degrade gracefully rather than
7
+ * propagate a parse error up to the consumer.
8
+ * @param value - JSON-encoded text.
9
+ * @returns Parsed value, or `null` if the input could not be parsed.
10
+ */
11
+ export declare function safeParseJson(value: string): unknown;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Parses a JSON column value, returning `null` on parse failure rather
3
+ * than throwing. JSON columns in `page_jsonld` (`parsed`) and `page_tags`
4
+ * (`categories`, `sources`) are written by `JSON.stringify` and round-trip
5
+ * cleanly under normal conditions; a hand-edited archive that has
6
+ * malformed JSON in those columns should degrade gracefully rather than
7
+ * propagate a parse error up to the consumer.
8
+ * @param value - JSON-encoded text.
9
+ * @returns Parsed value, or `null` if the input could not be parsed.
10
+ */
11
+ export function safeParseJson(value) {
12
+ try {
13
+ return JSON.parse(value);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
@@ -0,0 +1,53 @@
1
+ import type { HeaderTableCaches } from '../../populate-ref-tables/types.js';
2
+ import type { PageSource } from '../../types.js';
3
+ /**
4
+ * One cached `content_items` identity: the row id plus the provenance
5
+ * label at the time it was last read or written through the cache. The
6
+ * `source` copy lets {@link ../_shared/resolve-content-item-id.ts} apply
7
+ * the crawled-wins downgrade without re-reading the row on every anchor
8
+ * hit.
9
+ */
10
+ export interface ContentItemCacheEntry {
11
+ /** `content_items.id`. */
12
+ id: number;
13
+ /** `content_items.source` as last observed / written by this process. */
14
+ source: PageSource;
15
+ }
16
+ /**
17
+ * In-process id caches shared across one archive connection's write path.
18
+ *
19
+ * Every ref-table upsert primitive under `db-ops/_shared/` consults these
20
+ * maps before touching SQL. The caches are correct for the lifetime of a
21
+ * single writer connection because ref rows are append-only (`url_refs`,
22
+ * `text_refs`, `json_refs`, `blob_refs`, `content_type_refs`, header
23
+ * dictionaries never delete rows) and `content_items` identities
24
+ * (`id`, `url_id`) are never reassigned — only the cached `source` can
25
+ * change, and the sole writer of that column is the cache-aware downgrade
26
+ * in `resolve-content-item-id.ts` itself.
27
+ *
28
+ * NOT safe to share across processes or across two concurrent writer
29
+ * connections — the crawler holds a single writer connection per archive
30
+ * (enforced by the archive lock), so no such sharing exists.
31
+ */
32
+ export interface WriteRefCaches {
33
+ /** `url string → url_refs.id`. */
34
+ readonly urlIds: Map<string, number>;
35
+ /** `url string → content_items` identity (id + last-known source). */
36
+ readonly contentItems: Map<string, ContentItemCacheEntry>;
37
+ /** `url string → resource_items.id`. */
38
+ readonly resourceIds: Map<string, number>;
39
+ /** `normalized content-type raw value → content_type_refs.id`. */
40
+ readonly contentTypeIds: Map<string, number>;
41
+ /** `hex(content hash) → json_refs.id`. */
42
+ readonly jsonIds: Map<string, number>;
43
+ /** `hex(content hash) → blob_refs.id`. */
44
+ readonly blobIds: Map<string, number>;
45
+ /**
46
+ * Header dictionary caches (`header_name_refs` / `header_value_refs` /
47
+ * `header_sets` id maps). `null` until the first header-set upsert
48
+ * warms them from the DB — warming issues three SELECTs, which a
49
+ * crawl that never stores headers (e.g. list-mode with no responses)
50
+ * should not pay for.
51
+ */
52
+ headers: HeaderTableCaches | null;
53
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { WriteRefCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Resolves the `blob_refs.id` for one large `data:` URI, decoding and
5
+ * inserting the deduplicated (hash-keyed) payload row when it is new.
6
+ *
7
+ * Callers route values here per the data-URI threshold rule (`data:` URIs
8
+ * longer than `DATA_URI_URL_REFS_LIMIT` — everything else belongs in
9
+ * `url_refs`). The payload bytes are decoded from the base64 /
10
+ * percent-encoded tail, hashed, and stored zstd-compressed — the same
11
+ * storage contract as the rows produced when migrating an existing
12
+ * archive (`populate-ref-tables/populate-blob-refs.ts`).
13
+ *
14
+ * Returns `null` when the data URI fails to decode (malformed base64 /
15
+ * truncated percent escape). A single malformed image `src` must not
16
+ * abort the page write; the caller stores `*_blob_id = null` and the
17
+ * information loss is limited to that one slot.
18
+ * @param qb - Knex instance or transaction connected to the archive DB.
19
+ * @param caches - The connection's write-side id caches; mutated in place.
20
+ * @param dataUri - The raw `data:` URI value.
21
+ * @returns The `blob_refs.id`, or `null` when the URI cannot be decoded.
22
+ * @example
23
+ * const id = await upsertBlobRef(knex, caches, 'data:image/png;base64,<payload>');
24
+ */
25
+ export declare function upsertBlobRef(qb: Knex | Knex.Transaction, caches: WriteRefCaches, dataUri: string): Promise<number | null>;