@nitpicker/crawler 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/README.md +6 -4
  2. package/lib/archive/archive-accessor.d.ts +2 -2
  3. package/lib/archive/archive-accessor.js +2 -2
  4. package/lib/archive/archive-lock.d.ts +7 -0
  5. package/lib/archive/archive-lock.js +7 -0
  6. package/lib/archive/archive.d.ts +175 -13
  7. package/lib/archive/archive.js +198 -14
  8. package/lib/archive/cache/compute-archive-cache-key.d.ts +39 -0
  9. package/lib/archive/cache/compute-archive-cache-key.js +95 -0
  10. package/lib/archive/cache/extract-archive-to-cache.d.ts +43 -0
  11. package/lib/archive/cache/extract-archive-to-cache.js +309 -0
  12. package/lib/archive/cache/get-archive-cache-root.d.ts +20 -0
  13. package/lib/archive/cache/get-archive-cache-root.js +53 -0
  14. package/lib/archive/cache/is-archive-cache-disabled.d.ts +24 -0
  15. package/lib/archive/cache/is-archive-cache-disabled.js +34 -0
  16. package/lib/archive/cache/resolve-archive-cache-dir.d.ts +26 -0
  17. package/lib/archive/cache/resolve-archive-cache-dir.js +32 -0
  18. package/lib/archive/create-adjunct-tables.d.ts +43 -0
  19. package/lib/archive/create-adjunct-tables.js +213 -0
  20. package/lib/archive/create-entity-tables.d.ts +173 -0
  21. package/lib/archive/create-entity-tables.js +318 -0
  22. package/lib/archive/create-progress-reporter.d.ts +30 -0
  23. package/lib/archive/create-progress-reporter.js +38 -0
  24. package/lib/archive/create-ref-tables.d.ts +35 -0
  25. package/lib/archive/create-ref-tables.js +188 -0
  26. package/lib/archive/database.d.ts +130 -182
  27. package/lib/archive/database.js +524 -1777
  28. package/lib/archive/db-ops/_shared/clear-write-ref-caches.d.ts +27 -0
  29. package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +34 -0
  30. package/lib/archive/db-ops/_shared/create-write-ref-caches.d.ts +17 -0
  31. package/lib/archive/db-ops/_shared/create-write-ref-caches.js +26 -0
  32. package/lib/archive/db-ops/_shared/decode-json-ref.d.ts +17 -0
  33. package/lib/archive/db-ops/_shared/decode-json-ref.js +31 -0
  34. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.d.ts +20 -0
  35. package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.js +53 -0
  36. package/lib/archive/db-ops/_shared/resolve-content-item-id.d.ts +61 -0
  37. package/lib/archive/db-ops/_shared/resolve-content-item-id.js +111 -0
  38. package/lib/archive/db-ops/_shared/resolve-url-or-blob.d.ts +23 -0
  39. package/lib/archive/db-ops/_shared/resolve-url-or-blob.js +29 -0
  40. package/lib/archive/db-ops/_shared/retry-setting.d.ts +16 -0
  41. package/lib/archive/db-ops/_shared/retry-setting.js +18 -0
  42. package/lib/archive/db-ops/_shared/safe-parse-json.d.ts +11 -0
  43. package/lib/archive/db-ops/_shared/safe-parse-json.js +18 -0
  44. package/lib/archive/db-ops/_shared/types.d.ts +53 -0
  45. package/lib/archive/db-ops/_shared/types.js +1 -0
  46. package/lib/archive/db-ops/_shared/upsert-blob-ref.d.ts +25 -0
  47. package/lib/archive/db-ops/_shared/upsert-blob-ref.js +48 -0
  48. package/lib/archive/db-ops/_shared/upsert-content-type-ref.d.ts +30 -0
  49. package/lib/archive/db-ops/_shared/upsert-content-type-ref.js +45 -0
  50. package/lib/archive/db-ops/_shared/upsert-json-ref.d.ts +22 -0
  51. package/lib/archive/db-ops/_shared/upsert-json-ref.js +41 -0
  52. package/lib/archive/db-ops/_shared/upsert-response-headers.d.ts +35 -0
  53. package/lib/archive/db-ops/_shared/upsert-response-headers.js +49 -0
  54. package/lib/archive/db-ops/_shared/upsert-url-ref.d.ts +39 -0
  55. package/lib/archive/db-ops/_shared/upsert-url-ref.js +62 -0
  56. package/lib/archive/db-ops/analysis/replace-analysis-violations.d.ts +28 -0
  57. package/lib/archive/db-ops/analysis/replace-analysis-violations.js +152 -0
  58. package/lib/archive/db-ops/anchors/get-anchors-on-page.d.ts +10 -0
  59. package/lib/archive/db-ops/anchors/get-anchors-on-page.js +21 -0
  60. package/lib/archive/db-ops/config/get-base-url.d.ts +8 -0
  61. package/lib/archive/db-ops/config/get-base-url.js +14 -0
  62. package/lib/archive/db-ops/config/get-config.d.ts +10 -0
  63. package/lib/archive/db-ops/config/get-config.js +27 -0
  64. package/lib/archive/db-ops/config/get-name.d.ts +8 -0
  65. package/lib/archive/db-ops/config/get-name.js +14 -0
  66. package/lib/archive/db-ops/config/info-column-allowlist.d.ts +7 -0
  67. package/lib/archive/db-ops/config/info-column-allowlist.js +26 -0
  68. package/lib/archive/db-ops/config/info-json-columns.d.ts +5 -0
  69. package/lib/archive/db-ops/config/info-json-columns.js +10 -0
  70. package/lib/archive/db-ops/config/set-config.d.ts +12 -0
  71. package/lib/archive/db-ops/config/set-config.js +21 -0
  72. package/lib/archive/db-ops/config/update-config.d.ts +17 -0
  73. package/lib/archive/db-ops/config/update-config.js +36 -0
  74. package/lib/archive/db-ops/errors/insert-crawl-error.d.ts +15 -0
  75. package/lib/archive/db-ops/errors/insert-crawl-error.js +21 -0
  76. package/lib/archive/db-ops/errors/insert-page-error.d.ts +21 -0
  77. package/lib/archive/db-ops/errors/insert-page-error.js +28 -0
  78. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +22 -0
  79. package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +141 -0
  80. package/lib/archive/db-ops/html/get-html-of-page-by-id.d.ts +18 -0
  81. package/lib/archive/db-ops/html/get-html-of-page-by-id.js +29 -0
  82. package/lib/archive/db-ops/inventory/record-inventory-run.d.ts +21 -0
  83. package/lib/archive/db-ops/inventory/record-inventory-run.js +38 -0
  84. package/lib/archive/db-ops/lifecycle/checkpoint.d.ts +8 -0
  85. package/lib/archive/db-ops/lifecycle/checkpoint.js +9 -0
  86. package/lib/archive/db-ops/lifecycle/destroy.d.ts +6 -0
  87. package/lib/archive/db-ops/lifecycle/destroy.js +7 -0
  88. package/lib/archive/db-ops/lifecycle/init.d.ts +22 -0
  89. package/lib/archive/db-ops/lifecycle/init.js +42 -0
  90. package/lib/archive/db-ops/meta/get-jsonld-of-page.d.ts +13 -0
  91. package/lib/archive/db-ops/meta/get-jsonld-of-page.js +27 -0
  92. package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +12 -0
  93. package/lib/archive/db-ops/meta/get-tags-of-page.js +28 -0
  94. package/lib/archive/db-ops/pages/order/set-url-order.d.ts +8 -0
  95. package/lib/archive/db-ops/pages/order/set-url-order.js +32 -0
  96. package/lib/archive/db-ops/pages/read/build-page-query.d.ts +18 -0
  97. package/lib/archive/db-ops/pages/read/build-page-query.js +40 -0
  98. package/lib/archive/db-ops/pages/read/get-crawling-state.d.ts +70 -0
  99. package/lib/archive/db-ops/pages/read/get-crawling-state.js +98 -0
  100. package/lib/archive/db-ops/pages/read/get-existing-page-urls.d.ts +15 -0
  101. package/lib/archive/db-ops/pages/read/get-existing-page-urls.js +30 -0
  102. package/lib/archive/db-ops/pages/read/get-page-count.d.ts +12 -0
  103. package/lib/archive/db-ops/pages/read/get-page-count.js +21 -0
  104. package/lib/archive/db-ops/pages/read/get-page-source-by-url.d.ts +24 -0
  105. package/lib/archive/db-ops/pages/read/get-page-source-by-url.js +28 -0
  106. package/lib/archive/db-ops/pages/read/get-pages-with-rels.d.ts +38 -0
  107. package/lib/archive/db-ops/pages/read/get-pages-with-rels.js +107 -0
  108. package/lib/archive/db-ops/pages/read/get-pages.d.ts +11 -0
  109. package/lib/archive/db-ops/pages/read/get-pages.js +51 -0
  110. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.d.ts +18 -0
  111. package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.js +25 -0
  112. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.d.ts +31 -0
  113. package/lib/archive/db-ops/pages/read/reconstruct-page-rows.js +32 -0
  114. package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +24 -0
  115. package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +93 -0
  116. package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +47 -0
  117. package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +124 -0
  118. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.d.ts +37 -0
  119. package/lib/archive/db-ops/pages/write/insert-inventory-seeds.js +72 -0
  120. package/lib/archive/db-ops/pages/write/insert-jsonld.d.ts +17 -0
  121. package/lib/archive/db-ops/pages/write/insert-jsonld.js +49 -0
  122. package/lib/archive/db-ops/pages/write/insert-page.d.ts +36 -0
  123. package/lib/archive/db-ops/pages/write/insert-page.js +208 -0
  124. package/lib/archive/db-ops/pages/write/insert-tags.d.ts +16 -0
  125. package/lib/archive/db-ops/pages/write/insert-tags.js +34 -0
  126. package/lib/archive/db-ops/pages/write/link-redirect-sources.d.ts +36 -0
  127. package/lib/archive/db-ops/pages/write/link-redirect-sources.js +93 -0
  128. package/lib/archive/db-ops/pages/write/record-redirect.d.ts +35 -0
  129. package/lib/archive/db-ops/pages/write/record-redirect.js +100 -0
  130. package/lib/archive/db-ops/pages/write/set-skipped-page.d.ts +13 -0
  131. package/lib/archive/db-ops/pages/write/set-skipped-page.js +22 -0
  132. package/lib/archive/db-ops/pages/write/update-page.d.ts +29 -0
  133. package/lib/archive/db-ops/pages/write/update-page.js +334 -0
  134. package/lib/archive/db-ops/pages/write/write-page-html-blob.d.ts +19 -0
  135. package/lib/archive/db-ops/pages/write/write-page-html-blob.js +41 -0
  136. package/lib/archive/db-ops/referrers/get-redirects-for-pages.d.ts +9 -0
  137. package/lib/archive/db-ops/referrers/get-redirects-for-pages.js +15 -0
  138. package/lib/archive/db-ops/referrers/get-referrers-of-page.d.ts +17 -0
  139. package/lib/archive/db-ops/referrers/get-referrers-of-page.js +32 -0
  140. package/lib/archive/db-ops/referrers/get-referrers-of-resource.d.ts +8 -0
  141. package/lib/archive/db-ops/referrers/get-referrers-of-resource.js +15 -0
  142. package/lib/archive/db-ops/resources/build-resource-query.d.ts +25 -0
  143. package/lib/archive/db-ops/resources/build-resource-query.js +29 -0
  144. package/lib/archive/db-ops/resources/get-existing-resource-urls.d.ts +9 -0
  145. package/lib/archive/db-ops/resources/get-existing-resource-urls.js +24 -0
  146. package/lib/archive/db-ops/resources/get-resource-by-url.d.ts +13 -0
  147. package/lib/archive/db-ops/resources/get-resource-by-url.js +22 -0
  148. package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +9 -0
  149. package/lib/archive/db-ops/resources/get-resource-url-list.js +13 -0
  150. package/lib/archive/db-ops/resources/get-resources.d.ts +8 -0
  151. package/lib/archive/db-ops/resources/get-resources.js +11 -0
  152. package/lib/archive/db-ops/resources/insert-inventory-resources.d.ts +24 -0
  153. package/lib/archive/db-ops/resources/insert-inventory-resources.js +64 -0
  154. package/lib/archive/db-ops/resources/insert-resource-referrers.d.ts +15 -0
  155. package/lib/archive/db-ops/resources/insert-resource-referrers.js +54 -0
  156. package/lib/archive/db-ops/resources/insert-resource.d.ts +34 -0
  157. package/lib/archive/db-ops/resources/insert-resource.js +73 -0
  158. package/lib/archive/db-ops/resources/reconstruct-resource-rows.d.ts +26 -0
  159. package/lib/archive/db-ops/resources/reconstruct-resource-rows.js +30 -0
  160. package/lib/archive/decode-html-blob.d.ts +18 -0
  161. package/lib/archive/decode-html-blob.js +31 -0
  162. package/lib/archive/derive-lineage-from-parent.d.ts +37 -0
  163. package/lib/archive/derive-lineage-from-parent.js +42 -0
  164. package/lib/archive/drop-legacy-tables.d.ts +45 -0
  165. package/lib/archive/drop-legacy-tables.js +56 -0
  166. package/lib/archive/filesystem/rename.js +1 -1
  167. package/lib/archive/get-failed-page-messages.d.ts +44 -0
  168. package/lib/archive/get-failed-page-messages.js +132 -0
  169. package/lib/archive/init-schema.d.ts +35 -39
  170. package/lib/archive/init-schema.js +111 -320
  171. package/lib/archive/is-inventory-source.d.ts +21 -0
  172. package/lib/archive/is-inventory-source.js +22 -0
  173. package/lib/archive/limited-page-ids.d.ts +2 -1
  174. package/lib/archive/limited-page-ids.js +5 -4
  175. package/lib/archive/meta/assert-compatible-version.d.ts +24 -3
  176. package/lib/archive/meta/assert-compatible-version.js +24 -3
  177. package/lib/archive/meta/types.d.ts +87 -1
  178. package/lib/archive/meta/types.js +34 -2
  179. package/lib/archive/migrate-entity-tables.d.ts +45 -0
  180. package/lib/archive/migrate-entity-tables.js +56 -0
  181. package/lib/archive/migrate-ref-tables.d.ts +25 -0
  182. package/lib/archive/migrate-ref-tables.js +38 -0
  183. package/lib/archive/page-meta-column-maps.d.ts +32 -0
  184. package/lib/archive/page-meta-column-maps.js +43 -0
  185. package/lib/archive/page.d.ts +6 -6
  186. package/lib/archive/page.js +5 -5
  187. package/lib/archive/peek-archive-lock.d.ts +2 -2
  188. package/lib/archive/peek-archive-lock.js +2 -2
  189. package/lib/archive/populate-entity-tables/collapse-anchor-rows.d.ts +41 -0
  190. package/lib/archive/populate-entity-tables/collapse-anchor-rows.js +87 -0
  191. package/lib/archive/populate-entity-tables/derive-dom-path.d.ts +35 -0
  192. package/lib/archive/populate-entity-tables/derive-dom-path.js +72 -0
  193. package/lib/archive/populate-entity-tables/is-blob-ref-value.d.ts +16 -0
  194. package/lib/archive/populate-entity-tables/is-blob-ref-value.js +19 -0
  195. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.d.ts +66 -0
  196. package/lib/archive/populate-entity-tables/match-images-to-dom-paths.js +96 -0
  197. package/lib/archive/populate-entity-tables/populate-anchor-edges.d.ts +33 -0
  198. package/lib/archive/populate-entity-tables/populate-anchor-edges.js +153 -0
  199. package/lib/archive/populate-entity-tables/populate-content-items.d.ts +40 -0
  200. package/lib/archive/populate-entity-tables/populate-content-items.js +141 -0
  201. package/lib/archive/populate-entity-tables/populate-entities.d.ts +81 -0
  202. package/lib/archive/populate-entity-tables/populate-entities.js +111 -0
  203. package/lib/archive/populate-entity-tables/populate-image-items.d.ts +91 -0
  204. package/lib/archive/populate-entity-tables/populate-image-items.js +223 -0
  205. package/lib/archive/populate-entity-tables/populate-page-meta.d.ts +33 -0
  206. package/lib/archive/populate-entity-tables/populate-page-meta.js +267 -0
  207. package/lib/archive/populate-entity-tables/populate-resource-items.d.ts +22 -0
  208. package/lib/archive/populate-entity-tables/populate-resource-items.js +114 -0
  209. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.d.ts +31 -0
  210. package/lib/archive/populate-entity-tables/populate-resource-ref-edges.js +33 -0
  211. package/lib/archive/populate-entity-tables/resolve-blob-refs.d.ts +31 -0
  212. package/lib/archive/populate-entity-tables/resolve-blob-refs.js +100 -0
  213. package/lib/archive/populate-entity-tables/resolve-content-type-refs.d.ts +22 -0
  214. package/lib/archive/populate-entity-tables/resolve-content-type-refs.js +27 -0
  215. package/lib/archive/populate-entity-tables/resolve-header-sets.d.ts +49 -0
  216. package/lib/archive/populate-entity-tables/resolve-header-sets.js +122 -0
  217. package/lib/archive/populate-entity-tables/resolve-json-refs.d.ts +25 -0
  218. package/lib/archive/populate-entity-tables/resolve-json-refs.js +67 -0
  219. package/lib/archive/populate-entity-tables/resolve-text-refs.d.ts +30 -0
  220. package/lib/archive/populate-entity-tables/resolve-text-refs.js +61 -0
  221. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.d.ts +21 -0
  222. package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.js +27 -0
  223. package/lib/archive/populate-entity-tables/resolve-url-refs.d.ts +33 -0
  224. package/lib/archive/populate-entity-tables/resolve-url-refs.js +60 -0
  225. package/lib/archive/populate-entity-tables/test-utils/count-rows.d.ts +17 -0
  226. package/lib/archive/populate-entity-tables/test-utils/count-rows.js +20 -0
  227. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.d.ts +25 -0
  228. package/lib/archive/populate-entity-tables/test-utils/seed-content-items.js +42 -0
  229. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.d.ts +23 -0
  230. package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.js +178 -0
  231. package/lib/archive/populate-entity-tables/types.d.ts +157 -0
  232. package/lib/archive/populate-entity-tables/types.js +12 -0
  233. package/lib/archive/populate-entity-tables/upsert-text-refs.d.ts +38 -0
  234. package/lib/archive/populate-entity-tables/upsert-text-refs.js +78 -0
  235. package/lib/archive/populate-ref-tables/classify-content-type.d.ts +16 -0
  236. package/lib/archive/populate-ref-tables/classify-content-type.js +52 -0
  237. package/lib/archive/populate-ref-tables/compute-content-hash.d.ts +22 -0
  238. package/lib/archive/populate-ref-tables/compute-content-hash.js +26 -0
  239. package/lib/archive/populate-ref-tables/compute-header-flags.d.ts +16 -0
  240. package/lib/archive/populate-ref-tables/compute-header-flags.js +70 -0
  241. package/lib/archive/populate-ref-tables/content-type-rules.d.ts +38 -0
  242. package/lib/archive/populate-ref-tables/content-type-rules.js +133 -0
  243. package/lib/archive/populate-ref-tables/create-header-table-caches.d.ts +25 -0
  244. package/lib/archive/populate-ref-tables/create-header-table-caches.js +49 -0
  245. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.d.ts +15 -0
  246. package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.js +15 -0
  247. package/lib/archive/populate-ref-tables/decode-data-uri.d.ts +21 -0
  248. package/lib/archive/populate-ref-tables/decode-data-uri.js +126 -0
  249. package/lib/archive/populate-ref-tables/decompose-header-set.d.ts +29 -0
  250. package/lib/archive/populate-ref-tables/decompose-header-set.js +157 -0
  251. package/lib/archive/populate-ref-tables/decompose-url.d.ts +25 -0
  252. package/lib/archive/populate-ref-tables/decompose-url.js +70 -0
  253. package/lib/archive/populate-ref-tables/header-stability.d.ts +19 -0
  254. package/lib/archive/populate-ref-tables/header-stability.js +22 -0
  255. package/lib/archive/populate-ref-tables/header-value-cache-key.d.ts +17 -0
  256. package/lib/archive/populate-ref-tables/header-value-cache-key.js +19 -0
  257. package/lib/archive/populate-ref-tables/normalize-mime.d.ts +24 -0
  258. package/lib/archive/populate-ref-tables/normalize-mime.js +36 -0
  259. package/lib/archive/populate-ref-tables/populate-blob-refs.d.ts +38 -0
  260. package/lib/archive/populate-ref-tables/populate-blob-refs.js +134 -0
  261. package/lib/archive/populate-ref-tables/populate-content-type-refs.d.ts +27 -0
  262. package/lib/archive/populate-ref-tables/populate-content-type-refs.js +70 -0
  263. package/lib/archive/populate-ref-tables/populate-header-tables.d.ts +35 -0
  264. package/lib/archive/populate-ref-tables/populate-header-tables.js +80 -0
  265. package/lib/archive/populate-ref-tables/populate-json-refs.d.ts +29 -0
  266. package/lib/archive/populate-ref-tables/populate-json-refs.js +101 -0
  267. package/lib/archive/populate-ref-tables/populate-refs.d.ts +51 -0
  268. package/lib/archive/populate-ref-tables/populate-refs.js +62 -0
  269. package/lib/archive/populate-ref-tables/populate-text-refs.d.ts +32 -0
  270. package/lib/archive/populate-ref-tables/populate-text-refs.js +133 -0
  271. package/lib/archive/populate-ref-tables/populate-url-refs.d.ts +28 -0
  272. package/lib/archive/populate-ref-tables/populate-url-refs.js +148 -0
  273. package/lib/archive/populate-ref-tables/test-utils/count-rows.d.ts +15 -0
  274. package/lib/archive/populate-ref-tables/test-utils/count-rows.js +17 -0
  275. package/lib/archive/populate-ref-tables/types.d.ts +197 -0
  276. package/lib/archive/populate-ref-tables/types.js +7 -0
  277. package/lib/archive/populate-ref-tables/upsert-one-header-set.d.ts +34 -0
  278. package/lib/archive/populate-ref-tables/upsert-one-header-set.js +208 -0
  279. package/lib/archive/populate-ref-tables/volatile-header-names.d.ts +20 -0
  280. package/lib/archive/populate-ref-tables/volatile-header-names.js +33 -0
  281. package/lib/archive/redirect-table.d.ts +4 -2
  282. package/lib/archive/redirect-table.js +15 -10
  283. package/lib/archive/resolve-redirect-chain.d.ts +3 -3
  284. package/lib/archive/resolve-redirect-chain.js +2 -2
  285. package/lib/archive/resource.d.ts +1 -1
  286. package/lib/archive/retarget-legacy-fk-tables.d.ts +47 -0
  287. package/lib/archive/retarget-legacy-fk-tables.js +107 -0
  288. package/lib/archive/test-utils/fk-parent-tables.d.ts +15 -0
  289. package/lib/archive/test-utils/fk-parent-tables.js +19 -0
  290. package/lib/archive/test-utils/seed-content-item.d.ts +35 -0
  291. package/lib/archive/test-utils/seed-content-item.js +42 -0
  292. package/lib/archive/test-utils/setup-legacy-fk-db.d.ts +33 -0
  293. package/lib/archive/test-utils/setup-legacy-fk-db.js +270 -0
  294. package/lib/archive/types.d.ts +151 -15
  295. package/lib/archive/verify-migration/capture-rejection.d.ts +24 -0
  296. package/lib/archive/verify-migration/capture-rejection.js +31 -0
  297. package/lib/archive/verify-migration/check-anchor-edges-count.d.ts +34 -0
  298. package/lib/archive/verify-migration/check-anchor-edges-count.js +72 -0
  299. package/lib/archive/verify-migration/check-anchor-edges-sum.d.ts +13 -0
  300. package/lib/archive/verify-migration/check-anchor-edges-sum.js +27 -0
  301. package/lib/archive/verify-migration/check-content-items-count.d.ts +16 -0
  302. package/lib/archive/verify-migration/check-content-items-count.js +30 -0
  303. package/lib/archive/verify-migration/check-content-type-preservation.d.ts +22 -0
  304. package/lib/archive/verify-migration/check-content-type-preservation.js +40 -0
  305. package/lib/archive/verify-migration/check-foreign-key-integrity.d.ts +31 -0
  306. package/lib/archive/verify-migration/check-foreign-key-integrity.js +47 -0
  307. package/lib/archive/verify-migration/check-image-items-count.d.ts +12 -0
  308. package/lib/archive/verify-migration/check-image-items-count.js +26 -0
  309. package/lib/archive/verify-migration/check-page-meta-count.d.ts +15 -0
  310. package/lib/archive/verify-migration/check-page-meta-count.js +31 -0
  311. package/lib/archive/verify-migration/check-reader-parity.d.ts +23 -0
  312. package/lib/archive/verify-migration/check-reader-parity.js +211 -0
  313. package/lib/archive/verify-migration/check-resource-items-count.d.ts +17 -0
  314. package/lib/archive/verify-migration/check-resource-items-count.js +33 -0
  315. package/lib/archive/verify-migration/check-url-round-trip.d.ts +43 -0
  316. package/lib/archive/verify-migration/check-url-round-trip.js +112 -0
  317. package/lib/archive/verify-migration/types.d.ts +70 -0
  318. package/lib/archive/verify-migration/types.js +63 -0
  319. package/lib/archive/verify-migration/verify-migration.d.ts +41 -0
  320. package/lib/archive/verify-migration/verify-migration.js +120 -0
  321. package/lib/classify-error-kind.d.ts +19 -0
  322. package/lib/classify-error-kind.js +122 -0
  323. package/lib/crawler/build-js-redirect-edge.d.ts +68 -0
  324. package/lib/crawler/build-js-redirect-edge.js +57 -0
  325. package/lib/crawler/build-redirect-event.d.ts +24 -0
  326. package/lib/crawler/build-redirect-event.js +28 -0
  327. package/lib/crawler/capture-image-dom-paths.d.ts +33 -0
  328. package/lib/crawler/capture-image-dom-paths.js +39 -0
  329. package/lib/crawler/clear-dns-burned-host-cache.d.ts +6 -0
  330. package/lib/crawler/clear-dns-burned-host-cache.js +11 -0
  331. package/lib/crawler/collect-image-dom-paths.d.ts +23 -0
  332. package/lib/crawler/collect-image-dom-paths.js +64 -0
  333. package/lib/crawler/crawler.d.ts +22 -1
  334. package/lib/crawler/crawler.js +679 -117
  335. package/lib/crawler/derive-js-redirect-target.d.ts +68 -0
  336. package/lib/crawler/derive-js-redirect-target.js +129 -0
  337. package/lib/crawler/derive-resource-source.d.ts +25 -15
  338. package/lib/crawler/derive-resource-source.js +28 -17
  339. package/lib/crawler/dns-burned-host-cache.d.ts +26 -0
  340. package/lib/crawler/dns-burned-host-cache.js +25 -0
  341. package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +13 -0
  342. package/lib/crawler/dns-burned-host-short-circuit-counter.js +11 -0
  343. package/lib/crawler/fetch-destination.d.ts +12 -4
  344. package/lib/crawler/fetch-destination.js +94 -16
  345. package/lib/crawler/inject-scope-auth.d.ts +1 -1
  346. package/lib/crawler/inject-scope-auth.js +1 -1
  347. package/lib/crawler/is-js-redirect-error-shape.d.ts +40 -0
  348. package/lib/crawler/is-js-redirect-error-shape.js +53 -0
  349. package/lib/crawler/is-puppeteer-fallback-candidate.d.ts +16 -0
  350. package/lib/crawler/is-puppeteer-fallback-candidate.js +63 -0
  351. package/lib/crawler/link-list.d.ts +21 -1
  352. package/lib/crawler/link-list.js +23 -3
  353. package/lib/crawler/normalize-content-type.d.ts +1 -1
  354. package/lib/crawler/normalize-content-type.js +1 -1
  355. package/lib/crawler/plan-sub-resource-emits.d.ts +63 -0
  356. package/lib/crawler/plan-sub-resource-emits.js +44 -0
  357. package/lib/crawler/preload-short-circuit-error.d.ts +22 -0
  358. package/lib/crawler/preload-short-circuit-error.js +25 -0
  359. package/lib/crawler/should-burn-host.d.ts +78 -0
  360. package/lib/crawler/should-burn-host.js +61 -0
  361. package/lib/crawler/should-get-fallback-on-head-failure.d.ts +38 -0
  362. package/lib/crawler/should-get-fallback-on-head-failure.js +46 -0
  363. package/lib/crawler/types.d.ts +108 -1
  364. package/lib/crawler-orchestrator.d.ts +22 -3
  365. package/lib/crawler-orchestrator.js +312 -73
  366. package/lib/crawler.d.ts +15 -2
  367. package/lib/crawler.js +24 -1
  368. package/lib/permanent-error-kinds.d.ts +43 -0
  369. package/lib/permanent-error-kinds.js +48 -0
  370. package/lib/types.d.ts +84 -0
  371. package/lib/utils/compute-file-sha256.d.ts +24 -0
  372. package/lib/utils/compute-file-sha256.js +56 -0
  373. package/lib/utils/error/emit-error-with-retry.d.ts +40 -0
  374. package/lib/utils/error/emit-error-with-retry.js +44 -0
  375. package/lib/utils/error/emit-error.d.ts +39 -0
  376. package/lib/utils/error/emit-error.js +41 -0
  377. package/package.json +12 -12
  378. package/lib/archive/migrate-crawl-errors.d.ts +0 -20
  379. package/lib/archive/migrate-crawl-errors.js +0 -38
  380. package/lib/archive/migrate-html-blob-tables.d.ts +0 -24
  381. package/lib/archive/migrate-html-blob-tables.js +0 -53
  382. package/lib/archive/migrate-page-errors.d.ts +0 -16
  383. package/lib/archive/migrate-page-errors.js +0 -35
  384. package/lib/archive/migrate-pages-resources-source.d.ts +0 -16
  385. package/lib/archive/migrate-pages-resources-source.js +0 -46
  386. package/lib/utils/error/error-emitter.d.ts +0 -18
  387. package/lib/utils/error/error-emitter.js +0 -29
