@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,8 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves the crawl session name from the `info` table.
|
|
4
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
5
|
+
* @returns The name string.
|
|
6
|
+
* @throws {Error} If no name is found in the database.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getName(knex: Knex): Promise<string>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves the crawl session name from the `info` table.
|
|
3
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
4
|
+
* @returns The name string.
|
|
5
|
+
* @throws {Error} If no name is found in the database.
|
|
6
|
+
*/
|
|
7
|
+
export async function getName(knex) {
|
|
8
|
+
const selected = await knex.select('name').from('info');
|
|
9
|
+
if (!selected[0]) {
|
|
10
|
+
throw new Error('No name');
|
|
11
|
+
}
|
|
12
|
+
const [{ name }] = selected;
|
|
13
|
+
return name;
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Columns of the `info` table that `setConfig` / `updateConfig` are allowed to
|
|
3
|
+
* write. Any key outside this set is silently dropped so callers can splat a
|
|
4
|
+
* wider runtime config (with extras like `cwd`) without hitting "no such
|
|
5
|
+
* column" at the SQL layer.
|
|
6
|
+
*/
|
|
7
|
+
export declare const INFO_COLUMN_ALLOWLIST: ReadonlySet<string>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Columns of the `info` table that `setConfig` / `updateConfig` are allowed to
|
|
3
|
+
* write. Any key outside this set is silently dropped so callers can splat a
|
|
4
|
+
* wider runtime config (with extras like `cwd`) without hitting "no such
|
|
5
|
+
* column" at the SQL layer.
|
|
6
|
+
*/
|
|
7
|
+
export const INFO_COLUMN_ALLOWLIST = new Set([
|
|
8
|
+
'version',
|
|
9
|
+
'name',
|
|
10
|
+
'baseUrl',
|
|
11
|
+
'roots',
|
|
12
|
+
'recursive',
|
|
13
|
+
'interval',
|
|
14
|
+
'image',
|
|
15
|
+
'fetchExternal',
|
|
16
|
+
'parallels',
|
|
17
|
+
'excludes',
|
|
18
|
+
'excludeKeywords',
|
|
19
|
+
'excludeUrls',
|
|
20
|
+
'maxExcludedDepth',
|
|
21
|
+
'retry',
|
|
22
|
+
'fromList',
|
|
23
|
+
'disableQueries',
|
|
24
|
+
'userAgent',
|
|
25
|
+
'ignoreRobots',
|
|
26
|
+
]);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subset of {@link ./info-column-allowlist.ts} that is stored as a JSON-encoded
|
|
3
|
+
* string and therefore needs `JSON.stringify` on write.
|
|
4
|
+
*/
|
|
5
|
+
export const INFO_JSON_COLUMNS = new Set([
|
|
6
|
+
'roots',
|
|
7
|
+
'excludes',
|
|
8
|
+
'excludeKeywords',
|
|
9
|
+
'excludeUrls',
|
|
10
|
+
]);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Config } from '../../types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Stores the crawl configuration in the `info` table.
|
|
5
|
+
* Only fields in {@link ./info-column-allowlist.ts} are forwarded — any extra
|
|
6
|
+
* runtime-only field on the input is silently dropped so callers can splat
|
|
7
|
+
* a wider config object without producing SQL errors. JSON-array fields
|
|
8
|
+
* are serialized via `JSON.stringify`.
|
|
9
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
10
|
+
* @param config - The {@link Config} object to store.
|
|
11
|
+
*/
|
|
12
|
+
export declare function setConfig(knex: Knex, config: Config): Promise<number[]>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { INFO_COLUMN_ALLOWLIST } from './info-column-allowlist.js';
|
|
2
|
+
import { INFO_JSON_COLUMNS } from './info-json-columns.js';
|
|
3
|
+
/**
|
|
4
|
+
* Stores the crawl configuration in the `info` table.
|
|
5
|
+
* Only fields in {@link ./info-column-allowlist.ts} are forwarded — any extra
|
|
6
|
+
* runtime-only field on the input is silently dropped so callers can splat
|
|
7
|
+
* a wider config object without producing SQL errors. JSON-array fields
|
|
8
|
+
* are serialized via `JSON.stringify`.
|
|
9
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
10
|
+
* @param config - The {@link Config} object to store.
|
|
11
|
+
*/
|
|
12
|
+
export async function setConfig(knex, config) {
|
|
13
|
+
const payload = {};
|
|
14
|
+
for (const [key, value] of Object.entries(config)) {
|
|
15
|
+
if (!INFO_COLUMN_ALLOWLIST.has(key)) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
payload[key] = INFO_JSON_COLUMNS.has(key) ? JSON.stringify(value) : value;
|
|
19
|
+
}
|
|
20
|
+
return knex.from('info').insert(payload);
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Config } from '../../types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Update the single row in the `info` table with a partial config patch.
|
|
5
|
+
*
|
|
6
|
+
* Used by the append flow to extend `roots` (and any other tweakable
|
|
7
|
+
* field) without replacing the entire row. JSON-array fields are serialized on
|
|
8
|
+
* the fly; primitive fields are written verbatim. Unspecified fields stay as-is.
|
|
9
|
+
*
|
|
10
|
+
* Unknown keys (anything outside the allow-list of `info`-table columns) are
|
|
11
|
+
* silently dropped instead of being passed to SQL, so callers that splat a
|
|
12
|
+
* wider runtime config (e.g. `CrawlConfig` with `cwd` / `executablePath`)
|
|
13
|
+
* cannot accidentally trigger a "no such column" SQL error.
|
|
14
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
15
|
+
* @param patch - Partial {@link Config} fields to overwrite. `undefined` values are skipped.
|
|
16
|
+
*/
|
|
17
|
+
export declare function updateConfig(knex: Knex, patch: Partial<Config>): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { INFO_COLUMN_ALLOWLIST } from './info-column-allowlist.js';
|
|
2
|
+
import { INFO_JSON_COLUMNS } from './info-json-columns.js';
|
|
3
|
+
/**
|
|
4
|
+
* Update the single row in the `info` table with a partial config patch.
|
|
5
|
+
*
|
|
6
|
+
* Used by the append flow to extend `roots` (and any other tweakable
|
|
7
|
+
* field) without replacing the entire row. JSON-array fields are serialized on
|
|
8
|
+
* the fly; primitive fields are written verbatim. Unspecified fields stay as-is.
|
|
9
|
+
*
|
|
10
|
+
* Unknown keys (anything outside the allow-list of `info`-table columns) are
|
|
11
|
+
* silently dropped instead of being passed to SQL, so callers that splat a
|
|
12
|
+
* wider runtime config (e.g. `CrawlConfig` with `cwd` / `executablePath`)
|
|
13
|
+
* cannot accidentally trigger a "no such column" SQL error.
|
|
14
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
15
|
+
* @param patch - Partial {@link Config} fields to overwrite. `undefined` values are skipped.
|
|
16
|
+
*/
|
|
17
|
+
export async function updateConfig(knex, patch) {
|
|
18
|
+
const payload = {};
|
|
19
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
20
|
+
if (value === undefined) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (!INFO_COLUMN_ALLOWLIST.has(key)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (INFO_JSON_COLUMNS.has(key)) {
|
|
27
|
+
payload[key] = JSON.stringify(value);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
payload[key] = value;
|
|
31
|
+
}
|
|
32
|
+
if (Object.keys(payload).length === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await knex.from('info').update(payload);
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Records a crawler-level (`error` channel) failure into `crawl_errors`.
|
|
4
|
+
*
|
|
5
|
+
* Unlike `insertPageError` this is not tied to a scraped page: `url`
|
|
6
|
+
* may be an external link that never became a page row, or `null` for a
|
|
7
|
+
* process-level error. The cause is intentionally not stored — it is derived
|
|
8
|
+
* on read so that older archives (which only have `error.log`) and freshly
|
|
9
|
+
* captured rows classify identically.
|
|
10
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
11
|
+
* @param url - The URL the error is about, or `null` for a process-level error.
|
|
12
|
+
* @param message - The error message (one line is enough for classification).
|
|
13
|
+
* @param isExternal - Whether the URL is external to the crawl scope.
|
|
14
|
+
*/
|
|
15
|
+
export declare function insertCrawlError(knex: Knex, url: string | null, message: string, isExternal?: boolean): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records a crawler-level (`error` channel) failure into `crawl_errors`.
|
|
3
|
+
*
|
|
4
|
+
* Unlike `insertPageError` this is not tied to a scraped page: `url`
|
|
5
|
+
* may be an external link that never became a page row, or `null` for a
|
|
6
|
+
* process-level error. The cause is intentionally not stored — it is derived
|
|
7
|
+
* on read so that older archives (which only have `error.log`) and freshly
|
|
8
|
+
* captured rows classify identically.
|
|
9
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
10
|
+
* @param url - The URL the error is about, or `null` for a process-level error.
|
|
11
|
+
* @param message - The error message (one line is enough for classification).
|
|
12
|
+
* @param isExternal - Whether the URL is external to the crawl scope.
|
|
13
|
+
*/
|
|
14
|
+
export async function insertCrawlError(knex, url, message, isExternal = false) {
|
|
15
|
+
await knex('crawl_errors').insert({
|
|
16
|
+
url,
|
|
17
|
+
isExternal: isExternal ? 1 : 0,
|
|
18
|
+
message,
|
|
19
|
+
createdAt: Date.now(),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WriteRefCaches } from '../_shared/types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Records a partial scrape failure against the page identified by `url`.
|
|
5
|
+
*
|
|
6
|
+
* The page row is resolved (or inserted as a stub) via
|
|
7
|
+
* {@link resolveContentItemId} so the error can be recorded even before
|
|
8
|
+
* `setPage` has run — useful when the failure fires during scraping
|
|
9
|
+
* (e.g. mid-`scrapeStart`) and the orchestrator enqueues this write
|
|
10
|
+
* before the success write for the same URL.
|
|
11
|
+
*
|
|
12
|
+
* A single page can have multiple `page_errors` rows (e.g. both
|
|
13
|
+
* `desktop-compact` and `mobile-small` viewports failing).
|
|
14
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
15
|
+
* @param caches - The connection's write-side id caches.
|
|
16
|
+
* @param url - URL of the page being scraped.
|
|
17
|
+
* @param phase - Scrape phase name (typically `'retryExhausted'`).
|
|
18
|
+
* @param message - Human-readable failure message.
|
|
19
|
+
* @param isExternal - Whether the URL is external. Defaults to `false`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function insertPageError(knex: Knex, caches: WriteRefCaches, url: string, phase: string, message: string, isExternal?: boolean): Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { resolveContentItemId } from '../_shared/resolve-content-item-id.js';
|
|
2
|
+
/**
|
|
3
|
+
* Records a partial scrape failure against the page identified by `url`.
|
|
4
|
+
*
|
|
5
|
+
* The page row is resolved (or inserted as a stub) via
|
|
6
|
+
* {@link resolveContentItemId} so the error can be recorded even before
|
|
7
|
+
* `setPage` has run — useful when the failure fires during scraping
|
|
8
|
+
* (e.g. mid-`scrapeStart`) and the orchestrator enqueues this write
|
|
9
|
+
* before the success write for the same URL.
|
|
10
|
+
*
|
|
11
|
+
* A single page can have multiple `page_errors` rows (e.g. both
|
|
12
|
+
* `desktop-compact` and `mobile-small` viewports failing).
|
|
13
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
14
|
+
* @param caches - The connection's write-side id caches.
|
|
15
|
+
* @param url - URL of the page being scraped.
|
|
16
|
+
* @param phase - Scrape phase name (typically `'retryExhausted'`).
|
|
17
|
+
* @param message - Human-readable failure message.
|
|
18
|
+
* @param isExternal - Whether the URL is external. Defaults to `false`.
|
|
19
|
+
*/
|
|
20
|
+
export async function insertPageError(knex, caches, url, phase, message, isExternal = false) {
|
|
21
|
+
const pageId = await resolveContentItemId(knex, caches, url, isExternal ? 1 : 0);
|
|
22
|
+
await knex('page_errors').insert({
|
|
23
|
+
pageId,
|
|
24
|
+
phase,
|
|
25
|
+
message,
|
|
26
|
+
createdAt: Date.now(),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Hostnames whose `crawl_errors` history is consistently DNS failures and
|
|
4
|
+
* for which no recent 2xx-3xx page or resource is recorded — i.e. hosts
|
|
5
|
+
* the previous crawl already proved unreachable. Returned in lower-cased
|
|
6
|
+
* form. Used by `CrawlerOrchestrator.#preloadDnsBurnedHostCache` so the
|
|
7
|
+
* next session short-circuits HEAD pre-flight on these hosts.
|
|
8
|
+
*
|
|
9
|
+
* Implementation: a coarse `LIKE` filter over `crawl_errors.message`
|
|
10
|
+
* narrows the row set, then `classifyErrorKind` confirms `'dns'` in JS
|
|
11
|
+
* (the regex is the single truth source — DB-side filters never narrow
|
|
12
|
+
* it). Exclusion bags are built from a single `pages` and a single
|
|
13
|
+
* `resources` scan: any host with a 2xx-3xx page, a 2xx-3xx resource, or
|
|
14
|
+
* a `pages.lastCrawledAt` newer than its latest DNS error is dropped
|
|
15
|
+
* (the host probably recovered between the failure and the last crawl).
|
|
16
|
+
*
|
|
17
|
+
* Returns `[]` on legacy archives that pre-date the `crawl_errors`
|
|
18
|
+
* table — the `hasTable` guard keeps the call non-destructive.
|
|
19
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
20
|
+
* @returns Lower-cased hostnames safe to short-circuit.
|
|
21
|
+
*/
|
|
22
|
+
export declare function listDnsBurnedHostCandidates(knex: Knex): Promise<string[]>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { classifyErrorKind } from '../../../classify-error-kind.js';
|
|
2
|
+
/**
|
|
3
|
+
* Hostnames whose `crawl_errors` history is consistently DNS failures and
|
|
4
|
+
* for which no recent 2xx-3xx page or resource is recorded — i.e. hosts
|
|
5
|
+
* the previous crawl already proved unreachable. Returned in lower-cased
|
|
6
|
+
* form. Used by `CrawlerOrchestrator.#preloadDnsBurnedHostCache` so the
|
|
7
|
+
* next session short-circuits HEAD pre-flight on these hosts.
|
|
8
|
+
*
|
|
9
|
+
* Implementation: a coarse `LIKE` filter over `crawl_errors.message`
|
|
10
|
+
* narrows the row set, then `classifyErrorKind` confirms `'dns'` in JS
|
|
11
|
+
* (the regex is the single truth source — DB-side filters never narrow
|
|
12
|
+
* it). Exclusion bags are built from a single `pages` and a single
|
|
13
|
+
* `resources` scan: any host with a 2xx-3xx page, a 2xx-3xx resource, or
|
|
14
|
+
* a `pages.lastCrawledAt` newer than its latest DNS error is dropped
|
|
15
|
+
* (the host probably recovered between the failure and the last crawl).
|
|
16
|
+
*
|
|
17
|
+
* Returns `[]` on legacy archives that pre-date the `crawl_errors`
|
|
18
|
+
* table — the `hasTable` guard keeps the call non-destructive.
|
|
19
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
20
|
+
* @returns Lower-cased hostnames safe to short-circuit.
|
|
21
|
+
*/
|
|
22
|
+
export async function listDnsBurnedHostCandidates(knex) {
|
|
23
|
+
const hasCrawlErrors = await knex.schema.hasTable('crawl_errors');
|
|
24
|
+
if (!hasCrawlErrors) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
// Coarse SQL filter: cheap LIKE OR-chain over `message`. The dns regex
|
|
28
|
+
// truth source lives in `classifyErrorKind`, so we only need to feed it
|
|
29
|
+
// rows that COULD match a DNS token. Each LIKE is anchored on a known
|
|
30
|
+
// substring of the regex so future additions to the regex (without
|
|
31
|
+
// matching new SQL terms) widen the JS-side filter only — never narrow it.
|
|
32
|
+
//
|
|
33
|
+
// `%EAI_AGAIN%` is deliberately NOT in the SQL filter: it now classifies
|
|
34
|
+
// as `dns-transient` (local resolver hiccup), not `dns`, so it must not
|
|
35
|
+
// reach this candidate set. The `%getaddrinfo%` term still pulls
|
|
36
|
+
// `getaddrinfo EAI_AGAIN ...` rows but the JS-side `classifyErrorKind`
|
|
37
|
+
// check (first-match-wins) routes them to `dns-transient` and they
|
|
38
|
+
// silently drop out — keeping the cache focused on real NXDOMAIN.
|
|
39
|
+
const dnsLikeRows = (await knex('crawl_errors')
|
|
40
|
+
.select('url', 'message', 'createdAt')
|
|
41
|
+
.whereNotNull('url')
|
|
42
|
+
.where((qb) => {
|
|
43
|
+
qb.where('message', 'like', '%ENOTFOUND%')
|
|
44
|
+
.orWhere('message', 'like', '%getaddrinfo%')
|
|
45
|
+
.orWhere('message', 'like', '%ERR_NAME_NOT_RESOLVED%')
|
|
46
|
+
.orWhere('message', 'like', '%ERR_NAME_RESOLUTION_FAILED%');
|
|
47
|
+
}));
|
|
48
|
+
if (dnsLikeRows.length === 0) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
// Map<hostname, latestErrorCreatedAt> for hosts whose error message
|
|
52
|
+
// confidently classifies as DNS (LIKE matched but classifyErrorKind says
|
|
53
|
+
// e.g. `unknown` → drop).
|
|
54
|
+
const candidateLatestErrorAt = new Map();
|
|
55
|
+
for (const row of dnsLikeRows) {
|
|
56
|
+
if (classifyErrorKind(row.message) !== 'dns') {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
let host;
|
|
60
|
+
try {
|
|
61
|
+
host = new URL(row.url).hostname.toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (!host) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const createdAt = typeof row.createdAt === 'number' ? row.createdAt : 0;
|
|
70
|
+
const previous = candidateLatestErrorAt.get(host) ?? 0;
|
|
71
|
+
if (createdAt > previous) {
|
|
72
|
+
candidateLatestErrorAt.set(host, createdAt);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (candidateLatestErrorAt.size === 0) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
// Exclusion-bag #1: pages with a 2xx-3xx status anywhere on the host.
|
|
79
|
+
// Tracking the latest `last_crawled_at` per host lets us additionally
|
|
80
|
+
// drop hosts whose last successful contact post-dates the most recent
|
|
81
|
+
// DNS error (the host probably came back after a transient outage).
|
|
82
|
+
const pageOkRows = (await knex('content_items')
|
|
83
|
+
.join('url_refs', 'content_items.url_id', 'url_refs.id')
|
|
84
|
+
.select('url_refs.url as url', 'content_items.last_crawled_at as lastCrawledAt')
|
|
85
|
+
.whereBetween('content_items.status', [200, 399]));
|
|
86
|
+
const pageOkHosts = new Set();
|
|
87
|
+
const latestPageOkAt = new Map();
|
|
88
|
+
for (const row of pageOkRows) {
|
|
89
|
+
let host;
|
|
90
|
+
try {
|
|
91
|
+
host = new URL(row.url).hostname.toLowerCase();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
pageOkHosts.add(host);
|
|
97
|
+
if (typeof row.lastCrawledAt === 'number') {
|
|
98
|
+
const previous = latestPageOkAt.get(host) ?? 0;
|
|
99
|
+
if (row.lastCrawledAt > previous) {
|
|
100
|
+
latestPageOkAt.set(host, row.lastCrawledAt);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Exclusion-bag #2: non-HTML resources with a 2xx-3xx status. resources
|
|
105
|
+
// have no timestamp column so this is presence-only.
|
|
106
|
+
const resourceOkRows = (await knex('resource_items')
|
|
107
|
+
.join('url_refs', 'resource_items.url_id', 'url_refs.id')
|
|
108
|
+
.select('url_refs.url as url')
|
|
109
|
+
.whereBetween('resource_items.status', [200, 399]));
|
|
110
|
+
const resourceOkHosts = new Set();
|
|
111
|
+
for (const row of resourceOkRows) {
|
|
112
|
+
let host;
|
|
113
|
+
try {
|
|
114
|
+
host = new URL(row.url).hostname.toLowerCase();
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
resourceOkHosts.add(host);
|
|
120
|
+
}
|
|
121
|
+
// A candidate host is burned only if neither pages nor resources hold a
|
|
122
|
+
// 2xx-3xx for it, AND its latest 2xx page (if any) is not newer than
|
|
123
|
+
// the latest DNS error. The third check guards against re-burning a
|
|
124
|
+
// host that recovered between the last DNS failure and the most recent
|
|
125
|
+
// crawl.
|
|
126
|
+
const burned = [];
|
|
127
|
+
for (const [host, latestErrorAt] of candidateLatestErrorAt) {
|
|
128
|
+
if (pageOkHosts.has(host)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (resourceOkHosts.has(host)) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const latestOkAt = latestPageOkAt.get(host);
|
|
135
|
+
if (typeof latestOkAt === 'number' && latestOkAt > latestErrorAt) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
burned.push(host);
|
|
139
|
+
}
|
|
140
|
+
return burned;
|
|
141
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the HTML snapshot stored as a zstd-compressed BLOB for the given page.
|
|
4
|
+
*
|
|
5
|
+
* Joins `page_html_ref` → `page_html_blobs` and decompresses inline. Returns
|
|
6
|
+
* `null` when the page has no stored body (a non-HTML resource, a redirect
|
|
7
|
+
* source, a degraded render). Read works identically on read-only / stub
|
|
8
|
+
* connections — the special-cased "do we have a loose dir vs zip?" branching
|
|
9
|
+
* the previous file-backed layout required is gone.
|
|
10
|
+
*
|
|
11
|
+
* Tables `page_html_ref` and `page_html_blobs` are created by `initSchema`.
|
|
12
|
+
* Older `.nitpicker` archives that predate this migration must be passed
|
|
13
|
+
* through `scripts/migrate-to-0.10.mjs` before they can be read.
|
|
14
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
15
|
+
* @param pageId - The database ID of the page.
|
|
16
|
+
* @returns The decompressed HTML string, or `null` if no snapshot is stored.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getHtmlOfPageById(knex: Knex, pageId: number): Promise<string | null>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { decodeStoredBlob } from '../../decode-html-blob.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the HTML snapshot stored as a zstd-compressed BLOB for the given page.
|
|
4
|
+
*
|
|
5
|
+
* Joins `page_html_ref` → `page_html_blobs` and decompresses inline. Returns
|
|
6
|
+
* `null` when the page has no stored body (a non-HTML resource, a redirect
|
|
7
|
+
* source, a degraded render). Read works identically on read-only / stub
|
|
8
|
+
* connections — the special-cased "do we have a loose dir vs zip?" branching
|
|
9
|
+
* the previous file-backed layout required is gone.
|
|
10
|
+
*
|
|
11
|
+
* Tables `page_html_ref` and `page_html_blobs` are created by `initSchema`.
|
|
12
|
+
* Older `.nitpicker` archives that predate this migration must be passed
|
|
13
|
+
* through `scripts/migrate-to-0.10.mjs` before they can be read.
|
|
14
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
15
|
+
* @param pageId - The database ID of the page.
|
|
16
|
+
* @returns The decompressed HTML string, or `null` if no snapshot is stored.
|
|
17
|
+
*/
|
|
18
|
+
export async function getHtmlOfPageById(knex, pageId) {
|
|
19
|
+
const row = await knex
|
|
20
|
+
.from('page_html_ref')
|
|
21
|
+
.join('page_html_blobs', 'page_html_ref.hash', '=', 'page_html_blobs.hash')
|
|
22
|
+
.select('page_html_blobs.body as body', 'page_html_blobs.codec as codec')
|
|
23
|
+
.where('page_html_ref.page_id', pageId)
|
|
24
|
+
.first();
|
|
25
|
+
if (!row) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return decodeStoredBlob(row.body, row.codec);
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InventoryRunMeta } from '../../types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Appends one row to the `inventory_runs` audit log.
|
|
5
|
+
*
|
|
6
|
+
* Called by `CrawlerOrchestrator.inventory` on every successful
|
|
7
|
+
* `--inventory <list>` invocation so the archive carries a durable
|
|
8
|
+
* record of which deploy list was applied when and at what scale —
|
|
9
|
+
* the operational question "did we apply last month's list" the
|
|
10
|
+
* archive itself can answer without consulting external bookkeeping.
|
|
11
|
+
*
|
|
12
|
+
* Append-only. There is intentionally no UPDATE path and no UNIQUE
|
|
13
|
+
* constraint on `source_file_sha256`; two applies of the same list
|
|
14
|
+
* each get their own row. Duplicate detection is left to readers —
|
|
15
|
+
* the hash is recorded as the content-identity key they would use.
|
|
16
|
+
* Field-level NULL semantics live on {@link InventoryRunMeta}.
|
|
17
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
18
|
+
* @param meta - The run metadata to record. Only `ran_at` is required.
|
|
19
|
+
* @returns The autoincremented `id` of the newly-inserted row.
|
|
20
|
+
*/
|
|
21
|
+
export declare function recordInventoryRun(knex: Knex, meta: InventoryRunMeta): Promise<number>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Appends one row to the `inventory_runs` audit log.
|
|
3
|
+
*
|
|
4
|
+
* Called by `CrawlerOrchestrator.inventory` on every successful
|
|
5
|
+
* `--inventory <list>` invocation so the archive carries a durable
|
|
6
|
+
* record of which deploy list was applied when and at what scale —
|
|
7
|
+
* the operational question "did we apply last month's list" the
|
|
8
|
+
* archive itself can answer without consulting external bookkeeping.
|
|
9
|
+
*
|
|
10
|
+
* Append-only. There is intentionally no UPDATE path and no UNIQUE
|
|
11
|
+
* constraint on `source_file_sha256`; two applies of the same list
|
|
12
|
+
* each get their own row. Duplicate detection is left to readers —
|
|
13
|
+
* the hash is recorded as the content-identity key they would use.
|
|
14
|
+
* Field-level NULL semantics live on {@link InventoryRunMeta}.
|
|
15
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
16
|
+
* @param meta - The run metadata to record. Only `ran_at` is required.
|
|
17
|
+
* @returns The autoincremented `id` of the newly-inserted row.
|
|
18
|
+
*/
|
|
19
|
+
export async function recordInventoryRun(knex, meta) {
|
|
20
|
+
const inserted = await knex
|
|
21
|
+
.from('inventory_runs')
|
|
22
|
+
.insert({
|
|
23
|
+
ran_at: meta.ran_at,
|
|
24
|
+
list_label: meta.list_label ?? null,
|
|
25
|
+
source_file_sha256: meta.source_file_sha256 ?? null,
|
|
26
|
+
total_lines: meta.total_lines ?? null,
|
|
27
|
+
new_pages: meta.new_pages ?? null,
|
|
28
|
+
new_resources: meta.new_resources ?? null,
|
|
29
|
+
scope_skipped: meta.scope_skipped ?? null,
|
|
30
|
+
notes: meta.notes ?? null,
|
|
31
|
+
})
|
|
32
|
+
.returning('id');
|
|
33
|
+
const id = inserted[0]?.id;
|
|
34
|
+
if (typeof id !== 'number') {
|
|
35
|
+
throw new TypeError('recordInventoryRun: INSERT returned no row id');
|
|
36
|
+
}
|
|
37
|
+
return id;
|
|
38
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Forces a WAL checkpoint, writing all pending WAL data back to the main database file.
|
|
4
|
+
* Uses TRUNCATE mode to reset the WAL file to zero bytes after checkpointing.
|
|
5
|
+
* This ensures the database is fully self-contained in `db.sqlite` before archiving.
|
|
6
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
7
|
+
*/
|
|
8
|
+
export declare function checkpoint(knex: Knex): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forces a WAL checkpoint, writing all pending WAL data back to the main database file.
|
|
3
|
+
* Uses TRUNCATE mode to reset the WAL file to zero bytes after checkpointing.
|
|
4
|
+
* This ensures the database is fully self-contained in `db.sqlite` before archiving.
|
|
5
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
6
|
+
*/
|
|
7
|
+
export async function checkpoint(knex) {
|
|
8
|
+
await knex.raw('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Initializes the database schema if tables do not exist, then runs the
|
|
4
|
+
* one remaining lightweight migration (`info.roots`).
|
|
5
|
+
*
|
|
6
|
+
* There is deliberately no per-table lazy-migration chain here:
|
|
7
|
+
* `assertCompatibleVersion` (called below, before any schema work)
|
|
8
|
+
* rejects every archive older than the current format, so a connection
|
|
9
|
+
* that reaches `initSchema` is either brand new (initSchema provisions
|
|
10
|
+
* the full schema) or was produced by `scripts/migrate-to-0.13.mjs`
|
|
11
|
+
* (which guarantees the full table set before it repacks). A
|
|
12
|
+
* `hasTable`-guarded catch-up migration could therefore never fire —
|
|
13
|
+
* schema catch-up for old archives is the migration script's job, not
|
|
14
|
+
* the open path's.
|
|
15
|
+
*
|
|
16
|
+
* In read-only mode schema init + migration are SKIPPED so the same DB
|
|
17
|
+
* can be opened safely by a viewer attached to a live (or interrupted)
|
|
18
|
+
* crawl without rewriting the user's tmpDir.
|
|
19
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
20
|
+
* @param readOnly - When true, skip schema init + migrations.
|
|
21
|
+
*/
|
|
22
|
+
export declare function init(knex: Knex, readOnly: boolean): Promise<void>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { applyConnectionPragmas, initSchema } from '../../init-schema.js';
|
|
2
|
+
import { assertCompatibleVersion } from '../../meta/assert-compatible-version.js';
|
|
3
|
+
import { migrateInfoRoots } from '../../migrate-info-roots.js';
|
|
4
|
+
/**
|
|
5
|
+
* Initializes the database schema if tables do not exist, then runs the
|
|
6
|
+
* one remaining lightweight migration (`info.roots`).
|
|
7
|
+
*
|
|
8
|
+
* There is deliberately no per-table lazy-migration chain here:
|
|
9
|
+
* `assertCompatibleVersion` (called below, before any schema work)
|
|
10
|
+
* rejects every archive older than the current format, so a connection
|
|
11
|
+
* that reaches `initSchema` is either brand new (initSchema provisions
|
|
12
|
+
* the full schema) or was produced by `scripts/migrate-to-0.13.mjs`
|
|
13
|
+
* (which guarantees the full table set before it repacks). A
|
|
14
|
+
* `hasTable`-guarded catch-up migration could therefore never fire —
|
|
15
|
+
* schema catch-up for old archives is the migration script's job, not
|
|
16
|
+
* the open path's.
|
|
17
|
+
*
|
|
18
|
+
* In read-only mode schema init + migration are SKIPPED so the same DB
|
|
19
|
+
* can be opened safely by a viewer attached to a live (or interrupted)
|
|
20
|
+
* crawl without rewriting the user's tmpDir.
|
|
21
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
22
|
+
* @param readOnly - When true, skip schema init + migrations.
|
|
23
|
+
*/
|
|
24
|
+
export async function init(knex, readOnly) {
|
|
25
|
+
// Connection-level PRAGMAs (foreign_keys, mmap_size, …) must be
|
|
26
|
+
// reapplied on every connect — they are not persisted across opens.
|
|
27
|
+
// They are safe in read-only mode because they don't write to the
|
|
28
|
+
// user's tmpDir, just configure the libsql connection.
|
|
29
|
+
await applyConnectionPragmas(knex);
|
|
30
|
+
// Reject incompatible archives before any further work. Runs for both
|
|
31
|
+
// writer and read-only (stub viewer) connections so old
|
|
32
|
+
// `._nitpicker-*` stubs surface a clear error instead of
|
|
33
|
+
// dereferencing missing columns at query time. New archives (no
|
|
34
|
+
// `info` table yet) pass through; the schema is filled in by
|
|
35
|
+
// `initSchema` below.
|
|
36
|
+
await assertCompatibleVersion(knex);
|
|
37
|
+
if (readOnly) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
await initSchema(knex);
|
|
41
|
+
await migrateInfoRoots(knex);
|
|
42
|
+
}
|