@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,27 @@
|
|
|
1
|
+
import type { WriteRefCaches } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Empties every map in a {@link WriteRefCaches} bundle in place.
|
|
4
|
+
*
|
|
5
|
+
* Called when a write transaction that used the caches rolls back. SQLite
|
|
6
|
+
* `ROLLBACK` undoes every row the transaction inserted, but `AUTOINCREMENT`
|
|
7
|
+
* counters never rewind — so an id cached optimistically during the failed
|
|
8
|
+
* attempt would survive the rollback and no longer correspond to any row.
|
|
9
|
+
* A retried attempt reusing that id would issue `UPDATE ... WHERE id = ?`
|
|
10
|
+
* against a nonexistent row and silently affect zero rows instead of
|
|
11
|
+
* failing loudly.
|
|
12
|
+
*
|
|
13
|
+
* A full clear (rather than tracking which entries the failed transaction
|
|
14
|
+
* touched) is the simplest correct fix: transient write failures are rare
|
|
15
|
+
* (retry exists precisely because they are), so paying a one-time
|
|
16
|
+
* DB-again warm-up after a rollback is cheap next to the correctness risk
|
|
17
|
+
* of a partially-poisoned cache.
|
|
18
|
+
* @param caches - The cache bundle to clear.
|
|
19
|
+
* @example
|
|
20
|
+
* try {
|
|
21
|
+
* await knex.transaction(async (trx) => { ... });
|
|
22
|
+
* } catch (error) {
|
|
23
|
+
* clearWriteRefCaches(caches);
|
|
24
|
+
* throw error;
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
export declare function clearWriteRefCaches(caches: WriteRefCaches): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Empties every map in a {@link WriteRefCaches} bundle in place.
|
|
3
|
+
*
|
|
4
|
+
* Called when a write transaction that used the caches rolls back. SQLite
|
|
5
|
+
* `ROLLBACK` undoes every row the transaction inserted, but `AUTOINCREMENT`
|
|
6
|
+
* counters never rewind — so an id cached optimistically during the failed
|
|
7
|
+
* attempt would survive the rollback and no longer correspond to any row.
|
|
8
|
+
* A retried attempt reusing that id would issue `UPDATE ... WHERE id = ?`
|
|
9
|
+
* against a nonexistent row and silently affect zero rows instead of
|
|
10
|
+
* failing loudly.
|
|
11
|
+
*
|
|
12
|
+
* A full clear (rather than tracking which entries the failed transaction
|
|
13
|
+
* touched) is the simplest correct fix: transient write failures are rare
|
|
14
|
+
* (retry exists precisely because they are), so paying a one-time
|
|
15
|
+
* DB-again warm-up after a rollback is cheap next to the correctness risk
|
|
16
|
+
* of a partially-poisoned cache.
|
|
17
|
+
* @param caches - The cache bundle to clear.
|
|
18
|
+
* @example
|
|
19
|
+
* try {
|
|
20
|
+
* await knex.transaction(async (trx) => { ... });
|
|
21
|
+
* } catch (error) {
|
|
22
|
+
* clearWriteRefCaches(caches);
|
|
23
|
+
* throw error;
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
export function clearWriteRefCaches(caches) {
|
|
27
|
+
caches.urlIds.clear();
|
|
28
|
+
caches.contentItems.clear();
|
|
29
|
+
caches.resourceIds.clear();
|
|
30
|
+
caches.contentTypeIds.clear();
|
|
31
|
+
caches.jsonIds.clear();
|
|
32
|
+
caches.blobIds.clear();
|
|
33
|
+
caches.headers = null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WriteRefCaches } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates an empty {@link WriteRefCaches} bundle for one archive writer
|
|
4
|
+
* connection.
|
|
5
|
+
*
|
|
6
|
+
* All maps start empty and fill lazily as the write path touches URLs,
|
|
7
|
+
* content types, JSON payloads, and data-URI blobs. The header caches
|
|
8
|
+
* (`headers`) start as `null` and are warmed from the DB by the first
|
|
9
|
+
* header-set upsert — see {@link ./types.ts} for the cache-correctness
|
|
10
|
+
* argument (append-only ref tables + single writer connection).
|
|
11
|
+
* @returns A fresh cache bundle. One per `Database` instance; never share
|
|
12
|
+
* across connections.
|
|
13
|
+
* @example
|
|
14
|
+
* const caches = createWriteRefCaches();
|
|
15
|
+
* const urlId = await upsertUrlRef(knex, caches, 'https://example.com/');
|
|
16
|
+
*/
|
|
17
|
+
export declare function createWriteRefCaches(): WriteRefCaches;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an empty {@link WriteRefCaches} bundle for one archive writer
|
|
3
|
+
* connection.
|
|
4
|
+
*
|
|
5
|
+
* All maps start empty and fill lazily as the write path touches URLs,
|
|
6
|
+
* content types, JSON payloads, and data-URI blobs. The header caches
|
|
7
|
+
* (`headers`) start as `null` and are warmed from the DB by the first
|
|
8
|
+
* header-set upsert — see {@link ./types.ts} for the cache-correctness
|
|
9
|
+
* argument (append-only ref tables + single writer connection).
|
|
10
|
+
* @returns A fresh cache bundle. One per `Database` instance; never share
|
|
11
|
+
* across connections.
|
|
12
|
+
* @example
|
|
13
|
+
* const caches = createWriteRefCaches();
|
|
14
|
+
* const urlId = await upsertUrlRef(knex, caches, 'https://example.com/');
|
|
15
|
+
*/
|
|
16
|
+
export function createWriteRefCaches() {
|
|
17
|
+
return {
|
|
18
|
+
urlIds: new Map(),
|
|
19
|
+
contentItems: new Map(),
|
|
20
|
+
resourceIds: new Map(),
|
|
21
|
+
contentTypeIds: new Map(),
|
|
22
|
+
jsonIds: new Map(),
|
|
23
|
+
blobIds: new Map(),
|
|
24
|
+
headers: null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes one `json_refs` payload back into its JSON string.
|
|
3
|
+
*
|
|
4
|
+
* The write path (`upsert-json-ref.ts`) and the archive-migration
|
|
5
|
+
* populate (`populate-json-refs.ts`) both store bodies zstd-compressed;
|
|
6
|
+
* the `'none'` codec is accepted because the `json_refs.codec` column
|
|
7
|
+
* permits it, not because either writer currently produces it. Corrupt
|
|
8
|
+
* bodies fail closed to `null` rather than throwing — readers treat an
|
|
9
|
+
* undecodable payload the same as an absent one.
|
|
10
|
+
* @param body - The raw `json_refs.json_text` body, or null when absent.
|
|
11
|
+
* @param codec - The `json_refs.codec` value, or null when absent.
|
|
12
|
+
* @returns The decoded JSON string, or null when there is no body or the
|
|
13
|
+
* body cannot be decoded.
|
|
14
|
+
* @example
|
|
15
|
+
* const metaExtras = decodeJsonRef(row.extras_body, row.extras_codec);
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeJsonRef(body: Buffer | string | null, codec: 'zstd' | 'none' | null): string | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { zstdDecompressSync } from 'node:zlib';
|
|
2
|
+
/**
|
|
3
|
+
* Decodes one `json_refs` payload back into its JSON string.
|
|
4
|
+
*
|
|
5
|
+
* The write path (`upsert-json-ref.ts`) and the archive-migration
|
|
6
|
+
* populate (`populate-json-refs.ts`) both store bodies zstd-compressed;
|
|
7
|
+
* the `'none'` codec is accepted because the `json_refs.codec` column
|
|
8
|
+
* permits it, not because either writer currently produces it. Corrupt
|
|
9
|
+
* bodies fail closed to `null` rather than throwing — readers treat an
|
|
10
|
+
* undecodable payload the same as an absent one.
|
|
11
|
+
* @param body - The raw `json_refs.json_text` body, or null when absent.
|
|
12
|
+
* @param codec - The `json_refs.codec` value, or null when absent.
|
|
13
|
+
* @returns The decoded JSON string, or null when there is no body or the
|
|
14
|
+
* body cannot be decoded.
|
|
15
|
+
* @example
|
|
16
|
+
* const metaExtras = decodeJsonRef(row.extras_body, row.extras_codec);
|
|
17
|
+
*/
|
|
18
|
+
export function decodeJsonRef(body, codec) {
|
|
19
|
+
if (body == null) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
if (codec === 'zstd') {
|
|
24
|
+
return zstdDecompressSync(body).toString('utf8');
|
|
25
|
+
}
|
|
26
|
+
return typeof body === 'string' ? body : body.toString('utf8');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Loads and merges the response headers for every given `header_sets.id`
|
|
4
|
+
* back into flat `Record<name, value>` objects, in one chunked batch
|
|
5
|
+
* (never one query per row).
|
|
6
|
+
*
|
|
7
|
+
* Multi-value headers (repeated `Set-Cookie` etc.) are joined with
|
|
8
|
+
* `', '` in `occurrence` order within each name — the flat-record shape
|
|
9
|
+
* the pre-0.13 `responseHeaders` JSON column stored, which every reader
|
|
10
|
+
* of the reconstructed rows still expects.
|
|
11
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
12
|
+
* @param headerSetIds - Distinct `header_sets.id` values to load. Empty
|
|
13
|
+
* input returns an empty map without touching the DB.
|
|
14
|
+
* @returns Map from `header_sets.id` to the merged header record. Ids
|
|
15
|
+
* with no entries are absent from the map.
|
|
16
|
+
* @example
|
|
17
|
+
* const headersBySetId = await loadResponseHeadersBySetIds(knex, [1, 2]);
|
|
18
|
+
* const record = headersBySetId.get(1) ?? {};
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadResponseHeadersBySetIds(knex: Knex, headerSetIds: readonly number[]): Promise<Map<number, Record<string, string>>>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ids per `WHERE header_set_id IN (?, ...)` chunk. Modern SQLite builds
|
|
3
|
+
* (including the vendored libsql) reject statements binding more than
|
|
4
|
+
* 32,766 parameters, and header sets dedupe poorly on real sites (Date /
|
|
5
|
+
* ETag / Set-Cookie make most sets unique), so an IN-list sized by the
|
|
6
|
+
* page batch without chunking would exceed the limit on large archives.
|
|
7
|
+
* 800 matches
|
|
8
|
+
* the `LOOKUP_CHUNK_SIZE` convention the populate/upsert paths already
|
|
9
|
+
* use (`resolve-text-refs.ts` and siblings).
|
|
10
|
+
*/
|
|
11
|
+
const LOOKUP_CHUNK_SIZE = 800;
|
|
12
|
+
/**
|
|
13
|
+
* Loads and merges the response headers for every given `header_sets.id`
|
|
14
|
+
* back into flat `Record<name, value>` objects, in one chunked batch
|
|
15
|
+
* (never one query per row).
|
|
16
|
+
*
|
|
17
|
+
* Multi-value headers (repeated `Set-Cookie` etc.) are joined with
|
|
18
|
+
* `', '` in `occurrence` order within each name — the flat-record shape
|
|
19
|
+
* the pre-0.13 `responseHeaders` JSON column stored, which every reader
|
|
20
|
+
* of the reconstructed rows still expects.
|
|
21
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
22
|
+
* @param headerSetIds - Distinct `header_sets.id` values to load. Empty
|
|
23
|
+
* input returns an empty map without touching the DB.
|
|
24
|
+
* @returns Map from `header_sets.id` to the merged header record. Ids
|
|
25
|
+
* with no entries are absent from the map.
|
|
26
|
+
* @example
|
|
27
|
+
* const headersBySetId = await loadResponseHeadersBySetIds(knex, [1, 2]);
|
|
28
|
+
* const record = headersBySetId.get(1) ?? {};
|
|
29
|
+
*/
|
|
30
|
+
export async function loadResponseHeadersBySetIds(knex, headerSetIds) {
|
|
31
|
+
const merged = new Map();
|
|
32
|
+
for (let index = 0; index < headerSetIds.length; index += LOOKUP_CHUNK_SIZE) {
|
|
33
|
+
const chunk = headerSetIds.slice(index, index + LOOKUP_CHUNK_SIZE);
|
|
34
|
+
const headerRows = (await knex('header_set_entries as hse')
|
|
35
|
+
.join('header_name_refs as hnr', 'hnr.id', 'hse.name_id')
|
|
36
|
+
.join('header_value_refs as hvr', 'hvr.id', 'hse.value_id')
|
|
37
|
+
.whereIn('hse.header_set_id', chunk)
|
|
38
|
+
.orderBy(['hse.header_set_id', 'hnr.name', 'hse.occurrence'])
|
|
39
|
+
.select('hse.header_set_id as headerSetId', 'hnr.name as name', 'hvr.value as value'));
|
|
40
|
+
for (const row of headerRows) {
|
|
41
|
+
const bySet = merged.get(row.headerSetId) ?? new Map();
|
|
42
|
+
const values = bySet.get(row.name) ?? [];
|
|
43
|
+
values.push(row.value);
|
|
44
|
+
bySet.set(row.name, values);
|
|
45
|
+
merged.set(row.headerSetId, bySet);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const result = new Map();
|
|
49
|
+
for (const [setId, bySet] of merged) {
|
|
50
|
+
result.set(setId, Object.fromEntries([...bySet.entries()].map(([k, v]) => [k, v.join(', ')])));
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { WriteRefCaches } from './types.js';
|
|
2
|
+
import type { PageSource } from '../../types.js';
|
|
3
|
+
import type { Knex } from 'knex';
|
|
4
|
+
/**
|
|
5
|
+
* Returns the `content_items.id` for `url`, inserting a placeholder row
|
|
6
|
+
* (via `url_refs`) when the URL is not yet known. Foundational identity
|
|
7
|
+
* primitive shared by every op that needs to record an edge or lookup to
|
|
8
|
+
* a URL — the `content_items` successor of the legacy `pages.id`
|
|
9
|
+
* resolver.
|
|
10
|
+
*
|
|
11
|
+
* **Source semantics.** `source` is written ONLY on the INSERT path —
|
|
12
|
+
* when the row already exists, the INSERT is never reached and the
|
|
13
|
+
* existing row's `source` stays untouched. This is what keeps a second
|
|
14
|
+
* `crawl --inventory` from "demoting" a page that was first labelled
|
|
15
|
+
* `'inventory-seed'` back to `'inventory-discovered'` on later passes.
|
|
16
|
+
*
|
|
17
|
+
* **Crawled-wins downgrade.** When a row that was previously labelled
|
|
18
|
+
* `'inventory-seed'` or `'inventory-discovered'` is re-encountered via a
|
|
19
|
+
* `'crawled'`-lineage anchor (the parent page is part of the graph
|
|
20
|
+
* reachable from the original crawl roots), the row is downgraded to
|
|
21
|
+
* `'crawled'`. The inventory goal is finding orphans — anything reachable
|
|
22
|
+
* from the crawled chain is NOT an orphan and should not retain an
|
|
23
|
+
* inventory label. The cached `source` is updated in the same step so a
|
|
24
|
+
* later hit does not re-issue the UPDATE.
|
|
25
|
+
*
|
|
26
|
+
* **Cache poisoning on rollback.** Entries cached inside a transaction
|
|
27
|
+
* that later rolls back would point at ids that no longer exist. Every
|
|
28
|
+
* write op that opens a multi-statement transaction around this function
|
|
29
|
+
* must discard the whole cache bundle when that transaction fails,
|
|
30
|
+
* before any retry — see the try/catch + `clearWriteRefCaches` wrappers
|
|
31
|
+
* in `update-page.ts` and `record-redirect.ts`.
|
|
32
|
+
*
|
|
33
|
+
* **Race-condition safety.** The INSERT uses `ON CONFLICT(url_id) DO
|
|
34
|
+
* UPDATE SET url_id = url_id RETURNING id, source` — the same no-op
|
|
35
|
+
* -update idiom as `upsertUrlRef` — so when a concurrent transaction
|
|
36
|
+
* wins the insert race, the statement still returns the existing row's
|
|
37
|
+
* id and source in one round trip. `onConflict().ignore()` without
|
|
38
|
+
* `RETURNING` must NOT be used here: knex's sqlite-family dialects
|
|
39
|
+
* report the connection's stale `lastInsertRowid` for a conflict-ignored
|
|
40
|
+
* insert, which reads as a valid id belonging to an unrelated row.
|
|
41
|
+
* @param qb - Knex instance OR a transaction, used verbatim for every
|
|
42
|
+
* statement this function issues. Pass the `trx` when running inside a
|
|
43
|
+
* transaction so reads see uncommitted writes from that transaction.
|
|
44
|
+
* @param caches - The connection's write-side id caches; mutated in place.
|
|
45
|
+
* @param url - The URL to look up or insert (normalised
|
|
46
|
+
* `withoutHashAndAuth` form, matching the legacy identity contract).
|
|
47
|
+
* @param isExternal - Optional; recorded on new inserts only. `1` marks
|
|
48
|
+
* the row as an external URL that will never be scraped as a target.
|
|
49
|
+
* Defaults to `0` (in-scope) on insert, mirroring the legacy column
|
|
50
|
+
* default.
|
|
51
|
+
* @param source - Optional provenance label put on a newly-inserted row.
|
|
52
|
+
* Omit to let the `content_items.source` DEFAULT (`'crawled'`) apply.
|
|
53
|
+
* Pass `'crawled'` to arm the crawled-wins downgrade on existing
|
|
54
|
+
* inventory-labelled rows.
|
|
55
|
+
* @returns The `content_items.id` of the existing or newly inserted row.
|
|
56
|
+
* @throws {Error} When the upsert's `RETURNING` yields no row — should
|
|
57
|
+
* not happen, so it surfaces as a hard error.
|
|
58
|
+
* @example
|
|
59
|
+
* const pageId = await resolveContentItemId(trx, caches, anchor.href, 1, 'crawled');
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveContentItemId(qb: Knex | Knex.Transaction, caches: WriteRefCaches, url: string, isExternal?: 0 | 1, source?: PageSource): Promise<number>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { upsertUrlRef } from './upsert-url-ref.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the `content_items.id` for `url`, inserting a placeholder row
|
|
4
|
+
* (via `url_refs`) when the URL is not yet known. Foundational identity
|
|
5
|
+
* primitive shared by every op that needs to record an edge or lookup to
|
|
6
|
+
* a URL — the `content_items` successor of the legacy `pages.id`
|
|
7
|
+
* resolver.
|
|
8
|
+
*
|
|
9
|
+
* **Source semantics.** `source` is written ONLY on the INSERT path —
|
|
10
|
+
* when the row already exists, the INSERT is never reached and the
|
|
11
|
+
* existing row's `source` stays untouched. This is what keeps a second
|
|
12
|
+
* `crawl --inventory` from "demoting" a page that was first labelled
|
|
13
|
+
* `'inventory-seed'` back to `'inventory-discovered'` on later passes.
|
|
14
|
+
*
|
|
15
|
+
* **Crawled-wins downgrade.** When a row that was previously labelled
|
|
16
|
+
* `'inventory-seed'` or `'inventory-discovered'` is re-encountered via a
|
|
17
|
+
* `'crawled'`-lineage anchor (the parent page is part of the graph
|
|
18
|
+
* reachable from the original crawl roots), the row is downgraded to
|
|
19
|
+
* `'crawled'`. The inventory goal is finding orphans — anything reachable
|
|
20
|
+
* from the crawled chain is NOT an orphan and should not retain an
|
|
21
|
+
* inventory label. The cached `source` is updated in the same step so a
|
|
22
|
+
* later hit does not re-issue the UPDATE.
|
|
23
|
+
*
|
|
24
|
+
* **Cache poisoning on rollback.** Entries cached inside a transaction
|
|
25
|
+
* that later rolls back would point at ids that no longer exist. Every
|
|
26
|
+
* write op that opens a multi-statement transaction around this function
|
|
27
|
+
* must discard the whole cache bundle when that transaction fails,
|
|
28
|
+
* before any retry — see the try/catch + `clearWriteRefCaches` wrappers
|
|
29
|
+
* in `update-page.ts` and `record-redirect.ts`.
|
|
30
|
+
*
|
|
31
|
+
* **Race-condition safety.** The INSERT uses `ON CONFLICT(url_id) DO
|
|
32
|
+
* UPDATE SET url_id = url_id RETURNING id, source` — the same no-op
|
|
33
|
+
* -update idiom as `upsertUrlRef` — so when a concurrent transaction
|
|
34
|
+
* wins the insert race, the statement still returns the existing row's
|
|
35
|
+
* id and source in one round trip. `onConflict().ignore()` without
|
|
36
|
+
* `RETURNING` must NOT be used here: knex's sqlite-family dialects
|
|
37
|
+
* report the connection's stale `lastInsertRowid` for a conflict-ignored
|
|
38
|
+
* insert, which reads as a valid id belonging to an unrelated row.
|
|
39
|
+
* @param qb - Knex instance OR a transaction, used verbatim for every
|
|
40
|
+
* statement this function issues. Pass the `trx` when running inside a
|
|
41
|
+
* transaction so reads see uncommitted writes from that transaction.
|
|
42
|
+
* @param caches - The connection's write-side id caches; mutated in place.
|
|
43
|
+
* @param url - The URL to look up or insert (normalised
|
|
44
|
+
* `withoutHashAndAuth` form, matching the legacy identity contract).
|
|
45
|
+
* @param isExternal - Optional; recorded on new inserts only. `1` marks
|
|
46
|
+
* the row as an external URL that will never be scraped as a target.
|
|
47
|
+
* Defaults to `0` (in-scope) on insert, mirroring the legacy column
|
|
48
|
+
* default.
|
|
49
|
+
* @param source - Optional provenance label put on a newly-inserted row.
|
|
50
|
+
* Omit to let the `content_items.source` DEFAULT (`'crawled'`) apply.
|
|
51
|
+
* Pass `'crawled'` to arm the crawled-wins downgrade on existing
|
|
52
|
+
* inventory-labelled rows.
|
|
53
|
+
* @returns The `content_items.id` of the existing or newly inserted row.
|
|
54
|
+
* @throws {Error} When the upsert's `RETURNING` yields no row — should
|
|
55
|
+
* not happen, so it surfaces as a hard error.
|
|
56
|
+
* @example
|
|
57
|
+
* const pageId = await resolveContentItemId(trx, caches, anchor.href, 1, 'crawled');
|
|
58
|
+
*/
|
|
59
|
+
export async function resolveContentItemId(qb, caches, url, isExternal, source) {
|
|
60
|
+
const cached = caches.contentItems.get(url);
|
|
61
|
+
if (cached !== undefined) {
|
|
62
|
+
await applyCrawledWinsDowngrade(qb, cached, source);
|
|
63
|
+
return cached.id;
|
|
64
|
+
}
|
|
65
|
+
const urlId = await upsertUrlRef(qb, caches, url);
|
|
66
|
+
const [record] = (await qb
|
|
67
|
+
.select('id', 'source')
|
|
68
|
+
.from('content_items')
|
|
69
|
+
.where('url_id', urlId));
|
|
70
|
+
if (record !== undefined) {
|
|
71
|
+
const entry = { id: record.id, source: record.source };
|
|
72
|
+
await applyCrawledWinsDowngrade(qb, entry, source);
|
|
73
|
+
caches.contentItems.set(url, entry);
|
|
74
|
+
return entry.id;
|
|
75
|
+
}
|
|
76
|
+
const insertedRows = await qb.raw(`INSERT INTO content_items (url_id, scraped, is_target, is_external${source === undefined ? '' : ', source'})
|
|
77
|
+
VALUES (?, 0, 0, ?${source === undefined ? '' : ', ?'})
|
|
78
|
+
ON CONFLICT(url_id) DO UPDATE SET url_id = url_id
|
|
79
|
+
RETURNING id, source`, source === undefined ? [urlId, isExternal ?? 0] : [urlId, isExternal ?? 0, source]);
|
|
80
|
+
const inserted = insertedRows[0];
|
|
81
|
+
if (inserted === undefined) {
|
|
82
|
+
throw new Error(`Failed to insert a new content item: ${url}`);
|
|
83
|
+
}
|
|
84
|
+
const insertedEntry = {
|
|
85
|
+
id: inserted.id,
|
|
86
|
+
source: inserted.source,
|
|
87
|
+
};
|
|
88
|
+
// A conflict means a concurrent writer created the row between this
|
|
89
|
+
// function's SELECT miss and the INSERT — the returned `source` is that
|
|
90
|
+
// row's value, so the downgrade must be evaluated exactly as on the
|
|
91
|
+
// SELECT-hit path.
|
|
92
|
+
await applyCrawledWinsDowngrade(qb, insertedEntry, source);
|
|
93
|
+
caches.contentItems.set(url, insertedEntry);
|
|
94
|
+
return insertedEntry.id;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Fires the crawled-wins downgrade when a `'crawled'`-lineage resolution
|
|
98
|
+
* lands on a row whose last-known `source` is an inventory label, and
|
|
99
|
+
* keeps the cache entry in sync so the UPDATE runs at most once per
|
|
100
|
+
* (connection, row).
|
|
101
|
+
* @param qb - Knex instance or transaction.
|
|
102
|
+
* @param entry - The cached identity to check and mutate.
|
|
103
|
+
* @param source - The resolution's lineage label.
|
|
104
|
+
*/
|
|
105
|
+
async function applyCrawledWinsDowngrade(qb, entry, source) {
|
|
106
|
+
if (source !== 'crawled' || entry.source === 'crawled') {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
await qb('content_items').where('id', entry.id).update({ source: 'crawled' });
|
|
110
|
+
entry.source = 'crawled';
|
|
111
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { WriteRefCaches } from './types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Routes one URL-shaped value to either `url_refs` or `blob_refs` per the
|
|
5
|
+
* data-URI threshold rule (large data URIs live in `blob_refs`, everything
|
|
6
|
+
* else in `url_refs`), upserting into whichever dictionary applies.
|
|
7
|
+
*
|
|
8
|
+
* Shared by every writer that can receive a `data:` URI in a URL-shaped
|
|
9
|
+
* column: `image_items.src` / `current_src` and `resource_items`' own
|
|
10
|
+
* identity URL. At most one of `url` / `blob` is non-null; both are `null`
|
|
11
|
+
* when the value is empty or a malformed data URI that fails to decode.
|
|
12
|
+
* @param qb - Knex instance or transaction connected to the archive DB.
|
|
13
|
+
* @param caches - The connection's write-side id caches.
|
|
14
|
+
* @param value - Raw URL-shaped value.
|
|
15
|
+
* @returns `{ url, blob }` pair with at most one non-null field.
|
|
16
|
+
* @example
|
|
17
|
+
* const slot = await resolveUrlOrBlob(trx, caches, image.src);
|
|
18
|
+
* // slot.url set for a regular URL, slot.blob set for a large data: URI.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveUrlOrBlob(qb: Knex | Knex.Transaction, caches: WriteRefCaches, value: string | null | undefined): Promise<{
|
|
21
|
+
url: number | null;
|
|
22
|
+
blob: number | null;
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DATA_URI_URL_REFS_LIMIT } from '../../populate-ref-tables/data-uri-url-refs-limit.js';
|
|
2
|
+
import { upsertBlobRef } from './upsert-blob-ref.js';
|
|
3
|
+
import { upsertUrlRef } from './upsert-url-ref.js';
|
|
4
|
+
/**
|
|
5
|
+
* Routes one URL-shaped value to either `url_refs` or `blob_refs` per the
|
|
6
|
+
* data-URI threshold rule (large data URIs live in `blob_refs`, everything
|
|
7
|
+
* else in `url_refs`), upserting into whichever dictionary applies.
|
|
8
|
+
*
|
|
9
|
+
* Shared by every writer that can receive a `data:` URI in a URL-shaped
|
|
10
|
+
* column: `image_items.src` / `current_src` and `resource_items`' own
|
|
11
|
+
* identity URL. At most one of `url` / `blob` is non-null; both are `null`
|
|
12
|
+
* when the value is empty or a malformed data URI that fails to decode.
|
|
13
|
+
* @param qb - Knex instance or transaction connected to the archive DB.
|
|
14
|
+
* @param caches - The connection's write-side id caches.
|
|
15
|
+
* @param value - Raw URL-shaped value.
|
|
16
|
+
* @returns `{ url, blob }` pair with at most one non-null field.
|
|
17
|
+
* @example
|
|
18
|
+
* const slot = await resolveUrlOrBlob(trx, caches, image.src);
|
|
19
|
+
* // slot.url set for a regular URL, slot.blob set for a large data: URI.
|
|
20
|
+
*/
|
|
21
|
+
export async function resolveUrlOrBlob(qb, caches, value) {
|
|
22
|
+
if (typeof value !== 'string' || value === '') {
|
|
23
|
+
return { url: null, blob: null };
|
|
24
|
+
}
|
|
25
|
+
if (value.startsWith('data:') && value.length > DATA_URI_URL_REFS_LIMIT) {
|
|
26
|
+
return { url: null, blob: await upsertBlobRef(qb, caches, value) };
|
|
27
|
+
}
|
|
28
|
+
return { url: await upsertUrlRef(qb, caches, value), blob: null };
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RetryCallOptions } from '@d-zero/shared/retry';
|
|
2
|
+
/**
|
|
3
|
+
* Default retry options shared by every `Database.*` op that wraps its
|
|
4
|
+
* knex work in {@link ../../../utils/error/emit-error-with-retry.ts}
|
|
5
|
+
* (`emitErrorAndRetry`) or {@link ../../../utils/error/emit-error.ts}
|
|
6
|
+
* (`emitError`, no retry). Three retries at 300 ms intervals covers the
|
|
7
|
+
* transient libsql failure modes (WAL contention, brief lock waits)
|
|
8
|
+
* without letting a genuinely broken query hang the crawler for long.
|
|
9
|
+
*
|
|
10
|
+
* Two ops deliberately do NOT use these settings and instead wrap
|
|
11
|
+
* `retryCall` themselves so they can override `label` (`getResourceByUrl`)
|
|
12
|
+
* or opt out entirely (`checkpoint`, `destroy`, `getKnex`,
|
|
13
|
+
* `setUrlOrder`, the `constructor`, and `Database.connect`). See the
|
|
14
|
+
* corresponding op files for the reasoning at each site.
|
|
15
|
+
*/
|
|
16
|
+
export declare const retrySetting: RetryCallOptions;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default retry options shared by every `Database.*` op that wraps its
|
|
3
|
+
* knex work in {@link ../../../utils/error/emit-error-with-retry.ts}
|
|
4
|
+
* (`emitErrorAndRetry`) or {@link ../../../utils/error/emit-error.ts}
|
|
5
|
+
* (`emitError`, no retry). Three retries at 300 ms intervals covers the
|
|
6
|
+
* transient libsql failure modes (WAL contention, brief lock waits)
|
|
7
|
+
* without letting a genuinely broken query hang the crawler for long.
|
|
8
|
+
*
|
|
9
|
+
* Two ops deliberately do NOT use these settings and instead wrap
|
|
10
|
+
* `retryCall` themselves so they can override `label` (`getResourceByUrl`)
|
|
11
|
+
* or opt out entirely (`checkpoint`, `destroy`, `getKnex`,
|
|
12
|
+
* `setUrlOrder`, the `constructor`, and `Database.connect`). See the
|
|
13
|
+
* corresponding op files for the reasoning at each site.
|
|
14
|
+
*/
|
|
15
|
+
export const retrySetting = {
|
|
16
|
+
interval: 300,
|
|
17
|
+
retries: 3,
|
|
18
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a JSON column value, returning `null` on parse failure rather
|
|
3
|
+
* than throwing. JSON columns in `page_jsonld` (`parsed`) and `page_tags`
|
|
4
|
+
* (`categories`, `sources`) are written by `JSON.stringify` and round-trip
|
|
5
|
+
* cleanly under normal conditions; a hand-edited archive that has
|
|
6
|
+
* malformed JSON in those columns should degrade gracefully rather than
|
|
7
|
+
* propagate a parse error up to the consumer.
|
|
8
|
+
* @param value - JSON-encoded text.
|
|
9
|
+
* @returns Parsed value, or `null` if the input could not be parsed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function safeParseJson(value: string): unknown;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a JSON column value, returning `null` on parse failure rather
|
|
3
|
+
* than throwing. JSON columns in `page_jsonld` (`parsed`) and `page_tags`
|
|
4
|
+
* (`categories`, `sources`) are written by `JSON.stringify` and round-trip
|
|
5
|
+
* cleanly under normal conditions; a hand-edited archive that has
|
|
6
|
+
* malformed JSON in those columns should degrade gracefully rather than
|
|
7
|
+
* propagate a parse error up to the consumer.
|
|
8
|
+
* @param value - JSON-encoded text.
|
|
9
|
+
* @returns Parsed value, or `null` if the input could not be parsed.
|
|
10
|
+
*/
|
|
11
|
+
export function safeParseJson(value) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(value);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { HeaderTableCaches } from '../../populate-ref-tables/types.js';
|
|
2
|
+
import type { PageSource } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* One cached `content_items` identity: the row id plus the provenance
|
|
5
|
+
* label at the time it was last read or written through the cache. The
|
|
6
|
+
* `source` copy lets {@link ../_shared/resolve-content-item-id.ts} apply
|
|
7
|
+
* the crawled-wins downgrade without re-reading the row on every anchor
|
|
8
|
+
* hit.
|
|
9
|
+
*/
|
|
10
|
+
export interface ContentItemCacheEntry {
|
|
11
|
+
/** `content_items.id`. */
|
|
12
|
+
id: number;
|
|
13
|
+
/** `content_items.source` as last observed / written by this process. */
|
|
14
|
+
source: PageSource;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* In-process id caches shared across one archive connection's write path.
|
|
18
|
+
*
|
|
19
|
+
* Every ref-table upsert primitive under `db-ops/_shared/` consults these
|
|
20
|
+
* maps before touching SQL. The caches are correct for the lifetime of a
|
|
21
|
+
* single writer connection because ref rows are append-only (`url_refs`,
|
|
22
|
+
* `text_refs`, `json_refs`, `blob_refs`, `content_type_refs`, header
|
|
23
|
+
* dictionaries never delete rows) and `content_items` identities
|
|
24
|
+
* (`id`, `url_id`) are never reassigned — only the cached `source` can
|
|
25
|
+
* change, and the sole writer of that column is the cache-aware downgrade
|
|
26
|
+
* in `resolve-content-item-id.ts` itself.
|
|
27
|
+
*
|
|
28
|
+
* NOT safe to share across processes or across two concurrent writer
|
|
29
|
+
* connections — the crawler holds a single writer connection per archive
|
|
30
|
+
* (enforced by the archive lock), so no such sharing exists.
|
|
31
|
+
*/
|
|
32
|
+
export interface WriteRefCaches {
|
|
33
|
+
/** `url string → url_refs.id`. */
|
|
34
|
+
readonly urlIds: Map<string, number>;
|
|
35
|
+
/** `url string → content_items` identity (id + last-known source). */
|
|
36
|
+
readonly contentItems: Map<string, ContentItemCacheEntry>;
|
|
37
|
+
/** `url string → resource_items.id`. */
|
|
38
|
+
readonly resourceIds: Map<string, number>;
|
|
39
|
+
/** `normalized content-type raw value → content_type_refs.id`. */
|
|
40
|
+
readonly contentTypeIds: Map<string, number>;
|
|
41
|
+
/** `hex(content hash) → json_refs.id`. */
|
|
42
|
+
readonly jsonIds: Map<string, number>;
|
|
43
|
+
/** `hex(content hash) → blob_refs.id`. */
|
|
44
|
+
readonly blobIds: Map<string, number>;
|
|
45
|
+
/**
|
|
46
|
+
* Header dictionary caches (`header_name_refs` / `header_value_refs` /
|
|
47
|
+
* `header_sets` id maps). `null` until the first header-set upsert
|
|
48
|
+
* warms them from the DB — warming issues three SELECTs, which a
|
|
49
|
+
* crawl that never stores headers (e.g. list-mode with no responses)
|
|
50
|
+
* should not pay for.
|
|
51
|
+
*/
|
|
52
|
+
headers: HeaderTableCaches | null;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WriteRefCaches } from './types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the `blob_refs.id` for one large `data:` URI, decoding and
|
|
5
|
+
* inserting the deduplicated (hash-keyed) payload row when it is new.
|
|
6
|
+
*
|
|
7
|
+
* Callers route values here per the data-URI threshold rule (`data:` URIs
|
|
8
|
+
* longer than `DATA_URI_URL_REFS_LIMIT` — everything else belongs in
|
|
9
|
+
* `url_refs`). The payload bytes are decoded from the base64 /
|
|
10
|
+
* percent-encoded tail, hashed, and stored zstd-compressed — the same
|
|
11
|
+
* storage contract as the rows produced when migrating an existing
|
|
12
|
+
* archive (`populate-ref-tables/populate-blob-refs.ts`).
|
|
13
|
+
*
|
|
14
|
+
* Returns `null` when the data URI fails to decode (malformed base64 /
|
|
15
|
+
* truncated percent escape). A single malformed image `src` must not
|
|
16
|
+
* abort the page write; the caller stores `*_blob_id = null` and the
|
|
17
|
+
* information loss is limited to that one slot.
|
|
18
|
+
* @param qb - Knex instance or transaction connected to the archive DB.
|
|
19
|
+
* @param caches - The connection's write-side id caches; mutated in place.
|
|
20
|
+
* @param dataUri - The raw `data:` URI value.
|
|
21
|
+
* @returns The `blob_refs.id`, or `null` when the URI cannot be decoded.
|
|
22
|
+
* @example
|
|
23
|
+
* const id = await upsertBlobRef(knex, caches, 'data:image/png;base64,<payload>');
|
|
24
|
+
*/
|
|
25
|
+
export declare function upsertBlobRef(qb: Knex | Knex.Transaction, caches: WriteRefCaches, dataUri: string): Promise<number | null>;
|