@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.
- 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 +175 -13
- package/lib/archive/archive.js +198 -14
- package/lib/archive/cache/compute-archive-cache-key.d.ts +39 -0
- package/lib/archive/cache/compute-archive-cache-key.js +95 -0
- package/lib/archive/cache/extract-archive-to-cache.d.ts +43 -0
- package/lib/archive/cache/extract-archive-to-cache.js +309 -0
- package/lib/archive/cache/get-archive-cache-root.d.ts +20 -0
- package/lib/archive/cache/get-archive-cache-root.js +53 -0
- package/lib/archive/cache/is-archive-cache-disabled.d.ts +24 -0
- package/lib/archive/cache/is-archive-cache-disabled.js +34 -0
- package/lib/archive/cache/resolve-archive-cache-dir.d.ts +26 -0
- package/lib/archive/cache/resolve-archive-cache-dir.js +32 -0
- 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 +130 -182
- package/lib/archive/database.js +524 -1777
- 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 +37 -0
- package/lib/archive/derive-lineage-from-parent.js +42 -0
- 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 +44 -0
- package/lib/archive/get-failed-page-messages.js +132 -0
- package/lib/archive/init-schema.d.ts +35 -39
- package/lib/archive/init-schema.js +111 -320
- package/lib/archive/is-inventory-source.d.ts +21 -0
- package/lib/archive/is-inventory-source.js +22 -0
- 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 +151 -15
- 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/classify-error-kind.d.ts +19 -0
- package/lib/classify-error-kind.js +122 -0
- package/lib/crawler/build-js-redirect-edge.d.ts +68 -0
- package/lib/crawler/build-js-redirect-edge.js +57 -0
- package/lib/crawler/build-redirect-event.d.ts +24 -0
- package/lib/crawler/build-redirect-event.js +28 -0
- 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 +6 -0
- package/lib/crawler/clear-dns-burned-host-cache.js +11 -0
- 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 +22 -1
- package/lib/crawler/crawler.js +679 -117
- package/lib/crawler/derive-js-redirect-target.d.ts +68 -0
- package/lib/crawler/derive-js-redirect-target.js +129 -0
- package/lib/crawler/derive-resource-source.d.ts +25 -15
- package/lib/crawler/derive-resource-source.js +28 -17
- package/lib/crawler/dns-burned-host-cache.d.ts +26 -0
- package/lib/crawler/dns-burned-host-cache.js +25 -0
- package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +13 -0
- package/lib/crawler/dns-burned-host-short-circuit-counter.js +11 -0
- package/lib/crawler/fetch-destination.d.ts +12 -4
- package/lib/crawler/fetch-destination.js +94 -16
- package/lib/crawler/inject-scope-auth.d.ts +1 -1
- package/lib/crawler/inject-scope-auth.js +1 -1
- package/lib/crawler/is-js-redirect-error-shape.d.ts +40 -0
- package/lib/crawler/is-js-redirect-error-shape.js +53 -0
- package/lib/crawler/is-puppeteer-fallback-candidate.d.ts +16 -0
- package/lib/crawler/is-puppeteer-fallback-candidate.js +63 -0
- package/lib/crawler/link-list.d.ts +21 -1
- package/lib/crawler/link-list.js +23 -3
- package/lib/crawler/normalize-content-type.d.ts +1 -1
- package/lib/crawler/normalize-content-type.js +1 -1
- package/lib/crawler/plan-sub-resource-emits.d.ts +63 -0
- package/lib/crawler/plan-sub-resource-emits.js +44 -0
- package/lib/crawler/preload-short-circuit-error.d.ts +22 -0
- package/lib/crawler/preload-short-circuit-error.js +25 -0
- package/lib/crawler/should-burn-host.d.ts +78 -0
- package/lib/crawler/should-burn-host.js +61 -0
- package/lib/crawler/should-get-fallback-on-head-failure.d.ts +38 -0
- package/lib/crawler/should-get-fallback-on-head-failure.js +46 -0
- package/lib/crawler/types.d.ts +108 -1
- package/lib/crawler-orchestrator.d.ts +22 -3
- package/lib/crawler-orchestrator.js +312 -73
- package/lib/crawler.d.ts +15 -2
- package/lib/crawler.js +24 -1
- package/lib/permanent-error-kinds.d.ts +43 -0
- package/lib/permanent-error-kinds.js +48 -0
- package/lib/types.d.ts +84 -0
- package/lib/utils/compute-file-sha256.d.ts +24 -0
- package/lib/utils/compute-file-sha256.js +56 -0
- package/lib/utils/error/emit-error-with-retry.d.ts +40 -0
- package/lib/utils/error/emit-error-with-retry.js +44 -0
- package/lib/utils/error/emit-error.d.ts +39 -0
- package/lib/utils/error/emit-error.js +41 -0
- package/package.json +12 -12
- 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-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
- package/lib/utils/error/error-emitter.d.ts +0 -18
- package/lib/utils/error/error-emitter.js +0 -29
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapses consecutive `anchors`-shaped rows into `anchor_edges` rows by
|
|
3
|
+
* `(pageId, hrefId)` (issue #193).
|
|
4
|
+
*
|
|
5
|
+
* **Input contract**: `rows` MUST be sorted by `(pageId, hrefId, id)`
|
|
6
|
+
* ascending. The caller (`populate-anchor-edges.ts`) achieves this via a
|
|
7
|
+
* single keyset scan `ORDER BY pageId, hrefId, id` over `anchors`.
|
|
8
|
+
*
|
|
9
|
+
* The `id`-then-key sort matters because the dedup rule is
|
|
10
|
+
* "first instance wins": the smallest `anchors.id` for a given
|
|
11
|
+
* `(pageId, hrefId)` pair contributes its `hash` and `textContent` to
|
|
12
|
+
* the edge; every subsequent duplicate is counted but its body is
|
|
13
|
+
* discarded. A naive `min(hash) GROUP BY pageId, hrefId` would pick the
|
|
14
|
+
* lexicographically smallest hex string (uniformly distributed) rather
|
|
15
|
+
* than the earliest occurrence, so the pass has to happen in JS.
|
|
16
|
+
*
|
|
17
|
+
* The generator yields one {@link AnchorEdgeRowInProgress} per distinct
|
|
18
|
+
* pair as soon as the next pair boundary is observed. Emitting eagerly
|
|
19
|
+
* keeps peak memory bounded to O(1) — the collapser holds at most one
|
|
20
|
+
* open pair state at a time.
|
|
21
|
+
*
|
|
22
|
+
* `first_text_id` is intentionally left `null` here; the caller resolves
|
|
23
|
+
* it in a second pass after all edges are known (see
|
|
24
|
+
* `populate-anchor-edges.ts`). The `first_hash` field is set to the
|
|
25
|
+
* first row's `hash` verbatim; a first row with `hash === null` results
|
|
26
|
+
* in `first_hash = null` on the edge (rare but possible on legacy
|
|
27
|
+
* archives that failed to compute a hash).
|
|
28
|
+
* @param rows - Anchor input rows sorted by `(pageId, hrefId, id)`.
|
|
29
|
+
* @yields {AnchorEdgeRowInProgress} One entry per distinct
|
|
30
|
+
* `(pageId, hrefId)` pair in the input.
|
|
31
|
+
* @example
|
|
32
|
+
* const edges = [...collapseAnchorRows([
|
|
33
|
+
* { id: 1, pageId: 10, hrefId: 20, hash: 'a', textContent: 'first' },
|
|
34
|
+
* { id: 2, pageId: 10, hrefId: 20, hash: 'b', textContent: 'second' },
|
|
35
|
+
* { id: 3, pageId: 10, hrefId: 30, hash: 'c', textContent: 'x' },
|
|
36
|
+
* ])];
|
|
37
|
+
* // edges[0] = { page_id: 10, href_page_id: 20, count: 2, first_hash: 'a', ... }
|
|
38
|
+
* // edges[1] = { page_id: 10, href_page_id: 30, count: 1, first_hash: 'c', ... }
|
|
39
|
+
*/
|
|
40
|
+
export function* collapseAnchorRows(rows) {
|
|
41
|
+
let openPageId = null;
|
|
42
|
+
let openHrefId = null;
|
|
43
|
+
let openCount = 0;
|
|
44
|
+
let openFirstHash = null;
|
|
45
|
+
let openFirstTextContent = null;
|
|
46
|
+
let openLastId = null;
|
|
47
|
+
for (const row of rows) {
|
|
48
|
+
if (openPageId === row.pageId && openHrefId === row.hrefId) {
|
|
49
|
+
// Same pair — assert monotonically-increasing `id` so callers
|
|
50
|
+
// that regress the `ORDER BY id` fail loudly instead of
|
|
51
|
+
// silently producing the wrong "first instance". The count-
|
|
52
|
+
// based acceptance check does not catch this (it only sums
|
|
53
|
+
// counts, not first_hash values), so the collapser defends
|
|
54
|
+
// its own precondition.
|
|
55
|
+
if (openLastId !== null && row.id <= openLastId) {
|
|
56
|
+
throw new Error(`collapseAnchorRows: input not sorted by id within (pageId=${row.pageId}, hrefId=${row.hrefId}) — saw id=${row.id} after id=${openLastId}. Caller must ORDER BY pageId, hrefId, id ASC.`);
|
|
57
|
+
}
|
|
58
|
+
openLastId = row.id;
|
|
59
|
+
openCount += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (openPageId !== null && openHrefId !== null) {
|
|
63
|
+
yield {
|
|
64
|
+
page_id: openPageId,
|
|
65
|
+
href_page_id: openHrefId,
|
|
66
|
+
count: openCount,
|
|
67
|
+
first_hash: openFirstHash,
|
|
68
|
+
first_textContent: openFirstTextContent,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
openPageId = row.pageId;
|
|
72
|
+
openHrefId = row.hrefId;
|
|
73
|
+
openCount = 1;
|
|
74
|
+
openFirstHash = row.hash;
|
|
75
|
+
openFirstTextContent = row.textContent;
|
|
76
|
+
openLastId = row.id;
|
|
77
|
+
}
|
|
78
|
+
if (openPageId !== null && openHrefId !== null) {
|
|
79
|
+
yield {
|
|
80
|
+
page_id: openPageId,
|
|
81
|
+
href_page_id: openHrefId,
|
|
82
|
+
count: openCount,
|
|
83
|
+
first_hash: openFirstHash,
|
|
84
|
+
first_textContent: openFirstTextContent,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the `dom_path` string for one DOM element as required by
|
|
3
|
+
* `image_items.dom_path_text_id` (issue #193).
|
|
4
|
+
*
|
|
5
|
+
* Format: slash-joined ancestor tags starting at `<html>`, each tag
|
|
6
|
+
* followed by a 1-based sibling ordinal counting **same-tag** siblings
|
|
7
|
+
* only (e.g.
|
|
8
|
+
* `html/body[1]/main[1]/section[2]/picture[1]/img[1]`). The `<html>`
|
|
9
|
+
* root has no bracketed ordinal because there is only one document
|
|
10
|
+
* root per page.
|
|
11
|
+
*
|
|
12
|
+
* The walk uses `parentElement` + `previousElementSibling` — no
|
|
13
|
+
* document mutation, no reliance on `id` / `class` — so a re-render
|
|
14
|
+
* that only reshuffles class names or ids still produces the same path.
|
|
15
|
+
* The chain of tag names alone is stable for a given DOM shape.
|
|
16
|
+
*
|
|
17
|
+
* The function operates on the generic `Element` interface from DOM lib
|
|
18
|
+
* (available in the crawler's TS lib config) so it does not force
|
|
19
|
+
* jsdom into the crawler runtime — the migration script and unit tests
|
|
20
|
+
* inject jsdom-backed elements, while a future crawler-side wrapper
|
|
21
|
+
* can pass puppeteer's DOM handles through the same code.
|
|
22
|
+
*
|
|
23
|
+
* Detached elements (`element.parentElement === null` before reaching
|
|
24
|
+
* `<html>`) return the tag chain from wherever the walk terminates —
|
|
25
|
+
* a legally detached image (rare, but
|
|
26
|
+
* technically possible during scripted mutations) yields e.g.
|
|
27
|
+
* `img[1]` alone instead of throwing.
|
|
28
|
+
* @param element - The DOM element to derive the path for. Typically an
|
|
29
|
+
* `<img>` in the archived HTML, but the function is not tag-specific.
|
|
30
|
+
* @returns The dom_path string.
|
|
31
|
+
* @example
|
|
32
|
+
* // In an HTML snapshot `<html><body><main><img></main></body></html>`
|
|
33
|
+
* deriveDomPath(imgElement); // 'html/body[1]/main[1]/img[1]'
|
|
34
|
+
*/
|
|
35
|
+
export declare function deriveDomPath(element: Element): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the `dom_path` string for one DOM element as required by
|
|
3
|
+
* `image_items.dom_path_text_id` (issue #193).
|
|
4
|
+
*
|
|
5
|
+
* Format: slash-joined ancestor tags starting at `<html>`, each tag
|
|
6
|
+
* followed by a 1-based sibling ordinal counting **same-tag** siblings
|
|
7
|
+
* only (e.g.
|
|
8
|
+
* `html/body[1]/main[1]/section[2]/picture[1]/img[1]`). The `<html>`
|
|
9
|
+
* root has no bracketed ordinal because there is only one document
|
|
10
|
+
* root per page.
|
|
11
|
+
*
|
|
12
|
+
* The walk uses `parentElement` + `previousElementSibling` — no
|
|
13
|
+
* document mutation, no reliance on `id` / `class` — so a re-render
|
|
14
|
+
* that only reshuffles class names or ids still produces the same path.
|
|
15
|
+
* The chain of tag names alone is stable for a given DOM shape.
|
|
16
|
+
*
|
|
17
|
+
* The function operates on the generic `Element` interface from DOM lib
|
|
18
|
+
* (available in the crawler's TS lib config) so it does not force
|
|
19
|
+
* jsdom into the crawler runtime — the migration script and unit tests
|
|
20
|
+
* inject jsdom-backed elements, while a future crawler-side wrapper
|
|
21
|
+
* can pass puppeteer's DOM handles through the same code.
|
|
22
|
+
*
|
|
23
|
+
* Detached elements (`element.parentElement === null` before reaching
|
|
24
|
+
* `<html>`) return the tag chain from wherever the walk terminates —
|
|
25
|
+
* a legally detached image (rare, but
|
|
26
|
+
* technically possible during scripted mutations) yields e.g.
|
|
27
|
+
* `img[1]` alone instead of throwing.
|
|
28
|
+
* @param element - The DOM element to derive the path for. Typically an
|
|
29
|
+
* `<img>` in the archived HTML, but the function is not tag-specific.
|
|
30
|
+
* @returns The dom_path string.
|
|
31
|
+
* @example
|
|
32
|
+
* // In an HTML snapshot `<html><body><main><img></main></body></html>`
|
|
33
|
+
* deriveDomPath(imgElement); // 'html/body[1]/main[1]/img[1]'
|
|
34
|
+
*/
|
|
35
|
+
export function deriveDomPath(element) {
|
|
36
|
+
const segments = [];
|
|
37
|
+
let current = element;
|
|
38
|
+
while (current !== null) {
|
|
39
|
+
const tag = current.tagName.toLowerCase();
|
|
40
|
+
if (tag === 'html') {
|
|
41
|
+
segments.unshift('html');
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
segments.unshift(`${tag}[${computeSiblingOrdinal(current, tag)}]`);
|
|
45
|
+
current = current.parentElement;
|
|
46
|
+
}
|
|
47
|
+
return segments.join('/');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Counts how many earlier siblings share the same tag name as `element`,
|
|
51
|
+
* returning `count + 1` as the 1-based ordinal.
|
|
52
|
+
*
|
|
53
|
+
* Iterating `previousElementSibling` is O(k) where k is the sibling
|
|
54
|
+
* position; for typical DOM depth and sibling counts this is negligible
|
|
55
|
+
* next to the jsdom parse cost of the enclosing page.
|
|
56
|
+
* @param element - The element whose ordinal is being computed.
|
|
57
|
+
* @param tag - The lower-cased tag name to match on. Passed in by the
|
|
58
|
+
* caller so `element.tagName.toLowerCase()` is not recomputed inside
|
|
59
|
+
* the loop for every sibling comparison.
|
|
60
|
+
* @returns 1-based ordinal among same-tag siblings.
|
|
61
|
+
*/
|
|
62
|
+
function computeSiblingOrdinal(element, tag) {
|
|
63
|
+
let count = 1;
|
|
64
|
+
let sibling = element.previousElementSibling;
|
|
65
|
+
while (sibling !== null) {
|
|
66
|
+
if (sibling.tagName.toLowerCase() === tag) {
|
|
67
|
+
count += 1;
|
|
68
|
+
}
|
|
69
|
+
sibling = sibling.previousElementSibling;
|
|
70
|
+
}
|
|
71
|
+
return count;
|
|
72
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Predicate matching the ref-tables populate's routing rule
|
|
3
|
+
* (`populate-ref-tables/populate-url-refs.ts` /
|
|
4
|
+
* `populate-ref-tables/populate-blob-refs.ts`): a `data:` URI whose
|
|
5
|
+
* length exceeds {@link DATA_URI_URL_REFS_LIMIT} lands in `blob_refs`;
|
|
6
|
+
* anything else (regular URL or short data URI) lands in `url_refs`.
|
|
7
|
+
* Shared by every entity populate that partitions a URL-shaped column
|
|
8
|
+
* into the two dictionaries (`populate-image-items.ts`,
|
|
9
|
+
* `populate-resource-items.ts`).
|
|
10
|
+
* @param value - Raw URL-shaped column value.
|
|
11
|
+
* @returns `true` when the value belongs in `blob_refs`.
|
|
12
|
+
* @example
|
|
13
|
+
* isBlobRefValue('data:image/png;base64,' + 'x'.repeat(600)); // true
|
|
14
|
+
* isBlobRefValue('https://example.com/a.png'); // false
|
|
15
|
+
*/
|
|
16
|
+
export declare function isBlobRefValue(value: string): boolean;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DATA_URI_URL_REFS_LIMIT } from '../populate-ref-tables/data-uri-url-refs-limit.js';
|
|
2
|
+
/**
|
|
3
|
+
* Predicate matching the ref-tables populate's routing rule
|
|
4
|
+
* (`populate-ref-tables/populate-url-refs.ts` /
|
|
5
|
+
* `populate-ref-tables/populate-blob-refs.ts`): a `data:` URI whose
|
|
6
|
+
* length exceeds {@link DATA_URI_URL_REFS_LIMIT} lands in `blob_refs`;
|
|
7
|
+
* anything else (regular URL or short data URI) lands in `url_refs`.
|
|
8
|
+
* Shared by every entity populate that partitions a URL-shaped column
|
|
9
|
+
* into the two dictionaries (`populate-image-items.ts`,
|
|
10
|
+
* `populate-resource-items.ts`).
|
|
11
|
+
* @param value - Raw URL-shaped column value.
|
|
12
|
+
* @returns `true` when the value belongs in `blob_refs`.
|
|
13
|
+
* @example
|
|
14
|
+
* isBlobRefValue('data:image/png;base64,' + 'x'.repeat(600)); // true
|
|
15
|
+
* isBlobRefValue('https://example.com/a.png'); // false
|
|
16
|
+
*/
|
|
17
|
+
export function isBlobRefValue(value) {
|
|
18
|
+
return value.startsWith('data:') && value.length > DATA_URI_URL_REFS_LIMIT;
|
|
19
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { DomPathCandidate, DomPathResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* One image record projected onto the fields the matcher needs:
|
|
4
|
+
* `id` (used to build the returned Map) and `sourceCode` (the outerHTML
|
|
5
|
+
* string to match against document candidates). `id` may be a real
|
|
6
|
+
* `images.id` (migration) or a synthetic per-page index (crawler write
|
|
7
|
+
* path) — the matcher only requires ids to be unique within one call and
|
|
8
|
+
* ordered in crawler insertion order.
|
|
9
|
+
*/
|
|
10
|
+
interface ImageRowForMatching {
|
|
11
|
+
/** Unique-within-call id, in crawler insertion order. */
|
|
12
|
+
id: number;
|
|
13
|
+
/**
|
|
14
|
+
* The `<img>` element's `outerHTML` as captured by the crawler.
|
|
15
|
+
* `null` when the crawler failed to capture the source (a null blob
|
|
16
|
+
* or a puppeteer serialisation edge case).
|
|
17
|
+
*/
|
|
18
|
+
sourceCode: string | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Matches image records to their DOM-path strings using a 3-case
|
|
22
|
+
* algorithm (issue #193).
|
|
23
|
+
*
|
|
24
|
+
* The three cases:
|
|
25
|
+
*
|
|
26
|
+
* 1. **Single match** — `image.sourceCode` matches exactly one `<img>`
|
|
27
|
+
* outerHTML in the document. Assign that candidate's `dom_path`.
|
|
28
|
+
* 2. **Ordinal match** — multiple `<img>` elements share the same
|
|
29
|
+
* outerHTML (identical `src` / `alt` / attributes on the same page).
|
|
30
|
+
* Assign paths in `id` order, matching the crawler's insertion order
|
|
31
|
+
* which is expected to correspond to DOM order.
|
|
32
|
+
* 3. **Unknown** — `sourceCode` is null OR no matching `<img>` exists
|
|
33
|
+
* in the document (crawler-side rewriting drift). Fall back to the
|
|
34
|
+
* synthetic `unknown/<id>` marker so the `dom_path_text_id NOT NULL`
|
|
35
|
+
* constraint is still satisfied.
|
|
36
|
+
*
|
|
37
|
+
* The function is pure — no DB, no DOM. Callers produce the candidate
|
|
38
|
+
* list from whatever DOM they have: jsdom over an archived HTML snapshot
|
|
39
|
+
* (migration script) or an in-browser walk over the live page (crawler
|
|
40
|
+
* write path). See {@link ./types.ts}'s `DomPathCandidate`.
|
|
41
|
+
*
|
|
42
|
+
* `candidatesInDocumentOrder` MUST already be in document order — the
|
|
43
|
+
* matcher does not sort. `document.querySelectorAll('img')` satisfies
|
|
44
|
+
* this contract; ordering is the caller's responsibility.
|
|
45
|
+
*
|
|
46
|
+
* `images` MUST already be sorted by `id` ascending for the ordinal-
|
|
47
|
+
* match case to reproduce the crawler's insertion order deterministically.
|
|
48
|
+
* A minor deviation from this ordering only affects the ordinal-match
|
|
49
|
+
* branch — single-match and unknown branches are order-independent.
|
|
50
|
+
* @param images - The page's image records in `id` order.
|
|
51
|
+
* @param candidatesInDocumentOrder - `<img>` outerHTML + dom_path pairs
|
|
52
|
+
* from the document, in document order.
|
|
53
|
+
* @returns Map keyed by `images[].id`; every input row gets one entry.
|
|
54
|
+
* @example
|
|
55
|
+
* const jsdom = new JSDOM(html);
|
|
56
|
+
* const candidates = [...jsdom.window.document.querySelectorAll('img')].map(
|
|
57
|
+
* (img) => ({ outerHTML: img.outerHTML, path: deriveDomPath(img) }),
|
|
58
|
+
* );
|
|
59
|
+
* const result = matchImagesToDomPaths(
|
|
60
|
+
* [{ id: 1, sourceCode: '<img src="a.png">' }],
|
|
61
|
+
* candidates,
|
|
62
|
+
* );
|
|
63
|
+
* result.get(1); // { path: 'html/body[1]/img[1]', case: 'single-match' }
|
|
64
|
+
*/
|
|
65
|
+
export declare function matchImagesToDomPaths(images: readonly ImageRowForMatching[], candidatesInDocumentOrder: readonly DomPathCandidate[]): ReadonlyMap<number, DomPathResult>;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches image records to their DOM-path strings using a 3-case
|
|
3
|
+
* algorithm (issue #193).
|
|
4
|
+
*
|
|
5
|
+
* The three cases:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Single match** — `image.sourceCode` matches exactly one `<img>`
|
|
8
|
+
* outerHTML in the document. Assign that candidate's `dom_path`.
|
|
9
|
+
* 2. **Ordinal match** — multiple `<img>` elements share the same
|
|
10
|
+
* outerHTML (identical `src` / `alt` / attributes on the same page).
|
|
11
|
+
* Assign paths in `id` order, matching the crawler's insertion order
|
|
12
|
+
* which is expected to correspond to DOM order.
|
|
13
|
+
* 3. **Unknown** — `sourceCode` is null OR no matching `<img>` exists
|
|
14
|
+
* in the document (crawler-side rewriting drift). Fall back to the
|
|
15
|
+
* synthetic `unknown/<id>` marker so the `dom_path_text_id NOT NULL`
|
|
16
|
+
* constraint is still satisfied.
|
|
17
|
+
*
|
|
18
|
+
* The function is pure — no DB, no DOM. Callers produce the candidate
|
|
19
|
+
* list from whatever DOM they have: jsdom over an archived HTML snapshot
|
|
20
|
+
* (migration script) or an in-browser walk over the live page (crawler
|
|
21
|
+
* write path). See {@link ./types.ts}'s `DomPathCandidate`.
|
|
22
|
+
*
|
|
23
|
+
* `candidatesInDocumentOrder` MUST already be in document order — the
|
|
24
|
+
* matcher does not sort. `document.querySelectorAll('img')` satisfies
|
|
25
|
+
* this contract; ordering is the caller's responsibility.
|
|
26
|
+
*
|
|
27
|
+
* `images` MUST already be sorted by `id` ascending for the ordinal-
|
|
28
|
+
* match case to reproduce the crawler's insertion order deterministically.
|
|
29
|
+
* A minor deviation from this ordering only affects the ordinal-match
|
|
30
|
+
* branch — single-match and unknown branches are order-independent.
|
|
31
|
+
* @param images - The page's image records in `id` order.
|
|
32
|
+
* @param candidatesInDocumentOrder - `<img>` outerHTML + dom_path pairs
|
|
33
|
+
* from the document, in document order.
|
|
34
|
+
* @returns Map keyed by `images[].id`; every input row gets one entry.
|
|
35
|
+
* @example
|
|
36
|
+
* const jsdom = new JSDOM(html);
|
|
37
|
+
* const candidates = [...jsdom.window.document.querySelectorAll('img')].map(
|
|
38
|
+
* (img) => ({ outerHTML: img.outerHTML, path: deriveDomPath(img) }),
|
|
39
|
+
* );
|
|
40
|
+
* const result = matchImagesToDomPaths(
|
|
41
|
+
* [{ id: 1, sourceCode: '<img src="a.png">' }],
|
|
42
|
+
* candidates,
|
|
43
|
+
* );
|
|
44
|
+
* result.get(1); // { path: 'html/body[1]/img[1]', case: 'single-match' }
|
|
45
|
+
*/
|
|
46
|
+
export function matchImagesToDomPaths(images, candidatesInDocumentOrder) {
|
|
47
|
+
const byOuterHtml = new Map();
|
|
48
|
+
for (const candidate of candidatesInDocumentOrder) {
|
|
49
|
+
const bucket = byOuterHtml.get(candidate.outerHTML);
|
|
50
|
+
if (bucket === undefined) {
|
|
51
|
+
byOuterHtml.set(candidate.outerHTML, [candidate]);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
bucket.push(candidate);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const cursorByOuterHtml = new Map();
|
|
58
|
+
const result = new Map();
|
|
59
|
+
for (const image of images) {
|
|
60
|
+
if (image.sourceCode === null || image.sourceCode === '') {
|
|
61
|
+
result.set(image.id, { path: `unknown/${image.id}`, case: 'unknown' });
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const candidates = byOuterHtml.get(image.sourceCode);
|
|
65
|
+
if (candidates === undefined || candidates.length === 0) {
|
|
66
|
+
result.set(image.id, { path: `unknown/${image.id}`, case: 'unknown' });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (candidates.length === 1) {
|
|
70
|
+
result.set(image.id, {
|
|
71
|
+
path: candidates[0].path,
|
|
72
|
+
case: 'single-match',
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const cursor = cursorByOuterHtml.get(image.sourceCode) ?? 0;
|
|
77
|
+
cursorByOuterHtml.set(image.sourceCode, cursor + 1);
|
|
78
|
+
const chosen = candidates[cursor];
|
|
79
|
+
if (chosen === undefined) {
|
|
80
|
+
// More image records share this outerHTML than the document
|
|
81
|
+
// has matching `<img>` elements. Reusing the last candidate
|
|
82
|
+
// (`candidates.at(-1)`) would silently map every overflow row
|
|
83
|
+
// to the same element, producing multiple `image_items` rows
|
|
84
|
+
// with the same `dom_path_text_id`. Falling back to
|
|
85
|
+
// `unknown/<id>` keeps overflow rows individually
|
|
86
|
+
// distinguishable in the archive.
|
|
87
|
+
result.set(image.id, { path: `unknown/${image.id}`, case: 'unknown' });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
result.set(image.id, {
|
|
91
|
+
path: chosen.path,
|
|
92
|
+
case: 'ordinal-match',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ProgressCallback } from '../create-progress-reporter.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Populates `anchor_edges` from `anchors` (issue #193).
|
|
5
|
+
*
|
|
6
|
+
* The algorithm is a **single keyset-paginated scan** over `anchors`
|
|
7
|
+
* ordered by `(pageId, hrefId, id)`. Each chunk feeds
|
|
8
|
+
* {@link ./collapse-anchor-rows.ts}, which yields one edge per distinct
|
|
9
|
+
* `(pageId, hrefId)` pair as soon as the pair boundary is observed.
|
|
10
|
+
*
|
|
11
|
+
* A boundary can straddle two chunks (the last row of chunk N shares a
|
|
12
|
+
* pair with the first row of chunk N+1), so the outer loop keeps an
|
|
13
|
+
* "open edge" state that flushes only when the *next* pair actually
|
|
14
|
+
* starts. On end-of-stream the last open edge is emitted.
|
|
15
|
+
*
|
|
16
|
+
* `first_text_id` is left unresolved during the streaming pass — the
|
|
17
|
+
* text is buffered on each pending edge and resolved in bulk by
|
|
18
|
+
* {@link ./resolve-text-refs.ts} at INSERT time. Every INSERT batch
|
|
19
|
+
* therefore issues one text-refs lookup + one INSERT, keeping DB
|
|
20
|
+
* round-trips per edge low.
|
|
21
|
+
*
|
|
22
|
+
* `INSERT OR IGNORE` on the `(page_id, href_page_id)` UNIQUE composite
|
|
23
|
+
* makes the step idempotent: a re-run after partial failure re-emits the
|
|
24
|
+
* same edges but the writes no-op.
|
|
25
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
26
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
27
|
+
* ~5% of `anchors` scanned); see {@link ../create-progress-reporter.ts}.
|
|
28
|
+
* @example
|
|
29
|
+
* await knex.transaction(async (trx) => {
|
|
30
|
+
* await populateAnchorEdges(trx);
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
export declare function populateAnchorEdges(trx: Knex, onProgress?: ProgressCallback): Promise<void>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createProgressReporter } from '../create-progress-reporter.js';
|
|
2
|
+
import { collapseAnchorRows } from './collapse-anchor-rows.js';
|
|
3
|
+
import { resolveTextRefs } from './resolve-text-refs.js';
|
|
4
|
+
/**
|
|
5
|
+
* Rows scanned per keyset-paginated `SELECT` chunk against `anchors`.
|
|
6
|
+
* `anchors` is the largest table in the archive (≈ 13 M rows on the
|
|
7
|
+
* reference archive) so the chunk size trades off memory against
|
|
8
|
+
* round-trip overhead. 5 000 rows per SELECT keeps peak chunk memory
|
|
9
|
+
* ≈ 5 MB (per-row ≈ 1 KB with URL / textContent stored elsewhere) and
|
|
10
|
+
* amortises the per-query round-trip cost across many collapses.
|
|
11
|
+
*/
|
|
12
|
+
const READ_CHUNK_SIZE = 5000;
|
|
13
|
+
/**
|
|
14
|
+
* Edges buffered before a bulk `.insert(rows).onConflict(...).ignore()`.
|
|
15
|
+
* The real constraint here is NOT the SQLite bound-parameter limit —
|
|
16
|
+
* knex's sqlite3-family dialect compiles any multi-row `.insert()` (with
|
|
17
|
+
* or without `onConflict`) into `INSERT INTO ... SELECT ... UNION ALL
|
|
18
|
+
* SELECT ...`, which is capped by SQLite's `SQLITE_LIMIT_COMPOUND_SELECT`
|
|
19
|
+
* (default 500). A chunk size above 500 fails with "too many terms in
|
|
20
|
+
* compound SELECT" — confirmed against a real archive whose anchor count
|
|
21
|
+
* routinely produces 500+ distinct `(page_id, href_page_id)` pairs per
|
|
22
|
+
* page. Kept at exactly 500 (not lower) to match the established
|
|
23
|
+
* convention already used by `upsert-one-header-set.ts` and the
|
|
24
|
+
* ref-tables populate steps.
|
|
25
|
+
*/
|
|
26
|
+
const INSERT_CHUNK_SIZE = 500;
|
|
27
|
+
/**
|
|
28
|
+
* Populates `anchor_edges` from `anchors` (issue #193).
|
|
29
|
+
*
|
|
30
|
+
* The algorithm is a **single keyset-paginated scan** over `anchors`
|
|
31
|
+
* ordered by `(pageId, hrefId, id)`. Each chunk feeds
|
|
32
|
+
* {@link ./collapse-anchor-rows.ts}, which yields one edge per distinct
|
|
33
|
+
* `(pageId, hrefId)` pair as soon as the pair boundary is observed.
|
|
34
|
+
*
|
|
35
|
+
* A boundary can straddle two chunks (the last row of chunk N shares a
|
|
36
|
+
* pair with the first row of chunk N+1), so the outer loop keeps an
|
|
37
|
+
* "open edge" state that flushes only when the *next* pair actually
|
|
38
|
+
* starts. On end-of-stream the last open edge is emitted.
|
|
39
|
+
*
|
|
40
|
+
* `first_text_id` is left unresolved during the streaming pass — the
|
|
41
|
+
* text is buffered on each pending edge and resolved in bulk by
|
|
42
|
+
* {@link ./resolve-text-refs.ts} at INSERT time. Every INSERT batch
|
|
43
|
+
* therefore issues one text-refs lookup + one INSERT, keeping DB
|
|
44
|
+
* round-trips per edge low.
|
|
45
|
+
*
|
|
46
|
+
* `INSERT OR IGNORE` on the `(page_id, href_page_id)` UNIQUE composite
|
|
47
|
+
* makes the step idempotent: a re-run after partial failure re-emits the
|
|
48
|
+
* same edges but the writes no-op.
|
|
49
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
50
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
51
|
+
* ~5% of `anchors` scanned); see {@link ../create-progress-reporter.ts}.
|
|
52
|
+
* @example
|
|
53
|
+
* await knex.transaction(async (trx) => {
|
|
54
|
+
* await populateAnchorEdges(trx);
|
|
55
|
+
* });
|
|
56
|
+
*/
|
|
57
|
+
export async function populateAnchorEdges(trx, onProgress) {
|
|
58
|
+
let cursorPageId = 0;
|
|
59
|
+
let cursorHrefId = 0;
|
|
60
|
+
let cursorId = 0;
|
|
61
|
+
let carryOver = null;
|
|
62
|
+
const pending = [];
|
|
63
|
+
const countRows = await trx('anchors').count({ n: '*' });
|
|
64
|
+
const total = Number(countRows[0]?.n ?? 0);
|
|
65
|
+
const report = createProgressReporter('anchor_edges (anchors)', total, onProgress);
|
|
66
|
+
let processed = 0;
|
|
67
|
+
while (true) {
|
|
68
|
+
const rows = await trx('anchors')
|
|
69
|
+
.select('id', 'pageId', 'hrefId', 'hash', 'textContent')
|
|
70
|
+
.where(function () {
|
|
71
|
+
this.where('pageId', '>', cursorPageId)
|
|
72
|
+
.orWhere(function () {
|
|
73
|
+
this.where('pageId', cursorPageId).andWhere('hrefId', '>', cursorHrefId);
|
|
74
|
+
})
|
|
75
|
+
.orWhere(function () {
|
|
76
|
+
this.where('pageId', cursorPageId)
|
|
77
|
+
.andWhere('hrefId', cursorHrefId)
|
|
78
|
+
.andWhere('id', '>', cursorId);
|
|
79
|
+
});
|
|
80
|
+
})
|
|
81
|
+
.orderBy([
|
|
82
|
+
{ column: 'pageId', order: 'asc' },
|
|
83
|
+
{ column: 'hrefId', order: 'asc' },
|
|
84
|
+
{ column: 'id', order: 'asc' },
|
|
85
|
+
])
|
|
86
|
+
.limit(READ_CHUNK_SIZE);
|
|
87
|
+
if (rows.length === 0) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
const lastRow = rows.at(-1);
|
|
91
|
+
cursorPageId = lastRow.pageId;
|
|
92
|
+
cursorHrefId = lastRow.hrefId;
|
|
93
|
+
cursorId = lastRow.id;
|
|
94
|
+
processed += rows.length;
|
|
95
|
+
report(processed);
|
|
96
|
+
const chunkEdges = [...collapseAnchorRows(rows)];
|
|
97
|
+
for (const edge of chunkEdges) {
|
|
98
|
+
if (carryOver !== null &&
|
|
99
|
+
carryOver.page_id === edge.page_id &&
|
|
100
|
+
carryOver.href_page_id === edge.href_page_id) {
|
|
101
|
+
carryOver.count += edge.count;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (carryOver !== null) {
|
|
105
|
+
pending.push(carryOver);
|
|
106
|
+
}
|
|
107
|
+
carryOver = edge;
|
|
108
|
+
if (pending.length >= INSERT_CHUNK_SIZE) {
|
|
109
|
+
await flush(trx, pending);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (carryOver !== null) {
|
|
114
|
+
pending.push(carryOver);
|
|
115
|
+
}
|
|
116
|
+
if (pending.length > 0) {
|
|
117
|
+
await flush(trx, pending);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Resolves `first_text_id` for every buffered edge in `pending`, then
|
|
122
|
+
* bulk-inserts the batch into `anchor_edges` and clears the buffer.
|
|
123
|
+
*
|
|
124
|
+
* Extracted from the main loop so the "resolve + insert" pair happens in
|
|
125
|
+
* exactly one place — the streaming loop, the carry-over flush at
|
|
126
|
+
* end-of-stream, and any future re-order boundary all land here.
|
|
127
|
+
* @param trx - Knex instance or transaction.
|
|
128
|
+
* @param pending - Buffered edges awaiting `first_text_id` resolution
|
|
129
|
+
* and INSERT; mutated in place (cleared on return).
|
|
130
|
+
*/
|
|
131
|
+
async function flush(trx, pending) {
|
|
132
|
+
const texts = new Set();
|
|
133
|
+
for (const edge of pending) {
|
|
134
|
+
if (edge.first_textContent != null && edge.first_textContent !== '') {
|
|
135
|
+
texts.add(edge.first_textContent);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const textIds = await resolveTextRefs(trx, texts);
|
|
139
|
+
const inserts = pending.map((edge) => ({
|
|
140
|
+
page_id: edge.page_id,
|
|
141
|
+
href_page_id: edge.href_page_id,
|
|
142
|
+
count: edge.count,
|
|
143
|
+
first_hash: edge.first_hash,
|
|
144
|
+
first_text_id: edge.first_textContent != null && edge.first_textContent !== ''
|
|
145
|
+
? (textIds.get(edge.first_textContent) ?? null)
|
|
146
|
+
: null,
|
|
147
|
+
}));
|
|
148
|
+
await trx('anchor_edges')
|
|
149
|
+
.insert(inserts)
|
|
150
|
+
.onConflict(['page_id', 'href_page_id'])
|
|
151
|
+
.ignore();
|
|
152
|
+
pending.length = 0;
|
|
153
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ProgressCallback } from '../create-progress-reporter.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Populates `content_items` from `pages` (issue #193).
|
|
5
|
+
*
|
|
6
|
+
* Strategy per chunk:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Keyset-paginate** `pages` in id order so partial-failure re-runs
|
|
9
|
+
* resume from the last committed row without reading the full table.
|
|
10
|
+
* Same pattern as {@link ../populate-ref-tables/populate-url-refs.ts}.
|
|
11
|
+
* 2. **Batch-resolve** `url_refs.id`, `content_type_refs.id`, and
|
|
12
|
+
* `header_sets.id` for every distinct value in the chunk — one round
|
|
13
|
+
* trip per ref table instead of N round trips per row.
|
|
14
|
+
* 3. **Bulk INSERT** with explicit `id = pages.id`. Reusing the legacy
|
|
15
|
+
* PK preserves every FK reference in `page_errors` / `page_tags` /
|
|
16
|
+
* `page_jsonld` / `page_html_ref` without a per-row UPDATE.
|
|
17
|
+
*
|
|
18
|
+
* `redirect_dest_id` is copied verbatim from `redirectDestId` — the FK
|
|
19
|
+
* self-reference is `DEFERRABLE INITIALLY DEFERRED` (see
|
|
20
|
+
* {@link ../create-entity-tables.ts}) so a redirect source
|
|
21
|
+
* inserted before its destination is validated only at COMMIT time.
|
|
22
|
+
*
|
|
23
|
+
* `INSERT OR IGNORE` on the natural PK makes the step idempotent — a
|
|
24
|
+
* re-run after a partial failure only inserts the rows that are still
|
|
25
|
+
* missing.
|
|
26
|
+
*
|
|
27
|
+
* `content_type_refs` is preloaded once at the start because its
|
|
28
|
+
* cardinality is small (see {@link ./resolve-content-type-refs.ts}); the
|
|
29
|
+
* URL and header-set resolvers are called per chunk because their
|
|
30
|
+
* per-archive cardinality can be very large (one row per distinct URL /
|
|
31
|
+
* response JSON).
|
|
32
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
33
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
34
|
+
* ~5% of `pages` scanned); see {@link ../create-progress-reporter.ts}.
|
|
35
|
+
* @example
|
|
36
|
+
* await knex.transaction(async (trx) => {
|
|
37
|
+
* await populateContentItems(trx);
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
40
|
+
export declare function populateContentItems(trx: Knex, onProgress?: ProgressCallback): Promise<void>;
|