@@ -1,3 +1,6 @@
1
+ import { createAdjunctTables } from './create-adjunct-tables.js';
2
+ import { createEntityTables } from './create-entity-tables.js';
3
+ import { createRefTables } from './create-ref-tables.js';
1
4
  /**
2
5
  * Applies the connection-level PRAGMAs that govern foreign-key enforcement
3
6
  * and BLOB-read performance. These are **per-connection** settings (libsql
@@ -15,337 +18,125 @@ export async function applyConnectionPragmas(instance) {
15
18
  await instance.raw('PRAGMA foreign_keys = ON');
16
19
  await instance.raw('PRAGMA wal_autocheckpoint = 1000');
17
20
  // Negative value = KiB of memory (64 MiB). Helps large BLOB scans.
21
+ //
22
+ // Empirically validated against larger values on a 10 GB archive:
23
+ // bumping to 512 MiB regressed `getSummary` (1.9s → 5.7s), `pages`
24
+ // (2.3s → 21s), and `images` (3.7s → 12s) — libsql's page eviction
25
+ // policy interacts poorly with a cache sized comparable to the
26
+ // host's page-cache window when the DB itself far exceeds RAM.
27
+ // 64 MiB stays the sweet spot.
18
28
  await instance.raw('PRAGMA cache_size = -65536');
19
29
  // 256 MiB mmap window. SQLite falls back to read() past this so the
20
- // limit is a soft ceiling, not a hard one.
30
+ // limit is a soft ceiling, not a hard one. A 4 GiB window was
31
+ // catastrophic on a 10 GB archive on macOS (summary 1.9s → 43s,
32
+ // pages 2.3s → 21s) — the kernel's read-ahead policy and libsql's
33
+ // mmap path interact badly when the window can cover most of the
34
+ // DB. Keep this conservative.
21
35
  await instance.raw('PRAGMA mmap_size = 268435456');
22
36
  }
23
37
  /**
24
38
  * Initializes the archive database schema if tables do not exist.
25
39
  *
26
- * Schema notes:
40
+ * The schema is composed of four groups, each owned by a dedicated DDL
41
+ * function so the migration script (`scripts/migrate-to-0.13.mjs`) can
42
+ * provision the exact same shapes on archives it upgrades:
27
43
  *
28
- * - **Meta columns (v2)**: pages carries ~47 flat columns derived from
29
- * beholder 3.0.0's nested Meta shape (`canonical`, `og_*`, `twitter_*`,
30
- * `robots_*`, document basics, editorial fields) plus a `meta_extras`
31
- * JSON column for everything not flattened. URL-shaped columns are
32
- * absolutised against the page URL before write (see
33
- * `archive/meta/derive-flat-from-meta.ts`).
34
- * - **Denormalised aggregates** (`tag_count`, `jsonld_count`,
35
- * `tags_providers_csv`): computed at write time from `meta.tags` /
36
- * `meta.jsonLd` to avoid N+1 GROUP BY at Sheets-render / page-detail time.
37
- * Plan: "ファイルサイズが多少増えてもいいから取り出しパフォーマンスを優先".
38
- * - **Per-page timestamps** (`firstCrawledAt`, `lastCrawledAt`): UNIX ms.
39
- * Written by `#insertPage` on INSERT (`first = last = now`) and UPDATE
40
- * (`last = now`, `first` preserved). `resetFailedPages` deliberately
41
- * leaves them alone so failure-reset does not erase the last-success
42
- * record.
43
- * - **`page_tags`** (Wappalyzer): per-provider × external-id row shape, plus
44
- * `categories`/`sources` JSON columns. Compound indexes
45
- * `(provider, externalId)` / `(provider, pageId)` are pre-built for the
46
- * Phase 2+ "find duplicate IDs across pages" and "list pages using
47
- * provider X" hot paths — Phase 1 read perf > storage cost trade-off.
48
- * - **`page_jsonld`** (JSON-LD / SpeculationRules): one row per
49
- * `<script type="application/ld+json">` or `<script type="speculationrules">`.
50
- * `raw` is stored uncompressed (SQLite overflow pages handle large rows);
51
- * if cross-archive bulk export becomes a use case, add a `codec` column
52
- * à la `page_html_blobs`. Compound `(type, pageId)` accelerates streaming
53
- * `list_pages_by_jsonld_type` JOINs.
54
- * - **HTML snapshots** (`page_html_blobs` + `page_html_ref`): unchanged
55
- * from v1. zstd-compressed BLOBs keyed by SHA-256 for content-addressable
56
- * dedup. WITHOUT ROWID via raw SQL because knex's schema builder cannot
57
- * express it.
58
- * - **PRAGMA `page_size` and `journal_mode`** are set BEFORE any
59
- * `CREATE TABLE` because SQLite only honors `page_size` changes against
60
- * an empty database, and `journal_mode = WAL` is persistent. Other
61
- * per-connection PRAGMAs live in {@link applyConnectionPragmas}.
44
+ * - **`info`** (inline below): single-row crawl configuration.
45
+ * - **Ref / header dictionary tables** ({@link createRefTables}):
46
+ * `url_refs`, `content_type_refs`, `text_refs`, `json_refs`,
47
+ * `blob_refs`, `header_name_refs`, `header_value_refs`, `header_sets`,
48
+ * `header_set_entries`, `header_flags`. See that file for column-level
49
+ * rationale and the reason `blob_refs` uses a regular rowid PK instead
50
+ * of WITHOUT ROWID.
51
+ * - **Entity / edge tables** ({@link createEntityTables}):
52
+ * `content_items`, `page_meta`, `resource_items`, `anchor_edges`,
53
+ * `resource_ref_edges`, `image_items` — the write-model the crawler
54
+ * writes during a crawl and every reader queries. Must run AFTER
55
+ * `createRefTables` because most entity tables reference ref-table PKs.
56
+ * - **Adjunct tables** ({@link createAdjunctTables}): `page_errors`,
57
+ * `crawl_errors`, `page_tags`, `page_jsonld`, `inventory_runs`,
58
+ * `analysis_text_refs` + `analysis_violations`, `page_html_blobs` +
59
+ * `page_html_ref`. Must run AFTER `createEntityTables` because the
60
+ * page-scoped tables FK into `content_items(id)`.
62
61
  *
63
- * Pre-0.10 0.10 migration is intentionally absent. `assertCompatibleVersion`
64
- * (called before `initSchema`) rejects pre-0.10 archives with a friendly
65
- * error pointing the operator at `scripts/migrate-to-0.10.mjs`; `v0.x`
66
- * policy allows breaking changes.
62
+ * The legacy flat write-model tables (`pages` / `anchors` / `images` /
63
+ * `resources` / `resources-referrers`) are deliberately NOT created:
64
+ * they only exist inside pre-0.13 archives, where they serve as the
65
+ * populate source for `scripts/migrate-to-0.13.mjs` before that script
66
+ * drops them.
67
+ *
68
+ * **PRAGMA `page_size` and `journal_mode`** are set BEFORE any
69
+ * `CREATE TABLE` because SQLite only honors `page_size` changes against
70
+ * an empty database, and `journal_mode = WAL` is persistent. Other
71
+ * per-connection PRAGMAs live in {@link applyConnectionPragmas}.
72
+ *
73
+ * Pre-0.13 migration is intentionally absent. `assertCompatibleVersion`
74
+ * (called before `initSchema`) rejects older archives with a friendly
75
+ * error naming the migration scripts to run; `v0.x` policy allows
76
+ * breaking changes.
67
77
  * @param instance - The Knex query builder instance connected to the database.
68
78
  */
