@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,11 @@
|
|
|
1
|
+
import type { DB_Page, PageFilter } from '../../../types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves pages from the database with optional filtering, pagination via offset and limit.
|
|
5
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
6
|
+
* @param filter - An optional {@link PageFilter} to narrow results by content type and origin.
|
|
7
|
+
* @param offset - The number of rows to skip. Defaults to `0`.
|
|
8
|
+
* @param limit - The maximum number of rows to return. Defaults to `100000`.
|
|
9
|
+
* @returns An array of reconstructed {@link DB_Page} rows.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getPages(knex: Knex, filter?: PageFilter, offset?: number, limit?: number): Promise<DB_Page[]>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { buildPageQuery } from './build-page-query.js';
|
|
2
|
+
import { reconstructPageRows } from './reconstruct-page-rows.js';
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves pages from the database with optional filtering, pagination via offset and limit.
|
|
5
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
6
|
+
* @param filter - An optional {@link PageFilter} to narrow results by content type and origin.
|
|
7
|
+
* @param offset - The number of rows to skip. Defaults to `0`.
|
|
8
|
+
* @param limit - The maximum number of rows to return. Defaults to `100000`.
|
|
9
|
+
* @returns An array of reconstructed {@link DB_Page} rows.
|
|
10
|
+
*/
|
|
11
|
+
export async function getPages(knex, filter, offset = 0, limit = 100_000) {
|
|
12
|
+
const q = buildPageQuery(knex);
|
|
13
|
+
switch (filter) {
|
|
14
|
+
case 'page': {
|
|
15
|
+
q.where('ctr.raw', 'text/html').andWhere('ci.is_target', 1);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
case 'page-included-no-target': {
|
|
19
|
+
q.where('ctr.raw', 'text/html');
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
case 'external-page': {
|
|
23
|
+
q.where('ctr.raw', 'text/html').andWhere('ci.is_external', 1);
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
case 'internal-page': {
|
|
27
|
+
q.where('ctr.raw', 'text/html').andWhere('ci.is_external', 0);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case 'no-page': {
|
|
31
|
+
q.where((qb) => {
|
|
32
|
+
qb.whereNull('ctr.raw').orWhereNot('ctr.raw', 'text/html');
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case 'external-no-page': {
|
|
37
|
+
q.where((qb) => {
|
|
38
|
+
qb.whereNull('ctr.raw').orWhereNot('ctr.raw', 'text/html');
|
|
39
|
+
}).andWhere('ci.is_external', 1);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case 'internal-no-page': {
|
|
43
|
+
q.where((qb) => {
|
|
44
|
+
qb.whereNull('ctr.raw').orWhereNot('ctr.raw', 'text/html');
|
|
45
|
+
}).andWhere('ci.is_external', 0);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const rows = await q.limit(limit).offset(offset);
|
|
50
|
+
return reconstructPageRows(knex, rows);
|
|
51
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Counts pages that were scraped as crawl targets (full HTML render).
|
|
4
|
+
*
|
|
5
|
+
* Used by the crawler to seed its `pagesScraped` counter on resume so the
|
|
6
|
+
* progress display reflects all browser-rendered HTML pages across sessions,
|
|
7
|
+
* not just the current one.
|
|
8
|
+
*
|
|
9
|
+
* "HTML page" is guaranteed by `content_type_refs.raw = 'text/html'`, NOT by
|
|
10
|
+
* `is_target` alone: `is_target` means "in-scope crawl target" and is set
|
|
11
|
+
* for in-scope non-HTML resources too (e.g. a PDF reached via the HEAD
|
|
12
|
+
* pre-flight is `is_target = 1`). Counting those would over-report the HTML
|
|
13
|
+
* page total, so page-ness is asserted at the read layer here rather than by
|
|
14
|
+
* trusting `is_target`.
|
|
15
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
16
|
+
* @returns The number of `text/html` rows with `is_target = 1` and `scraped = 1`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getScrapedHtmlPageCount(knex: Knex): Promise<number>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Counts pages that were scraped as crawl targets (full HTML render).
|
|
3
|
+
*
|
|
4
|
+
* Used by the crawler to seed its `pagesScraped` counter on resume so the
|
|
5
|
+
* progress display reflects all browser-rendered HTML pages across sessions,
|
|
6
|
+
* not just the current one.
|
|
7
|
+
*
|
|
8
|
+
* "HTML page" is guaranteed by `content_type_refs.raw = 'text/html'`, NOT by
|
|
9
|
+
* `is_target` alone: `is_target` means "in-scope crawl target" and is set
|
|
10
|
+
* for in-scope non-HTML resources too (e.g. a PDF reached via the HEAD
|
|
11
|
+
* pre-flight is `is_target = 1`). Counting those would over-report the HTML
|
|
12
|
+
* page total, so page-ness is asserted at the read layer here rather than by
|
|
13
|
+
* trusting `is_target`.
|
|
14
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
15
|
+
* @returns The number of `text/html` rows with `is_target = 1` and `scraped = 1`.
|
|
16
|
+
*/
|
|
17
|
+
export async function getScrapedHtmlPageCount(knex) {
|
|
18
|
+
const [row] = await knex('content_items')
|
|
19
|
+
.join('content_type_refs', 'content_type_refs.id', 'content_items.content_type_id')
|
|
20
|
+
.where('content_items.is_target', 1)
|
|
21
|
+
.andWhere('content_items.scraped', 1)
|
|
22
|
+
.andWhere('content_type_refs.raw', 'text/html')
|
|
23
|
+
.count('* as count');
|
|
24
|
+
return row ? Number(row.count) : 0;
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DB_Page } from '../../../types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Raw row shape produced by {@link ../read/build-page-query.js} before
|
|
5
|
+
* `responseHeaders` / `meta_extras` reconstruction.
|
|
6
|
+
*/
|
|
7
|
+
interface RawPageRow extends Omit<DB_Page, 'responseHeaders' | 'meta_extras' | 'networkLogs'> {
|
|
8
|
+
/** `content_items.header_set_id`, or null when no headers were recorded. */
|
|
9
|
+
headerSetId: number | null;
|
|
10
|
+
/** `json_refs.json_text` for `meta_extras`, or null when absent. */
|
|
11
|
+
extras_body: Buffer | string | null;
|
|
12
|
+
/** `json_refs.codec` for `meta_extras` (`'zstd'` or `'none'`), or null when absent. */
|
|
13
|
+
extras_codec: 'zstd' | 'none' | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reconstructs `responseHeaders` (JSON string) and `meta_extras` (JSON
|
|
17
|
+
* string) for a batch of raw page rows. Headers load through
|
|
18
|
+
* {@link ../../_shared/load-response-headers-by-set-ids.js} (chunked
|
|
19
|
+
* batch lookup, never N+1); `meta_extras` decodes through
|
|
20
|
+
* {@link ../../_shared/decode-json-ref.js}. `networkLogs` has no 0.13
|
|
21
|
+
* equivalent (it is a legacy-only field nothing ever wrote past the
|
|
22
|
+
* pre-0.13 write path either) and is always `null`.
|
|
23
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
24
|
+
* @param rows - Raw rows from {@link ../read/build-page-query.js}.
|
|
25
|
+
* @returns Fully reconstructed `DB_Page` rows, in the same order as `rows`.
|
|
26
|
+
* @example
|
|
27
|
+
* const raw = await buildPageQuery(knex).where('ci.is_target', 1);
|
|
28
|
+
* const pages = await reconstructPageRows(knex, raw);
|
|
29
|
+
*/
|
|
30
|
+
export declare function reconstructPageRows(knex: Knex, rows: readonly RawPageRow[]): Promise<DB_Page[]>;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { decodeJsonRef } from '../../_shared/decode-json-ref.js';
|
|
2
|
+
import { loadResponseHeadersBySetIds } from '../../_shared/load-response-headers-by-set-ids.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reconstructs `responseHeaders` (JSON string) and `meta_extras` (JSON
|
|
5
|
+
* string) for a batch of raw page rows. Headers load through
|
|
6
|
+
* {@link ../../_shared/load-response-headers-by-set-ids.js} (chunked
|
|
7
|
+
* batch lookup, never N+1); `meta_extras` decodes through
|
|
8
|
+
* {@link ../../_shared/decode-json-ref.js}. `networkLogs` has no 0.13
|
|
9
|
+
* equivalent (it is a legacy-only field nothing ever wrote past the
|
|
10
|
+
* pre-0.13 write path either) and is always `null`.
|
|
11
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
12
|
+
* @param rows - Raw rows from {@link ../read/build-page-query.js}.
|
|
13
|
+
* @returns Fully reconstructed `DB_Page` rows, in the same order as `rows`.
|
|
14
|
+
* @example
|
|
15
|
+
* const raw = await buildPageQuery(knex).where('ci.is_target', 1);
|
|
16
|
+
* const pages = await reconstructPageRows(knex, raw);
|
|
17
|
+
*/
|
|
18
|
+
export async function reconstructPageRows(knex, rows) {
|
|
19
|
+
const headerSetIds = [
|
|
20
|
+
...new Set(rows.map((r) => r.headerSetId).filter((id) => id != null)),
|
|
21
|
+
];
|
|
22
|
+
const headersBySetId = await loadResponseHeadersBySetIds(knex, headerSetIds);
|
|
23
|
+
return rows.map((row) => {
|
|
24
|
+
const { headerSetId, extras_body, extras_codec, ...rest } = row;
|
|
25
|
+
return {
|
|
26
|
+
...rest,
|
|
27
|
+
responseHeaders: JSON.stringify(headerSetId == null ? {} : (headersBySetId.get(headerSetId) ?? {})),
|
|
28
|
+
meta_extras: decodeJsonRef(extras_body, extras_codec),
|
|
29
|
+
networkLogs: null,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ExURL, ParseURLOptions } from '@d-zero/shared/parse-url';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Promote previously-external pages whose URL falls under any of the new scope
|
|
5
|
+
* entries back to a "needs scraping" state so that the next crawl picks them up
|
|
6
|
+
* as full internal pages.
|
|
7
|
+
*
|
|
8
|
+
* For each matching page:
|
|
9
|
+
* - clears the scrape metadata (status, headers, snapshot path, etc.) by
|
|
10
|
+
* deleting the `page_meta` row outright (a re-scrape re-inserts it fresh),
|
|
11
|
+
* - flips `is_external` to `0` and `scraped` to `0` on `content_items`,
|
|
12
|
+
* - removes stale `anchor_edges`, `image_items`, and `resource_ref_edges`
|
|
13
|
+
* rows so that the re-scrape can re-insert fresh ones without duplicates.
|
|
14
|
+
*
|
|
15
|
+
* The page row itself is kept (id is preserved) so existing referrers via
|
|
16
|
+
* `anchor_edges.href_page_id` remain valid. SELECT and UPDATE/DELETE
|
|
17
|
+
* statements are chunked to stay below SQLite's
|
|
18
|
+
* `SQLITE_LIMIT_VARIABLE_NUMBER`.
|
|
19
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
20
|
+
* @param scopes - The hostname-indexed scope map after the new roots are merged.
|
|
21
|
+
* @param options - URL parsing options forwarded to {@link findScopeEntry}.
|
|
22
|
+
* @returns The URLs of the pages that were promoted.
|
|
23
|
+
*/
|
|
24
|
+
export declare function repromoteExternalPages(knex: Knex, scopes: ReadonlyMap<string, readonly ExURL[]>, options?: ParseURLOptions): Promise<string[]>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { tryParseUrl as parseUrl } from '@d-zero/shared/parse-url';
|
|
2
|
+
import { findScopeEntry } from '../../../../crawler/find-scope-entry.js';
|
|
3
|
+
import { dbLog } from '../../../debug.js';
|
|
4
|
+
/**
|
|
5
|
+
* Promote previously-external pages whose URL falls under any of the new scope
|
|
6
|
+
* entries back to a "needs scraping" state so that the next crawl picks them up
|
|
7
|
+
* as full internal pages.
|
|
8
|
+
*
|
|
9
|
+
* For each matching page:
|
|
10
|
+
* - clears the scrape metadata (status, headers, snapshot path, etc.) by
|
|
11
|
+
* deleting the `page_meta` row outright (a re-scrape re-inserts it fresh),
|
|
12
|
+
* - flips `is_external` to `0` and `scraped` to `0` on `content_items`,
|
|
13
|
+
* - removes stale `anchor_edges`, `image_items`, and `resource_ref_edges`
|
|
14
|
+
* rows so that the re-scrape can re-insert fresh ones without duplicates.
|
|
15
|
+
*
|
|
16
|
+
* The page row itself is kept (id is preserved) so existing referrers via
|
|
17
|
+
* `anchor_edges.href_page_id` remain valid. SELECT and UPDATE/DELETE
|
|
18
|
+
* statements are chunked to stay below SQLite's
|
|
19
|
+
* `SQLITE_LIMIT_VARIABLE_NUMBER`.
|
|
20
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
21
|
+
* @param scopes - The hostname-indexed scope map after the new roots are merged.
|
|
22
|
+
* @param options - URL parsing options forwarded to {@link findScopeEntry}.
|
|
23
|
+
* @returns The URLs of the pages that were promoted.
|
|
24
|
+
*/
|
|
25
|
+
export async function repromoteExternalPages(knex, scopes, options) {
|
|
26
|
+
if (scopes.size === 0) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const candidates = await knex('content_items')
|
|
30
|
+
.join('url_refs', 'content_items.url_id', 'url_refs.id')
|
|
31
|
+
.select('content_items.id as id', 'url_refs.url as url')
|
|
32
|
+
.where('content_items.is_external', 1);
|
|
33
|
+
const promotedIds = [];
|
|
34
|
+
const promotedUrls = [];
|
|
35
|
+
for (const row of candidates) {
|
|
36
|
+
const parsed = parseUrl(row.url, options);
|
|
37
|
+
if (!parsed) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (findScopeEntry(parsed, scopes, options) === null) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
promotedIds.push(row.id);
|
|
44
|
+
promotedUrls.push(row.url);
|
|
45
|
+
}
|
|
46
|
+
if (promotedIds.length === 0) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const chunkSize = 500;
|
|
50
|
+
for (let i = 0; i < promotedIds.length; i += chunkSize) {
|
|
51
|
+
const chunk = promotedIds.slice(i, i + chunkSize);
|
|
52
|
+
await knex('content_items').whereIn('id', chunk).update({
|
|
53
|
+
scraped: 0,
|
|
54
|
+
is_external: 0,
|
|
55
|
+
is_skipped: 0,
|
|
56
|
+
skip_reason: null,
|
|
57
|
+
status: null,
|
|
58
|
+
status_text: null,
|
|
59
|
+
content_type_id: null,
|
|
60
|
+
content_length: null,
|
|
61
|
+
header_set_id: null,
|
|
62
|
+
redirect_dest_id: null,
|
|
63
|
+
// `first_crawled_at` / `last_crawled_at` are deliberately
|
|
64
|
+
// left untouched — the last-success timestamp survives the
|
|
65
|
+
// demotion.
|
|
66
|
+
});
|
|
67
|
+
// Clear the prior crawl's data for the repromoted pages. `updatePage`
|
|
68
|
+
// also replaces anchor_edges/image_items/tags/jsonld when it
|
|
69
|
+
// re-scrapes them, but only when the new scrape is non-empty — so
|
|
70
|
+
// this pre-clear is still load-bearing for pages that get
|
|
71
|
+
// repromoted but then re-scrape to nothing (or are never reached
|
|
72
|
+
// again), and it is the only place `resource_ref_edges` is cleared.
|
|
73
|
+
// Deleting the `page_meta` row (rather than nulling every column)
|
|
74
|
+
// clears title / description / og:* / twitter:* / meta_extras in
|
|
75
|
+
// one statement; a re-scrape re-inserts it via
|
|
76
|
+
// `ON CONFLICT(page_id) DO UPDATE`. `page_tags` / `page_jsonld` are
|
|
77
|
+
// cleared explicitly even though both tables also carry ON DELETE
|
|
78
|
+
// CASCADE — we keep the existing pattern of explicit chunked
|
|
79
|
+
// DELETEs rather than relying on CASCADE indirectly (and would not
|
|
80
|
+
// cascade anyway: the parent `content_items` row is updated, not
|
|
81
|
+
// deleted). Orphan blobs in `page_html_blobs` are left behind; #23
|
|
82
|
+
// will add GC.
|
|
83
|
+
await knex('page_meta').whereIn('page_id', chunk).delete();
|
|
84
|
+
await knex('anchor_edges').whereIn('page_id', chunk).delete();
|
|
85
|
+
await knex('image_items').whereIn('page_id', chunk).delete();
|
|
86
|
+
await knex('resource_ref_edges').whereIn('page_id', chunk).delete();
|
|
87
|
+
await knex('page_html_ref').whereIn('page_id', chunk).delete();
|
|
88
|
+
await knex('page_tags').whereIn('pageId', chunk).delete();
|
|
89
|
+
await knex('page_jsonld').whereIn('pageId', chunk).delete();
|
|
90
|
+
}
|
|
91
|
+
dbLog('Repromoted %d external pages back to pending', promotedUrls.length);
|
|
92
|
+
return promotedUrls;
|
|
93
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Reset previously-attempted pages that ended in a recoverable failure so a
|
|
4
|
+
* follow-up crawl can re-fetch them from scratch.
|
|
5
|
+
*
|
|
6
|
+
* A page qualifies as a recoverable failure when it was already scraped
|
|
7
|
+
* (`scraped = 1`), is not a redirect source (`redirect_dest_id IS NULL`), was
|
|
8
|
+
* not intentionally skipped (`is_skipped` is not `1`), and one of the
|
|
9
|
+
* following holds:
|
|
10
|
+
*
|
|
11
|
+
* - `status = -1` — the sentinel a hard scrape failure (network error,
|
|
12
|
+
* timeout, browser crash) is recorded with (see `handle-scrape-error.ts`);
|
|
13
|
+
* - `status IS NULL` — no status was ever stored for the row;
|
|
14
|
+
* - the row has no `content_type_refs` link — the content type could not be
|
|
15
|
+
* determined;
|
|
16
|
+
* - `status` is in the `5xx` range — a (frequently transient) server error.
|
|
17
|
+
*
|
|
18
|
+
* Definitive `4xx` responses are intentionally excluded: re-fetching a 404
|
|
19
|
+
* almost always yields the same answer.
|
|
20
|
+
*
|
|
21
|
+
* A second exclusion runs in JS after the SQL candidate scan: any page whose
|
|
22
|
+
* latest recorded `page_errors` / `crawl_errors` message classifies into a
|
|
23
|
+
* permanent {@link PERMANENT_ERROR_KINDS} kind (dns / tls / client-blocked /
|
|
24
|
+
* parse-error / connection-refused) is left as-is rather than reset to
|
|
25
|
+
* pending. Without this filter, `--retry-failed` never converges: NXDOMAIN
|
|
26
|
+
* hosts, expired-cert hosts, and `ERR_BLOCKED_BY_CLIENT` ad pixels would be
|
|
27
|
+
* reset every iteration, re-attempted, fail identically, and rejoin the
|
|
28
|
+
* candidate pool for the next iteration. The exclusion keeps the retry
|
|
29
|
+
* target shrinking across `--retry-failed` passes by leaving deterministic
|
|
30
|
+
* dead-ends alone.
|
|
31
|
+
*
|
|
32
|
+
* Matching rows — internal and external alike — are demoted back to pending
|
|
33
|
+
* (`scraped = 0`) and have their stale scrape metadata cleared (the
|
|
34
|
+
* `page_meta` row is deleted outright rather than nulled column-by-column).
|
|
35
|
+
* The page row itself is kept (id preserved) so existing
|
|
36
|
+
* `anchor_edges.href_page_id` referrers stay valid, and `is_external` is
|
|
37
|
+
* left untouched so the next pass re-classifies each page from the crawl
|
|
38
|
+
* scope. Related `anchor_edges`, `image_items`, `resource_ref_edges`, and
|
|
39
|
+
* `page_errors` rows are deleted so the re-scrape can re-insert fresh data
|
|
40
|
+
* without duplicates.
|
|
41
|
+
*
|
|
42
|
+
* SELECT and UPDATE/DELETE statements are chunked to stay below SQLite's
|
|
43
|
+
* `SQLITE_LIMIT_VARIABLE_NUMBER`.
|
|
44
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
45
|
+
* @returns The URLs of the pages that were reset to pending.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resetFailedPages(knex: Knex): Promise<string[]>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { classifyErrorKind } from '../../../../classify-error-kind.js';
|
|
2
|
+
import { PERMANENT_ERROR_KINDS } from '../../../../permanent-error-kinds.js';
|
|
3
|
+
import { dbLog } from '../../../debug.js';
|
|
4
|
+
import { getFailedPageMessages } from '../../../get-failed-page-messages.js';
|
|
5
|
+
/**
|
|
6
|
+
* Reset previously-attempted pages that ended in a recoverable failure so a
|
|
7
|
+
* follow-up crawl can re-fetch them from scratch.
|
|
8
|
+
*
|
|
9
|
+
* A page qualifies as a recoverable failure when it was already scraped
|
|
10
|
+
* (`scraped = 1`), is not a redirect source (`redirect_dest_id IS NULL`), was
|
|
11
|
+
* not intentionally skipped (`is_skipped` is not `1`), and one of the
|
|
12
|
+
* following holds:
|
|
13
|
+
*
|
|
14
|
+
* - `status = -1` — the sentinel a hard scrape failure (network error,
|
|
15
|
+
* timeout, browser crash) is recorded with (see `handle-scrape-error.ts`);
|
|
16
|
+
* - `status IS NULL` — no status was ever stored for the row;
|
|
17
|
+
* - the row has no `content_type_refs` link — the content type could not be
|
|
18
|
+
* determined;
|
|
19
|
+
* - `status` is in the `5xx` range — a (frequently transient) server error.
|
|
20
|
+
*
|
|
21
|
+
* Definitive `4xx` responses are intentionally excluded: re-fetching a 404
|
|
22
|
+
* almost always yields the same answer.
|
|
23
|
+
*
|
|
24
|
+
* A second exclusion runs in JS after the SQL candidate scan: any page whose
|
|
25
|
+
* latest recorded `page_errors` / `crawl_errors` message classifies into a
|
|
26
|
+
* permanent {@link PERMANENT_ERROR_KINDS} kind (dns / tls / client-blocked /
|
|
27
|
+
* parse-error / connection-refused) is left as-is rather than reset to
|
|
28
|
+
* pending. Without this filter, `--retry-failed` never converges: NXDOMAIN
|
|
29
|
+
* hosts, expired-cert hosts, and `ERR_BLOCKED_BY_CLIENT` ad pixels would be
|
|
30
|
+
* reset every iteration, re-attempted, fail identically, and rejoin the
|
|
31
|
+
* candidate pool for the next iteration. The exclusion keeps the retry
|
|
32
|
+
* target shrinking across `--retry-failed` passes by leaving deterministic
|
|
33
|
+
* dead-ends alone.
|
|
34
|
+
*
|
|
35
|
+
* Matching rows — internal and external alike — are demoted back to pending
|
|
36
|
+
* (`scraped = 0`) and have their stale scrape metadata cleared (the
|
|
37
|
+
* `page_meta` row is deleted outright rather than nulled column-by-column).
|
|
38
|
+
* The page row itself is kept (id preserved) so existing
|
|
39
|
+
* `anchor_edges.href_page_id` referrers stay valid, and `is_external` is
|
|
40
|
+
* left untouched so the next pass re-classifies each page from the crawl
|
|
41
|
+
* scope. Related `anchor_edges`, `image_items`, `resource_ref_edges`, and
|
|
42
|
+
* `page_errors` rows are deleted so the re-scrape can re-insert fresh data
|
|
43
|
+
* without duplicates.
|
|
44
|
+
*
|
|
45
|
+
* SELECT and UPDATE/DELETE statements are chunked to stay below SQLite's
|
|
46
|
+
* `SQLITE_LIMIT_VARIABLE_NUMBER`.
|
|
47
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
48
|
+
* @returns The URLs of the pages that were reset to pending.
|
|
49
|
+
*/
|
|
50
|
+
export async function resetFailedPages(knex) {
|
|
51
|
+
const candidates = await knex('content_items')
|
|
52
|
+
.join('url_refs', 'content_items.url_id', 'url_refs.id')
|
|
53
|
+
.select('content_items.id as id', 'url_refs.url as url')
|
|
54
|
+
.where('content_items.scraped', 1)
|
|
55
|
+
.whereNull('content_items.redirect_dest_id')
|
|
56
|
+
.where((qb) => {
|
|
57
|
+
qb.where('content_items.is_skipped', 0).orWhereNull('content_items.is_skipped');
|
|
58
|
+
})
|
|
59
|
+
.where((qb) => {
|
|
60
|
+
qb.whereNull('content_items.status')
|
|
61
|
+
.orWhere('content_items.status', -1)
|
|
62
|
+
.orWhereNull('content_items.content_type_id')
|
|
63
|
+
.orWhereBetween('content_items.status', [500, 599]);
|
|
64
|
+
});
|
|
65
|
+
if (candidates.length === 0) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
const candidateIds = candidates.map((row) => row.id);
|
|
69
|
+
const candidateUrls = candidates.map((row) => row.url);
|
|
70
|
+
const messages = await getFailedPageMessages(knex, candidateIds, candidateUrls);
|
|
71
|
+
// Drop candidates whose latest recorded message classifies as permanent.
|
|
72
|
+
// An empty/absent message stays in the retry pool — we keep retrying when
|
|
73
|
+
// we don't know it's permanent, erring on the side of investigation.
|
|
74
|
+
const retryable = candidates.filter((row) => {
|
|
75
|
+
const message = messages.get(row.id) ?? '';
|
|
76
|
+
if (message === '') {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return !PERMANENT_ERROR_KINDS.has(classifyErrorKind(message));
|
|
80
|
+
});
|
|
81
|
+
const excludedCount = candidates.length - retryable.length;
|
|
82
|
+
if (excludedCount > 0) {
|
|
83
|
+
dbLog('Excluded %d page(s) from retry — permanent failure kinds (dns/tls/client-blocked/parse-error/connection-refused)', excludedCount);
|
|
84
|
+
}
|
|
85
|
+
if (retryable.length === 0) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const ids = retryable.map((row) => row.id);
|
|
89
|
+
const urls = retryable.map((row) => row.url);
|
|
90
|
+
const chunkSize = 500;
|
|
91
|
+
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
92
|
+
const chunk = ids.slice(i, i + chunkSize);
|
|
93
|
+
await knex('content_items').whereIn('id', chunk).update({
|
|
94
|
+
scraped: 0,
|
|
95
|
+
status: null,
|
|
96
|
+
status_text: null,
|
|
97
|
+
content_type_id: null,
|
|
98
|
+
content_length: null,
|
|
99
|
+
header_set_id: null,
|
|
100
|
+
// `first_crawled_at` / `last_crawled_at` are deliberately left
|
|
101
|
+
// untouched so the last-success timestamp records survive the
|
|
102
|
+
// demotion (the within-archive observation axis for #11/#17/#19).
|
|
103
|
+
});
|
|
104
|
+
// Clear the prior crawl's per-page data so the re-scrape starts clean.
|
|
105
|
+
// `updatePage` only replaces anchor_edges/image_items/tags/jsonld when
|
|
106
|
+
// the new scrape is non-empty, so this pre-clear is load-bearing for
|
|
107
|
+
// pages that reset but then fail again (or are never reached), and it
|
|
108
|
+
// is the only place `resource_ref_edges` and `page_errors` are
|
|
109
|
+
// cleared. Deleting the `page_meta` row (rather than nulling every
|
|
110
|
+
// column) clears title / description / og:* / twitter:* /
|
|
111
|
+
// meta_extras in one statement; a re-scrape re-inserts it via
|
|
112
|
+
// `ON CONFLICT(page_id) DO UPDATE`.
|
|
113
|
+
await knex('page_meta').whereIn('page_id', chunk).delete();
|
|
114
|
+
await knex('anchor_edges').whereIn('page_id', chunk).delete();
|
|
115
|
+
await knex('image_items').whereIn('page_id', chunk).delete();
|
|
116
|
+
await knex('resource_ref_edges').whereIn('page_id', chunk).delete();
|
|
117
|
+
await knex('page_errors').whereIn('pageId', chunk).delete();
|
|
118
|
+
await knex('page_html_ref').whereIn('page_id', chunk).delete();
|
|
119
|
+
await knex('page_tags').whereIn('pageId', chunk).delete();
|
|
120
|
+
await knex('page_jsonld').whereIn('pageId', chunk).delete();
|
|
121
|
+
}
|
|
122
|
+
dbLog('Reset %d failed pages back to pending', urls.length);
|
|
123
|
+
return urls;
|
|
124
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { WriteRefCaches } from '../../_shared/types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Pre-insert inventory HTML seeds into `content_items` as `scraped = 0`,
|
|
5
|
+
* `source = 'inventory-seed'` placeholders so the URL's existence in the
|
|
6
|
+
* archive is **durable before the scrape phase starts**.
|
|
7
|
+
*
|
|
8
|
+
* Why this is the linchpin of `--inventory` Ctrl+C tolerance: without
|
|
9
|
+
* pre-insertion, HTML seeds live only in the Crawler's in-memory
|
|
10
|
+
* `LinkList` until the dealer eventually calls `setPage`. A Ctrl+C /
|
|
11
|
+
* crash before that point loses the seed without trace, and `--resume`
|
|
12
|
+
* cannot recover it because `getCrawlingState`'s strict pending set
|
|
13
|
+
* requires a `content_items` row. Pre-inserting fills exactly that gap:
|
|
14
|
+
* the strict pending set picks these rows up via its
|
|
15
|
+
* `OR source != 'crawled'` clause, so `--resume` after an interrupted
|
|
16
|
+
* inventory pass picks every seed back up. See `getCrawlingState` for
|
|
17
|
+
* the strict-set rationale.
|
|
18
|
+
*
|
|
19
|
+
* Idempotent: both the `url_refs` and `content_items` inserts are
|
|
20
|
+
* `ON CONFLICT ... IGNORE`, keeping existing rows intact. The
|
|
21
|
+
* `resolveContentItemId` crawled-wins downgrade still fires later when a
|
|
22
|
+
* crawled-lineage anchor reaches one of these seeds — that's the right
|
|
23
|
+
* behaviour (a seed that turned out to be reachable is not an orphan
|
|
24
|
+
* and should not retain the inventory label).
|
|
25
|
+
*
|
|
26
|
+
* Chunked into 500-URL batches so SQLite's bound-parameter limit
|
|
27
|
+
* (`SQLITE_MAX_VARIABLE_NUMBER`) cannot be hit even on a
|
|
28
|
+
* tens-of-thousands inventory list.
|
|
29
|
+
*
|
|
30
|
+
* Called by `CrawlerOrchestrator.inventory` during the
|
|
31
|
+
* `.bak`-protected ingestion phase, so any failure here aborts the run
|
|
32
|
+
* and restores from backup — the operator reruns from scratch.
|
|
33
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
34
|
+
* @param caches
|
|
35
|
+
* @param urls - URL strings already in `withoutHashAndAuth` form.
|
|
36
|
+
*/
|
|
37
|
+
export declare function insertInventorySeeds(knex: Knex, caches: WriteRefCaches, urls: readonly string[]): Promise<void>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { eachSplitted } from '../../../../utils/array/each-splitted.js';
|
|
2
|
+
import { resolveUrlRefs } from '../../../populate-entity-tables/resolve-url-refs.js';
|
|
3
|
+
import { decomposeUrl } from '../../../populate-ref-tables/decompose-url.js';
|
|
4
|
+
/**
|
|
5
|
+
* Pre-insert inventory HTML seeds into `content_items` as `scraped = 0`,
|
|
6
|
+
* `source = 'inventory-seed'` placeholders so the URL's existence in the
|
|
7
|
+
* archive is **durable before the scrape phase starts**.
|
|
8
|
+
*
|
|
9
|
+
* Why this is the linchpin of `--inventory` Ctrl+C tolerance: without
|
|
10
|
+
* pre-insertion, HTML seeds live only in the Crawler's in-memory
|
|
11
|
+
* `LinkList` until the dealer eventually calls `setPage`. A Ctrl+C /
|
|
12
|
+
* crash before that point loses the seed without trace, and `--resume`
|
|
13
|
+
* cannot recover it because `getCrawlingState`'s strict pending set
|
|
14
|
+
* requires a `content_items` row. Pre-inserting fills exactly that gap:
|
|
15
|
+
* the strict pending set picks these rows up via its
|
|
16
|
+
* `OR source != 'crawled'` clause, so `--resume` after an interrupted
|
|
17
|
+
* inventory pass picks every seed back up. See `getCrawlingState` for
|
|
18
|
+
* the strict-set rationale.
|
|
19
|
+
*
|
|
20
|
+
* Idempotent: both the `url_refs` and `content_items` inserts are
|
|
21
|
+
* `ON CONFLICT ... IGNORE`, keeping existing rows intact. The
|
|
22
|
+
* `resolveContentItemId` crawled-wins downgrade still fires later when a
|
|
23
|
+
* crawled-lineage anchor reaches one of these seeds — that's the right
|
|
24
|
+
* behaviour (a seed that turned out to be reachable is not an orphan
|
|
25
|
+
* and should not retain the inventory label).
|
|
26
|
+
*
|
|
27
|
+
* Chunked into 500-URL batches so SQLite's bound-parameter limit
|
|
28
|
+
* (`SQLITE_MAX_VARIABLE_NUMBER`) cannot be hit even on a
|
|
29
|
+
* tens-of-thousands inventory list.
|
|
30
|
+
*
|
|
31
|
+
* Called by `CrawlerOrchestrator.inventory` during the
|
|
32
|
+
* `.bak`-protected ingestion phase, so any failure here aborts the run
|
|
33
|
+
* and restores from backup — the operator reruns from scratch.
|
|
34
|
+
* @param knex - Knex query builder connected to the archive DB.
|
|
35
|
+
* @param caches
|
|
36
|
+
* @param urls - URL strings already in `withoutHashAndAuth` form.
|
|
37
|
+
*/
|
|
38
|
+
export async function insertInventorySeeds(knex, caches, urls) {
|
|
39
|
+
if (urls.length === 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await eachSplitted([...urls], 500, async (chunk) => {
|
|
43
|
+
await knex('url_refs')
|
|
44
|
+
.insert(chunk.map((url) => ({ url, ...decomposeUrl(url) })))
|
|
45
|
+
.onConflict('url')
|
|
46
|
+
.ignore();
|
|
47
|
+
const urlIds = await resolveUrlRefs(knex, chunk);
|
|
48
|
+
const rows = chunk.map((url) => {
|
|
49
|
+
const urlId = urlIds.get(url);
|
|
50
|
+
if (urlId === undefined) {
|
|
51
|
+
throw new Error(`insertInventorySeeds: url_refs.id not resolved for ${url}`);
|
|
52
|
+
}
|
|
53
|
+
caches.urlIds.set(url, urlId);
|
|
54
|
+
return {
|
|
55
|
+
url_id: urlId,
|
|
56
|
+
scraped: 0,
|
|
57
|
+
is_external: 0,
|
|
58
|
+
is_target: 0,
|
|
59
|
+
source: 'inventory-seed',
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
await knex('content_items').insert(rows).onConflict('url_id').ignore();
|
|
63
|
+
const inserted = (await knex
|
|
64
|
+
.select('ci.id', 'ci.source', 'ur.url')
|
|
65
|
+
.from('content_items as ci')
|
|
66
|
+
.join('url_refs as ur', 'ur.id', 'ci.url_id')
|
|
67
|
+
.whereIn('ur.url', chunk));
|
|
68
|
+
for (const row of inserted) {
|
|
69
|
+
caches.contentItems.set(row.url, { id: row.id, source: row.source });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PageData } from '../../../../utils/types/types.js';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Replaces the page's JSON-LD / SpeculationRules rows with the freshly
|
|
5
|
+
* captured set. Called inside `updatePage`'s transaction.
|
|
6
|
+
*
|
|
7
|
+
* `writeHtml = false` branches (`setExternalPage`, metadata-only) skip
|
|
8
|
+
* this entirely — JSON-LD lives inside the HTML body, so external pages
|
|
9
|
+
* that are not rendered have no entries to write. An empty array on a
|
|
10
|
+
* normally-rendered page is treated as a degraded re-scrape: prior rows
|
|
11
|
+
* are kept (same `delete-only-when-replacing` invariant as `anchors` /
|
|
12
|
+
* `images`).
|
|
13
|
+
* @param pageId
|
|
14
|
+
* @param meta
|
|
15
|
+
* @param trx
|
|
16
|
+
*/
|
|
17
|
+
export declare function insertJsonLd(pageId: number, meta: PageData['meta'], trx: Knex.Transaction): Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { eachSplitted } from '../../../../utils/array/each-splitted.js';
|
|
2
|
+
import { classifyJsonLdType } from '../../../meta/classify-jsonld-type.js';
|
|
3
|
+
/**
|
|
4
|
+
* Replaces the page's JSON-LD / SpeculationRules rows with the freshly
|
|
5
|
+
* captured set. Called inside `updatePage`'s transaction.
|
|
6
|
+
*
|
|
7
|
+
* `writeHtml = false` branches (`setExternalPage`, metadata-only) skip
|
|
8
|
+
* this entirely — JSON-LD lives inside the HTML body, so external pages
|
|
9
|
+
* that are not rendered have no entries to write. An empty array on a
|
|
10
|
+
* normally-rendered page is treated as a degraded re-scrape: prior rows
|
|
11
|
+
* are kept (same `delete-only-when-replacing` invariant as `anchors` /
|
|
12
|
+
* `images`).
|
|
13
|
+
* @param pageId
|
|
14
|
+
* @param meta
|
|
15
|
+
* @param trx
|
|
16
|
+
*/
|
|
17
|
+
export async function insertJsonLd(pageId, meta, trx) {
|
|
18
|
+
// `??` guards tolerate the legacy "minimal meta" shape from older test
|
|
19
|
+
// fixtures. Real beholder 3.0.0 always populates these required fields.
|
|
20
|
+
const jsonLd = meta.jsonLd ?? [];
|
|
21
|
+
const speculationRules = meta.speculationRules ?? [];
|
|
22
|
+
const rows = [];
|
|
23
|
+
for (const entry of jsonLd) {
|
|
24
|
+
rows.push({
|
|
25
|
+
pageId,
|
|
26
|
+
kind: 'ld+json',
|
|
27
|
+
type: classifyJsonLdType(entry),
|
|
28
|
+
raw: entry.raw,
|
|
29
|
+
parsed: entry.parsed === undefined ? null : JSON.stringify(entry.parsed),
|
|
30
|
+
parseError: entry.parseError ?? null,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
for (const entry of speculationRules) {
|
|
34
|
+
rows.push({
|
|
35
|
+
pageId,
|
|
36
|
+
kind: 'speculationrules',
|
|
37
|
+
type: classifyJsonLdType(entry),
|
|
38
|
+
raw: entry.raw,
|
|
39
|
+
parsed: entry.parsed === undefined ? null : JSON.stringify(entry.parsed),
|
|
40
|
+
parseError: entry.parseError ?? null,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (rows.length === 0)
|
|
44
|
+
return;
|
|
45
|
+
await trx('page_jsonld').where('pageId', pageId).delete();
|
|
46
|
+
await eachSplitted(rows, 100, async (chunk) => {
|
|
47
|
+
await trx('page_jsonld').insert(chunk);
|
|
48
|
+
});
|
|
49
|
+
}
|