@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,43 @@
|
|
|
1
|
+
import type { ErrorKind } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Error kinds whose failure mode is **persistent** — re-fetching the same URL
|
|
4
|
+
* (without changing the network, the certificate, the destination host, or the
|
|
5
|
+
* browser's block-list state) will almost certainly produce the same failure.
|
|
6
|
+
*
|
|
7
|
+
* Used by `resetFailedPages` to exclude pages whose latest recorded error
|
|
8
|
+
* falls in this set, so `--retry-failed` actually converges: without the
|
|
9
|
+
* exclusion, NXDOMAIN / TLS mismatch / `ERR_BLOCKED_BY_CLIENT` /
|
|
10
|
+
* `ECONNREFUSED` / HTTP parse-error pages would be reset to pending on every
|
|
11
|
+
* iteration, the crawler would re-attempt them, they would fail again the
|
|
12
|
+
* same way, and the retry-target count would stay constant forever.
|
|
13
|
+
*
|
|
14
|
+
* Why these five and not others:
|
|
15
|
+
* - **dns** — `ENOTFOUND` / `ERR_NAME_NOT_RESOLVED` are authoritative DNS
|
|
16
|
+
* answers; the host is gone (or never existed). EAI_AGAIN is split out as
|
|
17
|
+
* `dns-transient` precisely so it is NOT in this set.
|
|
18
|
+
* - **tls** — cert expiry / SAN mismatch / SSL protocol errors require the
|
|
19
|
+
* server operator to fix something; retrying within the same archive run
|
|
20
|
+
* cannot succeed.
|
|
21
|
+
* - **client-blocked** — Chromium's `ERR_BLOCKED_*` family is a deliberate
|
|
22
|
+
* browser-side rejection; the request will be blocked identically every
|
|
23
|
+
* time the browser sees the same URL.
|
|
24
|
+
* - **parse-error** — the server's HTTP response is malformed (`Expected
|
|
25
|
+
* HTTP/, RTSP/ or ICE/`, `Unexpected end of stream`). Retrying the same
|
|
26
|
+
* request hits the same parser failure.
|
|
27
|
+
* - **connection-refused** — `ECONNREFUSED` is an authoritative TCP RST from
|
|
28
|
+
* the listener; either no process is listening on the port or its accept
|
|
29
|
+
* queue rejected the connection. Either way the answer is final until the
|
|
30
|
+
* server operator intervenes.
|
|
31
|
+
*
|
|
32
|
+
* Notably absent (intentionally retryable):
|
|
33
|
+
* - `connection-reset` / `connection-timeout` — could be middlebox or
|
|
34
|
+
* transient overload
|
|
35
|
+
* - `dns-transient` (EAI_AGAIN / EREFUSED) — local resolver hiccup
|
|
36
|
+
* - `local-network` — operator-side connectivity loss
|
|
37
|
+
* - `timeout` — slow but reachable server (HEAD-timeout escalation gives
|
|
38
|
+
* these a real chance)
|
|
39
|
+
* - `protocol` — puppeteer lifecycle race, often recovers on retry
|
|
40
|
+
* - `unknown` — by definition we don't know it's permanent, so we keep
|
|
41
|
+
* retrying (errs on the side of investigating)
|
|
42
|
+
*/
|
|
43
|
+
export declare const PERMANENT_ERROR_KINDS: ReadonlySet<ErrorKind>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error kinds whose failure mode is **persistent** — re-fetching the same URL
|
|
3
|
+
* (without changing the network, the certificate, the destination host, or the
|
|
4
|
+
* browser's block-list state) will almost certainly produce the same failure.
|
|
5
|
+
*
|
|
6
|
+
* Used by `resetFailedPages` to exclude pages whose latest recorded error
|
|
7
|
+
* falls in this set, so `--retry-failed` actually converges: without the
|
|
8
|
+
* exclusion, NXDOMAIN / TLS mismatch / `ERR_BLOCKED_BY_CLIENT` /
|
|
9
|
+
* `ECONNREFUSED` / HTTP parse-error pages would be reset to pending on every
|
|
10
|
+
* iteration, the crawler would re-attempt them, they would fail again the
|
|
11
|
+
* same way, and the retry-target count would stay constant forever.
|
|
12
|
+
*
|
|
13
|
+
* Why these five and not others:
|
|
14
|
+
* - **dns** — `ENOTFOUND` / `ERR_NAME_NOT_RESOLVED` are authoritative DNS
|
|
15
|
+
* answers; the host is gone (or never existed). EAI_AGAIN is split out as
|
|
16
|
+
* `dns-transient` precisely so it is NOT in this set.
|
|
17
|
+
* - **tls** — cert expiry / SAN mismatch / SSL protocol errors require the
|
|
18
|
+
* server operator to fix something; retrying within the same archive run
|
|
19
|
+
* cannot succeed.
|
|
20
|
+
* - **client-blocked** — Chromium's `ERR_BLOCKED_*` family is a deliberate
|
|
21
|
+
* browser-side rejection; the request will be blocked identically every
|
|
22
|
+
* time the browser sees the same URL.
|
|
23
|
+
* - **parse-error** — the server's HTTP response is malformed (`Expected
|
|
24
|
+
* HTTP/, RTSP/ or ICE/`, `Unexpected end of stream`). Retrying the same
|
|
25
|
+
* request hits the same parser failure.
|
|
26
|
+
* - **connection-refused** — `ECONNREFUSED` is an authoritative TCP RST from
|
|
27
|
+
* the listener; either no process is listening on the port or its accept
|
|
28
|
+
* queue rejected the connection. Either way the answer is final until the
|
|
29
|
+
* server operator intervenes.
|
|
30
|
+
*
|
|
31
|
+
* Notably absent (intentionally retryable):
|
|
32
|
+
* - `connection-reset` / `connection-timeout` — could be middlebox or
|
|
33
|
+
* transient overload
|
|
34
|
+
* - `dns-transient` (EAI_AGAIN / EREFUSED) — local resolver hiccup
|
|
35
|
+
* - `local-network` — operator-side connectivity loss
|
|
36
|
+
* - `timeout` — slow but reachable server (HEAD-timeout escalation gives
|
|
37
|
+
* these a real chance)
|
|
38
|
+
* - `protocol` — puppeteer lifecycle race, often recovers on retry
|
|
39
|
+
* - `unknown` — by definition we don't know it's permanent, so we keep
|
|
40
|
+
* retrying (errs on the side of investigating)
|
|
41
|
+
*/
|
|
42
|
+
export const PERMANENT_ERROR_KINDS = new Set([
|
|
43
|
+
'dns',
|
|
44
|
+
'tls',
|
|
45
|
+
'client-blocked',
|
|
46
|
+
'parse-error',
|
|
47
|
+
'connection-refused',
|
|
48
|
+
]);
|
package/lib/types.d.ts
CHANGED
|
@@ -1,4 +1,88 @@
|
|
|
1
1
|
import type { CrawlerError, PageData } from './utils/types/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Aggregate counts captured during a `--inventory` invocation, forwarded to
|
|
4
|
+
* `#writeInventoryRunRow` so the audit log row is consistent between the
|
|
5
|
+
* HTML-seed branch and the non-HTML-only branch of
|
|
6
|
+
* `CrawlerOrchestrator.inventory`.
|
|
7
|
+
*
|
|
8
|
+
* Spelled out here (not inlined at the call site) so a new field added to
|
|
9
|
+
* the audit row has a single edit point and so each field's semantics are
|
|
10
|
+
* documented per-property rather than scattered across the two emit sites.
|
|
11
|
+
*/
|
|
12
|
+
export interface InventoryRunAggregates {
|
|
13
|
+
/** Total non-empty lines in the input list (= `inventoryUrls.length` before any filtering). Stored verbatim as `inventory_runs.total_lines`. */
|
|
14
|
+
inventoryUrlsCount: number;
|
|
15
|
+
/** Number of novel URLs classified as HTML and queued for render. Stored as `new_pages` (excludes anchor-discovered descendants — those add later via the crawler graph and are NOT counted here). */
|
|
16
|
+
htmlSeedsCount: number;
|
|
17
|
+
/** Number of novel URLs classified as non-HTML and written directly into `resources`. Stored as `new_resources`. */
|
|
18
|
+
nonHtmlCount: number;
|
|
19
|
+
/** URLs dropped because they fell outside the archived scope. Stored as `scope_skipped`. */
|
|
20
|
+
outOfScope: number;
|
|
21
|
+
/**
|
|
22
|
+
* SHA-256 hex digest of the source `.txt`, **pre-computed by the caller**
|
|
23
|
+
* (typically the CLI's `inventoryCrawl`). Stored verbatim as
|
|
24
|
+
* `inventory_runs.source_file_sha256`.
|
|
25
|
+
*
|
|
26
|
+
* Pre-computation lifts the absolute path off the orchestrator
|
|
27
|
+
* boundary entirely — the path is privacy-sensitive (leaks
|
|
28
|
+
* user-home / OS structure when archives are shared), and the
|
|
29
|
+
* orchestrator has no business handling it after the audit-row
|
|
30
|
+
* column was dropped. Pass `null` for programmatic callers that
|
|
31
|
+
* built `inventoryUrls` in-memory; the audit row's
|
|
32
|
+
* `source_file_sha256` will be `NULL`.
|
|
33
|
+
*/
|
|
34
|
+
sourceFileSha256: string | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Coarse cause of a crawl/scrape failure.
|
|
38
|
+
*
|
|
39
|
+
* The crawler stores only the raw error message (in `crawl_errors`,
|
|
40
|
+
* `page_errors`, or `error.log`); the cause is derived on read by
|
|
41
|
+
* `classifyErrorKind`, so existing archives gain classification without a
|
|
42
|
+
* re-crawl.
|
|
43
|
+
*
|
|
44
|
+
* Owned by the crawler package because both the crawler (for DNS-burned host
|
|
45
|
+
* caching) and `@nitpicker/query` (for `getErrorKinds` / `getSummary`) need to
|
|
46
|
+
* classify error messages, and crawler cannot depend on query.
|
|
47
|
+
*
|
|
48
|
+
* ### transient vs persistent
|
|
49
|
+
*
|
|
50
|
+
* | kind | transient? | DNS-burn? | notes |
|
|
51
|
+
* | --- | --- | --- | --- |
|
|
52
|
+
* | `dns` | no | yes | NXDOMAIN; the host does not resolve at all |
|
|
53
|
+
* | `dns-transient` | **yes** | no | `EAI_AGAIN`; local resolver hiccup, retry often recovers |
|
|
54
|
+
* | `tls` | no | no | certificate issue, usually persistent until cert rotates |
|
|
55
|
+
* | `connection-refused` | mostly persistent | no | server actively rejecting on this port |
|
|
56
|
+
* | `connection-reset` | yes | no | TCP reset mid-stream, often transient |
|
|
57
|
+
* | `connection-timeout` | yes | no | TCP-level timeout (`ETIMEDOUT`); slow but reachable |
|
|
58
|
+
* | `local-network` | **yes** | no | local machine's network is unreachable / changed (WiFi, sleep, ICMP-unreachable, …) |
|
|
59
|
+
* | `parse-error` | mostly persistent | no | HTTP response could not be parsed (proxy, garbage, MITM) |
|
|
60
|
+
* | `client-blocked` | persistent (per browser) | no | Chromium-side `ERR_BLOCKED_BY_*` family — the browser actively refused the request (ad/tracker heuristics, CSP, CORP, administrator block list, …) |
|
|
61
|
+
* | `protocol` | yes | no | puppeteer protocol layer (frame detached, target closed, …) |
|
|
62
|
+
* | `timeout` | yes | no | puppeteer navigation timeout or HEAD pre-flight race timeout (`Timeout: <url>`) |
|
|
63
|
+
* | `unknown` | unknown | no | catch-all for messages no matcher recognised |
|
|
64
|
+
*
|
|
65
|
+
* Only `dns` is mark-target for the DNS-burned host cache; everything else is
|
|
66
|
+
* either too transient to burn (network glitch / browser hiccup) or too
|
|
67
|
+
* server-specific to extrapolate to "this whole host is dead."
|
|
68
|
+
*
|
|
69
|
+
* ### Derived constants that MUST be reviewed when this union changes
|
|
70
|
+
*
|
|
71
|
+
* - `PERMANENT_ERROR_KINDS` (`permanent-error-kinds.ts`) — the set of kinds
|
|
72
|
+
* excluded from `--retry-failed` so retry iterations actually converge.
|
|
73
|
+
* A new kind that is deterministically permanent (server-state, cert,
|
|
74
|
+
* browser-block, …) likely belongs here.
|
|
75
|
+
* - `PUPPETEER_FALLBACK_KINDS` (`crawler/is-puppeteer-fallback-candidate.ts`)
|
|
76
|
+
* — the set of kinds where one puppeteer attempt has a realistic chance
|
|
77
|
+
* of succeeding after HEAD+GET pre-flight exhausted retries. A new kind
|
|
78
|
+
* modelling a middlebox / WAF / slow-server quirk likely belongs here.
|
|
79
|
+
*
|
|
80
|
+
* Adding a kind without reviewing both sets risks a silent regression:
|
|
81
|
+
* `--retry-failed` re-trying a permanent failure forever (no PERMANENT
|
|
82
|
+
* entry), or a recoverable URL never reaching the puppeteer fallback (no
|
|
83
|
+
* PUPPETEER_FALLBACK entry).
|
|
84
|
+
*/
|
|
85
|
+
export type ErrorKind = 'dns' | 'dns-transient' | 'connection-refused' | 'connection-reset' | 'connection-timeout' | 'tls' | 'local-network' | 'parse-error' | 'client-blocked' | 'timeout' | 'protocol' | 'unknown';
|
|
2
86
|
/**
|
|
3
87
|
* Event map for the `CrawlerOrchestrator` class.
|
|
4
88
|
*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the SHA-256 digest of a file's bytes, streaming so memory stays
|
|
3
|
+
* O(1) regardless of file size.
|
|
4
|
+
*
|
|
5
|
+
* Used by `CrawlerOrchestrator.inventory` to fingerprint the source
|
|
6
|
+
* URL list `.txt` and store the digest on the `inventory_runs` row. The
|
|
7
|
+
* digest is the content-identity key for "was this exact list applied
|
|
8
|
+
* before" — recorded for operator audit; nothing dedupes on it at write
|
|
9
|
+
* time.
|
|
10
|
+
*
|
|
11
|
+
* Returns `null` instead of throwing when the file cannot be read (e.g.
|
|
12
|
+
* vanished mid-run, permissions issue) so the inventory run's audit row
|
|
13
|
+
* can still be written with a NULL digest. Hashing failure is an audit
|
|
14
|
+
* loss, not a correctness failure — the actual ingestion has already
|
|
15
|
+
* succeeded by the time this is called.
|
|
16
|
+
* @param filePath - Absolute or relative path to the file to hash.
|
|
17
|
+
* @returns Lower-case hex digest (64 chars), or `null` if reading failed.
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const sha = await computeFileSha256('/tmp/list.txt');
|
|
21
|
+
* if (sha) console.log(sha); // 64-char hex string
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function computeFileSha256(filePath: string): Promise<string | null>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { crawlerLog } from '../debug.js';
|
|
4
|
+
/**
|
|
5
|
+
* Compute the SHA-256 digest of a file's bytes, streaming so memory stays
|
|
6
|
+
* O(1) regardless of file size.
|
|
7
|
+
*
|
|
8
|
+
* Used by `CrawlerOrchestrator.inventory` to fingerprint the source
|
|
9
|
+
* URL list `.txt` and store the digest on the `inventory_runs` row. The
|
|
10
|
+
* digest is the content-identity key for "was this exact list applied
|
|
11
|
+
* before" — recorded for operator audit; nothing dedupes on it at write
|
|
12
|
+
* time.
|
|
13
|
+
*
|
|
14
|
+
* Returns `null` instead of throwing when the file cannot be read (e.g.
|
|
15
|
+
* vanished mid-run, permissions issue) so the inventory run's audit row
|
|
16
|
+
* can still be written with a NULL digest. Hashing failure is an audit
|
|
17
|
+
* loss, not a correctness failure — the actual ingestion has already
|
|
18
|
+
* succeeded by the time this is called.
|
|
19
|
+
* @param filePath - Absolute or relative path to the file to hash.
|
|
20
|
+
* @returns Lower-case hex digest (64 chars), or `null` if reading failed.
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const sha = await computeFileSha256('/tmp/list.txt');
|
|
24
|
+
* if (sha) console.log(sha); // 64-char hex string
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export async function computeFileSha256(filePath) {
|
|
28
|
+
return await new Promise((resolve) => {
|
|
29
|
+
const hash = createHash('sha256');
|
|
30
|
+
const stream = createReadStream(filePath);
|
|
31
|
+
let settled = false;
|
|
32
|
+
const settle = (value) => {
|
|
33
|
+
if (settled) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
settled = true;
|
|
37
|
+
resolve(value);
|
|
38
|
+
};
|
|
39
|
+
stream.on('data', (chunk) => hash.update(chunk));
|
|
40
|
+
stream.on('end', () => settle(hash.digest('hex')));
|
|
41
|
+
stream.on('error', (error) => {
|
|
42
|
+
// Trace the failure so operators inspecting `--inventory`
|
|
43
|
+
// audit rows with `source_file_sha256 = NULL` can recover
|
|
44
|
+
// the underlying cause via `DEBUG=Nitpicker:Crawler:*`.
|
|
45
|
+
crawlerLog('compute-file-sha256 failed for %s: %s', filePath, error.message);
|
|
46
|
+
settle(null);
|
|
47
|
+
});
|
|
48
|
+
// `'close'` is the last-resort settler. If the stream is
|
|
49
|
+
// destroyed externally between `'data'` and `'end'` (e.g. a
|
|
50
|
+
// test or signal handler), neither `'end'` nor `'error'` may
|
|
51
|
+
// fire — leaving the promise pending forever. Falling back to
|
|
52
|
+
// `null` matches the documented contract ("hashing failure
|
|
53
|
+
// yields null").
|
|
54
|
+
stream.on('close', () => settle(null));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { RetryCallOptions } from '@d-zero/shared/retry';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal object shape required by {@link emitErrorAndRetry}: an emitter
|
|
4
|
+
* able to publish an `'error'` event carrying an `Error` payload. Same
|
|
5
|
+
* contract as `emitError` — see {@link ./emit-error.js} for details.
|
|
6
|
+
*/
|
|
7
|
+
interface Emitter {
|
|
8
|
+
/**
|
|
9
|
+
* Emits the `'error'` event with the given `Error` payload.
|
|
10
|
+
* @param event - Event name; always `'error'` here.
|
|
11
|
+
* @param payload - The error instance to publish.
|
|
12
|
+
* @returns Anything; the caller ignores it.
|
|
13
|
+
*/
|
|
14
|
+
emit(event: 'error', payload: Error): unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Retries `fn` per `retryOptions`, then — if retries are exhausted with a
|
|
18
|
+
* final `Error` — logs and re-emits it as an `'error'` event on `emitter`
|
|
19
|
+
* before re-throwing. `label` is forwarded into `retryCall` so its own
|
|
20
|
+
* onWait / onGiveUp / timeout messages identify the call site (previously
|
|
21
|
+
* derived automatically by the `@retry` decorator via
|
|
22
|
+
* `ClassMethodDecoratorContext.name`).
|
|
23
|
+
*
|
|
24
|
+
* Inlined try/catch rather than composing `emitError` with a
|
|
25
|
+
* `() => retryCall(...)` closure to keep the per-invocation closure count
|
|
26
|
+
* at 1 — this method is called on the DB write hot path (`updatePage`,
|
|
27
|
+
* `insertResource`), where wrapping decorators previously ran with zero
|
|
28
|
+
* per-call allocation.
|
|
29
|
+
* @template T - The return type of `fn`.
|
|
30
|
+
* @param emitter - The event emitter that receives the `'error'` event.
|
|
31
|
+
* @param label - Human-readable label; forwarded into `retryCall`
|
|
32
|
+
* (visible in retry timeouts / onWait / onGiveUp) and used
|
|
33
|
+
* as the debug log prefix on final failure.
|
|
34
|
+
* @param fn - The async operation to retry.
|
|
35
|
+
* @param retryOptions - Retry configuration; `label` is overwritten
|
|
36
|
+
* with the `label` argument here.
|
|
37
|
+
* @returns The resolved value of `fn`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function emitErrorAndRetry<T>(emitter: Emitter, label: string, fn: () => Promise<T>, retryOptions: RetryCallOptions): Promise<T>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { retryCall } from '@d-zero/shared/retry';
|
|
2
|
+
import { log } from '../debug.js';
|
|
3
|
+
/**
|
|
4
|
+
* Debug logger for the ErrorEmitter helper family.
|
|
5
|
+
* Namespace: `Nitpicker:Utils:ErrorEmitter`. Kept identical to the namespace
|
|
6
|
+
* used by the deleted `@ErrorEmitter()` decorator so existing `DEBUG` env
|
|
7
|
+
* filters keep working after the decorator-to-HOF migration.
|
|
8
|
+
*/
|
|
9
|
+
const errorLog = log.extend('ErrorEmitter');
|
|
10
|
+
/**
|
|
11
|
+
* Retries `fn` per `retryOptions`, then — if retries are exhausted with a
|
|
12
|
+
* final `Error` — logs and re-emits it as an `'error'` event on `emitter`
|
|
13
|
+
* before re-throwing. `label` is forwarded into `retryCall` so its own
|
|
14
|
+
* onWait / onGiveUp / timeout messages identify the call site (previously
|
|
15
|
+
* derived automatically by the `@retry` decorator via
|
|
16
|
+
* `ClassMethodDecoratorContext.name`).
|
|
17
|
+
*
|
|
18
|
+
* Inlined try/catch rather than composing `emitError` with a
|
|
19
|
+
* `() => retryCall(...)` closure to keep the per-invocation closure count
|
|
20
|
+
* at 1 — this method is called on the DB write hot path (`updatePage`,
|
|
21
|
+
* `insertResource`), where wrapping decorators previously ran with zero
|
|
22
|
+
* per-call allocation.
|
|
23
|
+
* @template T - The return type of `fn`.
|
|
24
|
+
* @param emitter - The event emitter that receives the `'error'` event.
|
|
25
|
+
* @param label - Human-readable label; forwarded into `retryCall`
|
|
26
|
+
* (visible in retry timeouts / onWait / onGiveUp) and used
|
|
27
|
+
* as the debug log prefix on final failure.
|
|
28
|
+
* @param fn - The async operation to retry.
|
|
29
|
+
* @param retryOptions - Retry configuration; `label` is overwritten
|
|
30
|
+
* with the `label` argument here.
|
|
31
|
+
* @returns The resolved value of `fn`.
|
|
32
|
+
*/
|
|
33
|
+
export async function emitErrorAndRetry(emitter, label, fn, retryOptions) {
|
|
34
|
+
try {
|
|
35
|
+
return await retryCall(fn, { ...retryOptions, label });
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof Error) {
|
|
39
|
+
errorLog('%s: %O', label, error);
|
|
40
|
+
void emitter.emit('error', error);
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal object shape required by {@link emitError}: an emitter able to
|
|
3
|
+
* publish an `'error'` event carrying an `Error` payload. Any class that
|
|
4
|
+
* extends `TypedAwaitEventEmitter<{ error: Error, ... }>` satisfies this,
|
|
5
|
+
* and so does Node's built-in `EventEmitter`.
|
|
6
|
+
*/
|
|
7
|
+
interface Emitter {
|
|
8
|
+
/**
|
|
9
|
+
* Emits the `'error'` event with the given `Error` payload.
|
|
10
|
+
* The return value is intentionally unspecified — callers ignore it.
|
|
11
|
+
* @param event - Event name; always `'error'` here.
|
|
12
|
+
* @param payload - The error instance to publish.
|
|
13
|
+
* @returns Anything; the caller ignores it.
|
|
14
|
+
*/
|
|
15
|
+
emit(event: 'error', payload: Error): unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Wraps `fn` so that any thrown `Error` is logged with `label` and
|
|
19
|
+
* re-emitted as an `'error'` event on `emitter` before being re-thrown.
|
|
20
|
+
* Non-`Error` throws pass through unchanged — no log, no emit — matching
|
|
21
|
+
* the behaviour of the deleted `@ErrorEmitter()` decorator so that
|
|
22
|
+
* downstream `'error'` listeners (e.g. the crawler orchestrator that
|
|
23
|
+
* aborts on any emitted error) are only invoked with real `Error`
|
|
24
|
+
* instances.
|
|
25
|
+
*
|
|
26
|
+
* Kept as a standalone HOF (instead of a decorator) because Vite 8 /
|
|
27
|
+
* Vitest 4.1 switched from esbuild to Rolldown + oxc, which emit
|
|
28
|
+
* TC39 Stage 3 decorator syntax as-is and let it reach Node, causing
|
|
29
|
+
* `SyntaxError: Invalid or unexpected token` at test runtime.
|
|
30
|
+
* @template T - The return type of `fn`.
|
|
31
|
+
* @param emitter - The event emitter that receives the `'error'` event.
|
|
32
|
+
* @param label - Human-readable label included in the debug log line
|
|
33
|
+
* (typically `'ClassName.methodName'`). Also useful as the
|
|
34
|
+
* grep target when triaging failed crawls.
|
|
35
|
+
* @param fn - The async operation to wrap.
|
|
36
|
+
* @returns The resolved value of `fn`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function emitError<T>(emitter: Emitter, label: string, fn: () => Promise<T>): Promise<T>;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { log } from '../debug.js';
|
|
2
|
+
/**
|
|
3
|
+
* Debug logger for the ErrorEmitter helper family.
|
|
4
|
+
* Namespace: `Nitpicker:Utils:ErrorEmitter`. Kept identical to the namespace
|
|
5
|
+
* used by the deleted `@ErrorEmitter()` decorator so existing `DEBUG` env
|
|
6
|
+
* filters keep working after the decorator-to-HOF migration.
|
|
7
|
+
*/
|
|
8
|
+
const errorLog = log.extend('ErrorEmitter');
|
|
9
|
+
/**
|
|
10
|
+
* Wraps `fn` so that any thrown `Error` is logged with `label` and
|
|
11
|
+
* re-emitted as an `'error'` event on `emitter` before being re-thrown.
|
|
12
|
+
* Non-`Error` throws pass through unchanged — no log, no emit — matching
|
|
13
|
+
* the behaviour of the deleted `@ErrorEmitter()` decorator so that
|
|
14
|
+
* downstream `'error'` listeners (e.g. the crawler orchestrator that
|
|
15
|
+
* aborts on any emitted error) are only invoked with real `Error`
|
|
16
|
+
* instances.
|
|
17
|
+
*
|
|
18
|
+
* Kept as a standalone HOF (instead of a decorator) because Vite 8 /
|
|
19
|
+
* Vitest 4.1 switched from esbuild to Rolldown + oxc, which emit
|
|
20
|
+
* TC39 Stage 3 decorator syntax as-is and let it reach Node, causing
|
|
21
|
+
* `SyntaxError: Invalid or unexpected token` at test runtime.
|
|
22
|
+
* @template T - The return type of `fn`.
|
|
23
|
+
* @param emitter - The event emitter that receives the `'error'` event.
|
|
24
|
+
* @param label - Human-readable label included in the debug log line
|
|
25
|
+
* (typically `'ClassName.methodName'`). Also useful as the
|
|
26
|
+
* grep target when triaging failed crawls.
|
|
27
|
+
* @param fn - The async operation to wrap.
|
|
28
|
+
* @returns The resolved value of `fn`.
|
|
29
|
+
*/
|
|
30
|
+
export async function emitError(emitter, label, fn) {
|
|
31
|
+
try {
|
|
32
|
+
return await fn();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error instanceof Error) {
|
|
36
|
+
errorLog('%s: %O', label, error);
|
|
37
|
+
void emitter.emit('error', error);
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitpicker/crawler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Web crawler engine with headless browser rendering and archive storage",
|
|
5
5
|
"author": "D-ZERO",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,26 +27,26 @@
|
|
|
27
27
|
"clean": "tsc --build --clean"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@d-zero/beholder": "3.1.
|
|
31
|
-
"@d-zero/dealer": "1.9.
|
|
32
|
-
"@d-zero/fs": "0.2.
|
|
33
|
-
"@d-zero/shared": "0.
|
|
30
|
+
"@d-zero/beholder": "3.1.5",
|
|
31
|
+
"@d-zero/dealer": "1.9.4",
|
|
32
|
+
"@d-zero/fs": "0.2.6",
|
|
33
|
+
"@d-zero/shared": "0.22.2",
|
|
34
34
|
"ansi-colors": "4.1.3",
|
|
35
35
|
"debug": "4.4.3",
|
|
36
|
-
"follow-redirects": "1.
|
|
37
|
-
"fs-extra": "11.3.
|
|
38
|
-
"knex": "3.
|
|
36
|
+
"follow-redirects": "1.16.0",
|
|
37
|
+
"fs-extra": "11.3.6",
|
|
38
|
+
"knex": "3.3.0",
|
|
39
39
|
"libsql": "0.5.29",
|
|
40
|
-
"puppeteer": "
|
|
40
|
+
"puppeteer": "25.3.0",
|
|
41
41
|
"robots-parser": "3.0.1",
|
|
42
|
-
"tar": "7.5.
|
|
42
|
+
"tar": "7.5.20"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@types/debug": "4.1.
|
|
45
|
+
"@types/debug": "4.1.13",
|
|
46
46
|
"@types/follow-redirects": "1.14.4",
|
|
47
47
|
"@types/fs-extra": "11.0.4",
|
|
48
48
|
"@types/tar": "7.0.87",
|
|
49
49
|
"@types/unzipper": "0.10.11"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "2cd462ea6b183f4462b7caba27b4db04aaba7c70"
|
|
52
52
|
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Knex } from 'knex';
|
|
2
|
-
/**
|
|
3
|
-
* Adds the `crawl_errors` table to archives created before structured capture
|
|
4
|
-
* of the crawler-level `error` channel landed.
|
|
5
|
-
*
|
|
6
|
-
* `crawl_errors` records errors that previously only reached `error.log` (DNS
|
|
7
|
-
* failures, connection resets, TLS problems, process-level errors), in a
|
|
8
|
-
* queryable form so the `error-kinds` analysis can classify them without
|
|
9
|
-
* parsing the text log. Older `.nitpicker` files predate this table; this
|
|
10
|
-
* migration creates it idempotently. Existing archives stay empty until they
|
|
11
|
-
* are crawled again (resume / append / retry), at which point new errors are
|
|
12
|
-
* written here — analysis of pre-existing data falls back to parsing
|
|
13
|
-
* `error.log`.
|
|
14
|
-
*
|
|
15
|
-
* Idempotent: when the table already exists, the function exits without
|
|
16
|
-
* touching the schema or writing to stderr. It also skips empty archives (no
|
|
17
|
-
* `pages` table), which the regular initSchema path will fully provision.
|
|
18
|
-
* @param instance - The Knex query builder instance connected to the database.
|
|
19
|
-
*/
|
|
20
|
-
export declare function migrateCrawlErrors(instance: Knex): Promise<void>;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adds the `crawl_errors` table to archives created before structured capture
|
|
3
|
-
* of the crawler-level `error` channel landed.
|
|
4
|
-
*
|
|
5
|
-
* `crawl_errors` records errors that previously only reached `error.log` (DNS
|
|
6
|
-
* failures, connection resets, TLS problems, process-level errors), in a
|
|
7
|
-
* queryable form so the `error-kinds` analysis can classify them without
|
|
8
|
-
* parsing the text log. Older `.nitpicker` files predate this table; this
|
|
9
|
-
* migration creates it idempotently. Existing archives stay empty until they
|
|
10
|
-
* are crawled again (resume / append / retry), at which point new errors are
|
|
11
|
-
* written here — analysis of pre-existing data falls back to parsing
|
|
12
|
-
* `error.log`.
|
|
13
|
-
*
|
|
14
|
-
* Idempotent: when the table already exists, the function exits without
|
|
15
|
-
* touching the schema or writing to stderr. It also skips empty archives (no
|
|
16
|
-
* `pages` table), which the regular initSchema path will fully provision.
|
|
17
|
-
* @param instance - The Knex query builder instance connected to the database.
|
|
18
|
-
*/
|
|
19
|
-
export async function migrateCrawlErrors(instance) {
|
|
20
|
-
const hasTable = await instance.schema.hasTable('crawl_errors');
|
|
21
|
-
if (hasTable) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const hasPages = await instance.schema.hasTable('pages');
|
|
25
|
-
if (!hasPages) {
|
|
26
|
-
// Empty archive; the regular initSchema path will create the table.
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
await instance.schema.createTable('crawl_errors', (t) => {
|
|
30
|
-
t.increments('id');
|
|
31
|
-
t.string('url', 8190).nullable();
|
|
32
|
-
t.boolean('isExternal');
|
|
33
|
-
t.text('message').notNullable();
|
|
34
|
-
t.integer('createdAt').notNullable();
|
|
35
|
-
});
|
|
36
|
-
// eslint-disable-next-line no-console
|
|
37
|
-
console.error('[migrate] crawl_errors table created');
|
|
38
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Knex } from 'knex';
|
|
2
|
-
/**
|
|
3
|
-
* Adds the BLOB-backed HTML snapshot tables (`page_html_blobs` +
|
|
4
|
-
* `page_html_ref`) to archives created before #75.
|
|
5
|
-
*
|
|
6
|
-
* A pre-#75 `.nitpicker` already has the `info` table, so `initSchema`'s
|
|
7
|
-
* early-return path skips the freshly-introduced `CREATE TABLE`s. This
|
|
8
|
-
* migration brings the schema forward idempotently so the read API stops
|
|
9
|
-
* raising raw "no such table" errors against legacy archives. It does NOT
|
|
10
|
-
* touch the `pages.html` column or backfill the new tables from the
|
|
11
|
-
* archive's `snapshot-html.zip`: that data migration belongs to the
|
|
12
|
-
* `scripts/migrate-to-0.10.mjs` one-shot, which can run whenever the
|
|
13
|
-
* user is ready to commit the (multi-hour) CPU cost.
|
|
14
|
-
*
|
|
15
|
-
* Outcome on a legacy archive that has NOT yet been data-migrated:
|
|
16
|
-
* - new tables exist but are empty,
|
|
17
|
-
* - `getHtmlOfPageById` returns `null` for every page (no row in `page_html_ref`),
|
|
18
|
-
* - viewer / MCP / analyze plugins see "snapshot unavailable" instead of crashing.
|
|
19
|
-
*
|
|
20
|
-
* The migration prints a one-line notice when it runs so the user knows
|
|
21
|
-
* the archive was upgraded in place.
|
|
22
|
-
* @param instance - The Knex query builder instance connected to the database.
|
|
23
|
-
*/
|
|
24
|
-
export declare function migrateHtmlBlobTables(instance: Knex): Promise<void>;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adds the BLOB-backed HTML snapshot tables (`page_html_blobs` +
|
|
3
|
-
* `page_html_ref`) to archives created before #75.
|
|
4
|
-
*
|
|
5
|
-
* A pre-#75 `.nitpicker` already has the `info` table, so `initSchema`'s
|
|
6
|
-
* early-return path skips the freshly-introduced `CREATE TABLE`s. This
|
|
7
|
-
* migration brings the schema forward idempotently so the read API stops
|
|
8
|
-
* raising raw "no such table" errors against legacy archives. It does NOT
|
|
9
|
-
* touch the `pages.html` column or backfill the new tables from the
|
|
10
|
-
* archive's `snapshot-html.zip`: that data migration belongs to the
|
|
11
|
-
* `scripts/migrate-to-0.10.mjs` one-shot, which can run whenever the
|
|
12
|
-
* user is ready to commit the (multi-hour) CPU cost.
|
|
13
|
-
*
|
|
14
|
-
* Outcome on a legacy archive that has NOT yet been data-migrated:
|
|
15
|
-
* - new tables exist but are empty,
|
|
16
|
-
* - `getHtmlOfPageById` returns `null` for every page (no row in `page_html_ref`),
|
|
17
|
-
* - viewer / MCP / analyze plugins see "snapshot unavailable" instead of crashing.
|
|
18
|
-
*
|
|
19
|
-
* The migration prints a one-line notice when it runs so the user knows
|
|
20
|
-
* the archive was upgraded in place.
|
|
21
|
-
* @param instance - The Knex query builder instance connected to the database.
|
|
22
|
-
*/
|
|
23
|
-
export async function migrateHtmlBlobTables(instance) {
|
|
24
|
-
const hasBlobs = await instance.schema.hasTable('page_html_blobs');
|
|
25
|
-
if (hasBlobs) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const hasPages = await instance.schema.hasTable('pages');
|
|
29
|
-
if (!hasPages) {
|
|
30
|
-
// Empty archive; initSchema will create both tables on the
|
|
31
|
-
// fresh-DB path. Nothing to migrate.
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
await instance.raw(`
|
|
35
|
-
CREATE TABLE page_html_blobs (
|
|
36
|
-
hash BLOB PRIMARY KEY,
|
|
37
|
-
body BLOB NOT NULL,
|
|
38
|
-
codec TEXT NOT NULL CHECK(codec IN ('zstd', 'none')),
|
|
39
|
-
size_raw INTEGER NOT NULL,
|
|
40
|
-
size_stored INTEGER NOT NULL
|
|
41
|
-
) WITHOUT ROWID
|
|
42
|
-
`);
|
|
43
|
-
await instance.raw(`
|
|
44
|
-
CREATE TABLE page_html_ref (
|
|
45
|
-
page_id INTEGER PRIMARY KEY REFERENCES pages(id) ON DELETE CASCADE,
|
|
46
|
-
hash BLOB NOT NULL REFERENCES page_html_blobs(hash)
|
|
47
|
-
) WITHOUT ROWID
|
|
48
|
-
`);
|
|
49
|
-
await instance.raw('CREATE INDEX idx_page_html_ref_hash ON page_html_ref(hash)');
|
|
50
|
-
// eslint-disable-next-line no-console
|
|
51
|
-
console.error('[migrate] page_html_blobs / page_html_ref tables created. ' +
|
|
52
|
-
'HTML snapshots are empty until `node scripts/migrate-to-0.10.mjs` is run on the original archive.');
|
|
53
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Knex } from 'knex';
|
|
2
|
-
/**
|
|
3
|
-
* Adds the `page_errors` table to archives created before partial-failure
|
|
4
|
-
* recording landed.
|
|
5
|
-
*
|
|
6
|
-
* `page_errors` captures secondary scrape failures (e.g. a viewport switch
|
|
7
|
-
* that detaches the frame and surfaces `retryExhausted`) so they show up in
|
|
8
|
-
* the archive instead of being lost to stdout logs. Older `.nitpicker` files
|
|
9
|
-
* predate this table; this migration creates it idempotently.
|
|
10
|
-
*
|
|
11
|
-
* Idempotent: when the table already exists, the function exits without
|
|
12
|
-
* touching the schema or writing to stderr. When it has to run, a single
|
|
13
|
-
* notice is written so the user knows the file was upgraded.
|
|
14
|
-
* @param instance - The Knex query builder instance connected to the database.
|
|
15
|
-
*/
|
|
16
|
-
export declare function migratePageErrors(instance: Knex): Promise<void>;
|