@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.
- package/README.md +6 -4
- package/lib/archive/archive-accessor.d.ts +2 -2
- package/lib/archive/archive-accessor.js +2 -2
- package/lib/archive/archive-lock.d.ts +7 -0
- package/lib/archive/archive-lock.js +7 -0
- package/lib/archive/archive.d.ts +63 -16
- package/lib/archive/archive.js +56 -17
- package/lib/archive/create-adjunct-tables.d.ts +43 -0
- package/lib/archive/create-adjunct-tables.js +213 -0
- package/lib/archive/create-entity-tables.d.ts +173 -0
- package/lib/archive/create-entity-tables.js +318 -0
- package/lib/archive/create-progress-reporter.d.ts +30 -0
- package/lib/archive/create-progress-reporter.js +38 -0
- package/lib/archive/create-ref-tables.d.ts +35 -0
- package/lib/archive/create-ref-tables.js +188 -0
- package/lib/archive/database.d.ts +92 -345
- package/lib/archive/database.js +168 -1942
- package/lib/archive/db-ops/_shared/clear-write-ref-caches.d.ts +27 -0
- package/lib/archive/db-ops/_shared/clear-write-ref-caches.js +34 -0
- package/lib/archive/db-ops/_shared/create-write-ref-caches.d.ts +17 -0
- package/lib/archive/db-ops/_shared/create-write-ref-caches.js +26 -0
- package/lib/archive/db-ops/_shared/decode-json-ref.d.ts +17 -0
- package/lib/archive/db-ops/_shared/decode-json-ref.js +31 -0
- package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.d.ts +20 -0
- package/lib/archive/db-ops/_shared/load-response-headers-by-set-ids.js +53 -0
- package/lib/archive/db-ops/_shared/resolve-content-item-id.d.ts +61 -0
- package/lib/archive/db-ops/_shared/resolve-content-item-id.js +111 -0
- package/lib/archive/db-ops/_shared/resolve-url-or-blob.d.ts +23 -0
- package/lib/archive/db-ops/_shared/resolve-url-or-blob.js +29 -0
- package/lib/archive/db-ops/_shared/retry-setting.d.ts +16 -0
- package/lib/archive/db-ops/_shared/retry-setting.js +18 -0
- package/lib/archive/db-ops/_shared/safe-parse-json.d.ts +11 -0
- package/lib/archive/db-ops/_shared/safe-parse-json.js +18 -0
- package/lib/archive/db-ops/_shared/types.d.ts +53 -0
- package/lib/archive/db-ops/_shared/types.js +1 -0
- package/lib/archive/db-ops/_shared/upsert-blob-ref.d.ts +25 -0
- package/lib/archive/db-ops/_shared/upsert-blob-ref.js +48 -0
- package/lib/archive/db-ops/_shared/upsert-content-type-ref.d.ts +30 -0
- package/lib/archive/db-ops/_shared/upsert-content-type-ref.js +45 -0
- package/lib/archive/db-ops/_shared/upsert-json-ref.d.ts +22 -0
- package/lib/archive/db-ops/_shared/upsert-json-ref.js +41 -0
- package/lib/archive/db-ops/_shared/upsert-response-headers.d.ts +35 -0
- package/lib/archive/db-ops/_shared/upsert-response-headers.js +49 -0
- package/lib/archive/db-ops/_shared/upsert-url-ref.d.ts +39 -0
- package/lib/archive/db-ops/_shared/upsert-url-ref.js +62 -0
- package/lib/archive/db-ops/analysis/replace-analysis-violations.d.ts +28 -0
- package/lib/archive/db-ops/analysis/replace-analysis-violations.js +152 -0
- package/lib/archive/db-ops/anchors/get-anchors-on-page.d.ts +10 -0
- package/lib/archive/db-ops/anchors/get-anchors-on-page.js +21 -0
- package/lib/archive/db-ops/config/get-base-url.d.ts +8 -0
- package/lib/archive/db-ops/config/get-base-url.js +14 -0
- package/lib/archive/db-ops/config/get-config.d.ts +10 -0
- package/lib/archive/db-ops/config/get-config.js +27 -0
- package/lib/archive/db-ops/config/get-name.d.ts +8 -0
- package/lib/archive/db-ops/config/get-name.js +14 -0
- package/lib/archive/db-ops/config/info-column-allowlist.d.ts +7 -0
- package/lib/archive/db-ops/config/info-column-allowlist.js +26 -0
- package/lib/archive/db-ops/config/info-json-columns.d.ts +5 -0
- package/lib/archive/db-ops/config/info-json-columns.js +10 -0
- package/lib/archive/db-ops/config/set-config.d.ts +12 -0
- package/lib/archive/db-ops/config/set-config.js +21 -0
- package/lib/archive/db-ops/config/update-config.d.ts +17 -0
- package/lib/archive/db-ops/config/update-config.js +36 -0
- package/lib/archive/db-ops/errors/insert-crawl-error.d.ts +15 -0
- package/lib/archive/db-ops/errors/insert-crawl-error.js +21 -0
- package/lib/archive/db-ops/errors/insert-page-error.d.ts +21 -0
- package/lib/archive/db-ops/errors/insert-page-error.js +28 -0
- package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.d.ts +22 -0
- package/lib/archive/db-ops/errors/list-dns-burned-host-candidates.js +141 -0
- package/lib/archive/db-ops/html/get-html-of-page-by-id.d.ts +18 -0
- package/lib/archive/db-ops/html/get-html-of-page-by-id.js +29 -0
- package/lib/archive/db-ops/inventory/record-inventory-run.d.ts +21 -0
- package/lib/archive/db-ops/inventory/record-inventory-run.js +38 -0
- package/lib/archive/db-ops/lifecycle/checkpoint.d.ts +8 -0
- package/lib/archive/db-ops/lifecycle/checkpoint.js +9 -0
- package/lib/archive/db-ops/lifecycle/destroy.d.ts +6 -0
- package/lib/archive/db-ops/lifecycle/destroy.js +7 -0
- package/lib/archive/db-ops/lifecycle/init.d.ts +22 -0
- package/lib/archive/db-ops/lifecycle/init.js +42 -0
- package/lib/archive/db-ops/meta/get-jsonld-of-page.d.ts +13 -0
- package/lib/archive/db-ops/meta/get-jsonld-of-page.js +27 -0
- package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +12 -0
- package/lib/archive/db-ops/meta/get-tags-of-page.js +28 -0
- package/lib/archive/db-ops/pages/order/set-url-order.d.ts +8 -0
- package/lib/archive/db-ops/pages/order/set-url-order.js +32 -0
- package/lib/archive/db-ops/pages/read/build-page-query.d.ts +18 -0
- package/lib/archive/db-ops/pages/read/build-page-query.js +40 -0
- package/lib/archive/db-ops/pages/read/get-crawling-state.d.ts +70 -0
- package/lib/archive/db-ops/pages/read/get-crawling-state.js +98 -0
- package/lib/archive/db-ops/pages/read/get-existing-page-urls.d.ts +15 -0
- package/lib/archive/db-ops/pages/read/get-existing-page-urls.js +30 -0
- package/lib/archive/db-ops/pages/read/get-page-count.d.ts +12 -0
- package/lib/archive/db-ops/pages/read/get-page-count.js +21 -0
- package/lib/archive/db-ops/pages/read/get-page-source-by-url.d.ts +24 -0
- package/lib/archive/db-ops/pages/read/get-page-source-by-url.js +28 -0
- package/lib/archive/db-ops/pages/read/get-pages-with-rels.d.ts +38 -0
- package/lib/archive/db-ops/pages/read/get-pages-with-rels.js +107 -0
- package/lib/archive/db-ops/pages/read/get-pages.d.ts +11 -0
- package/lib/archive/db-ops/pages/read/get-pages.js +51 -0
- package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.d.ts +18 -0
- package/lib/archive/db-ops/pages/read/get-scraped-html-page-count.js +25 -0
- package/lib/archive/db-ops/pages/read/reconstruct-page-rows.d.ts +31 -0
- package/lib/archive/db-ops/pages/read/reconstruct-page-rows.js +32 -0
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +24 -0
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +93 -0
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +47 -0
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +124 -0
- package/lib/archive/db-ops/pages/write/insert-inventory-seeds.d.ts +37 -0
- package/lib/archive/db-ops/pages/write/insert-inventory-seeds.js +72 -0
- package/lib/archive/db-ops/pages/write/insert-jsonld.d.ts +17 -0
- package/lib/archive/db-ops/pages/write/insert-jsonld.js +49 -0
- package/lib/archive/db-ops/pages/write/insert-page.d.ts +36 -0
- package/lib/archive/db-ops/pages/write/insert-page.js +208 -0
- package/lib/archive/db-ops/pages/write/insert-tags.d.ts +16 -0
- package/lib/archive/db-ops/pages/write/insert-tags.js +34 -0
- package/lib/archive/db-ops/pages/write/link-redirect-sources.d.ts +36 -0
- package/lib/archive/db-ops/pages/write/link-redirect-sources.js +93 -0
- package/lib/archive/db-ops/pages/write/record-redirect.d.ts +35 -0
- package/lib/archive/db-ops/pages/write/record-redirect.js +100 -0
- package/lib/archive/db-ops/pages/write/set-skipped-page.d.ts +13 -0
- package/lib/archive/db-ops/pages/write/set-skipped-page.js +22 -0
- package/lib/archive/db-ops/pages/write/update-page.d.ts +29 -0
- package/lib/archive/db-ops/pages/write/update-page.js +334 -0
- package/lib/archive/db-ops/pages/write/write-page-html-blob.d.ts +19 -0
- package/lib/archive/db-ops/pages/write/write-page-html-blob.js +41 -0
- package/lib/archive/db-ops/referrers/get-redirects-for-pages.d.ts +9 -0
- package/lib/archive/db-ops/referrers/get-redirects-for-pages.js +15 -0
- package/lib/archive/db-ops/referrers/get-referrers-of-page.d.ts +17 -0
- package/lib/archive/db-ops/referrers/get-referrers-of-page.js +32 -0
- package/lib/archive/db-ops/referrers/get-referrers-of-resource.d.ts +8 -0
- package/lib/archive/db-ops/referrers/get-referrers-of-resource.js +15 -0
- package/lib/archive/db-ops/resources/build-resource-query.d.ts +25 -0
- package/lib/archive/db-ops/resources/build-resource-query.js +29 -0
- package/lib/archive/db-ops/resources/get-existing-resource-urls.d.ts +9 -0
- package/lib/archive/db-ops/resources/get-existing-resource-urls.js +24 -0
- package/lib/archive/db-ops/resources/get-resource-by-url.d.ts +13 -0
- package/lib/archive/db-ops/resources/get-resource-by-url.js +22 -0
- package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +9 -0
- package/lib/archive/db-ops/resources/get-resource-url-list.js +13 -0
- package/lib/archive/db-ops/resources/get-resources.d.ts +8 -0
- package/lib/archive/db-ops/resources/get-resources.js +11 -0
- package/lib/archive/db-ops/resources/insert-inventory-resources.d.ts +24 -0
- package/lib/archive/db-ops/resources/insert-inventory-resources.js +64 -0
- package/lib/archive/db-ops/resources/insert-resource-referrers.d.ts +15 -0
- package/lib/archive/db-ops/resources/insert-resource-referrers.js +54 -0
- package/lib/archive/db-ops/resources/insert-resource.d.ts +34 -0
- package/lib/archive/db-ops/resources/insert-resource.js +73 -0
- package/lib/archive/db-ops/resources/reconstruct-resource-rows.d.ts +26 -0
- package/lib/archive/db-ops/resources/reconstruct-resource-rows.js +30 -0
- package/lib/archive/decode-html-blob.d.ts +18 -0
- package/lib/archive/decode-html-blob.js +31 -0
- package/lib/archive/derive-lineage-from-parent.d.ts +1 -1
- package/lib/archive/derive-lineage-from-parent.js +1 -1
- package/lib/archive/drop-legacy-tables.d.ts +45 -0
- package/lib/archive/drop-legacy-tables.js +56 -0
- package/lib/archive/filesystem/rename.js +1 -1
- package/lib/archive/get-failed-page-messages.d.ts +5 -4
- package/lib/archive/get-failed-page-messages.js +5 -4
- package/lib/archive/init-schema.d.ts +35 -39
- package/lib/archive/init-schema.js +99 -460
- package/lib/archive/limited-page-ids.d.ts +2 -1
- package/lib/archive/limited-page-ids.js +5 -4
- package/lib/archive/meta/assert-compatible-version.d.ts +24 -3
- package/lib/archive/meta/assert-compatible-version.js +24 -3
- package/lib/archive/meta/types.d.ts +87 -1
- package/lib/archive/meta/types.js +34 -2
- package/lib/archive/migrate-entity-tables.d.ts +45 -0
- package/lib/archive/migrate-entity-tables.js +56 -0
- package/lib/archive/migrate-ref-tables.d.ts +25 -0
- package/lib/archive/migrate-ref-tables.js +38 -0
- package/lib/archive/page-meta-column-maps.d.ts +32 -0
- package/lib/archive/page-meta-column-maps.js +43 -0
- package/lib/archive/page.d.ts +6 -6
- package/lib/archive/page.js +5 -5
- package/lib/archive/peek-archive-lock.d.ts +2 -2
- package/lib/archive/peek-archive-lock.js +2 -2
- package/lib/archive/populate-entity-tables/collapse-anchor-rows.d.ts +41 -0
- package/lib/archive/populate-entity-tables/collapse-anchor-rows.js +87 -0
- package/lib/archive/populate-entity-tables/derive-dom-path.d.ts +35 -0
- package/lib/archive/populate-entity-tables/derive-dom-path.js +72 -0
- package/lib/archive/populate-entity-tables/is-blob-ref-value.d.ts +16 -0
- package/lib/archive/populate-entity-tables/is-blob-ref-value.js +19 -0
- package/lib/archive/populate-entity-tables/match-images-to-dom-paths.d.ts +66 -0
- package/lib/archive/populate-entity-tables/match-images-to-dom-paths.js +96 -0
- package/lib/archive/populate-entity-tables/populate-anchor-edges.d.ts +33 -0
- package/lib/archive/populate-entity-tables/populate-anchor-edges.js +153 -0
- package/lib/archive/populate-entity-tables/populate-content-items.d.ts +40 -0
- package/lib/archive/populate-entity-tables/populate-content-items.js +141 -0
- package/lib/archive/populate-entity-tables/populate-entities.d.ts +81 -0
- package/lib/archive/populate-entity-tables/populate-entities.js +111 -0
- package/lib/archive/populate-entity-tables/populate-image-items.d.ts +91 -0
- package/lib/archive/populate-entity-tables/populate-image-items.js +223 -0
- package/lib/archive/populate-entity-tables/populate-page-meta.d.ts +33 -0
- package/lib/archive/populate-entity-tables/populate-page-meta.js +267 -0
- package/lib/archive/populate-entity-tables/populate-resource-items.d.ts +22 -0
- package/lib/archive/populate-entity-tables/populate-resource-items.js +114 -0
- package/lib/archive/populate-entity-tables/populate-resource-ref-edges.d.ts +31 -0
- package/lib/archive/populate-entity-tables/populate-resource-ref-edges.js +33 -0
- package/lib/archive/populate-entity-tables/resolve-blob-refs.d.ts +31 -0
- package/lib/archive/populate-entity-tables/resolve-blob-refs.js +100 -0
- package/lib/archive/populate-entity-tables/resolve-content-type-refs.d.ts +22 -0
- package/lib/archive/populate-entity-tables/resolve-content-type-refs.js +27 -0
- package/lib/archive/populate-entity-tables/resolve-header-sets.d.ts +49 -0
- package/lib/archive/populate-entity-tables/resolve-header-sets.js +122 -0
- package/lib/archive/populate-entity-tables/resolve-json-refs.d.ts +25 -0
- package/lib/archive/populate-entity-tables/resolve-json-refs.js +67 -0
- package/lib/archive/populate-entity-tables/resolve-text-refs.d.ts +30 -0
- package/lib/archive/populate-entity-tables/resolve-text-refs.js +61 -0
- package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.d.ts +21 -0
- package/lib/archive/populate-entity-tables/resolve-url-or-blob-from-maps.js +27 -0
- package/lib/archive/populate-entity-tables/resolve-url-refs.d.ts +33 -0
- package/lib/archive/populate-entity-tables/resolve-url-refs.js +60 -0
- package/lib/archive/populate-entity-tables/test-utils/count-rows.d.ts +17 -0
- package/lib/archive/populate-entity-tables/test-utils/count-rows.js +20 -0
- package/lib/archive/populate-entity-tables/test-utils/seed-content-items.d.ts +25 -0
- package/lib/archive/populate-entity-tables/test-utils/seed-content-items.js +42 -0
- package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.d.ts +23 -0
- package/lib/archive/populate-entity-tables/test-utils/setup-entities-db.js +178 -0
- package/lib/archive/populate-entity-tables/types.d.ts +157 -0
- package/lib/archive/populate-entity-tables/types.js +12 -0
- package/lib/archive/populate-entity-tables/upsert-text-refs.d.ts +38 -0
- package/lib/archive/populate-entity-tables/upsert-text-refs.js +78 -0
- package/lib/archive/populate-ref-tables/classify-content-type.d.ts +16 -0
- package/lib/archive/populate-ref-tables/classify-content-type.js +52 -0
- package/lib/archive/populate-ref-tables/compute-content-hash.d.ts +22 -0
- package/lib/archive/populate-ref-tables/compute-content-hash.js +26 -0
- package/lib/archive/populate-ref-tables/compute-header-flags.d.ts +16 -0
- package/lib/archive/populate-ref-tables/compute-header-flags.js +70 -0
- package/lib/archive/populate-ref-tables/content-type-rules.d.ts +38 -0
- package/lib/archive/populate-ref-tables/content-type-rules.js +133 -0
- package/lib/archive/populate-ref-tables/create-header-table-caches.d.ts +25 -0
- package/lib/archive/populate-ref-tables/create-header-table-caches.js +49 -0
- package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.d.ts +15 -0
- package/lib/archive/populate-ref-tables/data-uri-url-refs-limit.js +15 -0
- package/lib/archive/populate-ref-tables/decode-data-uri.d.ts +21 -0
- package/lib/archive/populate-ref-tables/decode-data-uri.js +126 -0
- package/lib/archive/populate-ref-tables/decompose-header-set.d.ts +29 -0
- package/lib/archive/populate-ref-tables/decompose-header-set.js +157 -0
- package/lib/archive/populate-ref-tables/decompose-url.d.ts +25 -0
- package/lib/archive/populate-ref-tables/decompose-url.js +70 -0
- package/lib/archive/populate-ref-tables/header-stability.d.ts +19 -0
- package/lib/archive/populate-ref-tables/header-stability.js +22 -0
- package/lib/archive/populate-ref-tables/header-value-cache-key.d.ts +17 -0
- package/lib/archive/populate-ref-tables/header-value-cache-key.js +19 -0
- package/lib/archive/populate-ref-tables/normalize-mime.d.ts +24 -0
- package/lib/archive/populate-ref-tables/normalize-mime.js +36 -0
- package/lib/archive/populate-ref-tables/populate-blob-refs.d.ts +38 -0
- package/lib/archive/populate-ref-tables/populate-blob-refs.js +134 -0
- package/lib/archive/populate-ref-tables/populate-content-type-refs.d.ts +27 -0
- package/lib/archive/populate-ref-tables/populate-content-type-refs.js +70 -0
- package/lib/archive/populate-ref-tables/populate-header-tables.d.ts +35 -0
- package/lib/archive/populate-ref-tables/populate-header-tables.js +80 -0
- package/lib/archive/populate-ref-tables/populate-json-refs.d.ts +29 -0
- package/lib/archive/populate-ref-tables/populate-json-refs.js +101 -0
- package/lib/archive/populate-ref-tables/populate-refs.d.ts +51 -0
- package/lib/archive/populate-ref-tables/populate-refs.js +62 -0
- package/lib/archive/populate-ref-tables/populate-text-refs.d.ts +32 -0
- package/lib/archive/populate-ref-tables/populate-text-refs.js +133 -0
- package/lib/archive/populate-ref-tables/populate-url-refs.d.ts +28 -0
- package/lib/archive/populate-ref-tables/populate-url-refs.js +148 -0
- package/lib/archive/populate-ref-tables/test-utils/count-rows.d.ts +15 -0
- package/lib/archive/populate-ref-tables/test-utils/count-rows.js +17 -0
- package/lib/archive/populate-ref-tables/types.d.ts +197 -0
- package/lib/archive/populate-ref-tables/types.js +7 -0
- package/lib/archive/populate-ref-tables/upsert-one-header-set.d.ts +34 -0
- package/lib/archive/populate-ref-tables/upsert-one-header-set.js +208 -0
- package/lib/archive/populate-ref-tables/volatile-header-names.d.ts +20 -0
- package/lib/archive/populate-ref-tables/volatile-header-names.js +33 -0
- package/lib/archive/redirect-table.d.ts +4 -2
- package/lib/archive/redirect-table.js +15 -10
- package/lib/archive/resolve-redirect-chain.d.ts +3 -3
- package/lib/archive/resolve-redirect-chain.js +2 -2
- package/lib/archive/resource.d.ts +1 -1
- package/lib/archive/retarget-legacy-fk-tables.d.ts +47 -0
- package/lib/archive/retarget-legacy-fk-tables.js +107 -0
- package/lib/archive/test-utils/fk-parent-tables.d.ts +15 -0
- package/lib/archive/test-utils/fk-parent-tables.js +19 -0
- package/lib/archive/test-utils/seed-content-item.d.ts +35 -0
- package/lib/archive/test-utils/seed-content-item.js +42 -0
- package/lib/archive/test-utils/setup-legacy-fk-db.d.ts +33 -0
- package/lib/archive/test-utils/setup-legacy-fk-db.js +270 -0
- package/lib/archive/types.d.ts +127 -24
- package/lib/archive/verify-migration/capture-rejection.d.ts +24 -0
- package/lib/archive/verify-migration/capture-rejection.js +31 -0
- package/lib/archive/verify-migration/check-anchor-edges-count.d.ts +34 -0
- package/lib/archive/verify-migration/check-anchor-edges-count.js +72 -0
- package/lib/archive/verify-migration/check-anchor-edges-sum.d.ts +13 -0
- package/lib/archive/verify-migration/check-anchor-edges-sum.js +27 -0
- package/lib/archive/verify-migration/check-content-items-count.d.ts +16 -0
- package/lib/archive/verify-migration/check-content-items-count.js +30 -0
- package/lib/archive/verify-migration/check-content-type-preservation.d.ts +22 -0
- package/lib/archive/verify-migration/check-content-type-preservation.js +40 -0
- package/lib/archive/verify-migration/check-foreign-key-integrity.d.ts +31 -0
- package/lib/archive/verify-migration/check-foreign-key-integrity.js +47 -0
- package/lib/archive/verify-migration/check-image-items-count.d.ts +12 -0
- package/lib/archive/verify-migration/check-image-items-count.js +26 -0
- package/lib/archive/verify-migration/check-page-meta-count.d.ts +15 -0
- package/lib/archive/verify-migration/check-page-meta-count.js +31 -0
- package/lib/archive/verify-migration/check-reader-parity.d.ts +23 -0
- package/lib/archive/verify-migration/check-reader-parity.js +211 -0
- package/lib/archive/verify-migration/check-resource-items-count.d.ts +17 -0
- package/lib/archive/verify-migration/check-resource-items-count.js +33 -0
- package/lib/archive/verify-migration/check-url-round-trip.d.ts +43 -0
- package/lib/archive/verify-migration/check-url-round-trip.js +112 -0
- package/lib/archive/verify-migration/types.d.ts +70 -0
- package/lib/archive/verify-migration/types.js +63 -0
- package/lib/archive/verify-migration/verify-migration.d.ts +41 -0
- package/lib/archive/verify-migration/verify-migration.js +120 -0
- package/lib/crawler/build-redirect-event.d.ts +1 -1
- package/lib/crawler/build-redirect-event.js +1 -1
- package/lib/crawler/capture-image-dom-paths.d.ts +33 -0
- package/lib/crawler/capture-image-dom-paths.js +39 -0
- package/lib/crawler/clear-dns-burned-host-cache.d.ts +1 -1
- package/lib/crawler/clear-dns-burned-host-cache.js +1 -1
- package/lib/crawler/collect-image-dom-paths.d.ts +23 -0
- package/lib/crawler/collect-image-dom-paths.js +64 -0
- package/lib/crawler/crawler.d.ts +19 -0
- package/lib/crawler/crawler.js +40 -26
- package/lib/crawler/dns-burned-host-cache.d.ts +3 -3
- package/lib/crawler/dns-burned-host-cache.js +3 -3
- package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +2 -2
- package/lib/crawler/dns-burned-host-short-circuit-counter.js +2 -2
- package/lib/crawler/inject-scope-auth.d.ts +1 -1
- package/lib/crawler/inject-scope-auth.js +1 -1
- package/lib/crawler/normalize-content-type.d.ts +1 -1
- package/lib/crawler/normalize-content-type.js +1 -1
- package/lib/crawler/types.d.ts +3 -3
- package/lib/crawler-orchestrator.d.ts +9 -0
- package/lib/crawler-orchestrator.js +44 -28
- package/lib/crawler.d.ts +12 -0
- package/lib/crawler.js +21 -0
- package/lib/permanent-error-kinds.d.ts +1 -1
- package/lib/permanent-error-kinds.js +1 -1
- package/lib/types.d.ts +1 -1
- package/lib/utils/compute-file-sha256.d.ts +5 -4
- package/lib/utils/compute-file-sha256.js +5 -4
- package/lib/utils/error/emit-error-with-retry.d.ts +1 -1
- package/lib/utils/error/emit-error-with-retry.js +1 -1
- package/package.json +10 -10
- package/lib/archive/migrate-crawl-errors.d.ts +0 -20
- package/lib/archive/migrate-crawl-errors.js +0 -38
- package/lib/archive/migrate-html-blob-tables.d.ts +0 -24
- package/lib/archive/migrate-html-blob-tables.js +0 -53
- package/lib/archive/migrate-inventory-runs.d.ts +0 -29
- package/lib/archive/migrate-inventory-runs.js +0 -52
- package/lib/archive/migrate-page-errors.d.ts +0 -16
- package/lib/archive/migrate-page-errors.js +0 -35
- package/lib/archive/migrate-pages-resources-source.d.ts +0 -16
- package/lib/archive/migrate-pages-resources-source.js +0 -46
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MigrationVerificationError } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Verifies 0.13 invariant #6: every legacy `resources` row is mirrored
|
|
4
|
+
* by one row in `resource_items`.
|
|
5
|
+
*
|
|
6
|
+
* `populateResourceItems` fills `resource_items` with the same PK as
|
|
7
|
+
* `resources.id`
|
|
8
|
+
* (see `populate-resource-items.ts`); the invariant is broken only if the
|
|
9
|
+
* populate loop skipped rows during URL / header-set / content-type
|
|
10
|
+
* resolution. The paired invariant `resource_ref_edges = "resources-referrers"`
|
|
11
|
+
* is left unchecked here because issue #194's spec list stops at the
|
|
12
|
+
* six enumerated checks, and `resource_ref_edges` populate is a straight
|
|
13
|
+
* `INSERT … SELECT` (no dedup, no resolution) with negligible failure surface.
|
|
14
|
+
* @param trx - Knex instance or transaction connected to the populated archive.
|
|
15
|
+
* @throws {MigrationVerificationError} when the row counts diverge.
|
|
16
|
+
*/
|
|
17
|
+
export async function checkResourceItemsCount(trx) {
|
|
18
|
+
const resourceItemsRows = await trx('resource_items').count({
|
|
19
|
+
n: '*',
|
|
20
|
+
});
|
|
21
|
+
const resourcesRows = await trx('resources').count({ n: '*' });
|
|
22
|
+
const resourceItemsCount = Number(resourceItemsRows[0].n);
|
|
23
|
+
const resourcesCount = Number(resourcesRows[0].n);
|
|
24
|
+
if (resourceItemsCount !== resourcesCount) {
|
|
25
|
+
throw new MigrationVerificationError({
|
|
26
|
+
check: '#6 resource_items row count',
|
|
27
|
+
context: {
|
|
28
|
+
resource_items: resourceItemsCount,
|
|
29
|
+
resources: resourcesCount,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Verifies 0.13 invariant #8: `content_items.url_id → url_refs.url`
|
|
4
|
+
* round-trips back to the original `pages.url` on a deterministic
|
|
5
|
+
* ≤ {@link SAMPLE_SIZE} sample.
|
|
6
|
+
*
|
|
7
|
+
* A mismatch means one of two things:
|
|
8
|
+
*
|
|
9
|
+
* - `populate-url-refs.ts` collapsed two distinct URLs into the same
|
|
10
|
+
* `url_refs.id` (BINARY collation bug, whitespace normalisation, …).
|
|
11
|
+
* - `populate-content-items.ts` associated a page id with the wrong
|
|
12
|
+
* `url_ref.id` (row-order dependency in the batch resolver).
|
|
13
|
+
*
|
|
14
|
+
* Additionally an FK gap (`content_items.url_id` pointing at a
|
|
15
|
+
* non-existent `url_refs.id`) or a missing `pages` row surfaces as a
|
|
16
|
+
* `roundTripUrl` / `sourceUrl` of `null`; the check treats either as a
|
|
17
|
+
* round-trip failure. LEFT JOINs are used deliberately so orphan rows
|
|
18
|
+
* are observable — an INNER JOIN would silently drop them and let the
|
|
19
|
+
* bug the invariant is meant to catch slip through.
|
|
20
|
+
*
|
|
21
|
+
* Sampling is **deterministic**: stride = ⌈count(content_items) / N⌉
|
|
22
|
+
* gives approximately {@link SAMPLE_SIZE} rows spread across the id
|
|
23
|
+
* range for large archives, and the full table for archives with
|
|
24
|
+
* ≤ {@link SAMPLE_SIZE} rows. Deterministic sampling means the same
|
|
25
|
+
* archive always produces the same verdict; a random sample would let a
|
|
26
|
+
* failing archive pass on retry if the offending rows happen to fall
|
|
27
|
+
* outside the second draw.
|
|
28
|
+
*
|
|
29
|
+
* The check throws when the sampled row count is smaller than expected
|
|
30
|
+
* (LEFT JOINs may return `null` sides but never fewer rows than the
|
|
31
|
+
* driving `content_items` set — a shortfall means SQLite silently
|
|
32
|
+
* dropped rows we intended to inspect). "Expected" is a separate
|
|
33
|
+
* `COUNT(*)` against the same `(id % stride) = 0` predicate, not a
|
|
34
|
+
* `floor(total / stride)` estimate: `content_items.id` is copied
|
|
35
|
+
* verbatim from the legacy `pages.id`, which accumulates gaps over an
|
|
36
|
+
* archive's crawl history (redirect consolidation, deleted pages, …).
|
|
37
|
+
* A density estimate over-counts whenever the id range is sparser than
|
|
38
|
+
* `[1, total]`, which produced false-positive failures on real
|
|
39
|
+
* multi-thousand-page archives.
|
|
40
|
+
* @param trx - Knex instance or transaction connected to the populated archive.
|
|
41
|
+
* @throws {MigrationVerificationError} when at least one sampled URL does not round-trip.
|
|
42
|
+
*/
|
|
43
|
+
export declare function checkUrlRoundTrip(trx: Knex): Promise<void>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { MigrationVerificationError } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sample size for the URL round-trip smoke test — a 1,000-row sample per
|
|
4
|
+
* issue #194.
|
|
5
|
+
* Not statistical coverage — URL normalisation bugs tend to be systematic
|
|
6
|
+
* (all rows with a particular scheme or host), so a modest evenly-spread
|
|
7
|
+
* sample catches them reliably.
|
|
8
|
+
*/
|
|
9
|
+
const SAMPLE_SIZE = 1000;
|
|
10
|
+
/**
|
|
11
|
+
* Verifies 0.13 invariant #8: `content_items.url_id → url_refs.url`
|
|
12
|
+
* round-trips back to the original `pages.url` on a deterministic
|
|
13
|
+
* ≤ {@link SAMPLE_SIZE} sample.
|
|
14
|
+
*
|
|
15
|
+
* A mismatch means one of two things:
|
|
16
|
+
*
|
|
17
|
+
* - `populate-url-refs.ts` collapsed two distinct URLs into the same
|
|
18
|
+
* `url_refs.id` (BINARY collation bug, whitespace normalisation, …).
|
|
19
|
+
* - `populate-content-items.ts` associated a page id with the wrong
|
|
20
|
+
* `url_ref.id` (row-order dependency in the batch resolver).
|
|
21
|
+
*
|
|
22
|
+
* Additionally an FK gap (`content_items.url_id` pointing at a
|
|
23
|
+
* non-existent `url_refs.id`) or a missing `pages` row surfaces as a
|
|
24
|
+
* `roundTripUrl` / `sourceUrl` of `null`; the check treats either as a
|
|
25
|
+
* round-trip failure. LEFT JOINs are used deliberately so orphan rows
|
|
26
|
+
* are observable — an INNER JOIN would silently drop them and let the
|
|
27
|
+
* bug the invariant is meant to catch slip through.
|
|
28
|
+
*
|
|
29
|
+
* Sampling is **deterministic**: stride = ⌈count(content_items) / N⌉
|
|
30
|
+
* gives approximately {@link SAMPLE_SIZE} rows spread across the id
|
|
31
|
+
* range for large archives, and the full table for archives with
|
|
32
|
+
* ≤ {@link SAMPLE_SIZE} rows. Deterministic sampling means the same
|
|
33
|
+
* archive always produces the same verdict; a random sample would let a
|
|
34
|
+
* failing archive pass on retry if the offending rows happen to fall
|
|
35
|
+
* outside the second draw.
|
|
36
|
+
*
|
|
37
|
+
* The check throws when the sampled row count is smaller than expected
|
|
38
|
+
* (LEFT JOINs may return `null` sides but never fewer rows than the
|
|
39
|
+
* driving `content_items` set — a shortfall means SQLite silently
|
|
40
|
+
* dropped rows we intended to inspect). "Expected" is a separate
|
|
41
|
+
* `COUNT(*)` against the same `(id % stride) = 0` predicate, not a
|
|
42
|
+
* `floor(total / stride)` estimate: `content_items.id` is copied
|
|
43
|
+
* verbatim from the legacy `pages.id`, which accumulates gaps over an
|
|
44
|
+
* archive's crawl history (redirect consolidation, deleted pages, …).
|
|
45
|
+
* A density estimate over-counts whenever the id range is sparser than
|
|
46
|
+
* `[1, total]`, which produced false-positive failures on real
|
|
47
|
+
* multi-thousand-page archives.
|
|
48
|
+
* @param trx - Knex instance or transaction connected to the populated archive.
|
|
49
|
+
* @throws {MigrationVerificationError} when at least one sampled URL does not round-trip.
|
|
50
|
+
*/
|
|
51
|
+
export async function checkUrlRoundTrip(trx) {
|
|
52
|
+
const totalRows = await trx('content_items').count({ n: '*' });
|
|
53
|
+
const total = Number(totalRows[0].n);
|
|
54
|
+
if (total === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// stride = ⌈total / SAMPLE_SIZE⌉ so `id % stride = 0` picks approximately
|
|
58
|
+
// SAMPLE_SIZE rows evenly across the id range for large archives, and the
|
|
59
|
+
// full table for archives ≤ SAMPLE_SIZE.
|
|
60
|
+
const stride = Math.max(1, Math.ceil(total / SAMPLE_SIZE));
|
|
61
|
+
const matchingRows = await trx('content_items')
|
|
62
|
+
.whereRaw('(id % ?) = 0', [stride])
|
|
63
|
+
.count({ n: '*' });
|
|
64
|
+
const expectedSize = Math.min(SAMPLE_SIZE, Number(matchingRows[0].n));
|
|
65
|
+
const sample = await trx('content_items as ci')
|
|
66
|
+
.leftJoin('pages as p', 'p.id', 'ci.id')
|
|
67
|
+
.leftJoin('url_refs as ur', 'ur.id', 'ci.url_id')
|
|
68
|
+
.select('ci.id as id', 'p.url as sourceUrl', 'ur.url as roundTripUrl')
|
|
69
|
+
.whereRaw('(ci.id % ?) = 0', [stride])
|
|
70
|
+
.orderBy('ci.id')
|
|
71
|
+
.limit(SAMPLE_SIZE);
|
|
72
|
+
if (sample.length < expectedSize) {
|
|
73
|
+
// LEFT JOINs preserve driving-table row counts (`p.id` / `url_refs.id`
|
|
74
|
+
// are PKs → 1:1 or 0:1 match). A shortfall means SQLite silently
|
|
75
|
+
// dropped rows the WHERE clause matched — a real integrity signal.
|
|
76
|
+
throw new MigrationVerificationError({
|
|
77
|
+
check: '#8 URL round-trip',
|
|
78
|
+
context: {
|
|
79
|
+
content_items_total: total,
|
|
80
|
+
stride,
|
|
81
|
+
expected_sample_size: expectedSize,
|
|
82
|
+
actual_sample_size: sample.length,
|
|
83
|
+
reason: 'sample query returned fewer rows than expected',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
for (const row of sample) {
|
|
88
|
+
if (row.sourceUrl === null || row.roundTripUrl === null) {
|
|
89
|
+
throw new MigrationVerificationError({
|
|
90
|
+
check: '#8 URL round-trip',
|
|
91
|
+
context: {
|
|
92
|
+
page_id: row.id,
|
|
93
|
+
source_url: row.sourceUrl,
|
|
94
|
+
round_trip_url: row.roundTripUrl,
|
|
95
|
+
sample_size: sample.length,
|
|
96
|
+
reason: 'orphan row — pages or url_refs join returned null (FK gap)',
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (row.sourceUrl !== row.roundTripUrl) {
|
|
101
|
+
throw new MigrationVerificationError({
|
|
102
|
+
check: '#8 URL round-trip',
|
|
103
|
+
context: {
|
|
104
|
+
page_id: row.id,
|
|
105
|
+
source_url: row.sourceUrl,
|
|
106
|
+
round_trip_url: row.roundTripUrl,
|
|
107
|
+
sample_size: sample.length,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type definitions for 0.13 migration verification.
|
|
3
|
+
*
|
|
4
|
+
* These are the acceptance-invariant checks that `scripts/migrate-to-0.13.mjs`
|
|
5
|
+
* runs at the end of the migration to confirm that the 0.13 entity /
|
|
6
|
+
* edge populates produced a consistent archive. Each check is one file
|
|
7
|
+
* under `verify-migration/` and throws {@link MigrationVerificationError} on mismatch;
|
|
8
|
+
* the orchestrator {@link ./verify-migration.ts} chains them in the
|
|
9
|
+
* order defined by issue #194.
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Row-count snapshot collected by every successful verify run and returned
|
|
14
|
+
* by {@link import('./verify-migration.js').verifyMigration}.
|
|
15
|
+
* The migration script logs these on the successful path so operators can
|
|
16
|
+
* eyeball migration outcomes from stdout without re-opening the archive.
|
|
17
|
+
*/
|
|
18
|
+
export interface MigrationVerificationSummary {
|
|
19
|
+
/** `count(*) FROM content_items` — matches `count(pages)`. */
|
|
20
|
+
readonly contentItems: number;
|
|
21
|
+
/** `count(*) FROM page_meta` — matches `count(pages WHERE scraped=1)`. */
|
|
22
|
+
readonly pageMeta: number;
|
|
23
|
+
/** `count(*) FROM anchor_edges` — always ≤ `count(anchors)`. */
|
|
24
|
+
readonly anchorEdges: number;
|
|
25
|
+
/** `SUM(count) FROM anchor_edges` — matches `count(anchors)`. */
|
|
26
|
+
readonly anchorEdgesSum: number;
|
|
27
|
+
/** `count(*) FROM image_items` — matches `count(images)`. */
|
|
28
|
+
readonly imageItems: number;
|
|
29
|
+
/** `count(*) FROM resource_items` — matches `count(resources)`. */
|
|
30
|
+
readonly resourceItems: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Structured details of a single migration invariant that failed. Attached to
|
|
34
|
+
* {@link MigrationVerificationError} so operators can see which check tripped
|
|
35
|
+
* without parsing the error message.
|
|
36
|
+
*/
|
|
37
|
+
export interface MigrationVerificationErrorDetails {
|
|
38
|
+
/**
|
|
39
|
+
* Human-readable check identifier matching issue #194's numbered list
|
|
40
|
+
* (e.g. `'#3'` for the anchor-edge dedup range check). Included in the
|
|
41
|
+
* error message so migration logs point at the exact clause.
|
|
42
|
+
*/
|
|
43
|
+
readonly check: string;
|
|
44
|
+
/**
|
|
45
|
+
* Optional structured payload — typically `expected` / `actual` counts or
|
|
46
|
+
* the offending page id from the URL round-trip sample. Rendered into the
|
|
47
|
+
* error message so operators can diagnose without re-running the check.
|
|
48
|
+
*/
|
|
49
|
+
readonly context?: Readonly<Record<string, string | number | null>>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Error thrown by any 0.13 check when an invariant does not hold. The
|
|
53
|
+
* migration script catches this at the top level and aborts with a non-zero
|
|
54
|
+
* exit code; the enclosing transaction rolls back so the archive returns to
|
|
55
|
+
* its pre-populate state (ref tables from ref populate stay populated but are
|
|
56
|
+
* additive).
|
|
57
|
+
*
|
|
58
|
+
* Prefer a single error class with a structured {@link details} payload over
|
|
59
|
+
* one subclass per check: eight subclasses would create eight nearly
|
|
60
|
+
* identical constructors, and callers (script + integration test) only need
|
|
61
|
+
* to distinguish "verification failed" from other error kinds.
|
|
62
|
+
*/
|
|
63
|
+
export declare class MigrationVerificationError extends Error {
|
|
64
|
+
readonly details: MigrationVerificationErrorDetails;
|
|
65
|
+
/**
|
|
66
|
+
* @param details - Structured description of which check failed and why.
|
|
67
|
+
* Rendered into the error message on construction.
|
|
68
|
+
*/
|
|
69
|
+
constructor(details: MigrationVerificationErrorDetails);
|
|
70
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type definitions for 0.13 migration verification.
|
|
3
|
+
*
|
|
4
|
+
* These are the acceptance-invariant checks that `scripts/migrate-to-0.13.mjs`
|
|
5
|
+
* runs at the end of the migration to confirm that the 0.13 entity /
|
|
6
|
+
* edge populates produced a consistent archive. Each check is one file
|
|
7
|
+
* under `verify-migration/` and throws {@link MigrationVerificationError} on mismatch;
|
|
8
|
+
* the orchestrator {@link ./verify-migration.ts} chains them in the
|
|
9
|
+
* order defined by issue #194.
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown by any 0.13 check when an invariant does not hold. The
|
|
14
|
+
* migration script catches this at the top level and aborts with a non-zero
|
|
15
|
+
* exit code; the enclosing transaction rolls back so the archive returns to
|
|
16
|
+
* its pre-populate state (ref tables from ref populate stay populated but are
|
|
17
|
+
* additive).
|
|
18
|
+
*
|
|
19
|
+
* Prefer a single error class with a structured {@link details} payload over
|
|
20
|
+
* one subclass per check: eight subclasses would create eight nearly
|
|
21
|
+
* identical constructors, and callers (script + integration test) only need
|
|
22
|
+
* to distinguish "verification failed" from other error kinds.
|
|
23
|
+
*/
|
|
24
|
+
export class MigrationVerificationError extends Error {
|
|
25
|
+
details;
|
|
26
|
+
/**
|
|
27
|
+
* @param details - Structured description of which check failed and why.
|
|
28
|
+
* Rendered into the error message on construction.
|
|
29
|
+
*/
|
|
30
|
+
constructor(details) {
|
|
31
|
+
const contextText = details.context === undefined
|
|
32
|
+
? ''
|
|
33
|
+
: ' — ' +
|
|
34
|
+
Object.entries(details.context)
|
|
35
|
+
.map(([key, value]) =>
|
|
36
|
+
// Render null as the parenthesised literal `(null)` so
|
|
37
|
+
// operators reading the log can distinguish "the sample
|
|
38
|
+
// query returned no rows" from a caller that passed the
|
|
39
|
+
// four-character string `"null"`. Strings get quoted for
|
|
40
|
+
// the same reason.
|
|
41
|
+
`${key}=${formatContextValue(value)}`)
|
|
42
|
+
.join(', ');
|
|
43
|
+
super(`migration verification failed ${details.check}${contextText}`);
|
|
44
|
+
this.details = details;
|
|
45
|
+
this.name = 'MigrationVerificationError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Renders one context value for {@link MigrationVerificationError}'s message.
|
|
50
|
+
* `null` becomes the literal `(null)`; strings are wrapped in double quotes;
|
|
51
|
+
* numbers pass through as their decimal representation. The intent is to
|
|
52
|
+
* make missing-vs-present distinguishable in operator logs.
|
|
53
|
+
* @param value - Value from `MigrationVerificationErrorDetails.context`.
|
|
54
|
+
*/
|
|
55
|
+
function formatContextValue(value) {
|
|
56
|
+
if (value === null) {
|
|
57
|
+
return '(null)';
|
|
58
|
+
}
|
|
59
|
+
if (typeof value === 'string') {
|
|
60
|
+
return JSON.stringify(value);
|
|
61
|
+
}
|
|
62
|
+
return String(value);
|
|
63
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { MigrationVerificationSummary } from './types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Runs every 0.13 migration acceptance invariant against a populated archive and
|
|
5
|
+
* returns the verified row-count summary on success. The migration script
|
|
6
|
+
* (`scripts/migrate-to-0.13.mjs`) calls this **inside** the same
|
|
7
|
+
* `knex.transaction()` block that ran `populateEntityTables` so a thrown
|
|
8
|
+
* {@link MigrationVerificationError} rolls back the entire entity populate — ref
|
|
9
|
+
* tables from ref populate stay committed because they live in a separate, additive
|
|
10
|
+
* transaction and never lose facts on re-run.
|
|
11
|
+
*
|
|
12
|
+
* The returned {@link MigrationVerificationSummary} is echoed to stdout by the
|
|
13
|
+
* migration script so operators can eyeball migration outcomes (`did we lose
|
|
14
|
+
* rows silently within the invariant window?`) from batch pipeline logs
|
|
15
|
+
* without re-opening the archive. Callers must run this inside a `db.transaction()`
|
|
16
|
+
* for the counts and the JOIN-based checks (#7 / #8) to see a consistent
|
|
17
|
+
* snapshot of the archive.
|
|
18
|
+
*
|
|
19
|
+
* Checks run sequentially in issue #194's numbered order so error messages
|
|
20
|
+
* pinpoint the earliest broken invariant. Sequential (rather than parallel)
|
|
21
|
+
* execution keeps the failure diagnostics focused: fanning eight COUNTs out
|
|
22
|
+
* at once would return the first-completed rejection rather than the
|
|
23
|
+
* lowest-numbered one, which surprises operators reading migration logs.
|
|
24
|
+
* The individual COUNT queries are fast enough at any real archive scale
|
|
25
|
+
* for sequential wall-clock cost to be irrelevant.
|
|
26
|
+
*
|
|
27
|
+
* Non-{@link MigrationVerificationError} exceptions from a check (e.g. a
|
|
28
|
+
* transient libsql error or a schema-drift SqliteError) are wrapped in a
|
|
29
|
+
* {@link MigrationVerificationError} so the migration script's `catch` sees a
|
|
30
|
+
* uniform failure surface — otherwise operators grepping stderr for
|
|
31
|
+
* `migration verification failed` would miss driver-side errors.
|
|
32
|
+
* @param trx - Knex instance or transaction connected to the archive
|
|
33
|
+
* **after** every 0.13 migration populate has run. Must be a transaction
|
|
34
|
+
* in production so ref counts, JOIN samples, and error rollback see the
|
|
35
|
+
* same snapshot; non-transactional callers (unit tests) work but do not
|
|
36
|
+
* get snapshot isolation.
|
|
37
|
+
* @returns Row-count summary of the verified archive.
|
|
38
|
+
* @throws {MigrationVerificationError} on the first invariant that does not
|
|
39
|
+
* hold; subsequent checks are skipped.
|
|
40
|
+
*/
|
|
41
|
+
export declare function verifyMigration(trx: Knex): Promise<MigrationVerificationSummary>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { checkAnchorEdgesCount } from './check-anchor-edges-count.js';
|
|
2
|
+
import { checkAnchorEdgesSum } from './check-anchor-edges-sum.js';
|
|
3
|
+
import { checkContentItemsCount } from './check-content-items-count.js';
|
|
4
|
+
import { checkContentTypePreservation } from './check-content-type-preservation.js';
|
|
5
|
+
import { checkImageItemsCount } from './check-image-items-count.js';
|
|
6
|
+
import { checkPageMetaCount } from './check-page-meta-count.js';
|
|
7
|
+
import { checkReaderParity } from './check-reader-parity.js';
|
|
8
|
+
import { checkResourceItemsCount } from './check-resource-items-count.js';
|
|
9
|
+
import { checkUrlRoundTrip } from './check-url-round-trip.js';
|
|
10
|
+
import { MigrationVerificationError } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Runs every 0.13 migration acceptance invariant against a populated archive and
|
|
13
|
+
* returns the verified row-count summary on success. The migration script
|
|
14
|
+
* (`scripts/migrate-to-0.13.mjs`) calls this **inside** the same
|
|
15
|
+
* `knex.transaction()` block that ran `populateEntityTables` so a thrown
|
|
16
|
+
* {@link MigrationVerificationError} rolls back the entire entity populate — ref
|
|
17
|
+
* tables from ref populate stay committed because they live in a separate, additive
|
|
18
|
+
* transaction and never lose facts on re-run.
|
|
19
|
+
*
|
|
20
|
+
* The returned {@link MigrationVerificationSummary} is echoed to stdout by the
|
|
21
|
+
* migration script so operators can eyeball migration outcomes (`did we lose
|
|
22
|
+
* rows silently within the invariant window?`) from batch pipeline logs
|
|
23
|
+
* without re-opening the archive. Callers must run this inside a `db.transaction()`
|
|
24
|
+
* for the counts and the JOIN-based checks (#7 / #8) to see a consistent
|
|
25
|
+
* snapshot of the archive.
|
|
26
|
+
*
|
|
27
|
+
* Checks run sequentially in issue #194's numbered order so error messages
|
|
28
|
+
* pinpoint the earliest broken invariant. Sequential (rather than parallel)
|
|
29
|
+
* execution keeps the failure diagnostics focused: fanning eight COUNTs out
|
|
30
|
+
* at once would return the first-completed rejection rather than the
|
|
31
|
+
* lowest-numbered one, which surprises operators reading migration logs.
|
|
32
|
+
* The individual COUNT queries are fast enough at any real archive scale
|
|
33
|
+
* for sequential wall-clock cost to be irrelevant.
|
|
34
|
+
*
|
|
35
|
+
* Non-{@link MigrationVerificationError} exceptions from a check (e.g. a
|
|
36
|
+
* transient libsql error or a schema-drift SqliteError) are wrapped in a
|
|
37
|
+
* {@link MigrationVerificationError} so the migration script's `catch` sees a
|
|
38
|
+
* uniform failure surface — otherwise operators grepping stderr for
|
|
39
|
+
* `migration verification failed` would miss driver-side errors.
|
|
40
|
+
* @param trx - Knex instance or transaction connected to the archive
|
|
41
|
+
* **after** every 0.13 migration populate has run. Must be a transaction
|
|
42
|
+
* in production so ref counts, JOIN samples, and error rollback see the
|
|
43
|
+
* same snapshot; non-transactional callers (unit tests) work but do not
|
|
44
|
+
* get snapshot isolation.
|
|
45
|
+
* @returns Row-count summary of the verified archive.
|
|
46
|
+
* @throws {MigrationVerificationError} on the first invariant that does not
|
|
47
|
+
* hold; subsequent checks are skipped.
|
|
48
|
+
*/
|
|
49
|
+
export async function verifyMigration(trx) {
|
|
50
|
+
try {
|
|
51
|
+
await checkContentItemsCount(trx);
|
|
52
|
+
await checkPageMetaCount(trx);
|
|
53
|
+
await checkAnchorEdgesCount(trx);
|
|
54
|
+
await checkAnchorEdgesSum(trx);
|
|
55
|
+
await checkImageItemsCount(trx);
|
|
56
|
+
await checkResourceItemsCount(trx);
|
|
57
|
+
await checkContentTypePreservation(trx);
|
|
58
|
+
await checkUrlRoundTrip(trx);
|
|
59
|
+
// Reader-level parity between the legacy tables and the
|
|
60
|
+
// new entity tables (see `checkReaderParity` for the eight totals
|
|
61
|
+
// this catches that the row-count checks above miss).
|
|
62
|
+
await checkReaderParity(trx);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error instanceof MigrationVerificationError) {
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
throw new MigrationVerificationError({
|
|
69
|
+
check: 'runtime',
|
|
70
|
+
context: {
|
|
71
|
+
underlying_error: error instanceof Error ? error.message : String(error),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return collectSummary(trx);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Runs the six row counts that populate {@link MigrationVerificationSummary}.
|
|
79
|
+
* Separate from the invariant checks so those stay side-effect-free asserts,
|
|
80
|
+
* and this runs once at the end when all invariants have already held.
|
|
81
|
+
* @param trx - Same Knex handle passed to {@link verifyMigration}.
|
|
82
|
+
*/
|
|
83
|
+
async function collectSummary(trx) {
|
|
84
|
+
const [contentItems, pageMeta, anchorEdges, anchorEdgesSum, imageItems, resourceItems] = await Promise.all([
|
|
85
|
+
countOf(trx, 'content_items'),
|
|
86
|
+
countOf(trx, 'page_meta'),
|
|
87
|
+
countOf(trx, 'anchor_edges'),
|
|
88
|
+
sumOf(trx, 'anchor_edges', 'count'),
|
|
89
|
+
countOf(trx, 'image_items'),
|
|
90
|
+
countOf(trx, 'resource_items'),
|
|
91
|
+
]);
|
|
92
|
+
return {
|
|
93
|
+
contentItems,
|
|
94
|
+
pageMeta,
|
|
95
|
+
anchorEdges,
|
|
96
|
+
anchorEdgesSum,
|
|
97
|
+
imageItems,
|
|
98
|
+
resourceItems,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* `SELECT count(*) FROM <table>` returning a plain number.
|
|
103
|
+
* @param trx - Knex handle.
|
|
104
|
+
* @param table - Table to count.
|
|
105
|
+
*/
|
|
106
|
+
async function countOf(trx, table) {
|
|
107
|
+
const rows = await trx(table).count({ n: '*' });
|
|
108
|
+
return Number(rows[0].n);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* `SELECT SUM(<column>) FROM <table>` returning a plain number (`null` sum
|
|
112
|
+
* on an empty table collapses to zero).
|
|
113
|
+
* @param trx - Knex handle.
|
|
114
|
+
* @param table - Table to sum over.
|
|
115
|
+
* @param column - Column to sum.
|
|
116
|
+
*/
|
|
117
|
+
async function sumOf(trx, table, column) {
|
|
118
|
+
const rows = await trx(table).sum({ n: column });
|
|
119
|
+
return Number(rows[0].n ?? 0);
|
|
120
|
+
}
|
|
@@ -14,7 +14,7 @@ import type { PageData } from '../utils/types/types.js';
|
|
|
14
14
|
*
|
|
15
15
|
* The originating URL passed in `pageUrlWithoutHashAndAuth` must be the
|
|
16
16
|
* URL the redirect chain STARTS from (the page being scraped), not the
|
|
17
|
-
* destination — see
|
|
17
|
+
* destination — see `Database.recordRedirect` JSDoc for why
|
|
18
18
|
* lineage propagates from origin, not from the destination.
|
|
19
19
|
* @param pageData - HEAD-resolved page data carrying the redirect chain.
|
|
20
20
|
* @param inventoryMode - Inventory-mode config from `CrawlerOptions.inventoryMode`, or `null` outside `--inventory`.
|
|
@@ -13,7 +13,7 @@ import { derivePageSource } from './derive-page-source.js';
|
|
|
13
13
|
*
|
|
14
14
|
* The originating URL passed in `pageUrlWithoutHashAndAuth` must be the
|
|
15
15
|
* URL the redirect chain STARTS from (the page being scraped), not the
|
|
16
|
-
* destination — see
|
|
16
|
+
* destination — see `Database.recordRedirect` JSDoc for why
|
|
17
17
|
* lineage propagates from origin, not from the destination.
|
|
18
18
|
* @param pageData - HEAD-resolved page data carrying the redirect chain.
|
|
19
19
|
* @param inventoryMode - Inventory-mode config from `CrawlerOptions.inventoryMode`, or `null` outside `--inventory`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DomPathCandidate } from '../archive/populate-entity-tables/types.js';
|
|
2
|
+
import type { Page as PuppeteerPage } from 'puppeteer';
|
|
3
|
+
/**
|
|
4
|
+
* Captures every `<img>` in the rendered document — its `outerHTML` and
|
|
5
|
+
* its `dom_path` string — in document order, for `image_items.dom_path_text_id`
|
|
6
|
+
* resolution at write time.
|
|
7
|
+
*
|
|
8
|
+
* `@d-zero/beholder`'s image metadata carries the element's `outerHTML`
|
|
9
|
+
* (`sourceCode`) but no positional information, so the crawler runs this
|
|
10
|
+
* one extra `page.evaluate` after `scrapeStart` returns, while the page
|
|
11
|
+
* is still alive. The in-browser walk is
|
|
12
|
+
* {@link ./collect-image-dom-paths.ts} — a self-contained function whose
|
|
13
|
+
* source puppeteer serialises into the page, and whose spec pins its
|
|
14
|
+
* output against the Node-side
|
|
15
|
+
* {@link ../archive/populate-entity-tables/derive-dom-path.ts} so a
|
|
16
|
+
* live-crawled archive and one whose dom paths were reconstructed from
|
|
17
|
+
* HTML snapshots by the migration script produce identical strings for
|
|
18
|
+
* identical DOM shapes.
|
|
19
|
+
*
|
|
20
|
+
* Returns `undefined` on any evaluation failure (page context died,
|
|
21
|
+
* navigation raced the call) — dom paths are best-effort enrichment and
|
|
22
|
+
* a capture failure must not fail the scrape. Callers fall back to the
|
|
23
|
+
* `unknown/<id>` synthetic marker per image via
|
|
24
|
+
* {@link ../archive/populate-entity-tables/match-images-to-dom-paths.ts}.
|
|
25
|
+
* @param page - The live puppeteer page, after the scrape completed and
|
|
26
|
+
* before the browser closes.
|
|
27
|
+
* @returns Candidates in document order, or `undefined` when the page
|
|
28
|
+
* could not be evaluated.
|
|
29
|
+
* @example
|
|
30
|
+
* const result = await scraper.scrapeStart(page, url, options);
|
|
31
|
+
* const imageDomPaths = await captureImageDomPaths(page);
|
|
32
|
+
*/
|
|
33
|
+
export declare function captureImageDomPaths(page: PuppeteerPage): Promise<DomPathCandidate[] | undefined>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { collectImageDomPaths } from './collect-image-dom-paths.js';
|
|
2
|
+
/**
|
|
3
|
+
* Captures every `<img>` in the rendered document — its `outerHTML` and
|
|
4
|
+
* its `dom_path` string — in document order, for `image_items.dom_path_text_id`
|
|
5
|
+
* resolution at write time.
|
|
6
|
+
*
|
|
7
|
+
* `@d-zero/beholder`'s image metadata carries the element's `outerHTML`
|
|
8
|
+
* (`sourceCode`) but no positional information, so the crawler runs this
|
|
9
|
+
* one extra `page.evaluate` after `scrapeStart` returns, while the page
|
|
10
|
+
* is still alive. The in-browser walk is
|
|
11
|
+
* {@link ./collect-image-dom-paths.ts} — a self-contained function whose
|
|
12
|
+
* source puppeteer serialises into the page, and whose spec pins its
|
|
13
|
+
* output against the Node-side
|
|
14
|
+
* {@link ../archive/populate-entity-tables/derive-dom-path.ts} so a
|
|
15
|
+
* live-crawled archive and one whose dom paths were reconstructed from
|
|
16
|
+
* HTML snapshots by the migration script produce identical strings for
|
|
17
|
+
* identical DOM shapes.
|
|
18
|
+
*
|
|
19
|
+
* Returns `undefined` on any evaluation failure (page context died,
|
|
20
|
+
* navigation raced the call) — dom paths are best-effort enrichment and
|
|
21
|
+
* a capture failure must not fail the scrape. Callers fall back to the
|
|
22
|
+
* `unknown/<id>` synthetic marker per image via
|
|
23
|
+
* {@link ../archive/populate-entity-tables/match-images-to-dom-paths.ts}.
|
|
24
|
+
* @param page - The live puppeteer page, after the scrape completed and
|
|
25
|
+
* before the browser closes.
|
|
26
|
+
* @returns Candidates in document order, or `undefined` when the page
|
|
27
|
+
* could not be evaluated.
|
|
28
|
+
* @example
|
|
29
|
+
* const result = await scraper.scrapeStart(page, url, options);
|
|
30
|
+
* const imageDomPaths = await captureImageDomPaths(page);
|
|
31
|
+
*/
|
|
32
|
+
export async function captureImageDomPaths(page) {
|
|
33
|
+
try {
|
|
34
|
+
return await page.evaluate(collectImageDomPaths);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Clears the DNS-burned host cache and resets the short-circuit counter.
|
|
3
|
-
* Called between crawl sessions, alongside
|
|
3
|
+
* Called between crawl sessions, alongside `clearDestinationCache`, to
|
|
4
4
|
* avoid leaking state from a previous session into a fresh one.
|
|
5
5
|
*/
|
|
6
6
|
export declare function clearDnsBurnedHostCache(): void;
|
|
@@ -2,7 +2,7 @@ import { dnsBurnedHostCache } from './dns-burned-host-cache.js';
|
|
|
2
2
|
import { dnsBurnedHostShortCircuitCounter } from './dns-burned-host-short-circuit-counter.js';
|
|
3
3
|
/**
|
|
4
4
|
* Clears the DNS-burned host cache and resets the short-circuit counter.
|
|
5
|
-
* Called between crawl sessions, alongside
|
|
5
|
+
* Called between crawl sessions, alongside `clearDestinationCache`, to
|
|
6
6
|
* avoid leaking state from a previous session into a fresh one.
|
|
7
7
|
*/
|
|
8
8
|
export function clearDnsBurnedHostCache() {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DomPathCandidate } from '../archive/populate-entity-tables/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Collects every `<img>` in a document — its `outerHTML` and its
|
|
4
|
+
* `dom_path` string — in document order.
|
|
5
|
+
*
|
|
6
|
+
* **Self-contained by contract.** This function is passed verbatim to
|
|
7
|
+
* puppeteer's `page.evaluate`, which serialises the function source and
|
|
8
|
+
* executes it inside the browser: it must not reference imports,
|
|
9
|
+
* module-scope bindings, or any closure state. That constraint is also
|
|
10
|
+
* what makes single-sourcing possible — the exact same function body
|
|
11
|
+
* runs in the browser during a live crawl AND in Node (against a jsdom
|
|
12
|
+
* document) in its spec, so the dom-path derivation cannot drift between
|
|
13
|
+
* the two runtimes. The spec additionally pins its output against
|
|
14
|
+
* {@link ../archive/populate-entity-tables/derive-dom-path.ts} (the
|
|
15
|
+
* Node-side derivation the migration script uses) element-for-element.
|
|
16
|
+
* @param doc - The document to walk. Defaults to the global `document`,
|
|
17
|
+
* which is how the in-browser `page.evaluate(collectImageDomPaths)`
|
|
18
|
+
* call resolves it; Node callers (specs) pass a jsdom document.
|
|
19
|
+
* @returns Candidates in document order.
|
|
20
|
+
* @example
|
|
21
|
+
* const candidates = await page.evaluate(collectImageDomPaths);
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectImageDomPaths(doc?: Document): DomPathCandidate[];
|