@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,148 @@
1
+ import { createProgressReporter } from '../create-progress-reporter.js';
2
+ import { DATA_URI_URL_REFS_LIMIT } from './data-uri-url-refs-limit.js';
3
+ import { decomposeUrl } from './decompose-url.js';
4
+ /**
5
+ * Rows inserted per `INSERT INTO url_refs ... VALUES (...)` statement. Kept
6
+ * well under SQLite's `SQLITE_MAX_VARIABLE_NUMBER` (default 32766 on
7
+ * modern builds) — each row binds 7 parameters (url + scheme + host +
8
+ * port + path + query_hash + fragment), so 500 rows = 3500 params.
9
+ */
10
+ const INSERT_CHUNK_SIZE = 500;
11
+ /**
12
+ * Rows scanned per keyset-paginated `SELECT` chunk. One SELECT reads all
13
+ * URL columns for the source table's chunk simultaneously so the pages
14
+ * table is scanned exactly once per populate call, not N-times-once-
15
+ * per-URL-column — a per-column scan would multiply migration wall-clock
16
+ * by the count of URL-shaped columns.
17
+ */
18
+ const READ_CHUNK_SIZE = 5000;
19
+ /**
20
+ * Source table + its URL-shaped columns. Each column is scanned in
21
+ * lock-step per row, then filtered through
22
+ * {@link routeDataUriAwayFromUrlRefs} so large data URIs skip `url_refs`
23
+ * and route to `blob_refs` instead (handled by
24
+ * {@link ./populate-blob-refs.ts}).
25
+ *
26
+ * The filter is applied to EVERY URL column (not just image columns):
27
+ * `og:image`, `icon_href`, `apple-touch-icon`, `twitter:image`, and even
28
+ * `og:url` / `canonical` can legally hold data URIs in the wild, and a
29
+ * >512-byte data URI in any of those must land in `blob_refs`, not
30
+ * pollute the URL dictionary.
31
+ */
32
+ const URL_SOURCES = [
33
+ {
34
+ table: 'pages',
35
+ columns: [
36
+ 'url',
37
+ 'canonical',
38
+ 'og_url',
39
+ 'og_image',
40
+ 'icon_href',
41
+ 'appleTouchIcon_href',
42
+ 'amphtml',
43
+ 'manifest',
44
+ 'twitter_image',
45
+ ],
46
+ },
47
+ { table: 'resources', columns: ['url'] },
48
+ { table: 'images', columns: ['src', 'currentSrc'] },
49
+ ];
50
+ /**
51
+ * Predicate applied to every URL column value: keep the value in the
52
+ * `url_refs` stream unless it is a data URI larger than
53
+ * {@link DATA_URI_URL_REFS_LIMIT}. Large data URIs route to `blob_refs`
54
+ * instead (via {@link ./populate-blob-refs.ts}).
55
+ * @param value - Raw column value (non-null, non-empty by contract).
56
+ * @returns `true` if the value belongs in `url_refs`.
57
+ */
58
+ function routeDataUriAwayFromUrlRefs(value) {
59
+ if (!value.startsWith('data:')) {
60
+ return true;
61
+ }
62
+ return value.length <= DATA_URI_URL_REFS_LIMIT;
63
+ }
64
+ /**
65
+ * Populates `url_refs` from every URL-shaped column across `pages`,
66
+ * `resources`, and `images` (issue #191).
67
+ *
68
+ * Runs two passes per source table:
69
+ *
70
+ * 1. **Collect distinct URLs** into an in-process `Set<string>`, streaming
71
+ * rows from each source table by keyset pagination on `id`. All the
72
+ * table's URL columns are read in a single SELECT so the pages scan
73
+ * is O(rows), not O(rows × columns). Peak memory is bounded by the
74
+ * count of distinct URLs, not the row count.
75
+ * 2. **Bulk-insert** into `url_refs` in chunks of {@link INSERT_CHUNK_SIZE},
76
+ * with the decomposed columns (`scheme` / `host` / `port` / `path` /
77
+ * `query_hash` / `fragment`) derived in JS by {@link decomposeUrl}.
78
+ *
79
+ * `INSERT OR IGNORE` on `url_refs.url` makes the step idempotent —
80
+ * repeated invocations after partial failure only add new URLs.
81
+ * @param trx - Knex instance or transaction connected to the archive DB.
82
+ * @param onProgress - Optional sink for periodic progress lines (one per
83
+ * ~5% of each source table scanned); see {@link ../create-progress-reporter.ts}.
84
+ * @example
85
+ * await knex.transaction(async (trx) => {
86
+ * await populateUrlRefs(trx);
87
+ * });
88
+ */
89
+ export async function populateUrlRefs(trx, onProgress) {
90
+ const seen = new Set();
91
+ for (const source of URL_SOURCES) {
92
+ const hasTable = await trx.schema.hasTable(source.table);
93
+ if (!hasTable) {
94
+ continue;
95
+ }
96
+ const presentColumns = [];
97
+ for (const column of source.columns) {
98
+ if (await trx.schema.hasColumn(source.table, column)) {
99
+ presentColumns.push(column);
100
+ }
101
+ }
102
+ if (presentColumns.length === 0) {
103
+ continue;
104
+ }
105
+ const countRows = await trx(source.table).count({ n: '*' });
106
+ const total = Number(countRows[0]?.n ?? 0);
107
+ const report = createProgressReporter(`url_refs (${source.table})`, total, onProgress);
108
+ let processed = 0;
109
+ let cursor = 0;
110
+ while (true) {
111
+ const rows = await trx(source.table)
112
+ .select('id', ...presentColumns)
113
+ .where('id', '>', cursor)
114
+ .orderBy('id', 'asc')
115
+ .limit(READ_CHUNK_SIZE);
116
+ if (rows.length === 0) {
117
+ break;
118
+ }
119
+ cursor = rows.at(-1).id;
120
+ processed += rows.length;
121
+ report(processed);
122
+ for (const row of rows) {
123
+ for (const column of presentColumns) {
124
+ const value = row[column];
125
+ if (typeof value !== 'string' || value === '') {
126
+ continue;
127
+ }
128
+ if (!routeDataUriAwayFromUrlRefs(value)) {
129
+ continue;
130
+ }
131
+ seen.add(value);
132
+ }
133
+ }
134
+ }
135
+ }
136
+ if (seen.size === 0) {
137
+ return;
138
+ }
139
+ const inserts = [];
140
+ for (const url of seen) {
141
+ const decomposed = decomposeUrl(url);
142
+ inserts.push({ url, ...decomposed });
143
+ }
144
+ for (let index = 0; index < inserts.length; index += INSERT_CHUNK_SIZE) {
145
+ const chunk = inserts.slice(index, index + INSERT_CHUNK_SIZE);
146
+ await trx('url_refs').insert(chunk).onConflict('url').ignore();
147
+ }
148
+ }
@@ -0,0 +1,15 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Runs `SELECT count(?) FROM <table>` and returns the count as a plain
4
+ * JS number. Exists purely to keep spec files inside populate-ref-tables/ compliant
5
+ * with `unicorn/no-await-expression-member` — inlining
6
+ * `Number((await db(t).count(...))[0]!.n)` reads fine but violates the
7
+ * rule at every callsite.
8
+ * @param db - Knex instance (typically a spec-local in-memory DB).
9
+ * @param table - Table name.
10
+ * @param countColumn - Column to count; defaults to `'id'` because every
11
+ * populate-refs table except `header_set_entries` / `header_flags` uses
12
+ * that PK name. Pass `'header_set_id'` for the two exceptions.
13
+ * @returns Row count.
14
+ */
15
+ export declare function countRows(db: Knex, table: string, countColumn?: string): Promise<number>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Runs `SELECT count(?) FROM <table>` and returns the count as a plain
3
+ * JS number. Exists purely to keep spec files inside populate-ref-tables/ compliant
4
+ * with `unicorn/no-await-expression-member` — inlining
5
+ * `Number((await db(t).count(...))[0]!.n)` reads fine but violates the
6
+ * rule at every callsite.
7
+ * @param db - Knex instance (typically a spec-local in-memory DB).
8
+ * @param table - Table name.
9
+ * @param countColumn - Column to count; defaults to `'id'` because every
10
+ * populate-refs table except `header_set_entries` / `header_flags` uses
11
+ * that PK name. Pass `'header_set_id'` for the two exceptions.
12
+ * @returns Row count.
13
+ */
14
+ export async function countRows(db, table, countColumn = 'id') {
15
+ const rows = await db(table).count({ n: countColumn });
16
+ return Number(rows[0].n);
17
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Domain types for the 0.13 population step (issue #191). Every
3
+ * shared interface / type alias lives here per the repo-wide
4
+ * `型は types.ts に集約` rule; implementation modules export functions
5
+ * only.
6
+ */
7
+ /**
8
+ * Decomposed URL components that populate `url_refs.{scheme,host,port,path,
9
+ * query_hash,fragment}`. All slots are populated in a single pass over the
10
+ * `url` string so callers do not have to re-parse per column.
11
+ */
12
+ export interface DecomposedUrl {
13
+ /**
14
+ * URL scheme without the trailing colon (`https`, `http`, `mailto`, ...).
15
+ * `null` for URLs that fail `new URL(...)` parsing.
16
+ */
17
+ scheme: string | null;
18
+ /**
19
+ * Host lower-cased (`www.example.com`). `null` when the URL has no
20
+ * `authority` component (e.g. `mailto:`, `data:`, `javascript:`), and
21
+ * `null` on parse failure.
22
+ */
23
+ host: string | null;
24
+ /**
25
+ * TCP port when explicit in the URL; `null` when the URL uses the
26
+ * scheme default (WHATWG URL normalises default ports away — we do NOT
27
+ * synthesise a default here because two rows differing only by explicit
28
+ * vs implicit `:443` should hash to the same URL, and `url_refs.url` is
29
+ * the natural key, not this column).
30
+ */
31
+ port: number | null;
32
+ /**
33
+ * Path portion (`/foo/bar`). `null` when the URL has no path or the
34
+ * URL uses an opaque scheme whose "pathname" is really an in-band
35
+ * payload (`data:`, `blob:`, `javascript:`) — see
36
+ * {@link ../decompose-url.ts} for the opaque-scheme handling.
37
+ */
38
+ path: string | null;
39
+ /**
40
+ * 32-byte content hash of the raw query string exactly as it appears in
41
+ * the URL (`?a=1&b=2` — the leading `?` is stripped before hashing).
42
+ * `null` when the URL has no query string, when the query string is
43
+ * empty, or on parse failure.
44
+ *
45
+ * Storing the raw query would defeat dedup on tracker URLs whose
46
+ * per-request query keys blow up dictionary size; the hash retains the
47
+ * same-URL-⇒-same-row property without the payload.
48
+ */
49
+ query_hash: Buffer | null;
50
+ /**
51
+ * Fragment (`#section-2`) with the leading `#` stripped. `null` when
52
+ * the URL has no fragment or on parse failure.
53
+ */
54
+ fragment: string | null;
55
+ }
56
+ /**
57
+ * Successful decode of a `data:` URI: the raw payload bytes.
58
+ */
59
+ export interface DecodedDataUri {
60
+ /** Payload bytes after base64 or percent-decoding. */
61
+ bytes: Buffer;
62
+ }
63
+ /**
64
+ * One header entry after decomposition — flattened out of the parsed JSON
65
+ * `Record<name, string | string[]>` so that:
66
+ *
67
+ * - Multiple values for the same name (e.g. two `Set-Cookie` lines) each
68
+ * get their own entry with a distinct `occurrence` ordinal starting at 1.
69
+ * - `isVolatile` is pre-computed once per entry (rather than re-looked-up
70
+ * at hash time) so both `stableHash` and the `header_set_entries`
71
+ * inserts see identical classification.
72
+ */
73
+ export interface HeaderEntry {
74
+ /** Lower-cased header name; comparison and dedup keys always use this form. */
75
+ name: string;
76
+ /** Raw header value (each element of `string[]` inputs becomes one entry). */
77
+ value: string;
78
+ /**
79
+ * 1-based ordinal among entries sharing the same `name` within one
80
+ * response. Always `1` for single-value headers; ≥ 2 when a name
81
+ * repeats (e.g. multiple `Set-Cookie` lines).
82
+ */
83
+ occurrence: number;
84
+ /** Pre-computed volatility. */
85
+ isVolatile: boolean;
86
+ }
87
+ /**
88
+ * Result of decomposing one raw `responseHeaders` JSON string into the
89
+ * shape required for `header_sets`, `header_set_entries`, and
90
+ * `header_flags` inserts. `null` results from the decompose helper
91
+ * indicate "no header set to insert" (null / `{}` / parse failure);
92
+ * callers write `header_set_id = null` to the referring row.
93
+ */
94
+ export interface DecomposedHeaderSet {
95
+ /**
96
+ * 32-byte hash of the raw JSON string exactly as stored on the source
97
+ * row. Populates `header_sets.raw_json_hash` — a temporary column used
98
+ * by the 0.13 lookup to map an old `pages.responseHeaders` /
99
+ * `resources.responseHeaders` value back to its `header_sets.id`
100
+ * without any SQL function call.
101
+ */
102
+ rawJsonHash: Buffer;
103
+ /**
104
+ * 32-byte hash of the sorted `name=value` pairs of **all** entries
105
+ * (stable + volatile). Populates `header_sets.raw_hash` — a UNIQUE
106
+ * column so a JS-side upsert can dedup on identical decoded sets.
107
+ */
108
+ rawHash: Buffer;
109
+ /**
110
+ * 32-byte hash of the sorted `name=value` pairs of stable entries
111
+ * only. Populates `header_sets.stable_hash` — indexed so 0.13
112
+ * readers can answer "how many responses share this stable header
113
+ * profile" without scanning every entry.
114
+ */
115
+ stableHash: Buffer;
116
+ /**
117
+ * 32-byte hash of the sorted `name=value` pairs of volatile entries
118
+ * only, or `null` when the set has no volatile entries. Populates
119
+ * `header_sets.volatile_hash`.
120
+ */
121
+ volatileHash: Buffer | null;
122
+ /** Every decomposed entry, ordered by (name, occurrence). */
123
+ entries: readonly HeaderEntry[];
124
+ /** Total entry count — matches `header_sets.entry_count`. */
125
+ entryCount: number;
126
+ /**
127
+ * Number of stable entries — matches `header_sets.stable_entry_count`.
128
+ * `volatile_entry_count` is derived as `entry_count - stable_entry_count`
129
+ * and not stored separately.
130
+ */
131
+ stableEntryCount: number;
132
+ }
133
+ /**
134
+ * Shape written into `header_flags` for one `header_set_id`. `has_*`
135
+ * columns are `INTEGER NOT NULL` (0/1) per the 0.13 DDL;
136
+ * `cache_policy` is nullable and holds a compact summary of the
137
+ * `Cache-Control` value (or `null` when absent).
138
+ */
139
+ export interface HeaderFlagsRow {
140
+ /** `1` when at least one entry's name equals `content-security-policy`. */
141
+ has_csp: 0 | 1;
142
+ /** `1` when at least one entry's name equals `x-frame-options`. */
143
+ has_x_frame_options: 0 | 1;
144
+ /** `1` when at least one entry's name equals `x-content-type-options`. */
145
+ has_x_content_type_options: 0 | 1;
146
+ /** `1` when at least one entry's name equals `strict-transport-security`. */
147
+ has_hsts: 0 | 1;
148
+ /** `1` when at least one entry's name equals `referrer-policy`. */
149
+ has_referrer_policy: 0 | 1;
150
+ /** `1` when at least one entry's name equals `permissions-policy`. */
151
+ has_permissions_policy: 0 | 1;
152
+ /** `1` when at least one entry's name equals `set-cookie`. */
153
+ has_set_cookie: 0 | 1;
154
+ /**
155
+ * The `cache-control` value, verbatim, when present; `null` when
156
+ * absent. Multi-value `Cache-Control` headers are joined with `, ` so
157
+ * the summary captures every directive without losing per-occurrence
158
+ * context.
159
+ */
160
+ cache_policy: string | null;
161
+ }
162
+ /**
163
+ * In-process caches shared by every header-table upsert against one
164
+ * archive connection. The three id-maps resolve `header_name_refs.id` /
165
+ * `header_value_refs.id` / `header_sets.id` without hitting the DB once
166
+ * a value has been seen (or preloaded from existing rows).
167
+ *
168
+ * `setIdsProcessedThisRun` tracks the subset of `setIdByRawJsonHash` /
169
+ * `setIdByRawHash` that the current process has already written
170
+ * entries + flags for. A preloaded id from a prior run is present in the
171
+ * id caches but NOT in this set — so the entries + flags inserts still
172
+ * run once (safe under `INSERT OR IGNORE`) to repair any missing rows
173
+ * left by a crashed writer. Once entries + flags for a given setId have
174
+ * been written this run, the shortcut skips redundant no-op inserts.
175
+ */
176
+ export interface HeaderTableCaches {
177
+ /** `lower-cased name → header_name_refs.id`. */
178
+ readonly nameIdCache: Map<string, number>;
179
+ /** `hexHash|value → header_value_refs.id`. */
180
+ readonly valueIdCache: Map<string, number>;
181
+ /** `hex(raw_json_hash) → header_sets.id`. */
182
+ readonly setIdByRawJsonHash: Map<string, number>;
183
+ /** `hex(raw_hash) → header_sets.id`. */
184
+ readonly setIdByRawHash: Map<string, number>;
185
+ /** `header_sets.id` values already fully processed by this process. */
186
+ readonly setIdsProcessedThisRun: Set<number>;
187
+ }
188
+ /**
189
+ * Category label attached to each content-type rule. Mirrors the union
190
+ * `ContentTypeCategory` in `@nitpicker/query/src/types.ts` — kept as a
191
+ * plain string literal here so `@nitpicker/crawler`'s 0.13 step
192
+ * does not need to depend on `@nitpicker/query` (which itself depends
193
+ * on this package). If the two copies drift, `classify-content-type.spec.ts`
194
+ * here and `content-type-rules.spec.ts` on the query side will disagree
195
+ * on the same fixture.
196
+ */
197
+ export type ContentTypeCategory = 'html' | 'pdf' | 'csv' | 'word' | 'excel' | 'powerpoint' | 'image' | 'audio' | 'video' | 'font' | 'css' | 'javascript' | 'json' | 'xml' | 'archive' | 'text' | 'other' | 'unknown';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Domain types for the 0.13 population step (issue #191). Every
3
+ * shared interface / type alias lives here per the repo-wide
4
+ * `型は types.ts に集約` rule; implementation modules export functions
5
+ * only.
6
+ */
7
+ export {};
@@ -0,0 +1,34 @@
1
+ import type { DecomposedHeaderSet, HeaderTableCaches } from './types.js';
2
+ import type { Knex } from 'knex';
3
+ /**
4
+ * Upserts one decomposed header set and its entries + flags, returning
5
+ * the `header_sets.id`. Shared by the bulk archive populate
6
+ * ({@link ./populate-header-tables.ts}) and the crawler's per-response
7
+ * write path, so both produce byte-identical header rows for the same
8
+ * raw `responseHeaders` value.
9
+ *
10
+ * Set-id resolution order:
11
+ *
12
+ * 1. `raw_json_hash` cache — same JSON key ordering as a previous row.
13
+ * 2. `raw_hash` cache — same sorted-entries identity, different JSON key
14
+ * ordering. This branch links the new `raw_json_hash` to the existing
15
+ * setId in the in-process map so subsequent same-JSON hits fast-path.
16
+ * 3. Otherwise INSERT a new `header_sets` row.
17
+ *
18
+ * Entries + flags are inserted once per `setId` per process (guarded by
19
+ * `setIdsProcessedThisRun`); already-inserted rows short-circuit
20
+ * cheaply, and preloaded-but-unprocessed setIds (partial prior run) get
21
+ * their entries / flags repaired via `INSERT OR IGNORE`.
22
+ * @param trx - Knex instance or transaction connected to the archive DB.
23
+ * @param decomposed - Result of `decomposeHeaderSet` (never `null` — the
24
+ * caller short-circuits null decompositions to `header_set_id = null`).
25
+ * @param caches - The in-process caches, mutated in place. Create via
26
+ * {@link ./create-header-table-caches.ts}.
27
+ * @returns The `header_sets.id` for this set.
28
+ * @example
29
+ * const decomposed = decomposeHeaderSet(rawJson);
30
+ * if (decomposed !== null) {
31
+ * const setId = await upsertOneHeaderSet(knex, decomposed, caches);
32
+ * }
33
+ */
34
+ export declare function upsertOneHeaderSet(trx: Knex, decomposed: DecomposedHeaderSet, caches: HeaderTableCaches): Promise<number>;
@@ -0,0 +1,208 @@
1
+ import { computeContentHash } from './compute-content-hash.js';
2
+ import { computeHeaderFlags } from './compute-header-flags.js';
3
+ import { headerValueCacheKey } from './header-value-cache-key.js';
4
+ /**
5
+ * Rows inserted per INSERT statement into `header_set_entries`. Each row
6
+ * binds 5 params so 500 rows = 2500 params — safely under SQLite's
7
+ * default variable limit.
8
+ */
9
+ const ENTRY_INSERT_CHUNK_SIZE = 500;
10
+ /**
11
+ * Upserts one decomposed header set and its entries + flags, returning
12
+ * the `header_sets.id`. Shared by the bulk archive populate
13
+ * ({@link ./populate-header-tables.ts}) and the crawler's per-response
14
+ * write path, so both produce byte-identical header rows for the same
15
+ * raw `responseHeaders` value.
16
+ *
17
+ * Set-id resolution order:
18
+ *
19
+ * 1. `raw_json_hash` cache — same JSON key ordering as a previous row.
20
+ * 2. `raw_hash` cache — same sorted-entries identity, different JSON key
21
+ * ordering. This branch links the new `raw_json_hash` to the existing
22
+ * setId in the in-process map so subsequent same-JSON hits fast-path.
23
+ * 3. Otherwise INSERT a new `header_sets` row.
24
+ *
25
+ * Entries + flags are inserted once per `setId` per process (guarded by
26
+ * `setIdsProcessedThisRun`); already-inserted rows short-circuit
27
+ * cheaply, and preloaded-but-unprocessed setIds (partial prior run) get
28
+ * their entries / flags repaired via `INSERT OR IGNORE`.
29
+ * @param trx - Knex instance or transaction connected to the archive DB.
30
+ * @param decomposed - Result of `decomposeHeaderSet` (never `null` — the
31
+ * caller short-circuits null decompositions to `header_set_id = null`).
32
+ * @param caches - The in-process caches, mutated in place. Create via
33
+ * {@link ./create-header-table-caches.ts}.
34
+ * @returns The `header_sets.id` for this set.
35
+ * @example
36
+ * const decomposed = decomposeHeaderSet(rawJson);
37
+ * if (decomposed !== null) {
38
+ * const setId = await upsertOneHeaderSet(knex, decomposed, caches);
39
+ * }
40
+ */
41
+ export async function upsertOneHeaderSet(trx, decomposed, caches) {
42
+ const rawJsonHashHex = decomposed.rawJsonHash.toString('hex');
43
+ const rawHashHex = decomposed.rawHash.toString('hex');
44
+ let setId = caches.setIdByRawJsonHash.get(rawJsonHashHex);
45
+ if (setId === undefined) {
46
+ setId = caches.setIdByRawHash.get(rawHashHex);
47
+ if (setId === undefined) {
48
+ setId = await insertHeaderSet(trx, decomposed);
49
+ caches.setIdByRawJsonHash.set(rawJsonHashHex, setId);
50
+ caches.setIdByRawHash.set(rawHashHex, setId);
51
+ }
52
+ else {
53
+ // Same sorted-entries identity as an existing row, different JSON
54
+ // key ordering. Reuse the existing setId; the raw_json_hash of
55
+ // this variant has no persistent home in the 0.13 schema
56
+ // (raw_json_hash is UNIQUE per row) — linking it in the
57
+ // in-process map is enough for id resolution.
58
+ caches.setIdByRawJsonHash.set(rawJsonHashHex, setId);
59
+ }
60
+ }
61
+ if (caches.setIdsProcessedThisRun.has(setId)) {
62
+ return setId;
63
+ }
64
+ const nameIds = await resolveNameIds(trx, decomposed.entries, caches.nameIdCache);
65
+ const valueIds = await resolveValueIds(trx, decomposed.entries, caches.valueIdCache);
66
+ const entryRows = decomposed.entries.map((entry, index) => ({
67
+ header_set_id: setId,
68
+ name_id: nameIds[index],
69
+ occurrence: entry.occurrence,
70
+ value_id: valueIds[index],
71
+ is_volatile: entry.isVolatile ? 1 : 0,
72
+ }));
73
+ for (let index = 0; index < entryRows.length; index += ENTRY_INSERT_CHUNK_SIZE) {
74
+ const chunk = entryRows.slice(index, index + ENTRY_INSERT_CHUNK_SIZE);
75
+ await trx('header_set_entries')
76
+ .insert(chunk)
77
+ .onConflict(['header_set_id', 'name_id', 'occurrence'])
78
+ .ignore();
79
+ }
80
+ const flags = computeHeaderFlags(decomposed.entries);
81
+ await trx('header_flags')
82
+ .insert({ header_set_id: setId, ...flags })
83
+ .onConflict('header_set_id')
84
+ .ignore();
85
+ caches.setIdsProcessedThisRun.add(setId);
86
+ return setId;
87
+ }
88
+ /**
89
+ * Inserts a new `header_sets` row and returns its id. Neither
90
+ * `raw_json_hash` nor `raw_hash` collided with an existing row when
91
+ * this is called — the caller has already checked both caches.
92
+ * @param trx - Knex instance.
93
+ * @param decomposed - Result of `decomposeHeaderSet`.
94
+ * @returns The newly-inserted `header_sets.id`.
95
+ */
96
+ async function insertHeaderSet(trx, decomposed) {
97
+ const rows = await trx('header_sets')
98
+ .insert({
99
+ raw_json_hash: decomposed.rawJsonHash,
100
+ raw_hash: decomposed.rawHash,
101
+ stable_hash: decomposed.stableHash,
102
+ volatile_hash: decomposed.volatileHash,
103
+ entry_count: decomposed.entryCount,
104
+ stable_entry_count: decomposed.stableEntryCount,
105
+ })
106
+ .returning('id');
107
+ const first = rows[0];
108
+ if (first === undefined) {
109
+ throw new Error('upsertOneHeaderSet: header_sets INSERT returned no rows despite absent conflict');
110
+ }
111
+ return first.id;
112
+ }
113
+ /**
114
+ * Resolves the `name_id` for every entry in `entries`, inserting new
115
+ * names into `header_name_refs` as needed. Mutates `cache` with any new
116
+ * ids.
117
+ * @param trx - Knex instance.
118
+ * @param entries - Decomposed entries.
119
+ * @param cache - Name → id cache; mutated in place.
120
+ * @returns Parallel array of name ids (same order as `entries`).
121
+ */
122
+ async function resolveNameIds(trx, entries, cache) {
123
+ const missing = new Set();
124
+ for (const entry of entries) {
125
+ if (!cache.has(entry.name)) {
126
+ missing.add(entry.name);
127
+ }
128
+ }
129
+ if (missing.size > 0) {
130
+ const missingNames = [...missing];
131
+ // Chunked like the entries insert below: knex compiles a multi-row
132
+ // insert-with-onConflict into a compound SELECT, and SQLite rejects
133
+ // more than 500 compound terms per statement.
134
+ for (let index = 0; index < missingNames.length; index += ENTRY_INSERT_CHUNK_SIZE) {
135
+ const chunk = missingNames.slice(index, index + ENTRY_INSERT_CHUNK_SIZE);
136
+ await trx('header_name_refs')
137
+ .insert(chunk.map((name) => ({ name })))
138
+ .onConflict('name')
139
+ .ignore();
140
+ const inserted = await trx('header_name_refs')
141
+ .select('id', 'name')
142
+ .whereIn('name', chunk);
143
+ for (const row of inserted) {
144
+ cache.set(row.name, row.id);
145
+ }
146
+ }
147
+ }
148
+ return entries.map((entry) => {
149
+ const id = cache.get(entry.name);
150
+ if (id === undefined) {
151
+ throw new Error(`upsertOneHeaderSet: name_id not resolved for ${entry.name}`);
152
+ }
153
+ return id;
154
+ });
155
+ }
156
+ /**
157
+ * Resolves the `value_id` for every entry in `entries`, inserting new
158
+ * values into `header_value_refs` as needed. Values are hashed once and
159
+ * deduped by `(hash, value)` per the 0.13 UNIQUE constraint.
160
+ *
161
+ * The read-back after INSERT narrows on `.whereIn('value', ...)` — the
162
+ * string values just written — instead of binding the Buffer hashes into
163
+ * a `WHERE hash IN (...)`: a Buffer[] IN would return the correct rows
164
+ * in theory but leans on libsql's BLOB-parameter round-trip behaviour,
165
+ * while string values compare reliably. The cache key still includes the
166
+ * hash, so hash-distinct entries stay disambiguated.
167
+ * @param trx - Knex instance.
168
+ * @param entries - Decomposed entries.
169
+ * @param cache - `hexHash|value → id` cache; mutated in place.
170
+ * @returns Parallel array of value ids (same order as `entries`).
171
+ */
172
+ async function resolveValueIds(trx, entries, cache) {
173
+ const hashByEntryIndex = entries.map((entry) => computeContentHash(entry.value));
174
+ const missing = [];
175
+ const missingKeys = new Set();
176
+ for (const [i, entry] of entries.entries()) {
177
+ const hash = hashByEntryIndex[i];
178
+ const key = headerValueCacheKey(hash, entry.value);
179
+ if (!cache.has(key) && !missingKeys.has(key)) {
180
+ missing.push({ key, hash, value: entry.value });
181
+ missingKeys.add(key);
182
+ }
183
+ }
184
+ // Chunked like the entries insert: knex compiles a multi-row
185
+ // insert-with-onConflict into a compound SELECT, and SQLite rejects
186
+ // more than 500 compound terms per statement.
187
+ for (let index = 0; index < missing.length; index += ENTRY_INSERT_CHUNK_SIZE) {
188
+ const chunk = missing.slice(index, index + ENTRY_INSERT_CHUNK_SIZE);
189
+ await trx('header_value_refs')
190
+ .insert(chunk.map(({ hash, value }) => ({ hash, value })))
191
+ .onConflict(['hash', 'value'])
192
+ .ignore();
193
+ const inserted = await trx('header_value_refs')
194
+ .select('id', 'hash', 'value')
195
+ .whereIn('value', chunk.map((m) => m.value));
196
+ for (const row of inserted) {
197
+ cache.set(headerValueCacheKey(Buffer.from(row.hash), row.value), row.id);
198
+ }
199
+ }
200
+ return entries.map((entry, i) => {
201
+ const key = headerValueCacheKey(hashByEntryIndex[i], entry.value);
202
+ const id = cache.get(key);
203
+ if (id === undefined) {
204
+ throw new Error(`upsertOneHeaderSet: value_id not resolved for ${entry.value.slice(0, 40)}…`);
205
+ }
206
+ return id;
207
+ });
208
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Header names — always lower-cased — that are EXCLUDED from `stable_hash`.
3
+ * These change on every response (timestamps / request IDs / cache
4
+ * telemetry) and would defeat the dedup goal if hashed alongside the
5
+ * stable set.
6
+ *
7
+ * `set-cookie` sits here because per-session cookies rotate constantly on
8
+ * authenticated sites — it is deliberately classified as volatile even
9
+ * though it is technically a stable security signal.
10
+ *
11
+ * Any header not in this set is
12
+ * treated as **stable** by {@link ./header-stability.ts:isVolatileHeader}
13
+ * — the safer default because a genuinely volatile header misclassified
14
+ * as stable only weakens dedup (its per-response values fork
15
+ * otherwise-identical stable profiles into separate `stable_hash`
16
+ * clusters), while a stable header misclassified as volatile would drop
17
+ * a meaningful signal from `stable_hash` and falsely cluster distinct
18
+ * header configurations together.
19
+ */
20
+ export declare const VOLATILE_HEADER_NAMES: ReadonlySet<string>;