@nitpicker/crawler 0.12.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 +63 -16
- package/lib/archive/archive.js +56 -17
- 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 +92 -345
- package/lib/archive/database.js +168 -1942
- 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 +1 -1
- package/lib/archive/derive-lineage-from-parent.js +1 -1
- 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 +5 -4
- package/lib/archive/get-failed-page-messages.js +5 -4
- package/lib/archive/init-schema.d.ts +35 -39
- package/lib/archive/init-schema.js +99 -460
- 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 +127 -24
- 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/crawler/build-redirect-event.d.ts +1 -1
- package/lib/crawler/build-redirect-event.js +1 -1
- 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 +1 -1
- package/lib/crawler/clear-dns-burned-host-cache.js +1 -1
- 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 +19 -0
- package/lib/crawler/crawler.js +40 -26
- package/lib/crawler/dns-burned-host-cache.d.ts +3 -3
- package/lib/crawler/dns-burned-host-cache.js +3 -3
- package/lib/crawler/dns-burned-host-short-circuit-counter.d.ts +2 -2
- package/lib/crawler/dns-burned-host-short-circuit-counter.js +2 -2
- package/lib/crawler/inject-scope-auth.d.ts +1 -1
- package/lib/crawler/inject-scope-auth.js +1 -1
- package/lib/crawler/normalize-content-type.d.ts +1 -1
- package/lib/crawler/normalize-content-type.js +1 -1
- package/lib/crawler/types.d.ts +3 -3
- package/lib/crawler-orchestrator.d.ts +9 -0
- package/lib/crawler-orchestrator.js +44 -28
- package/lib/crawler.d.ts +12 -0
- package/lib/crawler.js +21 -0
- package/lib/permanent-error-kinds.d.ts +1 -1
- package/lib/permanent-error-kinds.js +1 -1
- package/lib/types.d.ts +1 -1
- package/lib/utils/compute-file-sha256.d.ts +5 -4
- package/lib/utils/compute-file-sha256.js +5 -4
- package/lib/utils/error/emit-error-with-retry.d.ts +1 -1
- package/lib/utils/error/emit-error-with-retry.js +1 -1
- package/package.json +10 -10
- 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-inventory-runs.d.ts +0 -29
- package/lib/archive/migrate-inventory-runs.js +0 -52
- 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
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the 6 core entity and edge tables of the 0.13 format (issue #192).
|
|
4
|
+
*
|
|
5
|
+
* These are the normalised entity / edge tables that replace the legacy
|
|
6
|
+
* write model (`pages`, `anchors`, `images`, `resources`, `resources-referrers`)
|
|
7
|
+
* in the 0.13 format. This migration is purely additive DDL — no existing table
|
|
8
|
+
* is modified and no row is inserted; the populate step
|
|
9
|
+
* (`populateEntityTables`) fills the tables from the legacy sources.
|
|
10
|
+
*
|
|
11
|
+
* Tables created:
|
|
12
|
+
*
|
|
13
|
+
* - `content_items` — replaces `pages` (core columns). The remaining
|
|
14
|
+
* per-page metadata splits into `page_meta`.
|
|
15
|
+
* - `page_meta` — page-specific metadata (title, description, meta tags,
|
|
16
|
+
* OpenGraph, Twitter, robots, denormalised aggregates, meta_extras JSON
|
|
17
|
+
* pointer). Split from `pages` so that filter / cursor queries against
|
|
18
|
+
* `content_items` scan a narrower row.
|
|
19
|
+
* - `resource_items` — replaces `resources`. Identity URL splits into
|
|
20
|
+
* `url_id` (regular URLs) vs `url_blob_id` (large `data:` URIs, routed
|
|
21
|
+
* via `blob_refs`) — a resource's own URL is captured verbatim from
|
|
22
|
+
* the network layer and, unlike a page's URL, can legally be a large
|
|
23
|
+
* inline `data:` URI (e.g. a CSS `background-image` sub-resource).
|
|
24
|
+
* - `anchor_edges` — replaces `anchors`. Deduped to distinct
|
|
25
|
+
* `(page_id, href_page_id)` pairs; `count` records how many instances
|
|
26
|
+
* were observed and `first_hash` / `first_text_id` capture the first
|
|
27
|
+
* instance's identity so the most important observable fact per link is
|
|
28
|
+
* preserved without every redundant row.
|
|
29
|
+
* - `resource_ref_edges` — replaces `resources-referrers`. Structural
|
|
30
|
+
* rename with an added `count` column; `WITHOUT ROWID` because the
|
|
31
|
+
* composite PK `(resource_id, page_id)` is the natural clustering key
|
|
32
|
+
* and every column is small — the same reasoning as `page_html_blobs`.
|
|
33
|
+
* - `image_items` — replaces `images`. `src` / `currentSrc` split into
|
|
34
|
+
* `*_url_id` (regular URLs) vs `*_blob_id` (large `data:` URIs, routed
|
|
35
|
+
* via `blob_refs`); `sourceCode` is replaced by `dom_path_text_id`,
|
|
36
|
+
* which the populate step derives from the `outerHTML` string.
|
|
37
|
+
*
|
|
38
|
+
* ### Key invariants
|
|
39
|
+
*
|
|
40
|
+
* **Same PK values as legacy.** `content_items.id`, `resource_items.id`,
|
|
41
|
+
* and `image_items.id` reuse the exact PK values from `pages.id`,
|
|
42
|
+
* `resources.id`, and `images.id` respectively. The populate step inserts
|
|
43
|
+
* rows with explicit IDs so every existing FK reference in `page_errors`,
|
|
44
|
+
* `page_tags`, `page_jsonld`, `page_html_ref`, and `resources-referrers`
|
|
45
|
+
* (before it becomes `resource_ref_edges`) survives the switch without
|
|
46
|
+
* any per-row UPDATE. `AUTOINCREMENT` is nevertheless applied on all four
|
|
47
|
+
* entity PKs (`content_items` / `resource_items` / `anchor_edges` /
|
|
48
|
+
* `image_items`) to match the legacy contract (`t.increments()` on
|
|
49
|
+
* `pages` / `resources` / `anchors` / `images`) — this prevents rowid
|
|
50
|
+
* reuse after DELETE, so external systems (analysis outputs, viewer
|
|
51
|
+
* bookmarks, Sheets exports) keyed on `content_item.id` cannot be
|
|
52
|
+
* silently reassigned to a different record.
|
|
53
|
+
*
|
|
54
|
+
* **UNIQUE(url_id) on content_items and resource_items.** The legacy
|
|
55
|
+
* `pages.url UNIQUE` / `resources.url UNIQUE` guaranteed one row per
|
|
56
|
+
* URL string. Under the new model the URL string is stored once in
|
|
57
|
+
* `url_refs` and each entity references `url_refs.id` via `url_id` —
|
|
58
|
+
* but `url_refs.url UNIQUE` only guarantees "one URL string → one
|
|
59
|
+
* ref id", it does NOT prevent two entity rows from claiming the same
|
|
60
|
+
* ref id. `UNIQUE(url_id)` on both `content_items` and `resource_items`
|
|
61
|
+
* re-establishes that guarantee at the entity level — without it a
|
|
62
|
+
* populate bug could attach two entity rows to the same URL, and every
|
|
63
|
+
* by-URL reader would silently return an arbitrary one. The UNIQUE
|
|
64
|
+
* constraint's auto-index also serves the by-URL seek path, so no
|
|
65
|
+
* separate `CREATE INDEX ... ON content_items(url_id)` is needed.
|
|
66
|
+
* `resource_items.url_id` is nullable (unlike `content_items.url_id`)
|
|
67
|
+
* because a resource's identity URL can itself be blob-routed — see the
|
|
68
|
+
* `resource_items url / blob mutual-exclusion CHECK` invariant below.
|
|
69
|
+
* `UNIQUE` on a nullable SQLite column only dedups the non-NULL values,
|
|
70
|
+
* so the guarantee still holds for every resource that does have a
|
|
71
|
+
* `url_id`.
|
|
72
|
+
*
|
|
73
|
+
* **`redirect_dest_id` self-reference is DEFERRABLE INITIALLY DEFERRED.**
|
|
74
|
+
* `content_items.redirect_dest_id` references `content_items(id)` (not
|
|
75
|
+
* `pages(id)`), mirroring the legacy `pages.redirectDestId → pages.id`
|
|
76
|
+
* self-reference. In the real archive shape, a redirect source can be
|
|
77
|
+
* discovered as an anchor (assigned a low `pages.id`) BEFORE its
|
|
78
|
+
* destination is crawled (higher `pages.id`), so a straight
|
|
79
|
+
* `ORDER BY id ASC` populate would violate the FK on the source row.
|
|
80
|
+
* Making the FK deferred lets the populate step commit all `content_items`
|
|
81
|
+
* inserts inside one transaction without any per-row ordering
|
|
82
|
+
* discipline — SQLite validates the constraint only at COMMIT time.
|
|
83
|
+
*
|
|
84
|
+
* **`page_meta.page_id` PK == FK ON DELETE CASCADE.** `page_meta` uses
|
|
85
|
+
* `page_id` as both the primary key and the FK back to `content_items(id)`.
|
|
86
|
+
* This is a 1:1 relation (every content_item has at most one meta row),
|
|
87
|
+
* so a separate autoincrement id would only bloat the row. The cascade
|
|
88
|
+
* mirrors the legacy `page_html_ref` / `page_tags` / `page_jsonld` FKs
|
|
89
|
+
* to `pages(id)` so a future cleanup pass can DELETE from
|
|
90
|
+
* `content_items` without leaving orphan meta rows.
|
|
91
|
+
*
|
|
92
|
+
* **`content_items.source` and `resource_items.source` DEFAULT 'crawled'.**
|
|
93
|
+
* The legacy `pages.source` / `resources.source` columns were
|
|
94
|
+
* `NOT NULL DEFAULT 'crawled'`, and multiple existing writer paths rely
|
|
95
|
+
* on the default (redirect-source insert, per-chunk resource insert, and
|
|
96
|
+
* `markBrowserScrape`). Preserving the default keeps those callsites
|
|
97
|
+
* working when the 0.13 write path re-points them at the new tables — dropping
|
|
98
|
+
* the default would turn a routine crawl into an immediate hard failure
|
|
99
|
+
* the first time a callsite omits `source`.
|
|
100
|
+
*
|
|
101
|
+
* **`anchor_edges` dedup shape.** `unique(page_id, href_page_id)` enforces
|
|
102
|
+
* the collapse from ≈ 13M legacy `anchors` rows to ≈ 9.7M `anchor_edges`
|
|
103
|
+
* rows (~25 % reduction on the reference archive).
|
|
104
|
+
* `first_hash` and `first_text_id` capture the anchor `hash` and
|
|
105
|
+
* `textContent` of the FIRST instance encountered (lowest `anchors.id`
|
|
106
|
+
* for the pair); duplicate observations are counted via `count` without
|
|
107
|
+
* retaining their bodies. The UNIQUE constraint's auto-index on
|
|
108
|
+
* `(page_id, href_page_id)` also serves any `WHERE page_id = ?` seek via
|
|
109
|
+
* SQLite's leading-prefix rule, so no separate `CREATE INDEX ... ON
|
|
110
|
+
* anchor_edges(page_id)` is needed; only the reverse-direction
|
|
111
|
+
* `idx_anchor_edges_href` on `href_page_id` alone is added.
|
|
112
|
+
*
|
|
113
|
+
* **`image_items.dom_path_text_id NOT NULL`.** Every image row must
|
|
114
|
+
* resolve to a `text_refs` entry describing its DOM position. The
|
|
115
|
+
* image-items populate derives the path from `images.sourceCode` (the
|
|
116
|
+
* stored `outerHTML`); an
|
|
117
|
+
* image whose `sourceCode` is empty falls back to the synthetic label
|
|
118
|
+
* `img[unknown]` (via the image-items populate) so the FK is never violated.
|
|
119
|
+
*
|
|
120
|
+
* **`image_items` src / blob mutual-exclusion CHECK.** `src_url_id` and
|
|
121
|
+
* `src_blob_id` (and their `current_src_*` mirror) are separate nullable
|
|
122
|
+
* columns because a `<img>` value can be either a plain URL or a large
|
|
123
|
+
* `data:` URI. A single legacy `images.src` TEXT column could not
|
|
124
|
+
* express the ambiguity; the new schema splits the two but adds a CHECK
|
|
125
|
+
* so at most one slot per (src, currentSrc) pair is non-null. Without
|
|
126
|
+
* the CHECK a populate bug or future writer could silently write both,
|
|
127
|
+
* and the viewer would arbitrarily pick one to render.
|
|
128
|
+
*
|
|
129
|
+
* **`resource_items` url / blob mutual-exclusion CHECK.** Same rationale
|
|
130
|
+
* as the image CHECK above, but stricter: `(url_id IS NULL) !=
|
|
131
|
+
* (url_blob_id IS NULL)` requires EXACTLY one slot to be non-null (not
|
|
132
|
+
* "at most one") because a resource — unlike an `<img>` that can
|
|
133
|
+
* legitimately have neither `src` nor `currentSrc` — is always
|
|
134
|
+
* identified by some URL. `resolveUrlOrBlob` (writer) /
|
|
135
|
+
* `resolveUrlOrBlobFromMaps` (populate) are shared with `image_items`'
|
|
136
|
+
* routing so the two entities never disagree on which values count as
|
|
137
|
+
* "large data URI".
|
|
138
|
+
*
|
|
139
|
+
* ### Index rationale
|
|
140
|
+
*
|
|
141
|
+
* Every index below reflects a legacy-baseline single-column index that
|
|
142
|
+
* an existing hot path already relies on. `content_items` gets six
|
|
143
|
+
* indexes: `url_id` (via `UNIQUE` auto-index), `is_external`, `scraped`,
|
|
144
|
+
* `redirect_dest_id` (for the Page Detail inbound-redirect list),
|
|
145
|
+
* `content_type_id`, `crawl_order`, and `source` — all present on
|
|
146
|
+
* legacy `pages`. `page_meta` gets `og_type` and `robots_noindex`
|
|
147
|
+
* because both were indexed on legacy `pages` for the noindex /
|
|
148
|
+
* og:type filter hot paths; after the split those readers land on
|
|
149
|
+
* `page_meta` and need the indexes to remain sub-second. `resource_items` gets `url_id`
|
|
150
|
+
* (via `UNIQUE` auto-index) and `source`. `anchor_edges` gets one
|
|
151
|
+
* reverse-direction index (`idx_anchor_edges_href`); the UNIQUE
|
|
152
|
+
* auto-index covers the forward direction. `resource_ref_edges` needs
|
|
153
|
+
* an explicit reverse-direction index on `page_id` because the
|
|
154
|
+
* composite PK `(resource_id, page_id)` only satisfies the `resource_id`-
|
|
155
|
+
* first prefix; the legacy `resources-referrers` table had exactly
|
|
156
|
+
* this reverse-direction index. `image_items` gets one index on
|
|
157
|
+
* `page_id`. No summary / listfilter compound indexes are created here —
|
|
158
|
+
* they belong to the viewer read model built directly against these
|
|
159
|
+
* tables.
|
|
160
|
+
*
|
|
161
|
+
* ### Idempotency
|
|
162
|
+
*
|
|
163
|
+
* All statements use `CREATE TABLE IF NOT EXISTS` / `CREATE INDEX IF NOT
|
|
164
|
+
* EXISTS` so the primitive is safe to call multiple times against the
|
|
165
|
+
* same DB. Both call sites (`initSchema` and
|
|
166
|
+
* `migrateEntityTables`) still guard on their own sentinels,
|
|
167
|
+
* but the function itself no longer depends on the sentinel being
|
|
168
|
+
* checked first — this closes the partial-corruption gap where, say,
|
|
169
|
+
* `content_items` exists but `page_meta` was dropped by an external
|
|
170
|
+
* repair pass.
|
|
171
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
172
|
+
*/
|
|
173
|
+
export declare function createEntityTables(instance: Knex): Promise<void>;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the 6 core entity and edge tables of the 0.13 format (issue #192).
|
|
3
|
+
*
|
|
4
|
+
* These are the normalised entity / edge tables that replace the legacy
|
|
5
|
+
* write model (`pages`, `anchors`, `images`, `resources`, `resources-referrers`)
|
|
6
|
+
* in the 0.13 format. This migration is purely additive DDL — no existing table
|
|
7
|
+
* is modified and no row is inserted; the populate step
|
|
8
|
+
* (`populateEntityTables`) fills the tables from the legacy sources.
|
|
9
|
+
*
|
|
10
|
+
* Tables created:
|
|
11
|
+
*
|
|
12
|
+
* - `content_items` — replaces `pages` (core columns). The remaining
|
|
13
|
+
* per-page metadata splits into `page_meta`.
|
|
14
|
+
* - `page_meta` — page-specific metadata (title, description, meta tags,
|
|
15
|
+
* OpenGraph, Twitter, robots, denormalised aggregates, meta_extras JSON
|
|
16
|
+
* pointer). Split from `pages` so that filter / cursor queries against
|
|
17
|
+
* `content_items` scan a narrower row.
|
|
18
|
+
* - `resource_items` — replaces `resources`. Identity URL splits into
|
|
19
|
+
* `url_id` (regular URLs) vs `url_blob_id` (large `data:` URIs, routed
|
|
20
|
+
* via `blob_refs`) — a resource's own URL is captured verbatim from
|
|
21
|
+
* the network layer and, unlike a page's URL, can legally be a large
|
|
22
|
+
* inline `data:` URI (e.g. a CSS `background-image` sub-resource).
|
|
23
|
+
* - `anchor_edges` — replaces `anchors`. Deduped to distinct
|
|
24
|
+
* `(page_id, href_page_id)` pairs; `count` records how many instances
|
|
25
|
+
* were observed and `first_hash` / `first_text_id` capture the first
|
|
26
|
+
* instance's identity so the most important observable fact per link is
|
|
27
|
+
* preserved without every redundant row.
|
|
28
|
+
* - `resource_ref_edges` — replaces `resources-referrers`. Structural
|
|
29
|
+
* rename with an added `count` column; `WITHOUT ROWID` because the
|
|
30
|
+
* composite PK `(resource_id, page_id)` is the natural clustering key
|
|
31
|
+
* and every column is small — the same reasoning as `page_html_blobs`.
|
|
32
|
+
* - `image_items` — replaces `images`. `src` / `currentSrc` split into
|
|
33
|
+
* `*_url_id` (regular URLs) vs `*_blob_id` (large `data:` URIs, routed
|
|
34
|
+
* via `blob_refs`); `sourceCode` is replaced by `dom_path_text_id`,
|
|
35
|
+
* which the populate step derives from the `outerHTML` string.
|
|
36
|
+
*
|
|
37
|
+
* ### Key invariants
|
|
38
|
+
*
|
|
39
|
+
* **Same PK values as legacy.** `content_items.id`, `resource_items.id`,
|
|
40
|
+
* and `image_items.id` reuse the exact PK values from `pages.id`,
|
|
41
|
+
* `resources.id`, and `images.id` respectively. The populate step inserts
|
|
42
|
+
* rows with explicit IDs so every existing FK reference in `page_errors`,
|
|
43
|
+
* `page_tags`, `page_jsonld`, `page_html_ref`, and `resources-referrers`
|
|
44
|
+
* (before it becomes `resource_ref_edges`) survives the switch without
|
|
45
|
+
* any per-row UPDATE. `AUTOINCREMENT` is nevertheless applied on all four
|
|
46
|
+
* entity PKs (`content_items` / `resource_items` / `anchor_edges` /
|
|
47
|
+
* `image_items`) to match the legacy contract (`t.increments()` on
|
|
48
|
+
* `pages` / `resources` / `anchors` / `images`) — this prevents rowid
|
|
49
|
+
* reuse after DELETE, so external systems (analysis outputs, viewer
|
|
50
|
+
* bookmarks, Sheets exports) keyed on `content_item.id` cannot be
|
|
51
|
+
* silently reassigned to a different record.
|
|
52
|
+
*
|
|
53
|
+
* **UNIQUE(url_id) on content_items and resource_items.** The legacy
|
|
54
|
+
* `pages.url UNIQUE` / `resources.url UNIQUE` guaranteed one row per
|
|
55
|
+
* URL string. Under the new model the URL string is stored once in
|
|
56
|
+
* `url_refs` and each entity references `url_refs.id` via `url_id` —
|
|
57
|
+
* but `url_refs.url UNIQUE` only guarantees "one URL string → one
|
|
58
|
+
* ref id", it does NOT prevent two entity rows from claiming the same
|
|
59
|
+
* ref id. `UNIQUE(url_id)` on both `content_items` and `resource_items`
|
|
60
|
+
* re-establishes that guarantee at the entity level — without it a
|
|
61
|
+
* populate bug could attach two entity rows to the same URL, and every
|
|
62
|
+
* by-URL reader would silently return an arbitrary one. The UNIQUE
|
|
63
|
+
* constraint's auto-index also serves the by-URL seek path, so no
|
|
64
|
+
* separate `CREATE INDEX ... ON content_items(url_id)` is needed.
|
|
65
|
+
* `resource_items.url_id` is nullable (unlike `content_items.url_id`)
|
|
66
|
+
* because a resource's identity URL can itself be blob-routed — see the
|
|
67
|
+
* `resource_items url / blob mutual-exclusion CHECK` invariant below.
|
|
68
|
+
* `UNIQUE` on a nullable SQLite column only dedups the non-NULL values,
|
|
69
|
+
* so the guarantee still holds for every resource that does have a
|
|
70
|
+
* `url_id`.
|
|
71
|
+
*
|
|
72
|
+
* **`redirect_dest_id` self-reference is DEFERRABLE INITIALLY DEFERRED.**
|
|
73
|
+
* `content_items.redirect_dest_id` references `content_items(id)` (not
|
|
74
|
+
* `pages(id)`), mirroring the legacy `pages.redirectDestId → pages.id`
|
|
75
|
+
* self-reference. In the real archive shape, a redirect source can be
|
|
76
|
+
* discovered as an anchor (assigned a low `pages.id`) BEFORE its
|
|
77
|
+
* destination is crawled (higher `pages.id`), so a straight
|
|
78
|
+
* `ORDER BY id ASC` populate would violate the FK on the source row.
|
|
79
|
+
* Making the FK deferred lets the populate step commit all `content_items`
|
|
80
|
+
* inserts inside one transaction without any per-row ordering
|
|
81
|
+
* discipline — SQLite validates the constraint only at COMMIT time.
|
|
82
|
+
*
|
|
83
|
+
* **`page_meta.page_id` PK == FK ON DELETE CASCADE.** `page_meta` uses
|
|
84
|
+
* `page_id` as both the primary key and the FK back to `content_items(id)`.
|
|
85
|
+
* This is a 1:1 relation (every content_item has at most one meta row),
|
|
86
|
+
* so a separate autoincrement id would only bloat the row. The cascade
|
|
87
|
+
* mirrors the legacy `page_html_ref` / `page_tags` / `page_jsonld` FKs
|
|
88
|
+
* to `pages(id)` so a future cleanup pass can DELETE from
|
|
89
|
+
* `content_items` without leaving orphan meta rows.
|
|
90
|
+
*
|
|
91
|
+
* **`content_items.source` and `resource_items.source` DEFAULT 'crawled'.**
|
|
92
|
+
* The legacy `pages.source` / `resources.source` columns were
|
|
93
|
+
* `NOT NULL DEFAULT 'crawled'`, and multiple existing writer paths rely
|
|
94
|
+
* on the default (redirect-source insert, per-chunk resource insert, and
|
|
95
|
+
* `markBrowserScrape`). Preserving the default keeps those callsites
|
|
96
|
+
* working when the 0.13 write path re-points them at the new tables — dropping
|
|
97
|
+
* the default would turn a routine crawl into an immediate hard failure
|
|
98
|
+
* the first time a callsite omits `source`.
|
|
99
|
+
*
|
|
100
|
+
* **`anchor_edges` dedup shape.** `unique(page_id, href_page_id)` enforces
|
|
101
|
+
* the collapse from ≈ 13M legacy `anchors` rows to ≈ 9.7M `anchor_edges`
|
|
102
|
+
* rows (~25 % reduction on the reference archive).
|
|
103
|
+
* `first_hash` and `first_text_id` capture the anchor `hash` and
|
|
104
|
+
* `textContent` of the FIRST instance encountered (lowest `anchors.id`
|
|
105
|
+
* for the pair); duplicate observations are counted via `count` without
|
|
106
|
+
* retaining their bodies. The UNIQUE constraint's auto-index on
|
|
107
|
+
* `(page_id, href_page_id)` also serves any `WHERE page_id = ?` seek via
|
|
108
|
+
* SQLite's leading-prefix rule, so no separate `CREATE INDEX ... ON
|
|
109
|
+
* anchor_edges(page_id)` is needed; only the reverse-direction
|
|
110
|
+
* `idx_anchor_edges_href` on `href_page_id` alone is added.
|
|
111
|
+
*
|
|
112
|
+
* **`image_items.dom_path_text_id NOT NULL`.** Every image row must
|
|
113
|
+
* resolve to a `text_refs` entry describing its DOM position. The
|
|
114
|
+
* image-items populate derives the path from `images.sourceCode` (the
|
|
115
|
+
* stored `outerHTML`); an
|
|
116
|
+
* image whose `sourceCode` is empty falls back to the synthetic label
|
|
117
|
+
* `img[unknown]` (via the image-items populate) so the FK is never violated.
|
|
118
|
+
*
|
|
119
|
+
* **`image_items` src / blob mutual-exclusion CHECK.** `src_url_id` and
|
|
120
|
+
* `src_blob_id` (and their `current_src_*` mirror) are separate nullable
|
|
121
|
+
* columns because a `<img>` value can be either a plain URL or a large
|
|
122
|
+
* `data:` URI. A single legacy `images.src` TEXT column could not
|
|
123
|
+
* express the ambiguity; the new schema splits the two but adds a CHECK
|
|
124
|
+
* so at most one slot per (src, currentSrc) pair is non-null. Without
|
|
125
|
+
* the CHECK a populate bug or future writer could silently write both,
|
|
126
|
+
* and the viewer would arbitrarily pick one to render.
|
|
127
|
+
*
|
|
128
|
+
* **`resource_items` url / blob mutual-exclusion CHECK.** Same rationale
|
|
129
|
+
* as the image CHECK above, but stricter: `(url_id IS NULL) !=
|
|
130
|
+
* (url_blob_id IS NULL)` requires EXACTLY one slot to be non-null (not
|
|
131
|
+
* "at most one") because a resource — unlike an `<img>` that can
|
|
132
|
+
* legitimately have neither `src` nor `currentSrc` — is always
|
|
133
|
+
* identified by some URL. `resolveUrlOrBlob` (writer) /
|
|
134
|
+
* `resolveUrlOrBlobFromMaps` (populate) are shared with `image_items`'
|
|
135
|
+
* routing so the two entities never disagree on which values count as
|
|
136
|
+
* "large data URI".
|
|
137
|
+
*
|
|
138
|
+
* ### Index rationale
|
|
139
|
+
*
|
|
140
|
+
* Every index below reflects a legacy-baseline single-column index that
|
|
141
|
+
* an existing hot path already relies on. `content_items` gets six
|
|
142
|
+
* indexes: `url_id` (via `UNIQUE` auto-index), `is_external`, `scraped`,
|
|
143
|
+
* `redirect_dest_id` (for the Page Detail inbound-redirect list),
|
|
144
|
+
* `content_type_id`, `crawl_order`, and `source` — all present on
|
|
145
|
+
* legacy `pages`. `page_meta` gets `og_type` and `robots_noindex`
|
|
146
|
+
* because both were indexed on legacy `pages` for the noindex /
|
|
147
|
+
* og:type filter hot paths; after the split those readers land on
|
|
148
|
+
* `page_meta` and need the indexes to remain sub-second. `resource_items` gets `url_id`
|
|
149
|
+
* (via `UNIQUE` auto-index) and `source`. `anchor_edges` gets one
|
|
150
|
+
* reverse-direction index (`idx_anchor_edges_href`); the UNIQUE
|
|
151
|
+
* auto-index covers the forward direction. `resource_ref_edges` needs
|
|
152
|
+
* an explicit reverse-direction index on `page_id` because the
|
|
153
|
+
* composite PK `(resource_id, page_id)` only satisfies the `resource_id`-
|
|
154
|
+
* first prefix; the legacy `resources-referrers` table had exactly
|
|
155
|
+
* this reverse-direction index. `image_items` gets one index on
|
|
156
|
+
* `page_id`. No summary / listfilter compound indexes are created here —
|
|
157
|
+
* they belong to the viewer read model built directly against these
|
|
158
|
+
* tables.
|
|
159
|
+
*
|
|
160
|
+
* ### Idempotency
|
|
161
|
+
*
|
|
162
|
+
* All statements use `CREATE TABLE IF NOT EXISTS` / `CREATE INDEX IF NOT
|
|
163
|
+
* EXISTS` so the primitive is safe to call multiple times against the
|
|
164
|
+
* same DB. Both call sites (`initSchema` and
|
|
165
|
+
* `migrateEntityTables`) still guard on their own sentinels,
|
|
166
|
+
* but the function itself no longer depends on the sentinel being
|
|
167
|
+
* checked first — this closes the partial-corruption gap where, say,
|
|
168
|
+
* `content_items` exists but `page_meta` was dropped by an external
|
|
169
|
+
* repair pass.
|
|
170
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
171
|
+
*/
|
|
172
|
+
export async function createEntityTables(instance) {
|
|
173
|
+
await instance.raw(`
|
|
174
|
+
CREATE TABLE IF NOT EXISTS content_items (
|
|
175
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
176
|
+
url_id INTEGER NOT NULL UNIQUE REFERENCES url_refs(id),
|
|
177
|
+
is_external INTEGER NOT NULL,
|
|
178
|
+
scraped INTEGER NOT NULL,
|
|
179
|
+
is_target INTEGER NOT NULL,
|
|
180
|
+
status INTEGER,
|
|
181
|
+
status_text TEXT,
|
|
182
|
+
content_type_id INTEGER REFERENCES content_type_refs(id),
|
|
183
|
+
content_length INTEGER,
|
|
184
|
+
header_set_id INTEGER REFERENCES header_sets(id),
|
|
185
|
+
redirect_dest_id INTEGER REFERENCES content_items(id) DEFERRABLE INITIALLY DEFERRED,
|
|
186
|
+
source TEXT NOT NULL DEFAULT 'crawled',
|
|
187
|
+
first_crawled_at INTEGER,
|
|
188
|
+
last_crawled_at INTEGER,
|
|
189
|
+
crawl_order INTEGER,
|
|
190
|
+
is_skipped INTEGER,
|
|
191
|
+
skip_reason TEXT
|
|
192
|
+
)
|
|
193
|
+
`);
|
|
194
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_external ON content_items(is_external)');
|
|
195
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_scraped ON content_items(scraped)');
|
|
196
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_redirect_dest_id ON content_items(redirect_dest_id)');
|
|
197
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_content_type_id ON content_items(content_type_id)');
|
|
198
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_crawl_order ON content_items(crawl_order)');
|
|
199
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_content_items_source ON content_items(source)');
|
|
200
|
+
await instance.raw(`
|
|
201
|
+
CREATE TABLE IF NOT EXISTS page_meta (
|
|
202
|
+
page_id INTEGER PRIMARY KEY REFERENCES content_items(id) ON DELETE CASCADE,
|
|
203
|
+
lang TEXT,
|
|
204
|
+
dir TEXT,
|
|
205
|
+
charset TEXT,
|
|
206
|
+
base_href TEXT,
|
|
207
|
+
viewport_raw TEXT,
|
|
208
|
+
theme_color TEXT,
|
|
209
|
+
application_name TEXT,
|
|
210
|
+
author TEXT,
|
|
211
|
+
generator TEXT,
|
|
212
|
+
publisher TEXT,
|
|
213
|
+
title_text_id INTEGER REFERENCES text_refs(id),
|
|
214
|
+
description_text_id INTEGER REFERENCES text_refs(id),
|
|
215
|
+
keywords_text_id INTEGER REFERENCES text_refs(id),
|
|
216
|
+
robots_raw_text_id INTEGER REFERENCES text_refs(id),
|
|
217
|
+
robots_noindex INTEGER,
|
|
218
|
+
robots_nofollow INTEGER,
|
|
219
|
+
robots_noarchive INTEGER,
|
|
220
|
+
robots_noimageindex INTEGER,
|
|
221
|
+
googlebot TEXT,
|
|
222
|
+
canonical_url_id INTEGER REFERENCES url_refs(id),
|
|
223
|
+
amphtml_url_id INTEGER REFERENCES url_refs(id),
|
|
224
|
+
manifest_url_id INTEGER REFERENCES url_refs(id),
|
|
225
|
+
icon_url_id INTEGER REFERENCES url_refs(id),
|
|
226
|
+
apple_touch_icon_url_id INTEGER REFERENCES url_refs(id),
|
|
227
|
+
og_type TEXT,
|
|
228
|
+
og_title_text_id INTEGER REFERENCES text_refs(id),
|
|
229
|
+
og_description_text_id INTEGER REFERENCES text_refs(id),
|
|
230
|
+
og_url_id INTEGER REFERENCES url_refs(id),
|
|
231
|
+
og_image_url_id INTEGER REFERENCES url_refs(id),
|
|
232
|
+
og_site_name TEXT,
|
|
233
|
+
og_image_alt TEXT,
|
|
234
|
+
og_image_width TEXT,
|
|
235
|
+
og_image_height TEXT,
|
|
236
|
+
og_locale TEXT,
|
|
237
|
+
og_article_published_time TEXT,
|
|
238
|
+
og_article_modified_time TEXT,
|
|
239
|
+
twitter_card TEXT,
|
|
240
|
+
twitter_site TEXT,
|
|
241
|
+
twitter_creator TEXT,
|
|
242
|
+
twitter_title_text_id INTEGER REFERENCES text_refs(id),
|
|
243
|
+
twitter_description_text_id INTEGER REFERENCES text_refs(id),
|
|
244
|
+
twitter_image_url_id INTEGER REFERENCES url_refs(id),
|
|
245
|
+
fb_app_id TEXT,
|
|
246
|
+
verification_google TEXT,
|
|
247
|
+
format_detection_telephone INTEGER,
|
|
248
|
+
tag_count INTEGER,
|
|
249
|
+
jsonld_count INTEGER,
|
|
250
|
+
tags_providers_csv TEXT,
|
|
251
|
+
meta_extras_json_id INTEGER REFERENCES json_refs(id)
|
|
252
|
+
)
|
|
253
|
+
`);
|
|
254
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_page_meta_og_type ON page_meta(og_type)');
|
|
255
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_page_meta_robots_noindex ON page_meta(robots_noindex)');
|
|
256
|
+
await instance.raw(`
|
|
257
|
+
CREATE TABLE IF NOT EXISTS resource_items (
|
|
258
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
259
|
+
url_id INTEGER UNIQUE REFERENCES url_refs(id),
|
|
260
|
+
url_blob_id INTEGER UNIQUE REFERENCES blob_refs(id),
|
|
261
|
+
is_external INTEGER NOT NULL,
|
|
262
|
+
status INTEGER,
|
|
263
|
+
status_text TEXT,
|
|
264
|
+
content_type_id INTEGER REFERENCES content_type_refs(id),
|
|
265
|
+
content_length INTEGER,
|
|
266
|
+
header_set_id INTEGER REFERENCES header_sets(id),
|
|
267
|
+
compress TEXT,
|
|
268
|
+
cdn TEXT,
|
|
269
|
+
source TEXT NOT NULL DEFAULT 'crawled',
|
|
270
|
+
CHECK ((url_id IS NULL) != (url_blob_id IS NULL))
|
|
271
|
+
)
|
|
272
|
+
`);
|
|
273
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_resource_items_source ON resource_items(source)');
|
|
274
|
+
await instance.raw(`
|
|
275
|
+
CREATE TABLE IF NOT EXISTS anchor_edges (
|
|
276
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
277
|
+
page_id INTEGER NOT NULL REFERENCES content_items(id),
|
|
278
|
+
href_page_id INTEGER NOT NULL REFERENCES content_items(id),
|
|
279
|
+
count INTEGER NOT NULL,
|
|
280
|
+
first_hash TEXT,
|
|
281
|
+
first_text_id INTEGER REFERENCES text_refs(id),
|
|
282
|
+
UNIQUE(page_id, href_page_id)
|
|
283
|
+
)
|
|
284
|
+
`);
|
|
285
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_anchor_edges_href ON anchor_edges(href_page_id)');
|
|
286
|
+
await instance.raw(`
|
|
287
|
+
CREATE TABLE IF NOT EXISTS resource_ref_edges (
|
|
288
|
+
resource_id INTEGER NOT NULL REFERENCES resource_items(id),
|
|
289
|
+
page_id INTEGER NOT NULL REFERENCES content_items(id),
|
|
290
|
+
count INTEGER NOT NULL DEFAULT 1,
|
|
291
|
+
PRIMARY KEY(resource_id, page_id)
|
|
292
|
+
) WITHOUT ROWID
|
|
293
|
+
`);
|
|
294
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_resource_ref_edges_page ON resource_ref_edges(page_id)');
|
|
295
|
+
await instance.raw(`
|
|
296
|
+
CREATE TABLE IF NOT EXISTS image_items (
|
|
297
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
298
|
+
page_id INTEGER NOT NULL REFERENCES content_items(id),
|
|
299
|
+
src_url_id INTEGER REFERENCES url_refs(id),
|
|
300
|
+
current_src_url_id INTEGER REFERENCES url_refs(id),
|
|
301
|
+
src_blob_id INTEGER REFERENCES blob_refs(id),
|
|
302
|
+
current_src_blob_id INTEGER REFERENCES blob_refs(id),
|
|
303
|
+
alt_text_id INTEGER REFERENCES text_refs(id),
|
|
304
|
+
width REAL,
|
|
305
|
+
height REAL,
|
|
306
|
+
natural_width INTEGER,
|
|
307
|
+
natural_height INTEGER,
|
|
308
|
+
is_lazy INTEGER,
|
|
309
|
+
viewport_width INTEGER,
|
|
310
|
+
dom_path_text_id INTEGER NOT NULL REFERENCES text_refs(id),
|
|
311
|
+
CHECK (
|
|
312
|
+
(src_url_id IS NULL OR src_blob_id IS NULL)
|
|
313
|
+
AND (current_src_url_id IS NULL OR current_src_blob_id IS NULL)
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
`);
|
|
317
|
+
await instance.raw('CREATE INDEX IF NOT EXISTS idx_image_items_page ON image_items(page_id)');
|
|
318
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emits a human-readable progress line for a long-running populate step.
|
|
3
|
+
* Injected the same way as `PageDomPathResolver`
|
|
4
|
+
* (`populate-entity-tables/populate-image-items.ts`) — the crawler
|
|
5
|
+
* package stays silent by default for library consumers, while
|
|
6
|
+
* `scripts/migrate-to-0.13.mjs` supplies a `console.log`-backed callback.
|
|
7
|
+
*/
|
|
8
|
+
export type ProgressCallback = (message: string) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Returns a `report(processed)` function that calls `onProgress` at most
|
|
11
|
+
* once per 5%-of-`total` tier crossed, so a keyset-paginated loop over a
|
|
12
|
+
* multi-million-row table can report progress every chunk without
|
|
13
|
+
* flooding the log with one line per chunk.
|
|
14
|
+
* @param label - Prefix identifying which populate step / source table
|
|
15
|
+
* this reporter tracks, e.g. `"url_refs (resources)"`.
|
|
16
|
+
* @param total - Total row/page count the loop will scan, precomputed by
|
|
17
|
+
* the caller via a `COUNT(*)`.
|
|
18
|
+
* @param onProgress - Caller-supplied sink, or `undefined` to disable
|
|
19
|
+
* reporting entirely (zero overhead — `report` becomes a no-op).
|
|
20
|
+
* @returns `report(processed)` — call after each chunk with the
|
|
21
|
+
* cumulative count processed so far.
|
|
22
|
+
* @example
|
|
23
|
+
* const total = Number((await trx('url_refs').count({ n: '*' }))[0]?.n ?? 0);
|
|
24
|
+
* const report = createProgressReporter('url_refs', total, onProgress);
|
|
25
|
+
* let processed = 0;
|
|
26
|
+
* // ...inside the read-chunk loop...
|
|
27
|
+
* processed += rows.length;
|
|
28
|
+
* report(processed);
|
|
29
|
+
*/
|
|
30
|
+
export declare function createProgressReporter(label: string, total: number, onProgress: ProgressCallback | undefined): (processed: number) => void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Progress tiers per run — one report per 5% of `total`, so a 13M-row table produces ~20 lines, not one per chunk. */
|
|
2
|
+
const TIERS = 20;
|
|
3
|
+
/**
|
|
4
|
+
* Returns a `report(processed)` function that calls `onProgress` at most
|
|
5
|
+
* once per 5%-of-`total` tier crossed, so a keyset-paginated loop over a
|
|
6
|
+
* multi-million-row table can report progress every chunk without
|
|
7
|
+
* flooding the log with one line per chunk.
|
|
8
|
+
* @param label - Prefix identifying which populate step / source table
|
|
9
|
+
* this reporter tracks, e.g. `"url_refs (resources)"`.
|
|
10
|
+
* @param total - Total row/page count the loop will scan, precomputed by
|
|
11
|
+
* the caller via a `COUNT(*)`.
|
|
12
|
+
* @param onProgress - Caller-supplied sink, or `undefined` to disable
|
|
13
|
+
* reporting entirely (zero overhead — `report` becomes a no-op).
|
|
14
|
+
* @returns `report(processed)` — call after each chunk with the
|
|
15
|
+
* cumulative count processed so far.
|
|
16
|
+
* @example
|
|
17
|
+
* const total = Number((await trx('url_refs').count({ n: '*' }))[0]?.n ?? 0);
|
|
18
|
+
* const report = createProgressReporter('url_refs', total, onProgress);
|
|
19
|
+
* let processed = 0;
|
|
20
|
+
* // ...inside the read-chunk loop...
|
|
21
|
+
* processed += rows.length;
|
|
22
|
+
* report(processed);
|
|
23
|
+
*/
|
|
24
|
+
export function createProgressReporter(label, total, onProgress) {
|
|
25
|
+
let lastTier = 0;
|
|
26
|
+
return (processed) => {
|
|
27
|
+
if (onProgress === undefined || total === 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const tier = Math.floor((processed / total) * TIERS);
|
|
31
|
+
if (tier <= lastTier) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
lastTier = tier;
|
|
35
|
+
const percent = Math.min(100, Math.round((processed / total) * 100));
|
|
36
|
+
onProgress(`${label}: ${processed}/${total} (${percent}%)`);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the 10 dictionary tables of the 0.13 format (issue #190) and their index.
|
|
4
|
+
*
|
|
5
|
+
* These are the ref / header dictionary tables that form the durable
|
|
6
|
+
* write-model of the 0.13 format: `url_refs`, `content_type_refs`,
|
|
7
|
+
* `text_refs`, `json_refs`, `blob_refs`, `header_name_refs`,
|
|
8
|
+
* `header_value_refs`, `header_sets` (+ `idx_header_sets_stable`),
|
|
9
|
+
* `header_set_entries`, `header_flags`.
|
|
10
|
+
*
|
|
11
|
+
* The DDL is shared between fresh-archive provisioning ({@link initSchema}
|
|
12
|
+
* calls this on a brand new DB) and the lazy migration path
|
|
13
|
+
* ({@link migrateRefTables} calls it on archives created before the
|
|
14
|
+
* 0.13 format). Keeping the schema in one function guarantees that both
|
|
15
|
+
* origin points produce byte-identical tables — a divergence would silently
|
|
16
|
+
* break the 0.13 population step, which relies on the exact UNIQUE
|
|
17
|
+
* constraints and CHECK clauses declared here.
|
|
18
|
+
*
|
|
19
|
+
* Hash columns store 32-byte BLAKE3 as `BLOB`, matching `page_html_blobs.hash`.
|
|
20
|
+
* `blob_refs` intentionally uses a regular integer rowid PK (unlike
|
|
21
|
+
* `page_html_blobs`) because `image_items.src_blob_id` (added in 0.13)
|
|
22
|
+
* needs a plain integer FK and auto-increment is incompatible with WITHOUT
|
|
23
|
+
* ROWID; `UNIQUE(hash)` still enforces content-addressable dedup.
|
|
24
|
+
*
|
|
25
|
+
* `header_set_entries` is WITHOUT ROWID because the composite PK
|
|
26
|
+
* `(header_set_id, name_id, occurrence)` is the natural clustering key and
|
|
27
|
+
* every column is small — the same reasoning as `page_html_blobs`.
|
|
28
|
+
*
|
|
29
|
+
* `header_sets.raw_json_hash` is a temporary column used only by the
|
|
30
|
+
* legacy-to-0.13 population step for lookups against the legacy
|
|
31
|
+
* `responseHeaders` JSON; a follow-up cleanup migration is expected to
|
|
32
|
+
* drop it.
|
|
33
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createRefTables(instance: Knex): Promise<void>;
|