69
79
  export async function initSchema(instance) {
70
- const isExists = await instance.schema.hasTable('info');
71
- if (isExists) {
72
- return;
80
+ // Only the one-shot work (PRAGMAs + `info` creation) is gated on the
81
+ // `info` table's existence. The three DDL groups below run on EVERY
82
+ // call: each is internally idempotent (sentinel / IF NOT EXISTS /
83
+ // per-table guards), and re-running them self-heals an archive whose
84
+ // provisioning crashed partway through — `info` created but a later
85
+ // group missing. With a single all-or-nothing gate, such a stub would
86
+ // short-circuit here forever and every subsequent write would die with
87
+ // `no such table` and no way back.
88
+ if (!(await instance.schema.hasTable('info'))) {
89
+ // Page size must be set on an empty database file; once any data is
90
+ // written, only VACUUM can change it. journal_mode is also one-shot
91
+ // (persistent) and so stays here.
92
+ await instance.raw('PRAGMA page_size = 16384');
93
+ await instance.raw('PRAGMA journal_mode = WAL');
94
+ await instance.schema.createTable('info', (t) => {
95
+ t.increments('id');
96
+ t.string('version');
97
+ t.string('name');
98
+ t.string('baseUrl');
99
+ t.json('roots');
100
+ t.boolean('recursive');
101
+ t.integer('interval');
102
+ t.boolean('image');
103
+ t.boolean('fetchExternal');
104
+ t.integer('parallels');
105
+ t.json('excludes');
106
+ t.json('excludeKeywords');
107
+ t.json('excludeUrls');
108
+ t.integer('maxExcludedDepth');
109
+ t.integer('retry');
110
+ t.boolean('fromList');
111
+ t.boolean('disableQueries');
112
+ t.string('userAgent');
113
+ t.boolean('ignoreRobots');
114
+ });
115
+ }
116
+ // 0.13 ref / header dictionary tables. DDL + column-level rationale
117
+ // lives in {@link createRefTables}, shared with the migration script's
118
+ // schema catch-up — a divergence between the two paths would silently
119
+ // break the population step's UNIQUE / CHECK contract.
120
+ // `createRefTables` uses bare CREATE TABLE, so it needs a sentinel
121
+ // guard here (all 10 tables are created together — any one of them
122
+ // works as the sentinel).
123
+ if (!(await instance.schema.hasTable('url_refs'))) {
124
+ await createRefTables(instance);
73
125
  }
74
- // Page size must be set on an empty database file; once any data is
75
- // written, only VACUUM can change it. journal_mode is also one-shot
76
- // (persistent) and so stays here.
77
- await instance.raw('PRAGMA page_size = 16384');
78
- await instance.raw('PRAGMA journal_mode = WAL');
79
- await instance.schema
80
- .createTable('info', (t) => {
81
- t.increments('id');
82
- t.string('version');
83
- t.string('name');
84
- t.string('baseUrl');
85
- t.json('roots');
86
- t.boolean('recursive');
87
- t.integer('interval');
88
- t.boolean('image');
89
- t.boolean('fetchExternal');
90
- t.integer('parallels');
91
- t.json('excludes');
92
- t.json('excludeKeywords');
93
- t.json('excludeUrls');
94
- t.integer('maxExcludedDepth');
95
- t.integer('retry');
96
- t.boolean('fromList');
97
- t.boolean('disableQueries');
98
- t.string('userAgent');
99
- t.boolean('ignoreRobots');
100
- })
101
- .createTable('pages', (t) => {
102
- t.increments('id');
103
- t.string('url', 8190).notNullable().unique();
104
- t.integer('redirectDestId').unsigned().references('pages.id').defaultTo(null);
105
- t.boolean('scraped').notNullable();
106
- t.boolean('isTarget').notNullable();
107
- t.boolean('isExternal');
108
- t.integer('status');
109
- t.string('statusText');
110
- t.string('contentType').nullable();
111
- t.integer('contentLength').unsigned().nullable();
112
- t.json('responseHeaders').nullable();
113
- // Document basics
114
- t.string('lang');
115
- t.string('dir');
116
- t.string('charset');
117
- t.string('baseHref');
118
- t.text('viewport_raw');
119
- t.string('themeColor');
120
- t.string('applicationName');
121
- t.string('author');
122
- t.string('generator');
123
- t.string('publisher');
124
- // Title / description / keywords (top-level Meta fields)
125
- t.string('title');
126
- t.text('description');
127
- t.text('keywords');
128
- // Robots
129
- t.text('robots_raw');
130
- t.integer('robots_noindex');
131
- t.integer('robots_nofollow');
132
- t.integer('robots_noarchive');
133
- t.integer('robots_noimageindex');
134
- t.string('googlebot');
135
- // Link (1:1 only — array shapes live in meta_extras)
136
- t.string('canonical', 8190);
137
- t.string('amphtml', 8190);
138
- t.string('manifest', 8190);
139
- t.string('icon_href', 8190);
140
- t.string('appleTouchIcon_href', 8190);
141
- // Open Graph
142
- t.string('og_type');
143
- t.string('og_title');
144
- t.string('og_url', 8190);
145
- t.string('og_site_name');
146
- t.text('og_description');
147
- t.string('og_image', 8190);
148
- t.string('og_image_alt');
149
- t.string('og_image_width');
150
- t.string('og_image_height');
151
- t.string('og_locale');
152
- t.string('og_article_published_time');
153
- t.string('og_article_modified_time');
154
- // Twitter
155
- t.string('twitter_card');
156
- t.string('twitter_site');
157
- t.string('twitter_creator');
158
- t.string('twitter_title');
159
- t.text('twitter_description');
160
- t.string('twitter_image', 8190);
161
- // One-offs
162
- t.string('fb_app_id');
163
- t.string('verification_google');
164
- t.integer('formatDetection_telephone');
165
- // Within-archive observation timestamps (UNIX ms)
166
- t.integer('firstCrawledAt');
167
- t.integer('lastCrawledAt');
168
- // Denormalised aggregates (written at scrape time, see
169
- // archive/meta/compute-page-denormalized.ts)
170
- t.integer('tag_count');
171
- t.integer('jsonld_count');
172
- t.text('tags_providers_csv');
173
- // Catch-all JSON for nested Meta sub-objects not flattened above
174
- t.json('meta_extras');
175
- // Crawl lifecycle
176
- t.boolean('isSkipped');
177
- t.string('skipReason');
178
- t.integer('order').unsigned().nullable();
179
- // Provenance: which channel inserted this row. Values:
180
- // 'crawled' — discovered via the recursive crawl from one of `info.roots`
181
- // 'inventory-seed' — supplied directly by `crawl --inventory` URL list
182
- // 'inventory-discovered' — found by following links from an `inventory-seed` page
183
- // Used by `listIsolatedPages` only for badge display; isolation
184
- // itself is judged by `anchors.hrefId IS NULL`, not by source.
185
- t.string('source').notNullable().defaultTo('crawled');
186
- t.index('isExternal');
187
- t.index('contentType');
188
- t.index('scraped');
189
- t.index('redirectDestId');
190
- t.index('order');
191
- // Phase 1: noindex filter (list_pages) and og:type filter
192
- // (analytics) are the only new flat-column filters with enough
193
- // selectivity to benefit from an index. `lang` has cardinality 1
194
- // on mono-language sites (D-Zero's typical customer) so it is
195
- // skipped.
196
- t.index('robots_noindex');
197
- t.index('og_type');
198
- t.index('source');
199
- })
200
- .createTable('anchors', (t) => {
201
- t.increments('id');
202
- t.integer('pageId').notNullable().unsigned().references('pages.id');
203
- t.integer('hrefId').notNullable().unsigned().references('pages.id');
204
- t.string('hash');
205
- t.string('textContent').nullable();
206
- t.index('pageId');
207
- t.index('hrefId');
208
- })
209
- .createTable('images', (t) => {
210
- t.increments('id');
211
- t.integer('pageId').notNullable().unsigned().references('pages.id');
212
- t.string('src', 8190);
213
- t.string('currentSrc', 8190);
214
- t.string('alt');
215
- t.float('width').unsigned().notNullable();
216
- t.float('height').unsigned().notNullable();
217
- t.integer('naturalWidth').unsigned().notNullable();
218
- t.integer('naturalHeight').unsigned().notNullable();
219
- t.boolean('isLazy');
220
- t.integer('viewportWidth').unsigned().notNullable();
221
- t.string('sourceCode');
222
- t.index('pageId');
223
- })
224
- .createTable('resources', (t) => {
225
- t.increments('id');
226
- t.string('url', 8190).notNullable().unique();
227
- t.boolean('isExternal');
228
- t.integer('status');
229
- t.string('statusText');
230
- t.string('contentType').nullable();
231
- t.integer('contentLength').unsigned().nullable();
232
- t.string('compress').nullable();
233
- t.string('cdn').nullable();
234
- t.json('responseHeaders').nullable();
235
- // See `pages.source` for the provenance taxonomy. `inventory-seed`
236
- // rows here come from non-HTML URLs handed in by
237
- // `crawl --inventory`; `inventory-discovered` rows are sub-resources
238
- // pulled in while puppeteer rendered an inventory-seed page.
239
- t.string('source').notNullable().defaultTo('crawled');
240
- t.index('source');
241
- })
242
- .createTable('resources-referrers', (t) => {
243
- t.increments('id');
244
- t.integer('resourceId').notNullable().unsigned().references('resources.id');
245
- t.integer('pageId').notNullable().unsigned().references('pages.id');
246
- t.unique(['resourceId', 'pageId']);
247
- t.index('resourceId');
248
- t.index('pageId');
249
- })
250
- .createTable('page_errors', (t) => {
251
- // Records partial scrape failures (e.g. a viewport switch that
252
- // detaches the frame and trips beholder's @retryable into the
253
- // `retryExhausted` phase). A page can have zero or more rows here
254
- // in addition to its normal `pages` entry — the page itself is
255
- // considered successfully scraped, but image capture or another
256
- // secondary step failed for at least one device preset.
257
- t.increments('id');
258
- t.integer('pageId').notNullable().unsigned().references('pages.id');
259
- t.string('phase').notNullable();
260
- t.text('message').notNullable();
261
- t.integer('createdAt').notNullable();
262
- t.index('pageId');
263
- })
264
- .createTable('crawl_errors', (t) => {
265
- // Structured form of the crawler-level `error` channel that otherwise
266
- // only lands in `error.log`. Unlike `page_errors` these are not tied to
267
- // a scraped page (the URL may be an external link that failed DNS, or
268
- // null for a process-level error), so there is no `pageId` FK and `url`
269
- // is nullable. The cause is NOT stored — it is classified on read from
270
- // `message` so older archives (which only have `error.log`) classify the
271
- // same way.
272
- t.increments('id');
273
- t.string('url', 8190).nullable();
274
- t.boolean('isExternal');
275
- t.text('message').notNullable();
276
- t.integer('createdAt').notNullable();
277
- })
278
- .createTable('page_tags', (t) => {
279
- // Wappalyzer-derived technology detection. One row per
280
- // (provider × externalId) tuple per page. `category` is the first
281
- // element of `categories`; the full list lives in the JSON
282
- // `categories` column. `sources` records where the provider was
283
- // detected (script-src / inline / iframe-src / window-global / …).
284
- t.increments('id');
285
- t.integer('pageId')
286
- .notNullable()
287
- .unsigned()
288
- .references('pages.id')
289
- .onDelete('CASCADE');
290
- t.string('provider').notNullable();
291
- t.string('category');
292
- t.string('externalId');
293
- t.string('version');
294
- t.integer('confidence');
295
- t.json('categories');
296
- t.json('sources');
297
- t.index('pageId');
298
- t.index('provider');
299
- t.index('externalId');
300
- })
301
- .createTable('page_jsonld', (t) => {
302
- // JSON-LD and SpeculationRules entries captured from
303
- // `<script type="application/ld+json">` and
304
- // `<script type="speculationrules">`. `kind` discriminates; `type`
305
- // is the top-level `@type` extracted by classify-jsonld-type for
306
- // indexable filtering. `raw` is stored uncompressed; SQLite
307
- // overflow pages handle multi-KB JSON bodies transparently.
308
- t.increments('id');
309
- t.integer('pageId')
310
- .notNullable()
311
- .unsigned()
312
- .references('pages.id')
313
- .onDelete('CASCADE');
314
- t.string('kind').notNullable();
315
- t.string('type');
316
- t.text('raw').notNullable();
317
- t.json('parsed');
318
- t.text('parseError');
319
- t.index('pageId');
320
- t.index('type');
321
- });
322
- // ON DELETE CASCADE and compound indexes for the new tables. Knex's
323
- // schema builder can't express CASCADE / compound indexes inline in a
324
- // way that round-trips through libsql consistently, so we use raw SQL
325
- // to mirror the `page_html_ref` pattern.
326
- await instance.raw('CREATE INDEX page_tags_provider_extId ON page_tags(provider, externalId)');
327
- await instance.raw('CREATE INDEX page_tags_provider_pageId ON page_tags(provider, pageId)');
328
- await instance.raw('CREATE INDEX page_jsonld_type_pageId ON page_jsonld(type, pageId)');
329
- // Content-addressable HTML blob storage. Knex's schema builder doesn't
330
- // expose a WITHOUT ROWID toggle, so the BLOB tables are created via raw
331
- // SQL. WITHOUT ROWID keeps the rows packed inside the b-tree leaves
332
- // (no hidden rowid + secondary index pair), which matters for the blob
333
- // table where a 32-byte hash PK + multi-KB body is the dominant row
334
- // shape.
335
- await instance.raw(`
336
- CREATE TABLE page_html_blobs (
337
- hash BLOB PRIMARY KEY,
338
- body BLOB NOT NULL,
339
- codec TEXT NOT NULL CHECK(codec IN ('zstd', 'none')),
340
- size_raw INTEGER NOT NULL,
341
- size_stored INTEGER NOT NULL
342
- ) WITHOUT ROWID
343
- `);
344
- await instance.raw(`
345
- CREATE TABLE page_html_ref (
346
- page_id INTEGER PRIMARY KEY REFERENCES pages(id) ON DELETE CASCADE,
347
- hash BLOB NOT NULL REFERENCES page_html_blobs(hash)
348
- ) WITHOUT ROWID
349
- `);
350
- await instance.raw('CREATE INDEX idx_page_html_ref_hash ON page_html_ref(hash)');
126
+ // 0.13 core entity / edge tables the write-model the crawler writes
127
+ // during a crawl and every reader queries. MUST run after
128
+ // {@link createRefTables} because `content_items`, `page_meta`,
129
+ // `resource_items`, `anchor_edges`, and `image_items` all reference
130
+ // the ref tables (`url_refs`, `content_type_refs`, `text_refs`,
131
+ // `json_refs`, `blob_refs`, `header_sets`) via FK clauses.
132
+ await createEntityTables(instance);
133
+ // Adjunct tables that FK into `content_items` (page_errors / page_tags /
134
+ // page_jsonld / analysis_* / page_html_*) plus the standalone log tables
135
+ // (crawl_errors / inventory_runs). MUST run after
136
+ // {@link createEntityTables} so the FK targets exist. DDL +
137
+ // column-level rationale lives in {@link createAdjunctTables}, which is
138
+ // shared with `scripts/migrate-to-0.13.mjs` — a divergence between the
139
+ // two paths is exactly how migrated archives ended up with stale
140
+ // `REFERENCES pages(id)` declarations in the pre-0.13 era.
141
+ await createAdjunctTables(instance);
351
142
  }
@@ -0,0 +1,21 @@
1
+ import type { PageSource } from './types.js';
2
+ /**
3
+ * Predicate that returns `true` when the given page source value belongs to
4
+ * the inventory chain — i.e. it is one of the `'inventory-*'` variants of
5
+ * {@link PageSource}.
6
+ *
7
+ * Centralises the membership check that decides whether lineage
8
+ * propagation should fire. Three call sites used to inline
9
+ * `s === 'inventory-seed' || s === 'inventory-discovered'`, which is both
10
+ * a DRY violation AND a future-proofing trap: when a new inventory-family
11
+ * label gets added (e.g. `'inventory-promoted'`), every inlined check has
12
+ * to be located and updated by hand. Routing through this predicate
13
+ * keeps the membership rule in one place.
14
+ *
15
+ * Returns `false` for `undefined` so callers can pass the raw `source`
16
+ * column value (which is non-NULL in the DB schema but reads as
17
+ * `undefined` from a missing row in JS) without a separate null check.
18
+ * @param source - The source value to test, or `undefined` when no row matched.
19
+ * @returns `true` if the source is in the inventory chain, `false` otherwise.
20
+ */
21
+ export declare function isInventorySource(source: PageSource | undefined): boolean;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Predicate that returns `true` when the given page source value belongs to
3
+ * the inventory chain — i.e. it is one of the `'inventory-*'` variants of
4
+ * {@link PageSource}.
5
+ *
6
+ * Centralises the membership check that decides whether lineage
7
+ * propagation should fire. Three call sites used to inline
8
+ * `s === 'inventory-seed' || s === 'inventory-discovered'`, which is both
9
+ * a DRY violation AND a future-proofing trap: when a new inventory-family
10
+ * label gets added (e.g. `'inventory-promoted'`), every inlined check has
11
+ * to be located and updated by hand. Routing through this predicate
12
+ * keeps the membership rule in one place.
13
+ *
14
+ * Returns `false` for `undefined` so callers can pass the raw `source`
15
+ * column value (which is non-NULL in the DB schema but reads as
16
+ * `undefined` from a missing row in JS) without a separate null check.
17
+ * @param source - The source value to test, or `undefined` when no row matched.
18
+ * @returns `true` if the source is in the inventory chain, `false` otherwise.
19
+ */
20
+ export function isInventorySource(source) {
21
+ return source === 'inventory-seed' || source === 'inventory-discovered';
22
+ }
@@ -1,7 +1,8 @@
1
1
  import type { Knex } from 'knex';
2
2
  /**
3
3
  * Returns a Knex subquery builder that selects page IDs with pagination,
4
- * ordered by the `order` column (nulls last), excluding redirected pages.
4
+ * ordered by `content_items.crawl_order` (nulls last), excluding
5
+ * redirected pages.
5
6
  * @param limit - The maximum number of page IDs to return.
6
7
  * @param offset - The number of page IDs to skip before returning results.
7
8
  */
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Returns a Knex subquery builder that selects page IDs with pagination,
3
- * ordered by the `order` column (nulls last), excluding redirected pages.
3
+ * ordered by `content_items.crawl_order` (nulls last), excluding
4
+ * redirected pages.
4
5
  * @param limit - The maximum number of page IDs to return.
5
6
  * @param offset - The number of page IDs to skip before returning results.
6
7
  */
@@ -8,9 +9,9 @@ export function limitedPageIds(limit, offset) {
8
9
  return async (qb) => {
9
10
  await qb
10
11
  .select('id')
11
- .from('pages')
12
- .orderByRaw('`order` ASC NULLS LAST')
13
- .whereNull('redirectDestId')
12
+ .from('content_items')
13
+ .orderByRaw('`crawl_order` ASC NULLS LAST')
14
+ .whereNull('redirect_dest_id')
14
15
  .limit(limit)
15
16
  .offset(offset);
16
17
  };
@@ -1,18 +1,39 @@
1
1
  import type { Knex } from 'knex';
2
2
  /**
3
3
  * Minimum `info.version` this build accepts. Archives older than this must
4
- * be upgraded with `scripts/migrate-to-0.10.mjs` before they can be opened.
4
+ * be upgraded by running the matching migration script before they can be
5
+ * opened (see {@link IncompatibleArchiveError}'s message for the mapping
6
+ * from archive version to script).
7
+ *
8
+ * This value tracks the **archive format cut**, not the npm `pkg.version`:
9
+ * a format-breaking change bumps it ahead of the corresponding release,
10
+ * and releases that do not cut the format leave it unchanged. The crawler
11
+ * writes this exact value into every new archive's `info.version`.
5
12
  *
6
13
  * History:
7
14
  *
8
15
  * - **pre-0.10**: HTML snapshots in `snapshot-html.zip` (#75), then
9
16
  * relocated to `page_html_blobs` (#84); pages table has flat `noindex`,
10
17
  * `og:type`-style columns derived from beholder 2.x's flat `Meta`.
11
- * - **0.10.0**: this build. `page_html_blobs` BLOB storage (#75/#84) +
18
+ * - **0.10.0**: `page_html_blobs` BLOB storage (#75/#84) +
12
19
  * nested-`Meta`-derived flat columns, `meta_extras` JSON, `page_tags` /
13
20
  * `page_jsonld` tables, denormalised aggregates (#85).
21
+ * - **0.13.0**: this build. Write-model refactor (epic #103) —
22
+ * `content_items` / `page_meta` / `resource_items` / `anchor_edges` /
23
+ * `resource_ref_edges` / `image_items` entity tables plus `url_refs` /
24
+ * `text_refs` / `content_type_refs` / `json_refs` / `blob_refs` /
25
+ * `header_flags` ref tables. Readers query the new tables exclusively;
26
+ * pre-0.13 archives must be upgraded with `scripts/migrate-to-0.13.mjs`
27
+ * which bumps `info.version` to `0.13.0` on completion.
28
+ * @example
29
+ * import { REQUIRED_FORMAT_VERSION } from '@nitpicker/crawler';
30
+ *
31
+ * // Warn an operator before attempting to open an old archive:
32
+ * if (compareSemver(archiveInfo.version, REQUIRED_FORMAT_VERSION) < 0) {
33
+ * console.error(`Archive needs migration to format ${REQUIRED_FORMAT_VERSION}`);
34
+ * }
14
35
  */
15
- export declare const REQUIRED_FORMAT_VERSION = "0.10.0";
36
+ export declare const REQUIRED_FORMAT_VERSION = "0.13.0";
16
37
  /**
17
38
  * Verifies that the archive's on-disk format is compatible with this build.
18
39
  *
@@ -2,18 +2,39 @@ import { compareSemver } from './compare-semver.js';
2
2
  import { IncompatibleArchiveError } from './types.js';
3
3
  /**
4
4
  * Minimum `info.version` this build accepts. Archives older than this must
5
- * be upgraded with `scripts/migrate-to-0.10.mjs` before they can be opened.
5
+ * be upgraded by running the matching migration script before they can be
6
+ * opened (see {@link IncompatibleArchiveError}'s message for the mapping
7
+ * from archive version to script).
8
+ *
9
+ * This value tracks the **archive format cut**, not the npm `pkg.version`:
10
+ * a format-breaking change bumps it ahead of the corresponding release,
11
+ * and releases that do not cut the format leave it unchanged. The crawler
12
+ * writes this exact value into every new archive's `info.version`.
6
13
  *
7
14
  * History:
8
15
  *
9
16
  * - **pre-0.10**: HTML snapshots in `snapshot-html.zip` (#75), then
10
17
  * relocated to `page_html_blobs` (#84); pages table has flat `noindex`,
11
18
  * `og:type`-style columns derived from beholder 2.x's flat `Meta`.
12
- * - **0.10.0**: this build. `page_html_blobs` BLOB storage (#75/#84) +
19
+ * - **0.10.0**: `page_html_blobs` BLOB storage (#75/#84) +
13
20
  * nested-`Meta`-derived flat columns, `meta_extras` JSON, `page_tags` /
14
21
  * `page_jsonld` tables, denormalised aggregates (#85).
22
+ * - **0.13.0**: this build. Write-model refactor (epic #103) —
23
+ * `content_items` / `page_meta` / `resource_items` / `anchor_edges` /
24
+ * `resource_ref_edges` / `image_items` entity tables plus `url_refs` /
25
+ * `text_refs` / `content_type_refs` / `json_refs` / `blob_refs` /
26
+ * `header_flags` ref tables. Readers query the new tables exclusively;
27
+ * pre-0.13 archives must be upgraded with `scripts/migrate-to-0.13.mjs`
28
+ * which bumps `info.version` to `0.13.0` on completion.
29
+ * @example
30
+ * import { REQUIRED_FORMAT_VERSION } from '@nitpicker/crawler';
31
+ *
32
+ * // Warn an operator before attempting to open an old archive:
33
+ * if (compareSemver(archiveInfo.version, REQUIRED_FORMAT_VERSION) < 0) {
34
+ * console.error(`Archive needs migration to format ${REQUIRED_FORMAT_VERSION}`);
35
+ * }
15
36
  */
16
- export const REQUIRED_FORMAT_VERSION = '0.10.0';
37
+ export const REQUIRED_FORMAT_VERSION = '0.13.0';
17
38
  /**
18
39
  * Verifies that the archive's on-disk format is compatible with this build.
19
40
  *