@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
|
@@ -5,11 +5,16 @@ import { sortUrl } from '@d-zero/shared/sort-url';
|
|
|
5
5
|
import { TypedAwaitEventEmitter as EventEmitter } from '@d-zero/shared/typed-await-event-emitter';
|
|
6
6
|
import pkg from '../package.json' with { type: 'json' };
|
|
7
7
|
import Archive from './archive/archive.js';
|
|
8
|
+
import { REQUIRED_FORMAT_VERSION } from './archive/meta/assert-compatible-version.js';
|
|
8
9
|
import { clearDestinationCache } from './crawler/clear-destination-cache.js';
|
|
10
|
+
import { clearDnsBurnedHostCache } from './crawler/clear-dns-burned-host-cache.js';
|
|
9
11
|
import Crawler from './crawler/crawler.js';
|
|
10
|
-
import {
|
|
12
|
+
import { dnsBurnedHostCache } from './crawler/dns-burned-host-cache.js';
|
|
13
|
+
import { dnsBurnedHostShortCircuitCounter } from './crawler/dns-burned-host-short-circuit-counter.js';
|
|
11
14
|
import { findScopeEntry } from './crawler/find-scope-entry.js';
|
|
12
|
-
import {
|
|
15
|
+
import { isLikelyHtmlUrl } from './crawler/is-likely-html-url.js';
|
|
16
|
+
import { PreloadShortCircuitError } from './crawler/preload-short-circuit-error.js';
|
|
17
|
+
import { protocolAgnosticKey } from './crawler/protocol-agnostic-key.js';
|
|
13
18
|
import { crawlerLog, log } from './debug.js';
|
|
14
19
|
import { normalizeToArray } from './normalize-to-array.js';
|
|
15
20
|
import { resolveOutputPath } from './resolve-output-path.js';
|
|
@@ -20,6 +25,9 @@ import { WriteQueue } from './write-queue.js';
|
|
|
20
25
|
* Default list of external URL prefixes excluded from crawling.
|
|
21
26
|
* Includes social media sharing endpoints that are commonly linked
|
|
22
27
|
* but provide no useful crawl data.
|
|
28
|
+
* @example
|
|
29
|
+
* // Merged ahead of user-supplied excludeUrls when a crawl starts:
|
|
30
|
+
* const excludeUrls = [...DEFAULT_EXCLUDED_EXTERNAL_URLS, 'https://ads.example.com'];
|
|
23
31
|
*/
|
|
24
32
|
export const DEFAULT_EXCLUDED_EXTERNAL_URLS = [
|
|
25
33
|
'https://social-plugins.line.me',
|
|
@@ -116,8 +124,16 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
116
124
|
const row = await this.#writeQueue.enqueue(() => this.#archive.getResourceByUrl(urls));
|
|
117
125
|
return row ? resourceRowToLookupResult(row) : null;
|
|
118
126
|
},
|
|
119
|
-
//
|
|
120
|
-
//
|
|
127
|
+
// Let the crawler propagate the parent's source lineage to
|
|
128
|
+
// sub-resources on `--resume` / `--retry-failed` sessions, where
|
|
129
|
+
// `inventoryMode` is not in memory but the DB still remembers
|
|
130
|
+
// the parent's `source`. Without this, sub-resources captured
|
|
131
|
+
// during a re-render of an inventory-labelled page would fall
|
|
132
|
+
// back to the DB DEFAULT `'crawled'` and lose their
|
|
133
|
+
// `'inventory-discovered'` provenance.
|
|
134
|
+
lookupPageSource: async (url) => this.#archive.getPageSourceByUrl(url),
|
|
135
|
+
// Inventory mode is opted into by `CrawlerOrchestrator.inventory`;
|
|
136
|
+
// the default crawl path stays in normal mode so new
|
|
121
137
|
// rows continue to land in pages/resources with the DB DEFAULT
|
|
122
138
|
// `'crawled'` provenance label.
|
|
123
139
|
inventoryMode: options?.inventoryMode ?? null,
|
|
@@ -139,22 +155,30 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
139
155
|
* Sets up event listeners on the crawler, starts crawling, and resolves
|
|
140
156
|
* when the crawl completes. Discovered pages, external pages, skipped pages,
|
|
141
157
|
* and resources are forwarded to the archive for storage.
|
|
142
|
-
* @param list - The list of parsed URLs to crawl.
|
|
158
|
+
* @param list - The list of parsed URLs to crawl. May be empty when a resumed
|
|
159
|
+
* session already has pending pages queued (for example `--retry-failed`).
|
|
143
160
|
* @param opts - Optional crawl overrides.
|
|
144
161
|
* @param opts.recursive - Whether discovered URLs are followed. Defaults to
|
|
145
162
|
* `!fromList` (recursive unless the archive was created from a URL list), so
|
|
146
163
|
* existing callers keep their behaviour; the retry flow passes it explicitly.
|
|
147
164
|
* @returns A promise that resolves when crawling is complete.
|
|
148
|
-
* @throws {Error} If the URL list is empty.
|
|
149
165
|
*/
|
|
150
166
|
async crawling(list, opts) {
|
|
151
|
-
const root = list[0];
|
|
152
|
-
if (!root) {
|
|
153
|
-
throw new Error('URL is empty');
|
|
154
|
-
}
|
|
155
167
|
const writeQueue = this.#writeQueue;
|
|
156
168
|
return new Promise((resolve, reject) => {
|
|
157
169
|
this.#crawler.on('error', (error) => {
|
|
170
|
+
if (error.error instanceof PreloadShortCircuitError) {
|
|
171
|
+
// DNS-burned host short-circuit: the underlying cause already
|
|
172
|
+
// lives in `crawl_errors` from the original DNS failure.
|
|
173
|
+
// Writing it again on every subsequent URL would amplify the
|
|
174
|
+
// row count on each `--retry-failed` re-run and could even
|
|
175
|
+
// inflate the preload selection on the next open. Drop it
|
|
176
|
+
// here; `pages.status = -1` still gets set via the normal
|
|
177
|
+
// scrape-error path (handleScrapeError → addPageError) so the
|
|
178
|
+
// page record itself is unchanged.
|
|
179
|
+
crawlerLog('Skipping addError for preload short-circuit: %s', error.url);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
158
182
|
crawlerLog('On error: %O', error);
|
|
159
183
|
writeQueue
|
|
160
184
|
.enqueue(() => this.#archive.addError(error))
|
|
@@ -181,9 +205,9 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
181
205
|
.enqueue(() => this.#archive.addPageError(url, phase, message, isExternal))
|
|
182
206
|
.catch((error) => reject(error));
|
|
183
207
|
});
|
|
184
|
-
this.#crawler.on('redirect', ({ result }) => {
|
|
208
|
+
this.#crawler.on('redirect', ({ result, source }) => {
|
|
185
209
|
writeQueue
|
|
186
|
-
.enqueue(() => this.#archive.setRedirect(result))
|
|
210
|
+
.enqueue(() => this.#archive.setRedirect(result, source))
|
|
187
211
|
.catch((error) => reject(error));
|
|
188
212
|
void this.emit('redirect', { result });
|
|
189
213
|
});
|
|
@@ -238,6 +262,12 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
238
262
|
/**
|
|
239
263
|
* Write the archive to its configured file path.
|
|
240
264
|
*
|
|
265
|
+
* The crawler's write path inserts directly into the 0.13 entity
|
|
266
|
+
* tables (`content_items` / `page_meta` / `anchor_edges` / …) during
|
|
267
|
+
* `crawling` / `append` / `resume` / `retryFailed` / `inventory`, so by
|
|
268
|
+
* the time `write()` is called those tables are already populated.
|
|
269
|
+
* This method just tars.
|
|
270
|
+
*
|
|
241
271
|
* Emits `writeFileStart` before writing and `writeFileEnd` after
|
|
242
272
|
* the write completes successfully.
|
|
243
273
|
*/
|
|
@@ -279,7 +309,14 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
279
309
|
// Each positional URL is both a starting point and a scope entry.
|
|
280
310
|
const rootHrefs = list.map((u) => u.withoutHash);
|
|
281
311
|
await archive.setConfig({
|
|
282
|
-
version
|
|
312
|
+
// `version` is the archive-format version (see
|
|
313
|
+
// `assertCompatibleVersion`), NOT the npm package version. Decoupled
|
|
314
|
+
// because format-breaking changes and code-release cadence are
|
|
315
|
+
// different concerns — a patch release must not silently bump the
|
|
316
|
+
// format version and reject older archives, and a dev build of an
|
|
317
|
+
// unreleased breaking change must be able to produce archives the
|
|
318
|
+
// same build can read back.
|
|
319
|
+
version: REQUIRED_FORMAT_VERSION,
|
|
283
320
|
name: fileName,
|
|
284
321
|
baseUrl: rootHrefs[0],
|
|
285
322
|
roots: rootHrefs,
|
|
@@ -314,7 +351,7 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
314
351
|
log('Config %O', config);
|
|
315
352
|
await orchestrator.crawling(list);
|
|
316
353
|
log('Crawling completed');
|
|
317
|
-
|
|
354
|
+
CrawlerOrchestrator.#finalizeCrawlSession();
|
|
318
355
|
log('Set order natural URL sort');
|
|
319
356
|
await archive.setUrlOrder();
|
|
320
357
|
log('Sorting done');
|
|
@@ -400,8 +437,9 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
400
437
|
log('Archive %s', absFilePath);
|
|
401
438
|
log('New roots %O', newRoots);
|
|
402
439
|
log('Merged roots %O', mergedRoots);
|
|
440
|
+
await CrawlerOrchestrator.#preloadDnsBurnedHostCache(archive);
|
|
403
441
|
await orchestrator.crawling(newParsed);
|
|
404
|
-
|
|
442
|
+
CrawlerOrchestrator.#finalizeCrawlSession();
|
|
405
443
|
await archive.setUrlOrder();
|
|
406
444
|
await ignoreEnoent(unlinkFile(backupPath));
|
|
407
445
|
return orchestrator;
|
|
@@ -466,10 +504,20 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
466
504
|
* @param inventoryUrls - Pre-read URL list (one URL per element).
|
|
467
505
|
* @param options - Optional config overrides — most callers leave this blank and let the archived config flow through.
|
|
468
506
|
* @param initializedCallback - Hook invoked once the orchestrator is constructed but before `crawling` runs (the CLI uses it to attach progress reporting).
|
|
507
|
+
* @param sourceFileSha256 - **Pre-computed** SHA-256 hex digest of the
|
|
508
|
+
* source URL list. The orchestrator deliberately does NOT receive
|
|
509
|
+
* the file path: the path is privacy-sensitive (leaks user-home /
|
|
510
|
+
* OS structure when archives are shared) and we want it lifted off
|
|
511
|
+
* this boundary so no future log line / breadcrumb / error message
|
|
512
|
+
* inside the orchestrator can accidentally re-leak it. The CLI
|
|
513
|
+
* computes the digest via `computeFileSha256(resolvedListFile)`
|
|
514
|
+
* and passes it through here. Pass `null` for programmatic
|
|
515
|
+
* callers that built `inventoryUrls` in-memory; the audit row's
|
|
516
|
+
* `source_file_sha256` column will be `NULL`.
|
|
469
517
|
* @returns The orchestrator instance after a successful inventory pass.
|
|
470
518
|
* @throws {Error} When `inventoryUrls` is empty, the archive is in list mode, or pending URLs from a previous crawl remain unresolved.
|
|
471
519
|
*/
|
|
472
|
-
static async inventory(archivePath, inventoryUrls, options, initializedCallback) {
|
|
520
|
+
static async inventory(archivePath, inventoryUrls, options, initializedCallback, sourceFileSha256 = null) {
|
|
473
521
|
if (inventoryUrls.length === 0) {
|
|
474
522
|
throw new Error('inventory: URL list is empty');
|
|
475
523
|
}
|
|
@@ -483,9 +531,21 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
483
531
|
if (archived.fromList) {
|
|
484
532
|
throw new Error('Cannot run inventory on a list-mode archive: this archive was created with --list/--list-file and contains metadata-only pages. Create a fresh archive instead.');
|
|
485
533
|
}
|
|
486
|
-
const {
|
|
534
|
+
const { pending } = await archive.getCrawlingState();
|
|
487
535
|
if (pending.length > 0) {
|
|
488
|
-
|
|
536
|
+
// `getCrawlingState` returns the STRICT pending set — in-scope,
|
|
537
|
+
// anchor-referenced, `scraped=0` rows. Predicted-discard leaks
|
|
538
|
+
// and external anomalies are filtered out at the reader, so a
|
|
539
|
+
// non-empty pending here means the previous session genuinely
|
|
540
|
+
// stopped with interrupted in-scope work. A hard rejection is
|
|
541
|
+
// still not warranted (with a looser reader it would block
|
|
542
|
+
// legitimate inventory runs whenever leak rows polluted the
|
|
543
|
+
// count), so a warning is
|
|
544
|
+
// enough — the inventory pass continues and the crawled-wins
|
|
545
|
+
// source priority keeps stale labels stable even if some of
|
|
546
|
+
// the strict-pending rows happen to land on inventory seeds.
|
|
547
|
+
// eslint-disable-next-line no-console -- operator-facing warning, must be visible regardless of DEBUG filters
|
|
548
|
+
console.warn(`inventory: archive has ${pending.length} pending URLs from a previous crawl. Proceeding — crawled-wins priority keeps their labels stable. Consider \`--resume\` first if you want the prior work finalized.`);
|
|
489
549
|
}
|
|
490
550
|
// Parse + scope-classify the candidate URLs. sortUrl drops
|
|
491
551
|
// unparseable strings; findScopeEntry separates in-scope from
|
|
@@ -514,7 +574,7 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
514
574
|
}
|
|
515
575
|
// Drop URLs that are already represented in the archive (either
|
|
516
576
|
// as pages or resources). Comparison key is `withoutHashAndAuth`
|
|
517
|
-
// to mirror what
|
|
577
|
+
// to mirror what `resolveContentItemId` / `insertResource` actually store.
|
|
518
578
|
// Two independent reads — Promise.all halves the wait on large
|
|
519
579
|
// archives where each `WHERE url IN (?)` chunk costs real I/O.
|
|
520
580
|
const candidateUrls = inScope.map((u) => u.withoutHashAndAuth);
|
|
@@ -546,56 +606,115 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
546
606
|
}
|
|
547
607
|
const backupPath = absFilePath + '.bak';
|
|
548
608
|
await copyFile(absFilePath, backupPath);
|
|
609
|
+
// Ingestion (pre-insert + audit) is `.bak`-protected — a failure
|
|
610
|
+
// there restores the archive and the operator reruns. Once
|
|
611
|
+
// ingestion completes and the `.bak` is released, the scrape
|
|
612
|
+
// phase runs without `.bak` protection: a Ctrl+C / crash leaves
|
|
613
|
+
// the pre-inserted `inventory-seed` rows in `pages` so
|
|
614
|
+
// `crawl --resume` recovers them via the strict-pending set
|
|
615
|
+
// (see {@link Database.getCrawlingState}'s `OR p.source != 'crawled'`
|
|
616
|
+
// clause). This flag steers the catch below.
|
|
617
|
+
let ingestionComplete = false;
|
|
549
618
|
try {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
619
|
+
// Classify novel URLs by URL-extension heuristic (no I/O).
|
|
620
|
+
// Source file lists come from `ls` on the doc-root, so the
|
|
621
|
+
// extension reflects the real file type — a HEAD pre-flight
|
|
622
|
+
// here would be pure wasted I/O. Edge cases:
|
|
623
|
+
//
|
|
624
|
+
// - `.html` returning 404 / 200: the normal crawler HEAD/GET
|
|
625
|
+
// path absorbs this because every HTML-classified URL is
|
|
626
|
+
// fed through the dealer and gets its real HEAD/GET there.
|
|
627
|
+
//
|
|
628
|
+
// - Extensionless API endpoints (e.g. `/api/foo`) that the
|
|
629
|
+
// server returns as `text/html`: `isLikelyHtmlUrl` accepts
|
|
630
|
+
// them as HTML so the dealer's render path runs — the
|
|
631
|
+
// real content-type wins downstream.
|
|
632
|
+
//
|
|
633
|
+
// - `.aspx` / `.do` / `.jsp` / other server-handler
|
|
634
|
+
// extensions that the heuristic does NOT recognise as
|
|
635
|
+
// HTML: these are classified as non-HTML here, recorded
|
|
636
|
+
// as `resources` rows with all-null metadata, and never
|
|
637
|
+
// get a HEAD/GET probe. The accepted trade-off for
|
|
638
|
+
// `--inventory`'s "list of static-looking server files"
|
|
639
|
+
// contract; sites that mix server-handlers into the
|
|
640
|
+
// inventory list will need a follow-up `--retry-failed`
|
|
641
|
+
// pass (or a re-`--inventory` with the corrected list)
|
|
642
|
+
// to populate metadata.
|
|
643
|
+
//
|
|
644
|
+
// non-HTML rows are recorded with null status/content-type
|
|
645
|
+
// which is sufficient for `listUnusedResources` (referrer
|
|
646
|
+
// count = 0) but means downstream consumers must treat
|
|
647
|
+
// null as "not probed" rather than "failed".
|
|
648
|
+
const rawHtmlSeeds = [];
|
|
649
|
+
const nonHtmlSeeds = [];
|
|
650
|
+
for (const url of novelUrls) {
|
|
651
|
+
if (isLikelyHtmlUrl(url)) {
|
|
652
|
+
rawHtmlSeeds.push(url);
|
|
558
653
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return { url, head: null, error };
|
|
654
|
+
else {
|
|
655
|
+
nonHtmlSeeds.push(url);
|
|
562
656
|
}
|
|
563
|
-
}
|
|
657
|
+
}
|
|
658
|
+
// Dedup HTML seeds by `protocolAgnosticKey` so an inventory
|
|
659
|
+
// list that mixes `http://` and `https://` for the same
|
|
660
|
+
// origin does not produce two `pages` rows that the dealer
|
|
661
|
+
// later collapses to one — the loser would otherwise stay
|
|
662
|
+
// `scraped=0, source='inventory-seed'` forever and look like
|
|
663
|
+
// a real recovery candidate on `--resume`. `getExistingPageUrls`
|
|
664
|
+
// keys on the full URL (with protocol), so it cannot catch
|
|
665
|
+
// the cross-scheme duplicate; this is the dedup boundary.
|
|
666
|
+
const seenKeys = new Set();
|
|
564
667
|
const htmlSeeds = [];
|
|
565
|
-
for (const
|
|
566
|
-
const
|
|
567
|
-
if (
|
|
568
|
-
// HEAD failure is recorded as a crawl_errors row so
|
|
569
|
-
// the URL is visible in `query error-kinds`, but does
|
|
570
|
-
// NOT abort the whole inventory pass — other novel
|
|
571
|
-
// URLs may still succeed.
|
|
572
|
-
await archive.addError({
|
|
573
|
-
pid: process.pid,
|
|
574
|
-
isMainProcess: true,
|
|
575
|
-
url: url.href,
|
|
576
|
-
isExternal: false,
|
|
577
|
-
error,
|
|
578
|
-
});
|
|
668
|
+
for (const url of rawHtmlSeeds) {
|
|
669
|
+
const key = protocolAgnosticKey(url.withoutHashAndAuth);
|
|
670
|
+
if (seenKeys.has(key)) {
|
|
579
671
|
continue;
|
|
580
672
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
else {
|
|
585
|
-
await archive.setResources({
|
|
586
|
-
url,
|
|
587
|
-
isExternal: false,
|
|
588
|
-
isError: false,
|
|
589
|
-
status: head.status,
|
|
590
|
-
statusText: head.statusText,
|
|
591
|
-
contentType: head.contentType,
|
|
592
|
-
contentLength: head.contentLength,
|
|
593
|
-
compress: false,
|
|
594
|
-
cdn: false,
|
|
595
|
-
headers: head.responseHeaders ?? null,
|
|
596
|
-
}, 'inventory-seed');
|
|
597
|
-
}
|
|
673
|
+
seenKeys.add(key);
|
|
674
|
+
htmlSeeds.push(url);
|
|
598
675
|
}
|
|
676
|
+
// Bulk-record non-HTML novel URLs in `resources` as
|
|
677
|
+
// `source='inventory-seed'` placeholders. A
|
|
678
|
+
// per-URL `await setResources(...)` loop would spend minutes
|
|
679
|
+
// inside the `.bak`-protected window on large inventory
|
|
680
|
+
// lists; the chunked bulk path collapses N round-trips
|
|
681
|
+
// to N/500.
|
|
682
|
+
await archive.insertInventoryResources(nonHtmlSeeds);
|
|
683
|
+
// Pre-insert HTML seeds as `scraped = 0`,
|
|
684
|
+
// `source = 'inventory-seed'` placeholders *before* the
|
|
685
|
+
// scrape phase, so a Ctrl+C between here and `setPage`
|
|
686
|
+
// cannot lose the URL. The strict-pending set picks
|
|
687
|
+
// these rows up on the next `--resume` via the
|
|
688
|
+
// `OR p.source != 'crawled'` clause.
|
|
689
|
+
await archive.insertInventorySeeds(htmlSeeds);
|
|
690
|
+
log('[inventory] %d HTML seed(s), %d non-HTML resource(s) recorded', htmlSeeds.length, nonHtmlSeeds.length);
|
|
691
|
+
// Audit row is written *inside* the `.bak` window: a libsql
|
|
692
|
+
// hiccup or transient lock on the INSERT aborts the ingestion
|
|
693
|
+
// and the `.bak` restore wipes the pre-inserted seeds too,
|
|
694
|
+
// so "either the whole run took or none of it did" holds at
|
|
695
|
+
// the ingestion boundary. Audit failures are deliberately
|
|
696
|
+
// NOT swallowed — inside the `.bak` window a restore is
|
|
697
|
+
// safe and useful (see
|
|
698
|
+
// {@link CrawlerOrchestrator.#writeInventoryRunRow}).
|
|
699
|
+
await CrawlerOrchestrator.#writeInventoryRunRow(archive, {
|
|
700
|
+
inventoryUrlsCount: inventoryUrls.length,
|
|
701
|
+
htmlSeedsCount: htmlSeeds.length,
|
|
702
|
+
nonHtmlCount: nonHtmlSeeds.length,
|
|
703
|
+
outOfScope,
|
|
704
|
+
sourceFileSha256,
|
|
705
|
+
});
|
|
706
|
+
// Ingestion's DB writes are now committed. From here on a
|
|
707
|
+
// throw must NOT trigger the `.bak` restore (it would wipe
|
|
708
|
+
// the durable seeds + audit row). Setting the flag *before*
|
|
709
|
+
// the `.bak` unlink covers the rare Windows / antivirus
|
|
710
|
+
// path where `unlinkFile` itself fails with EBUSY/EPERM —
|
|
711
|
+
// the `.bak` may leak on disk for the operator to delete
|
|
712
|
+
// manually, but the archive state stays intact.
|
|
713
|
+
ingestionComplete = true;
|
|
714
|
+
// Release `.bak` — ingestion succeeded. Beyond this point a
|
|
715
|
+
// throw is the scrape phase's problem; the archive stays
|
|
716
|
+
// intact and the operator runs `--resume` to recover.
|
|
717
|
+
await ignoreEnoent(unlinkFile(backupPath));
|
|
599
718
|
// Config sent to the user-facing `initializedCallback`
|
|
600
719
|
// (matches the rest of the orchestrator's public surface —
|
|
601
720
|
// no inventory bookkeeping leaks out).
|
|
@@ -615,22 +734,33 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
615
734
|
};
|
|
616
735
|
if (htmlSeeds.length > 0) {
|
|
617
736
|
const orchestrator = new CrawlerOrchestrator(archive, orchestratorOptions);
|
|
737
|
+
// Re-read pending *after* the pre-insert so the strict-
|
|
738
|
+
// pending set includes the freshly inserted
|
|
739
|
+
// `inventory-seed` rows; feed that into `crawler.resume`
|
|
740
|
+
// and start a seedless `crawling([])` — the same pattern
|
|
741
|
+
// `retryFailed` uses to drive the dealer from the
|
|
742
|
+
// pending set alone (see retryFailed's
|
|
743
|
+
// `crawling([], { recursive })` invocation).
|
|
744
|
+
const { scraped: scrapedAfter, pending: pendingAfter } = await archive.getCrawlingState();
|
|
618
745
|
const resources = await archive.getResourceUrlList();
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
622
|
-
//
|
|
623
|
-
|
|
746
|
+
// Pre-existing rendered HTML page count seeds the
|
|
747
|
+
// session-spanning `pagesScraped` counter so the progress
|
|
748
|
+
// header reads `internalDone(cumulative pagesScraped)`
|
|
749
|
+
// rather than session-only — matches the `append` /
|
|
750
|
+
// `retryFailed` / `resume` paths and avoids users reading
|
|
751
|
+
// the parenthesised number as "inner pages dropped to N".
|
|
752
|
+
const pagesScrapedOffset = await archive.getScrapedHtmlPageCount();
|
|
753
|
+
orchestrator.#crawler.resume(pendingAfter, scrapedAfter, resources, pagesScrapedOffset);
|
|
624
754
|
if (initializedCallback) {
|
|
625
755
|
await initializedCallback(orchestrator, baseConfig);
|
|
626
756
|
}
|
|
627
757
|
log('Start inventory');
|
|
628
758
|
log('Archive %s', absFilePath);
|
|
629
759
|
log('HTML seeds %O', htmlSeeds.map((u) => u.href));
|
|
630
|
-
await
|
|
631
|
-
|
|
760
|
+
await CrawlerOrchestrator.#preloadDnsBurnedHostCache(archive);
|
|
761
|
+
await orchestrator.crawling([], { recursive: true });
|
|
762
|
+
CrawlerOrchestrator.#finalizeCrawlSession();
|
|
632
763
|
await archive.setUrlOrder();
|
|
633
|
-
await ignoreEnoent(unlinkFile(backupPath));
|
|
634
764
|
return orchestrator;
|
|
635
765
|
}
|
|
636
766
|
// Only non-HTML URLs were imported — nothing left to render,
|
|
@@ -640,10 +770,34 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
640
770
|
await initializedCallback(orchestrator, baseConfig);
|
|
641
771
|
}
|
|
642
772
|
await archive.setUrlOrder();
|
|
643
|
-
await ignoreEnoent(unlinkFile(backupPath));
|
|
644
773
|
return orchestrator;
|
|
645
774
|
}
|
|
646
775
|
catch (error) {
|
|
776
|
+
if (ingestionComplete) {
|
|
777
|
+
// Scrape phase failed; the pre-inserted seeds + audit
|
|
778
|
+
// row are durable inside `tmpDir/db.sqlite` but not yet
|
|
779
|
+
// on disk as a `.nitpicker` tar. The outer catch below
|
|
780
|
+
// runs `archive.close()`, which sees the original
|
|
781
|
+
// (pre-inventory) `.nitpicker` already on disk and
|
|
782
|
+
// would just `remove(tmpDir)` — silently wiping every
|
|
783
|
+
// `inventory-seed` row and the audit row.
|
|
784
|
+
//
|
|
785
|
+
// Persist the ingested state ourselves before letting
|
|
786
|
+
// the outer catch unwind, then re-throw so the operator
|
|
787
|
+
// learns about the scrape failure (and can recover via
|
|
788
|
+
// `crawl --resume <archive>`). `releaseHandle` shares
|
|
789
|
+
// the orchestrator's `#closeOnce` guard, so the outer
|
|
790
|
+
// catch's `close()` becomes a no-op for the destructive
|
|
791
|
+
// step and only runs `releaseLock` cleanup.
|
|
792
|
+
try {
|
|
793
|
+
await archive.write();
|
|
794
|
+
await archive.releaseHandle();
|
|
795
|
+
}
|
|
796
|
+
catch (persistError) {
|
|
797
|
+
throw new AggregateError([error, persistError], 'inventory scrape phase failed AND persisting the ingested state to disk also failed. The archive may be in an inconsistent state — check tmpDir.');
|
|
798
|
+
}
|
|
799
|
+
throw error;
|
|
800
|
+
}
|
|
647
801
|
try {
|
|
648
802
|
await copyFile(backupPath, absFilePath);
|
|
649
803
|
await ignoreEnoent(unlinkFile(backupPath));
|
|
@@ -732,8 +886,9 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
732
886
|
if (initializedCallback) {
|
|
733
887
|
await initializedCallback(orchestrator, config);
|
|
734
888
|
}
|
|
735
|
-
await
|
|
736
|
-
|
|
889
|
+
await CrawlerOrchestrator.#preloadDnsBurnedHostCache(archive);
|
|
890
|
+
await orchestrator.crawling([], { recursive: config.recursive });
|
|
891
|
+
CrawlerOrchestrator.#finalizeCrawlSession();
|
|
737
892
|
await archive.setUrlOrder();
|
|
738
893
|
await ignoreEnoent(unlinkFile(backupPath));
|
|
739
894
|
return orchestrator;
|
|
@@ -793,9 +948,93 @@ export class CrawlerOrchestrator extends EventEmitter {
|
|
|
793
948
|
log('Data %s', stubPath);
|
|
794
949
|
log('URL %s', url.href);
|
|
795
950
|
log('Config %O', config);
|
|
951
|
+
await CrawlerOrchestrator.#preloadDnsBurnedHostCache(archive);
|
|
796
952
|
await orchestrator.crawling([url]);
|
|
953
|
+
CrawlerOrchestrator.#finalizeCrawlSession();
|
|
797
954
|
return orchestrator;
|
|
798
955
|
}
|
|
956
|
+
/**
|
|
957
|
+
* Seeds {@link dnsBurnedHostCache} from `crawl_errors` history at re-open
|
|
958
|
+
* (append / inventory / retryFailed / resume). Called after Archive.open
|
|
959
|
+
* succeeds and before crawling starts, so the first URL on a burned host
|
|
960
|
+
* already short-circuits — no retry budget is spent on a dead host that
|
|
961
|
+
* the previous crawl already proved was dead.
|
|
962
|
+
*
|
|
963
|
+
* Fresh `crawling()` skips this — there is no archive history to seed
|
|
964
|
+
* from. Within-session learning still kicks in via the `onGiveUp` mark.
|
|
965
|
+
* @param archive - The opened archive whose `crawl_errors` is read.
|
|
966
|
+
*/
|
|
967
|
+
static async #preloadDnsBurnedHostCache(archive) {
|
|
968
|
+
const hosts = await archive.listDnsBurnedHostCandidates();
|
|
969
|
+
for (const host of hosts) {
|
|
970
|
+
dnsBurnedHostCache.set(host, 'dns');
|
|
971
|
+
}
|
|
972
|
+
if (hosts.length > 0) {
|
|
973
|
+
// eslint-disable-next-line no-console
|
|
974
|
+
console.error(`[preload] DNS-burned hosts: ${hosts.length} (will short-circuit subsequent URLs)`);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Persist one `inventory_runs` audit row inside the ingestion phase of a
|
|
979
|
+
* `--inventory` invocation, before the `.bak` is released. Lives as a
|
|
980
|
+
* static helper because the audit-row shape (timestamp stamping + label
|
|
981
|
+
* auto-gen + the privacy-driven path elision documented below) is a
|
|
982
|
+
* cohesive concern that benefits from staying outside the long
|
|
983
|
+
* `inventory()` body even though it has a single caller.
|
|
984
|
+
*
|
|
985
|
+
* `ran_at` is stamped now (ingestion-completion timestamp; the scrape
|
|
986
|
+
* phase that may follow is treated as separate). `list_label` is
|
|
987
|
+
* auto-generated from `ran_at` when the CLI did not pass one — there is
|
|
988
|
+
* no `--label` flag, so this is always the auto form.
|
|
989
|
+
* `source_file_sha256` arrives pre-computed via
|
|
990
|
+
* `aggregates.sourceFileSha256` (the CLI's `inventoryCrawl` ran
|
|
991
|
+
* `computeFileSha256` against the input txt before the orchestrator
|
|
992
|
+
* was even invoked). The orchestrator boundary deliberately never sees
|
|
993
|
+
* the absolute path — see {@link InventoryRunAggregates} for the
|
|
994
|
+
* privacy rationale.
|
|
995
|
+
*
|
|
996
|
+
* **Audit-write failures abort the ingestion phase.** Swallowing them
|
|
997
|
+
* would only be justified if the audit were the last write after the
|
|
998
|
+
* scrape (re-throwing there would wipe a completed crawl); inside the
|
|
999
|
+
* `.bak`-protected ingestion phase the
|
|
1000
|
+
* trade-off flips. A failed audit row is restorable: the outer catch
|
|
1001
|
+
* copies `.bak` back over the archive and the operator reruns the
|
|
1002
|
+
* (short) ingestion from scratch. That keeps `inventory_runs` honest
|
|
1003
|
+
* (no "ran but unrecorded" rows) at the cost of one rerun.
|
|
1004
|
+
*
|
|
1005
|
+
* Forward-compat: if an explicit `--label` flag is ever added, thread
|
|
1006
|
+
* `labelOverride` through {@link inventory} into the `aggregates`
|
|
1007
|
+
* shape so the auto-name can be overridden.
|
|
1008
|
+
* @param archive - The opened archive to write the audit row into.
|
|
1009
|
+
* @param aggregates - The counts captured during the inventory pass; see {@link InventoryRunAggregates}.
|
|
1010
|
+
*/
|
|
1011
|
+
static async #writeInventoryRunRow(archive, aggregates) {
|
|
1012
|
+
const ranAt = new Date().toISOString();
|
|
1013
|
+
await archive.recordInventoryRun({
|
|
1014
|
+
ran_at: ranAt,
|
|
1015
|
+
list_label: `inventory-${ranAt}`,
|
|
1016
|
+
source_file_sha256: aggregates.sourceFileSha256,
|
|
1017
|
+
total_lines: aggregates.inventoryUrlsCount,
|
|
1018
|
+
new_pages: aggregates.htmlSeedsCount,
|
|
1019
|
+
new_resources: aggregates.nonHtmlCount,
|
|
1020
|
+
scope_skipped: aggregates.outOfScope,
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Tears down session-scoped crawler caches and prints a short-circuit
|
|
1025
|
+
* summary if any URL fetches were skipped. Invoked at every
|
|
1026
|
+
* crawl-session boundary (`crawling` / `append` / `inventory` /
|
|
1027
|
+
* `retryFailed` / `resume`).
|
|
1028
|
+
*/
|
|
1029
|
+
static #finalizeCrawlSession() {
|
|
1030
|
+
const skipped = dnsBurnedHostShortCircuitCounter.count;
|
|
1031
|
+
if (skipped > 0) {
|
|
1032
|
+
// eslint-disable-next-line no-console
|
|
1033
|
+
console.error(`[preload] Short-circuited ${skipped} URL(s) on DNS-burned hosts`);
|
|
1034
|
+
}
|
|
1035
|
+
clearDestinationCache();
|
|
1036
|
+
clearDnsBurnedHostCache();
|
|
1037
|
+
}
|
|
799
1038
|
}
|
|
800
1039
|
/**
|
|
801
1040
|
* Await a filesystem promise but silently swallow only `ENOENT` errors. Any
|
package/lib/crawler.d.ts
CHANGED
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
export * from './utils/types/types.js';
|
|
8
8
|
export { eachSplitted } from './utils/array/each-splitted.js';
|
|
9
9
|
export { DOMEvaluationError } from './utils/error/dom-evaluation-error.js';
|
|
10
|
-
export { ErrorEmitter } from './utils/error/error-emitter.js';
|
|
11
|
-
export type { ErrorEvent } from './utils/error/error-emitter.js';
|
|
12
10
|
export * from './utils/object/clean-object.js';
|
|
13
11
|
export { globalLog as log } from './utils/debug.js';
|
|
14
12
|
export { ArchiveAccessor } from './archive/archive-accessor.js';
|
|
@@ -17,6 +15,8 @@ export { default as Page } from './archive/page.js';
|
|
|
17
15
|
export { default as ArchiveResource } from './archive/resource.js';
|
|
18
16
|
export * from './archive/types.js';
|
|
19
17
|
export { default as Archive } from './archive/archive.js';
|
|
18
|
+
export { isArchiveCacheDisabled } from './archive/cache/is-archive-cache-disabled.js';
|
|
19
|
+
export { acquireArchiveLock, ArchiveLockError } from './archive/archive-lock.js';
|
|
20
20
|
export { peekArchiveLockHolder } from './archive/peek-archive-lock.js';
|
|
21
21
|
export type { ArchiveLockHolder } from './archive/peek-archive-lock.js';
|
|
22
22
|
export type { FlatPageMetaColumns, JsonLdRow, JsonLdRowForInsert, TagRow, TagRowForInsert, JsonLdSummary, TagsSummary, TagInventoryEntry, PageDenormalizedColumns, } from './archive/meta/types.js';
|
|
@@ -25,3 +25,16 @@ export { REQUIRED_FORMAT_VERSION } from './archive/meta/assert-compatible-versio
|
|
|
25
25
|
export { DEFAULT_EXCLUDED_EXTERNAL_URLS, CrawlerOrchestrator, } from './crawler-orchestrator.js';
|
|
26
26
|
export * from './types.js';
|
|
27
27
|
export * from './crawler/types.js';
|
|
28
|
+
export { classifyErrorKind } from './classify-error-kind.js';
|
|
29
|
+
export { computeFileSha256 } from './utils/compute-file-sha256.js';
|
|
30
|
+
export { populateEntityTables } from './archive/populate-entity-tables/populate-entities.js';
|
|
31
|
+
export type { PageDomPathResolver } from './archive/populate-entity-tables/populate-image-items.js';
|
|
32
|
+
export { populateRefTables } from './archive/populate-ref-tables/populate-refs.js';
|
|
33
|
+
export { populateContentTypeRefs } from './archive/populate-ref-tables/populate-content-type-refs.js';
|
|
34
|
+
export { populateUrlRefs } from './archive/populate-ref-tables/populate-url-refs.js';
|
|
35
|
+
export { populateTextRefs } from './archive/populate-ref-tables/populate-text-refs.js';
|
|
36
|
+
export { populateJsonRefs } from './archive/populate-ref-tables/populate-json-refs.js';
|
|
37
|
+
export { populateBlobRefs } from './archive/populate-ref-tables/populate-blob-refs.js';
|
|
38
|
+
export { populateHeaderTables } from './archive/populate-ref-tables/populate-header-tables.js';
|
|
39
|
+
export { loadResponseHeadersBySetIds } from './archive/db-ops/_shared/load-response-headers-by-set-ids.js';
|
|
40
|
+
export { decodeJsonRef } from './archive/db-ops/_shared/decode-json-ref.js';
|
package/lib/crawler.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
export * from './utils/types/types.js';
|
|
9
9
|
export { eachSplitted } from './utils/array/each-splitted.js';
|
|
10
10
|
export { DOMEvaluationError } from './utils/error/dom-evaluation-error.js';
|
|
11
|
-
export { ErrorEmitter } from './utils/error/error-emitter.js';
|
|
12
11
|
export * from './utils/object/clean-object.js';
|
|
13
12
|
export { globalLog as log } from './utils/debug.js';
|
|
14
13
|
// Archive
|
|
@@ -17,6 +16,8 @@ export { default as Page } from './archive/page.js';
|
|
|
17
16
|
export { default as ArchiveResource } from './archive/resource.js';
|
|
18
17
|
export * from './archive/types.js';
|
|
19
18
|
export { default as Archive } from './archive/archive.js';
|
|
19
|
+
export { isArchiveCacheDisabled } from './archive/cache/is-archive-cache-disabled.js';
|
|
20
|
+
export { acquireArchiveLock, ArchiveLockError } from './archive/archive-lock.js';
|
|
20
21
|
export { peekArchiveLockHolder } from './archive/peek-archive-lock.js';
|
|
21
22
|
export { IncompatibleArchiveError } from './archive/meta/types.js';
|
|
22
23
|
export { REQUIRED_FORMAT_VERSION } from './archive/meta/assert-compatible-version.js';
|
|
@@ -24,3 +25,25 @@ export { REQUIRED_FORMAT_VERSION } from './archive/meta/assert-compatible-versio
|
|
|
24
25
|
export { DEFAULT_EXCLUDED_EXTERNAL_URLS, CrawlerOrchestrator, } from './crawler-orchestrator.js';
|
|
25
26
|
export * from './types.js';
|
|
26
27
|
export * from './crawler/types.js';
|
|
28
|
+
export { classifyErrorKind } from './classify-error-kind.js';
|
|
29
|
+
export { computeFileSha256 } from './utils/compute-file-sha256.js';
|
|
30
|
+
// 0.13 ref-table population (issue #191, epic #103). Exposed as the
|
|
31
|
+
// public seam that the migration script (`scripts/migrate-to-0.13.mjs`)
|
|
32
|
+
// drives against an already-connected archive.
|
|
33
|
+
// The individual sub-steps are also exported so the migration script can
|
|
34
|
+
// resume mid-way if the caller decides to split the transaction.
|
|
35
|
+
export { populateEntityTables } from './archive/populate-entity-tables/populate-entities.js';
|
|
36
|
+
export { populateRefTables } from './archive/populate-ref-tables/populate-refs.js';
|
|
37
|
+
export { populateContentTypeRefs } from './archive/populate-ref-tables/populate-content-type-refs.js';
|
|
38
|
+
export { populateUrlRefs } from './archive/populate-ref-tables/populate-url-refs.js';
|
|
39
|
+
export { populateTextRefs } from './archive/populate-ref-tables/populate-text-refs.js';
|
|
40
|
+
export { populateJsonRefs } from './archive/populate-ref-tables/populate-json-refs.js';
|
|
41
|
+
export { populateBlobRefs } from './archive/populate-ref-tables/populate-blob-refs.js';
|
|
42
|
+
export { populateHeaderTables } from './archive/populate-ref-tables/populate-header-tables.js';
|
|
43
|
+
// 0.13 read-side reconstruction primitives. Exported so downstream
|
|
44
|
+
// readers (`@nitpicker/query`'s page-detail view) reconstruct
|
|
45
|
+
// `responseHeaders` / json_refs payloads with the exact same merge and
|
|
46
|
+
// decode semantics as the crawler's own read paths — one implementation,
|
|
47
|
+
// no cross-package drift.
|
|
48
|
+
export { loadResponseHeadersBySetIds } from './archive/db-ops/_shared/load-response-headers-by-set-ids.js';
|
|
49
|
+
export { decodeJsonRef } from './archive/db-ops/_shared/decode-json-ref.js';
|