@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,33 @@
|
|
|
1
|
+
import type { ProgressCallback } from '../create-progress-reporter.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Populates `page_meta` from `pages WHERE scraped = 1` (issue #193).
|
|
5
|
+
*
|
|
6
|
+
* Pages that were never scraped contribute no meta rows — they have no rendered title,
|
|
7
|
+
* description, or JSON-LD to preserve, so filtering at the SELECT keeps
|
|
8
|
+
* the reader working set narrow and matches the migration acceptance check
|
|
9
|
+
* `count(*) FROM page_meta = count(*) FROM pages WHERE scraped = 1`.
|
|
10
|
+
*
|
|
11
|
+
* Per chunk:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Keyset-paginate** `pages.id > cursor AND scraped = 1`.
|
|
14
|
+
* 2. **Batch-resolve** all text / URL / JSON ref ids for every value in
|
|
15
|
+
* the chunk — three resolvers, one round-trip per ref table.
|
|
16
|
+
* 3. **Bulk INSERT** with `page_id = pages.id`. `page_id` is both PK and
|
|
17
|
+
* FK on `content_items(id)`; `populateContentItems` must have already
|
|
18
|
+
* inserted the corresponding `content_items` row for the FK to be
|
|
19
|
+
* valid at COMMIT time.
|
|
20
|
+
*
|
|
21
|
+
* `INSERT OR IGNORE` on the `page_id` PK makes the step idempotent so
|
|
22
|
+
* partial-failure re-runs are safe.
|
|
23
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
24
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
25
|
+
* ~5% of `pages WHERE scraped = 1` scanned); see
|
|
26
|
+
* {@link ../create-progress-reporter.ts}.
|
|
27
|
+
* @example
|
|
28
|
+
* await knex.transaction(async (trx) => {
|
|
29
|
+
* await populateContentItems(trx);
|
|
30
|
+
* await populatePageMeta(trx);
|
|
31
|
+
* });
|
|
32
|
+
*/
|
|
33
|
+
export declare function populatePageMeta(trx: Knex, onProgress?: ProgressCallback): Promise<void>;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createProgressReporter } from '../create-progress-reporter.js';
|
|
2
|
+
import { PAGE_META_COLUMN_MAPS } from '../page-meta-column-maps.js';
|
|
3
|
+
import { DATA_URI_URL_REFS_LIMIT } from '../populate-ref-tables/data-uri-url-refs-limit.js';
|
|
4
|
+
import { resolveJsonRefs } from './resolve-json-refs.js';
|
|
5
|
+
import { resolveTextRefs } from './resolve-text-refs.js';
|
|
6
|
+
import { resolveUrlRefs } from './resolve-url-refs.js';
|
|
7
|
+
/**
|
|
8
|
+
* Rows scanned per keyset-paginated `SELECT` chunk against
|
|
9
|
+
* `pages WHERE scraped = 1`. The row width here is dominated by long
|
|
10
|
+
* text columns (`meta_extras` JSON, `description`, `twitter_description`)
|
|
11
|
+
* so 400 rows caps peak chunk memory at ≈ 40 MB on the reference
|
|
12
|
+
* archive.
|
|
13
|
+
*/
|
|
14
|
+
const READ_CHUNK_SIZE = 400;
|
|
15
|
+
/**
|
|
16
|
+
* Rows sent per `INSERT INTO page_meta ... VALUES (...)` statement. Each
|
|
17
|
+
* row binds 47 params (see the column list below), so 200 rows =
|
|
18
|
+
* 9 400 params — safely under SQLite's default variable limit.
|
|
19
|
+
*/
|
|
20
|
+
const INSERT_CHUNK_SIZE = 200;
|
|
21
|
+
/**
|
|
22
|
+
* Populates `page_meta` from `pages WHERE scraped = 1` (issue #193).
|
|
23
|
+
*
|
|
24
|
+
* Pages that were never scraped contribute no meta rows — they have no rendered title,
|
|
25
|
+
* description, or JSON-LD to preserve, so filtering at the SELECT keeps
|
|
26
|
+
* the reader working set narrow and matches the migration acceptance check
|
|
27
|
+
* `count(*) FROM page_meta = count(*) FROM pages WHERE scraped = 1`.
|
|
28
|
+
*
|
|
29
|
+
* Per chunk:
|
|
30
|
+
*
|
|
31
|
+
* 1. **Keyset-paginate** `pages.id > cursor AND scraped = 1`.
|
|
32
|
+
* 2. **Batch-resolve** all text / URL / JSON ref ids for every value in
|
|
33
|
+
* the chunk — three resolvers, one round-trip per ref table.
|
|
34
|
+
* 3. **Bulk INSERT** with `page_id = pages.id`. `page_id` is both PK and
|
|
35
|
+
* FK on `content_items(id)`; `populateContentItems` must have already
|
|
36
|
+
* inserted the corresponding `content_items` row for the FK to be
|
|
37
|
+
* valid at COMMIT time.
|
|
38
|
+
*
|
|
39
|
+
* `INSERT OR IGNORE` on the `page_id` PK makes the step idempotent so
|
|
40
|
+
* partial-failure re-runs are safe.
|
|
41
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
42
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
43
|
+
* ~5% of `pages WHERE scraped = 1` scanned); see
|
|
44
|
+
* {@link ../create-progress-reporter.ts}.
|
|
45
|
+
* @example
|
|
46
|
+
* await knex.transaction(async (trx) => {
|
|
47
|
+
* await populateContentItems(trx);
|
|
48
|
+
* await populatePageMeta(trx);
|
|
49
|
+
* });
|
|
50
|
+
*/
|
|
51
|
+
export async function populatePageMeta(trx, onProgress) {
|
|
52
|
+
const textColumns = PAGE_META_COLUMN_MAPS.text.map((entry) => entry.source);
|
|
53
|
+
const urlColumns = PAGE_META_COLUMN_MAPS.url.map((entry) => entry.source);
|
|
54
|
+
const plainColumns = [
|
|
55
|
+
'id',
|
|
56
|
+
'lang',
|
|
57
|
+
'dir',
|
|
58
|
+
'charset',
|
|
59
|
+
'baseHref',
|
|
60
|
+
'viewport_raw',
|
|
61
|
+
'themeColor',
|
|
62
|
+
'applicationName',
|
|
63
|
+
'author',
|
|
64
|
+
'generator',
|
|
65
|
+
'publisher',
|
|
66
|
+
'robots_noindex',
|
|
67
|
+
'robots_nofollow',
|
|
68
|
+
'robots_noarchive',
|
|
69
|
+
'robots_noimageindex',
|
|
70
|
+
'googlebot',
|
|
71
|
+
'og_type',
|
|
72
|
+
'og_site_name',
|
|
73
|
+
'og_image_alt',
|
|
74
|
+
'og_image_width',
|
|
75
|
+
'og_image_height',
|
|
76
|
+
'og_locale',
|
|
77
|
+
'og_article_published_time',
|
|
78
|
+
'og_article_modified_time',
|
|
79
|
+
'twitter_card',
|
|
80
|
+
'twitter_site',
|
|
81
|
+
'twitter_creator',
|
|
82
|
+
'fb_app_id',
|
|
83
|
+
'verification_google',
|
|
84
|
+
'formatDetection_telephone',
|
|
85
|
+
'tag_count',
|
|
86
|
+
'jsonld_count',
|
|
87
|
+
'tags_providers_csv',
|
|
88
|
+
'meta_extras',
|
|
89
|
+
];
|
|
90
|
+
const countRows = await trx('pages').where('scraped', true).count({ n: '*' });
|
|
91
|
+
const total = Number(countRows[0]?.n ?? 0);
|
|
92
|
+
const report = createProgressReporter('page_meta (pages)', total, onProgress);
|
|
93
|
+
let processed = 0;
|
|
94
|
+
let cursor = 0;
|
|
95
|
+
while (true) {
|
|
96
|
+
const rows = await trx('pages')
|
|
97
|
+
.select(...plainColumns, ...textColumns, ...urlColumns)
|
|
98
|
+
.where('id', '>', cursor)
|
|
99
|
+
.andWhere('scraped', true)
|
|
100
|
+
.orderBy('id', 'asc')
|
|
101
|
+
.limit(READ_CHUNK_SIZE);
|
|
102
|
+
if (rows.length === 0) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
cursor = rows.at(-1).id;
|
|
106
|
+
processed += rows.length;
|
|
107
|
+
report(processed);
|
|
108
|
+
const texts = new Set();
|
|
109
|
+
const urls = new Set();
|
|
110
|
+
const jsonStrings = new Set();
|
|
111
|
+
for (const row of rows) {
|
|
112
|
+
for (const column of textColumns) {
|
|
113
|
+
const value = row[column];
|
|
114
|
+
if (typeof value === 'string' && value !== '') {
|
|
115
|
+
texts.add(value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const column of urlColumns) {
|
|
119
|
+
const value = row[column];
|
|
120
|
+
if (typeof value === 'string' && value !== '' && !isLargeDataUri(value)) {
|
|
121
|
+
urls.add(value);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const metaExtras = row.meta_extras;
|
|
125
|
+
if (typeof metaExtras === 'string' && metaExtras !== '') {
|
|
126
|
+
jsonStrings.add(metaExtras);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const textIds = await resolveTextRefs(trx, texts);
|
|
130
|
+
const urlIds = await resolveUrlRefs(trx, urls);
|
|
131
|
+
const jsonIds = await resolveJsonRefs(trx, jsonStrings);
|
|
132
|
+
const inserts = rows.map((row) => {
|
|
133
|
+
const rowId = row.id;
|
|
134
|
+
const insert = {
|
|
135
|
+
page_id: rowId,
|
|
136
|
+
lang: row.lang ?? null,
|
|
137
|
+
dir: row.dir ?? null,
|
|
138
|
+
charset: row.charset ?? null,
|
|
139
|
+
base_href: row.baseHref ?? null,
|
|
140
|
+
viewport_raw: row.viewport_raw ?? null,
|
|
141
|
+
theme_color: row.themeColor ?? null,
|
|
142
|
+
application_name: row.applicationName ?? null,
|
|
143
|
+
author: row.author ?? null,
|
|
144
|
+
generator: row.generator ?? null,
|
|
145
|
+
publisher: row.publisher ?? null,
|
|
146
|
+
robots_noindex: row.robots_noindex ?? null,
|
|
147
|
+
robots_nofollow: row.robots_nofollow ?? null,
|
|
148
|
+
robots_noarchive: row.robots_noarchive ?? null,
|
|
149
|
+
robots_noimageindex: row.robots_noimageindex ?? null,
|
|
150
|
+
googlebot: row.googlebot ?? null,
|
|
151
|
+
og_type: row.og_type ?? null,
|
|
152
|
+
og_site_name: row.og_site_name ?? null,
|
|
153
|
+
og_image_alt: row.og_image_alt ?? null,
|
|
154
|
+
og_image_width: row.og_image_width ?? null,
|
|
155
|
+
og_image_height: row.og_image_height ?? null,
|
|
156
|
+
og_locale: row.og_locale ?? null,
|
|
157
|
+
og_article_published_time: row.og_article_published_time ?? null,
|
|
158
|
+
og_article_modified_time: row.og_article_modified_time ?? null,
|
|
159
|
+
twitter_card: row.twitter_card ?? null,
|
|
160
|
+
twitter_site: row.twitter_site ?? null,
|
|
161
|
+
twitter_creator: row.twitter_creator ?? null,
|
|
162
|
+
fb_app_id: row.fb_app_id ?? null,
|
|
163
|
+
verification_google: row.verification_google ?? null,
|
|
164
|
+
format_detection_telephone: row.formatDetection_telephone ?? null,
|
|
165
|
+
tag_count: row.tag_count ?? null,
|
|
166
|
+
jsonld_count: row.jsonld_count ?? null,
|
|
167
|
+
tags_providers_csv: row.tags_providers_csv ?? null,
|
|
168
|
+
meta_extras_json_id: resolveOptionalJsonId(row.meta_extras, jsonIds, rowId),
|
|
169
|
+
};
|
|
170
|
+
for (const { source, target } of PAGE_META_COLUMN_MAPS.text) {
|
|
171
|
+
insert[target] = resolveOptionalTextId(row[source], textIds, rowId, source);
|
|
172
|
+
}
|
|
173
|
+
for (const { source, target } of PAGE_META_COLUMN_MAPS.url) {
|
|
174
|
+
insert[target] = resolveOptionalUrlId(row[source], urlIds, rowId, source);
|
|
175
|
+
}
|
|
176
|
+
return insert;
|
|
177
|
+
});
|
|
178
|
+
for (let index = 0; index < inserts.length; index += INSERT_CHUNK_SIZE) {
|
|
179
|
+
const chunk = inserts.slice(index, index + INSERT_CHUNK_SIZE);
|
|
180
|
+
await trx('page_meta').insert(chunk).onConflict('page_id').ignore();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns `true` when `value` is a `data:` URI larger than the routing
|
|
186
|
+
* threshold — i.e. one that the `url_refs` populate
|
|
187
|
+
* (`populate-ref-tables/populate-url-refs.ts`) sent to `blob_refs` instead
|
|
188
|
+
* of `url_refs`. `page_meta` deliberately has no `*_blob_id` companion
|
|
189
|
+
* columns for its URL slots, so we skip these values from
|
|
190
|
+
* the `url_refs` lookup rather than issue a doomed `WHERE url IN (?)`
|
|
191
|
+
* that would burn a chunk slot per multi-KB URI. The resulting
|
|
192
|
+
* `*_url_id` is null with a `console.warn` breadcrumb; the operator
|
|
193
|
+
* can then decide whether the archive needs a schema extension or the
|
|
194
|
+
* raw data warrants normalisation.
|
|
195
|
+
* @param value - Raw URL column value.
|
|
196
|
+
*/
|
|
197
|
+
function isLargeDataUri(value) {
|
|
198
|
+
return value.startsWith('data:') && value.length > DATA_URI_URL_REFS_LIMIT;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Resolves `text_refs.id` for a `page_meta.<name>_text_id` slot or
|
|
202
|
+
* throws when a non-empty source value has no matching `text_refs` row
|
|
203
|
+
* (parity with `populateContentItems`' `url_id` throw — silent NULL
|
|
204
|
+
* corruption would still let the count-based acceptance invariant pass
|
|
205
|
+
* while individual `page_meta` rows lose their meta text).
|
|
206
|
+
* @param value - Raw column value from the source `pages` row.
|
|
207
|
+
* @param textIds - `text → text_refs.id` map batch-resolved for the
|
|
208
|
+
* current chunk (see {@link ./resolve-text-refs.ts}).
|
|
209
|
+
* @param pageId - Owning page id (for the error message).
|
|
210
|
+
* @param column - Source column name (for the error message).
|
|
211
|
+
*/
|
|
212
|
+
function resolveOptionalTextId(value, textIds, pageId, column) {
|
|
213
|
+
if (typeof value !== 'string' || value === '') {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const id = textIds.get(value);
|
|
217
|
+
if (id === undefined) {
|
|
218
|
+
throw new Error(`populatePageMeta: text_refs.id not resolved for page id=${pageId} column=${column} — populateTextRefs must run first`);
|
|
219
|
+
}
|
|
220
|
+
return id;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Resolves `url_refs.id` for a `page_meta.<name>_url_id` slot, throws
|
|
224
|
+
* on a missing lookup for a regular URL, and returns `null` (with a
|
|
225
|
+
* warning) for large data URIs that the `url_refs` populate routed to
|
|
226
|
+
* `blob_refs` — those cannot land in `page_meta` because the schema has
|
|
227
|
+
* no `*_blob_id` companion columns.
|
|
228
|
+
* @param value - Raw column value from the source `pages` row.
|
|
229
|
+
* @param urlIds - `url → url_refs.id` map batch-resolved for the
|
|
230
|
+
* current chunk (see {@link ./resolve-url-refs.ts}).
|
|
231
|
+
* @param pageId - Owning page id (for the error message).
|
|
232
|
+
* @param column - Source column name (for the error message).
|
|
233
|
+
*/
|
|
234
|
+
function resolveOptionalUrlId(value, urlIds, pageId, column) {
|
|
235
|
+
if (typeof value !== 'string' || value === '') {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
if (isLargeDataUri(value)) {
|
|
239
|
+
// eslint-disable-next-line no-console
|
|
240
|
+
console.warn(`populatePageMeta: dropping large data URI in page id=${pageId} column=${column} (page_meta has no *_blob_id companion for URL columns)`);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
const id = urlIds.get(value);
|
|
244
|
+
if (id === undefined) {
|
|
245
|
+
throw new Error(`populatePageMeta: url_refs.id not resolved for page id=${pageId} column=${column} — populateUrlRefs must run first`);
|
|
246
|
+
}
|
|
247
|
+
return id;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Resolves `json_refs.id` for `page_meta.meta_extras_json_id` or throws
|
|
251
|
+
* when the source `meta_extras` string has no matching `json_refs` row
|
|
252
|
+
* (parity with the text/url branches above).
|
|
253
|
+
* @param value - Raw `meta_extras` value from the source `pages` row.
|
|
254
|
+
* @param jsonIds - `JSON string → json_refs.id` map batch-resolved for
|
|
255
|
+
* the current chunk (see {@link ./resolve-json-refs.ts}).
|
|
256
|
+
* @param pageId - Owning page id (for the error message).
|
|
257
|
+
*/
|
|
258
|
+
function resolveOptionalJsonId(value, jsonIds, pageId) {
|
|
259
|
+
if (typeof value !== 'string' || value === '') {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const id = jsonIds.get(value);
|
|
263
|
+
if (id === undefined) {
|
|
264
|
+
throw new Error(`populatePageMeta: json_refs.id not resolved for page id=${pageId} column=meta_extras — populateJsonRefs must run first`);
|
|
265
|
+
}
|
|
266
|
+
return id;
|
|
267
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProgressCallback } from '../create-progress-reporter.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Populates `resource_items` from `resources` (issue #193).
|
|
5
|
+
*
|
|
6
|
+
* Structurally analogous to {@link ./populate-content-items.ts}: for each
|
|
7
|
+
* `resources` chunk, batch-resolve `url_refs.id`, `content_type_refs.id`,
|
|
8
|
+
* and `header_sets.id`, then bulk-INSERT with explicit `id = resources.id`.
|
|
9
|
+
* Reusing the legacy PK preserves the `resources-referrers.resourceId`
|
|
10
|
+
* FK reference (which `populateResourceRefEdges` later carries over as
|
|
11
|
+
* `resource_ref_edges.resource_id`) without any per-row UPDATE.
|
|
12
|
+
*
|
|
13
|
+
* `INSERT OR IGNORE` on `id` makes the step idempotent.
|
|
14
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
15
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
16
|
+
* ~5% of `resources` scanned); see {@link ../create-progress-reporter.ts}.
|
|
17
|
+
* @example
|
|
18
|
+
* await knex.transaction(async (trx) => {
|
|
19
|
+
* await populateResourceItems(trx);
|
|
20
|
+
* });
|
|
21
|
+
*/
|
|
22
|
+
export declare function populateResourceItems(trx: Knex, onProgress?: ProgressCallback): Promise<void>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createProgressReporter } from '../create-progress-reporter.js';
|
|
2
|
+
import { isBlobRefValue } from './is-blob-ref-value.js';
|
|
3
|
+
import { resolveBlobRefs } from './resolve-blob-refs.js';
|
|
4
|
+
import { loadContentTypeRefs } from './resolve-content-type-refs.js';
|
|
5
|
+
import { resolveHeaderSets } from './resolve-header-sets.js';
|
|
6
|
+
import { resolveUrlOrBlobFromMaps } from './resolve-url-or-blob-from-maps.js';
|
|
7
|
+
import { resolveUrlRefs } from './resolve-url-refs.js';
|
|
8
|
+
/**
|
|
9
|
+
* Rows scanned per keyset-paginated `SELECT` chunk against `resources`.
|
|
10
|
+
* `resources` rows are narrow (no meta columns) so 800 rows fits
|
|
11
|
+
* comfortably in a chunk without stressing memory. Bumping this higher
|
|
12
|
+
* would help throughput but risks pushing large `responseHeaders` JSON
|
|
13
|
+
* blobs past a healthy per-result-set size on archives with per-row
|
|
14
|
+
* multi-KB header payloads.
|
|
15
|
+
*/
|
|
16
|
+
const READ_CHUNK_SIZE = 800;
|
|
17
|
+
/**
|
|
18
|
+
* Rows sent per `INSERT INTO resource_items ... VALUES (...)` statement.
|
|
19
|
+
* Each row binds 11 params (id + 10 columns), so 500 rows = 5 500 params
|
|
20
|
+
* — well under SQLite's default variable limit.
|
|
21
|
+
*/
|
|
22
|
+
const INSERT_CHUNK_SIZE = 500;
|
|
23
|
+
/**
|
|
24
|
+
* Populates `resource_items` from `resources` (issue #193).
|
|
25
|
+
*
|
|
26
|
+
* Structurally analogous to {@link ./populate-content-items.ts}: for each
|
|
27
|
+
* `resources` chunk, batch-resolve `url_refs.id`, `content_type_refs.id`,
|
|
28
|
+
* and `header_sets.id`, then bulk-INSERT with explicit `id = resources.id`.
|
|
29
|
+
* Reusing the legacy PK preserves the `resources-referrers.resourceId`
|
|
30
|
+
* FK reference (which `populateResourceRefEdges` later carries over as
|
|
31
|
+
* `resource_ref_edges.resource_id`) without any per-row UPDATE.
|
|
32
|
+
*
|
|
33
|
+
* `INSERT OR IGNORE` on `id` makes the step idempotent.
|
|
34
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
35
|
+
* @param onProgress - Optional sink for periodic progress lines (one per
|
|
36
|
+
* ~5% of `resources` scanned); see {@link ../create-progress-reporter.ts}.
|
|
37
|
+
* @example
|
|
38
|
+
* await knex.transaction(async (trx) => {
|
|
39
|
+
* await populateResourceItems(trx);
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
export async function populateResourceItems(trx, onProgress) {
|
|
43
|
+
const contentTypeIds = await loadContentTypeRefs(trx);
|
|
44
|
+
const countRows = await trx('resources').count({ n: '*' });
|
|
45
|
+
const total = Number(countRows[0]?.n ?? 0);
|
|
46
|
+
const report = createProgressReporter('resource_items (resources)', total, onProgress);
|
|
47
|
+
let processed = 0;
|
|
48
|
+
let cursor = 0;
|
|
49
|
+
while (true) {
|
|
50
|
+
const rows = await trx('resources')
|
|
51
|
+
.select('id', 'url', 'isExternal', 'status', 'statusText', 'contentType', 'contentLength', 'responseHeaders', 'compress', 'cdn', 'source')
|
|
52
|
+
.where('id', '>', cursor)
|
|
53
|
+
.orderBy('id', 'asc')
|
|
54
|
+
.limit(READ_CHUNK_SIZE);
|
|
55
|
+
if (rows.length === 0) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
cursor = rows.at(-1).id;
|
|
59
|
+
processed += rows.length;
|
|
60
|
+
report(processed);
|
|
61
|
+
const urls = new Set();
|
|
62
|
+
const dataUris = new Set();
|
|
63
|
+
const headerJsonStrings = new Set();
|
|
64
|
+
for (const row of rows) {
|
|
65
|
+
if (isBlobRefValue(row.url)) {
|
|
66
|
+
dataUris.add(row.url);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
urls.add(row.url);
|
|
70
|
+
}
|
|
71
|
+
if (typeof row.responseHeaders === 'string' && row.responseHeaders !== '') {
|
|
72
|
+
headerJsonStrings.add(row.responseHeaders);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const urlIds = await resolveUrlRefs(trx, urls);
|
|
76
|
+
const blobIds = await resolveBlobRefs(trx, dataUris);
|
|
77
|
+
const headerSetIds = await resolveHeaderSets(trx, headerJsonStrings);
|
|
78
|
+
const inserts = rows.map((row) => {
|
|
79
|
+
const slot = resolveUrlOrBlobFromMaps(row.url, urlIds, blobIds);
|
|
80
|
+
if (slot.url === null && slot.blob === null) {
|
|
81
|
+
throw new Error(`populateResourceItems: neither url_refs.id nor blob_refs.id resolved for resource id=${row.id} url=${row.url.slice(0, 80)} — populateUrlRefs/populateBlobRefs must run first`);
|
|
82
|
+
}
|
|
83
|
+
let contentTypeId = null;
|
|
84
|
+
if (row.contentType != null && row.contentType !== '') {
|
|
85
|
+
const resolved = contentTypeIds.get(row.contentType);
|
|
86
|
+
if (resolved === undefined) {
|
|
87
|
+
throw new Error(`populateResourceItems: content_type_refs.id not resolved for resource id=${row.id} contentType=${row.contentType} — populateContentTypeRefs must run first`);
|
|
88
|
+
}
|
|
89
|
+
contentTypeId = resolved;
|
|
90
|
+
}
|
|
91
|
+
const headerSetId = typeof row.responseHeaders === 'string' && row.responseHeaders !== ''
|
|
92
|
+
? (headerSetIds.get(row.responseHeaders) ?? null)
|
|
93
|
+
: null;
|
|
94
|
+
return {
|
|
95
|
+
id: row.id,
|
|
96
|
+
url_id: slot.url,
|
|
97
|
+
url_blob_id: slot.blob,
|
|
98
|
+
is_external: row.isExternal == null ? 0 : row.isExternal ? 1 : 0,
|
|
99
|
+
status: row.status ?? null,
|
|
100
|
+
status_text: row.statusText ?? null,
|
|
101
|
+
content_type_id: contentTypeId,
|
|
102
|
+
content_length: row.contentLength ?? null,
|
|
103
|
+
header_set_id: headerSetId,
|
|
104
|
+
compress: row.compress ?? null,
|
|
105
|
+
cdn: row.cdn ?? null,
|
|
106
|
+
source: row.source,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
for (let index = 0; index < inserts.length; index += INSERT_CHUNK_SIZE) {
|
|
110
|
+
const chunk = inserts.slice(index, index + INSERT_CHUNK_SIZE);
|
|
111
|
+
await trx('resource_items').insert(chunk).onConflict('id').ignore();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Populates `resource_ref_edges` from `resources-referrers`
|
|
4
|
+
* (issue #193).
|
|
5
|
+
*
|
|
6
|
+
* The transformation is a **direct SQL `INSERT ... SELECT`** — no ref
|
|
7
|
+
* lookups, no per-row JS work. Legacy `resources-referrers` already
|
|
8
|
+
* enforces `UNIQUE(resourceId, pageId)` (see `init-schema.ts`), so every
|
|
9
|
+
* source row maps 1:1 to a distinct `resource_ref_edges` PK; every
|
|
10
|
+
* `count` starts at `1` because the legacy shape observed each
|
|
11
|
+
* (resource, page) pair exactly once.
|
|
12
|
+
*
|
|
13
|
+
* `INSERT OR IGNORE` on the `(resource_id, page_id)` PK is redundant
|
|
14
|
+
* with the legacy unique constraint on the source but present anyway to
|
|
15
|
+
* make partial-failure re-runs safe.
|
|
16
|
+
*
|
|
17
|
+
* The PKs on both `resource_items.id` and `content_items.id` were
|
|
18
|
+
* preserved from `resources.id` / `pages.id`, so no per-row translation
|
|
19
|
+
* is required — the FKs `resource_items(id)` and `content_items(id)`
|
|
20
|
+
* declared in `resource_ref_edges` are already satisfied for every
|
|
21
|
+
* source row by `populateContentItems` / `populateResourceItems`, which
|
|
22
|
+
* run before this function in `populateEntityTables`.
|
|
23
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
24
|
+
* @example
|
|
25
|
+
* await knex.transaction(async (trx) => {
|
|
26
|
+
* await populateResourceItems(trx);
|
|
27
|
+
* await populateContentItems(trx);
|
|
28
|
+
* await populateResourceRefEdges(trx);
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
export declare function populateResourceRefEdges(trx: Knex): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Populates `resource_ref_edges` from `resources-referrers`
|
|
3
|
+
* (issue #193).
|
|
4
|
+
*
|
|
5
|
+
* The transformation is a **direct SQL `INSERT ... SELECT`** — no ref
|
|
6
|
+
* lookups, no per-row JS work. Legacy `resources-referrers` already
|
|
7
|
+
* enforces `UNIQUE(resourceId, pageId)` (see `init-schema.ts`), so every
|
|
8
|
+
* source row maps 1:1 to a distinct `resource_ref_edges` PK; every
|
|
9
|
+
* `count` starts at `1` because the legacy shape observed each
|
|
10
|
+
* (resource, page) pair exactly once.
|
|
11
|
+
*
|
|
12
|
+
* `INSERT OR IGNORE` on the `(resource_id, page_id)` PK is redundant
|
|
13
|
+
* with the legacy unique constraint on the source but present anyway to
|
|
14
|
+
* make partial-failure re-runs safe.
|
|
15
|
+
*
|
|
16
|
+
* The PKs on both `resource_items.id` and `content_items.id` were
|
|
17
|
+
* preserved from `resources.id` / `pages.id`, so no per-row translation
|
|
18
|
+
* is required — the FKs `resource_items(id)` and `content_items(id)`
|
|
19
|
+
* declared in `resource_ref_edges` are already satisfied for every
|
|
20
|
+
* source row by `populateContentItems` / `populateResourceItems`, which
|
|
21
|
+
* run before this function in `populateEntityTables`.
|
|
22
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
23
|
+
* @example
|
|
24
|
+
* await knex.transaction(async (trx) => {
|
|
25
|
+
* await populateResourceItems(trx);
|
|
26
|
+
* await populateContentItems(trx);
|
|
27
|
+
* await populateResourceRefEdges(trx);
|
|
28
|
+
* });
|
|
29
|
+
*/
|
|
30
|
+
export async function populateResourceRefEdges(trx) {
|
|
31
|
+
await trx.raw(`INSERT OR IGNORE INTO resource_ref_edges (resource_id, page_id, count)
|
|
32
|
+
SELECT resourceId, pageId, 1 FROM "resources-referrers"`);
|
|
33
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BlobRefIdMap } from './types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Batch-resolves `blob_refs.id` for a set of large data-URI values
|
|
5
|
+
* (issue #193).
|
|
6
|
+
*
|
|
7
|
+
* The routing rule matches {@link ../populate-ref-tables/populate-blob-refs.ts}:
|
|
8
|
+
* only `data:` URIs longer than {@link DATA_URI_URL_REFS_LIMIT} bytes
|
|
9
|
+
* land in `blob_refs`; smaller data URIs (and every regular URL) live
|
|
10
|
+
* in `url_refs` instead. Callers pass URL-column values verbatim; this
|
|
11
|
+
* function filters out anything that does not match the routing rule
|
|
12
|
+
* before hashing.
|
|
13
|
+
*
|
|
14
|
+
* Malformed data URIs that fail {@link ../populate-ref-tables/decode-data-uri.ts}
|
|
15
|
+
* are skipped silently — `populateBlobRefs` also skipped them (with a
|
|
16
|
+
* warning), so no matching row exists to resolve.
|
|
17
|
+
*
|
|
18
|
+
* Duplicate values in `values` are deduped internally so an image whose
|
|
19
|
+
* `src` and `currentSrc` share the same data URI only counts once.
|
|
20
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
21
|
+
* @param values - Iterable of raw URL-column values that may or may not
|
|
22
|
+
* be large data URIs.
|
|
23
|
+
* @returns Map keyed by the raw URI string; missing entries indicate
|
|
24
|
+
* the value is not stored in `blob_refs` (either not a data URI at
|
|
25
|
+
* all, below the size threshold, malformed, or absent from the
|
|
26
|
+
* dictionary).
|
|
27
|
+
* @example
|
|
28
|
+
* const idMap = await resolveBlobRefs(trx, [longDataUri, 'https://…']);
|
|
29
|
+
* const blobId = idMap.get(longDataUri); // number | undefined
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveBlobRefs(trx: Knex, values: Iterable<string>): Promise<BlobRefIdMap>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { computeContentHash } from '../populate-ref-tables/compute-content-hash.js';
|
|
2
|
+
import { DATA_URI_URL_REFS_LIMIT } from '../populate-ref-tables/data-uri-url-refs-limit.js';
|
|
3
|
+
import { decodeDataUri } from '../populate-ref-tables/decode-data-uri.js';
|
|
4
|
+
/**
|
|
5
|
+
* Rows sent per `SELECT ... WHERE hash IN (?, ...)` chunk. `blob_refs`
|
|
6
|
+
* cardinality is tiny in the reference archive (≈ 429 rows) so 200 per
|
|
7
|
+
* chunk covers most realistic uses in a single query.
|
|
8
|
+
*/
|
|
9
|
+
const LOOKUP_CHUNK_SIZE = 200;
|
|
10
|
+
/**
|
|
11
|
+
* Batch-resolves `blob_refs.id` for a set of large data-URI values
|
|
12
|
+
* (issue #193).
|
|
13
|
+
*
|
|
14
|
+
* The routing rule matches {@link ../populate-ref-tables/populate-blob-refs.ts}:
|
|
15
|
+
* only `data:` URIs longer than {@link DATA_URI_URL_REFS_LIMIT} bytes
|
|
16
|
+
* land in `blob_refs`; smaller data URIs (and every regular URL) live
|
|
17
|
+
* in `url_refs` instead. Callers pass URL-column values verbatim; this
|
|
18
|
+
* function filters out anything that does not match the routing rule
|
|
19
|
+
* before hashing.
|
|
20
|
+
*
|
|
21
|
+
* Malformed data URIs that fail {@link ../populate-ref-tables/decode-data-uri.ts}
|
|
22
|
+
* are skipped silently — `populateBlobRefs` also skipped them (with a
|
|
23
|
+
* warning), so no matching row exists to resolve.
|
|
24
|
+
*
|
|
25
|
+
* Duplicate values in `values` are deduped internally so an image whose
|
|
26
|
+
* `src` and `currentSrc` share the same data URI only counts once.
|
|
27
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
28
|
+
* @param values - Iterable of raw URL-column values that may or may not
|
|
29
|
+
* be large data URIs.
|
|
30
|
+
* @returns Map keyed by the raw URI string; missing entries indicate
|
|
31
|
+
* the value is not stored in `blob_refs` (either not a data URI at
|
|
32
|
+
* all, below the size threshold, malformed, or absent from the
|
|
33
|
+
* dictionary).
|
|
34
|
+
* @example
|
|
35
|
+
* const idMap = await resolveBlobRefs(trx, [longDataUri, 'https://…']);
|
|
36
|
+
* const blobId = idMap.get(longDataUri); // number | undefined
|
|
37
|
+
*/
|
|
38
|
+
export async function resolveBlobRefs(trx, values) {
|
|
39
|
+
const distinct = new Set();
|
|
40
|
+
for (const value of values) {
|
|
41
|
+
if (typeof value !== 'string' || value === '') {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (value.length <= DATA_URI_URL_REFS_LIMIT || !value.startsWith('data:')) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
distinct.add(value);
|
|
48
|
+
}
|
|
49
|
+
if (distinct.size === 0) {
|
|
50
|
+
return new Map();
|
|
51
|
+
}
|
|
52
|
+
const rawValues = [...distinct];
|
|
53
|
+
const hashByValue = new Map();
|
|
54
|
+
// `hashHexToValues` maps `hex(hash) → [raw URI strings that decoded to
|
|
55
|
+
// those bytes]`. Two distinct raw data-URI strings can decode to the
|
|
56
|
+
// same payload bytes (e.g. `data:image/png;base64,XXXX…` and
|
|
57
|
+
// `data:image/svg+xml;base64,XXXX…` when the underlying base64 is
|
|
58
|
+
// identical). `populateBlobRefs` dedups them into one row keyed by the
|
|
59
|
+
// payload hash, so both raw URIs must resolve to that shared id — a
|
|
60
|
+
// last-wins Map keyed by hash would drop the earlier variant, and
|
|
61
|
+
// downstream `image_items` writes would lose the blob reference.
|
|
62
|
+
const hashHexToValues = new Map();
|
|
63
|
+
for (const value of rawValues) {
|
|
64
|
+
const decoded = decodeDataUri(value);
|
|
65
|
+
if (decoded === null) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const hash = computeContentHash(decoded.bytes);
|
|
69
|
+
hashByValue.set(value, hash);
|
|
70
|
+
const hex = hash.toString('hex');
|
|
71
|
+
const bucket = hashHexToValues.get(hex);
|
|
72
|
+
if (bucket === undefined) {
|
|
73
|
+
hashHexToValues.set(hex, [value]);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
bucket.push(value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (hashByValue.size === 0) {
|
|
80
|
+
return new Map();
|
|
81
|
+
}
|
|
82
|
+
const hashes = [...hashByValue.values()];
|
|
83
|
+
const result = new Map();
|
|
84
|
+
for (let index = 0; index < hashes.length; index += LOOKUP_CHUNK_SIZE) {
|
|
85
|
+
const chunkHashes = hashes.slice(index, index + LOOKUP_CHUNK_SIZE);
|
|
86
|
+
const rows = await trx('blob_refs')
|
|
87
|
+
.select('id', 'hash')
|
|
88
|
+
.whereIn('hash', chunkHashes);
|
|
89
|
+
for (const row of rows) {
|
|
90
|
+
const hex = Buffer.from(row.hash).toString('hex');
|
|
91
|
+
const values = hashHexToValues.get(hex);
|
|
92
|
+
if (values !== undefined) {
|
|
93
|
+
for (const value of values) {
|
|
94
|
+
result.set(value, row.id);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ContentTypeRefIdMap } from './types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Loads the entire `content_type_refs` dictionary into an in-process map
|
|
5
|
+
* keyed by the raw wire value (issue #193).
|
|
6
|
+
*
|
|
7
|
+
* Content-type cardinality is small in practice — the reference archive
|
|
8
|
+
* carries ≈ 400 distinct raw values across `pages` + `resources`, so
|
|
9
|
+
* loading everything into a `Map` costs ≈ 40 KB and eliminates every
|
|
10
|
+
* per-chunk round-trip. This is the same trade-off {@link
|
|
11
|
+
* ../populate-ref-tables/populate-header-tables.ts} makes for `header_name_refs`.
|
|
12
|
+
*
|
|
13
|
+
* Callers should invoke this once at the start of a 0.13 populate
|
|
14
|
+
* step (e.g. `populateContentItems`, `populateResourceItems`) and reuse
|
|
15
|
+
* the map for every chunk.
|
|
16
|
+
* @param trx - Knex instance or transaction connected to the archive DB.
|
|
17
|
+
* @returns Map keyed by `content_type_refs.raw`.
|
|
18
|
+
* @example
|
|
19
|
+
* const contentTypeIds = await loadContentTypeRefs(trx);
|
|
20
|
+
* const id = contentTypeIds.get('text/html; charset=utf-8'); // number | undefined
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadContentTypeRefs(trx: Knex): Promise<ContentTypeRefIdMap>;
|