@nitpicker/crawler 0.18.1 → 0.20.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/lib/append-retry-failed-common-setup-phases.d.ts +15 -0
- package/lib/append-retry-failed-common-setup-phases.js +21 -0
- package/lib/append-setup-phases.d.ts +17 -0
- package/lib/append-setup-phases.js +22 -0
- package/lib/archive/archive-accessor.d.ts +31 -5
- package/lib/archive/archive-accessor.js +46 -24
- package/lib/archive/archive.d.ts +115 -37
- package/lib/archive/archive.js +118 -52
- package/lib/archive/cache/extract-archive-to-cache.d.ts +10 -1
- package/lib/archive/cache/extract-archive-to-cache.js +14 -4
- package/lib/archive/create-adjunct-tables.d.ts +12 -2
- package/lib/archive/create-adjunct-tables.js +91 -29
- package/lib/archive/create-entity-tables.d.ts +5 -1
- package/lib/archive/create-entity-tables.js +6 -1
- package/lib/archive/database.d.ts +39 -9
- package/lib/archive/database.js +63 -18
- package/lib/archive/db-ops/lifecycle/init.d.ts +14 -2
- package/lib/archive/db-ops/lifecycle/init.js +29 -11
- package/lib/archive/db-ops/meta/get-custom-elements-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-custom-elements-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-page-technologies-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-page-technologies-of-page.js +15 -0
- package/lib/archive/db-ops/meta/get-technology-signals-of-page.d.ts +11 -0
- package/lib/archive/db-ops/meta/get-technology-signals-of-page.js +15 -0
- package/lib/archive/db-ops/pages/order/set-url-order.d.ts +6 -1
- package/lib/archive/db-ops/pages/order/set-url-order.js +7 -1
- package/lib/archive/db-ops/pages/read/build-page-query.js +1 -1
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.d.ts +6 -1
- package/lib/archive/db-ops/pages/reset/repromote-external-pages.js +12 -3
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.d.ts +6 -1
- package/lib/archive/db-ops/pages/reset/reset-failed-pages.js +10 -2
- package/lib/archive/db-ops/pages/write/insert-custom-elements.d.ts +27 -0
- package/lib/archive/db-ops/pages/write/insert-custom-elements.js +40 -0
- package/lib/archive/db-ops/pages/write/insert-page.d.ts +4 -4
- package/lib/archive/db-ops/pages/write/insert-page.js +3 -2
- package/lib/archive/db-ops/pages/write/insert-technologies.d.ts +25 -0
- package/lib/archive/db-ops/pages/write/insert-technologies.js +45 -0
- package/lib/archive/db-ops/pages/write/update-page.d.ts +4 -1
- package/lib/archive/db-ops/pages/write/update-page.js +38 -16
- package/lib/archive/db-ops/resources/get-resource-url-list.d.ts +12 -1
- package/lib/archive/db-ops/resources/get-resource-url-list.js +43 -5
- package/lib/archive/filesystem/copy-file-with-progress.d.ts +20 -0
- package/lib/archive/filesystem/copy-file-with-progress.js +34 -0
- package/lib/archive/filesystem/parse-pax-path.d.ts +18 -0
- package/lib/archive/filesystem/parse-pax-path.js +47 -0
- package/lib/archive/filesystem/parse-tar-size-field.d.ts +13 -0
- package/lib/archive/filesystem/parse-tar-size-field.js +40 -0
- package/lib/archive/filesystem/peek-tar-top-dir.d.ts +10 -3
- package/lib/archive/filesystem/peek-tar-top-dir.js +166 -16
- package/lib/archive/filesystem/tar.d.ts +13 -1
- package/lib/archive/filesystem/tar.js +63 -7
- package/lib/archive/filesystem/untar.d.ts +13 -0
- package/lib/archive/filesystem/untar.js +68 -8
- package/lib/archive/init-schema.d.ts +1 -1
- package/lib/archive/init-schema.js +6 -4
- package/lib/archive/meta/compute-main-contents-denormalized.d.ts +13 -2
- package/lib/archive/meta/compute-main-contents-denormalized.js +15 -2
- package/lib/archive/meta/technologies/combine-technology-confidence.d.ts +46 -0
- package/lib/archive/meta/technologies/combine-technology-confidence.js +93 -0
- package/lib/archive/meta/technologies/convert-legacy-page-tags-to-inserts.d.ts +45 -0
- package/lib/archive/meta/technologies/convert-legacy-page-tags-to-inserts.js +53 -0
- package/lib/archive/meta/technologies/convert-tag-row-to-wappalyzer-signal.d.ts +33 -0
- package/lib/archive/meta/technologies/convert-tag-row-to-wappalyzer-signal.js +46 -0
- package/lib/archive/meta/technologies/extract-technologies-for-archive.d.ts +30 -0
- package/lib/archive/meta/technologies/extract-technologies-for-archive.js +27 -0
- package/lib/archive/meta/technologies/match-structural-technology-signals.d.ts +13 -0
- package/lib/archive/meta/technologies/match-structural-technology-signals.js +44 -0
- package/lib/archive/meta/technologies/normalize-wappalyzer-entries.d.ts +25 -0
- package/lib/archive/meta/technologies/normalize-wappalyzer-entries.js +35 -0
- package/lib/archive/meta/technologies/technology-signal-definitions.d.ts +43 -0
- package/lib/archive/meta/technologies/technology-signal-definitions.js +193 -0
- package/lib/archive/meta/technologies/types.d.ts +68 -0
- package/lib/archive/meta/technologies/types.js +13 -0
- package/lib/archive/meta/types.d.ts +97 -30
- package/lib/archive/migrate-content-items-alias-of-id.d.ts +6 -1
- package/lib/archive/migrate-content-items-alias-of-id.js +14 -3
- package/lib/archive/migrate-content-items-dedupe-cap-event-id.d.ts +6 -1
- package/lib/archive/migrate-content-items-dedupe-cap-event-id.js +14 -3
- package/lib/archive/migrate-info-main-content-selector.d.ts +6 -1
- package/lib/archive/migrate-info-main-content-selector.js +14 -3
- package/lib/archive/migrate-info-roots.d.ts +6 -1
- package/lib/archive/migrate-info-roots.js +14 -3
- package/lib/archive/migrate-inventory-runs-exclude-skipped.d.ts +6 -1
- package/lib/archive/migrate-inventory-runs-exclude-skipped.js +14 -3
- package/lib/archive/migrate-inventory-runs-invalid-skipped.d.ts +6 -1
- package/lib/archive/migrate-inventory-runs-invalid-skipped.js +14 -3
- package/lib/archive/migrate-main-contents-columns.d.ts +6 -1
- package/lib/archive/migrate-main-contents-columns.js +14 -3
- package/lib/archive/migrate-page-meta-body-hash.d.ts +6 -1
- package/lib/archive/migrate-page-meta-body-hash.js +14 -3
- package/lib/archive/migrate-page-meta-console-error-count.d.ts +6 -1
- package/lib/archive/migrate-page-meta-console-error-count.js +14 -3
- package/lib/archive/migrate-page-meta-custom-element-count.d.ts +33 -0
- package/lib/archive/migrate-page-meta-custom-element-count.js +51 -0
- package/lib/archive/migrate-page-tags-to-page-technologies.d.ts +39 -0
- package/lib/archive/migrate-page-tags-to-page-technologies.js +79 -0
- package/lib/archive/page.d.ts +37 -13
- package/lib/archive/page.js +45 -17
- package/lib/archive/retarget-legacy-fk-tables.d.ts +27 -14
- package/lib/archive/retarget-legacy-fk-tables.js +61 -15
- package/lib/archive/types.d.ts +18 -0
- package/lib/crawler/assert-puppeteer-shared-with-beholder.d.ts +24 -0
- package/lib/crawler/assert-puppeteer-shared-with-beholder.js +36 -0
- package/lib/crawler/capture-custom-elements.d.ts +33 -0
- package/lib/crawler/capture-custom-elements.js +39 -0
- package/lib/crawler/close-browser-safely.d.ts +3 -3
- package/lib/crawler/close-browser-safely.js +8 -17
- package/lib/crawler/collect-custom-elements.d.ts +33 -0
- package/lib/crawler/collect-custom-elements.js +110 -0
- package/lib/crawler/crawler.js +140 -56
- package/lib/crawler/fetch-destination.js +6 -13
- package/lib/crawler/find-package-dir.d.ts +24 -0
- package/lib/crawler/find-package-dir.js +39 -0
- package/lib/crawler/handle-scrape-end.js +19 -10
- package/lib/crawler/resolve-result-went-off-host.d.ts +34 -0
- package/lib/crawler/resolve-result-went-off-host.js +39 -0
- package/lib/crawler/scan-js-resource-for-license-comment.d.ts +32 -0
- package/lib/crawler/scan-js-resource-for-license-comment.js +140 -0
- package/lib/crawler/scan-js-resources-for-technology-signals.d.ts +58 -0
- package/lib/crawler/scan-js-resources-for-technology-signals.js +196 -0
- package/lib/crawler/types.d.ts +34 -1
- package/lib/crawler-orchestrator.d.ts +53 -9
- package/lib/crawler-orchestrator.js +223 -46
- package/lib/crawler.d.ts +11 -1
- package/lib/crawler.js +9 -0
- package/lib/inventory-setup-phases.d.ts +32 -0
- package/lib/inventory-setup-phases.js +45 -0
- package/lib/resume-setup-phases.d.ts +16 -0
- package/lib/resume-setup-phases.js +24 -0
- package/lib/retry-failed-setup-phases.d.ts +19 -0
- package/lib/retry-failed-setup-phases.js +24 -0
- package/lib/setup-recovery-phase-labels.d.ts +21 -0
- package/lib/setup-recovery-phase-labels.js +24 -0
- package/lib/types.d.ts +145 -0
- package/package.json +11 -8
- package/lib/archive/db-ops/meta/get-tags-of-page.d.ts +0 -12
- package/lib/archive/db-ops/meta/get-tags-of-page.js +0 -28
- package/lib/archive/db-ops/pages/write/insert-tags.d.ts +0 -16
- package/lib/archive/db-ops/pages/write/insert-tags.js +0 -34
- package/lib/archive/meta/summarize-tags.d.ts +0 -16
- package/lib/archive/meta/summarize-tags.js +0 -33
|
@@ -7,5 +7,10 @@ import type { Knex } from 'knex';
|
|
|
7
7
|
* `initSchema` call — idempotent, and self-healing for archives whose
|
|
8
8
|
* provisioning crashed partway through.
|
|
9
9
|
* @param instance - The Knex query builder instance connected to the database.
|
|
10
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
11
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
12
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
13
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
14
|
+
* `console.error` when omitted (direct/test callers).
|
|
10
15
|
*/
|
|
11
|
-
export declare function migrateInventoryRunsInvalidSkipped(instance: Knex): Promise<void>;
|
|
16
|
+
export declare function migrateInventoryRunsInvalidSkipped(instance: Knex, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
* `initSchema` call — idempotent, and self-healing for archives whose
|
|
7
7
|
* provisioning crashed partway through.
|
|
8
8
|
* @param instance - The Knex query builder instance connected to the database.
|
|
9
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
10
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
11
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
12
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
13
|
+
* `console.error` when omitted (direct/test callers).
|
|
9
14
|
*/
|
|
10
|
-
export async function migrateInventoryRunsInvalidSkipped(instance) {
|
|
15
|
+
export async function migrateInventoryRunsInvalidSkipped(instance, onLog) {
|
|
11
16
|
const hasTable = await instance.schema.hasTable('inventory_runs');
|
|
12
17
|
if (!hasTable) {
|
|
13
18
|
return;
|
|
@@ -19,6 +24,12 @@ export async function migrateInventoryRunsInvalidSkipped(instance) {
|
|
|
19
24
|
await instance.schema.table('inventory_runs', (t) => {
|
|
20
25
|
t.integer('invalid_skipped');
|
|
21
26
|
});
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
const message = '[migrate] inventory_runs.invalid_skipped column added';
|
|
28
|
+
if (onLog) {
|
|
29
|
+
onLog(message);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.error(message);
|
|
34
|
+
}
|
|
24
35
|
}
|
|
@@ -17,5 +17,10 @@ import type { Knex } from 'knex';
|
|
|
17
17
|
* itself after `assertCompatibleVersion` rejects pre-0.13 archives) the
|
|
18
18
|
* table is always present.
|
|
19
19
|
* @param instance - The Knex query builder instance connected to the database.
|
|
20
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
21
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
22
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
23
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
24
|
+
* `console.error` when omitted (direct/test callers).
|
|
20
25
|
*/
|
|
21
|
-
export declare function migrateMainContentsColumns(instance: Knex): Promise<void>;
|
|
26
|
+
export declare function migrateMainContentsColumns(instance: Knex, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
* itself after `assertCompatibleVersion` rejects pre-0.13 archives) the
|
|
17
17
|
* table is always present.
|
|
18
18
|
* @param instance - The Knex query builder instance connected to the database.
|
|
19
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
20
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
21
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
22
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
23
|
+
* `console.error` when omitted (direct/test callers).
|
|
19
24
|
*/
|
|
20
|
-
export async function migrateMainContentsColumns(instance) {
|
|
25
|
+
export async function migrateMainContentsColumns(instance, onLog) {
|
|
21
26
|
const hasPageMeta = await instance.schema.hasTable('page_meta');
|
|
22
27
|
if (!hasPageMeta) {
|
|
23
28
|
return;
|
|
@@ -45,6 +50,12 @@ export async function migrateMainContentsColumns(instance) {
|
|
|
45
50
|
t.integer('scroll_height_desktop');
|
|
46
51
|
t.integer('scroll_height_mobile');
|
|
47
52
|
});
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
const message = '[migrate] page_meta.main_content_* / scroll_height_* columns added';
|
|
54
|
+
if (onLog) {
|
|
55
|
+
onLog(message);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error(message);
|
|
60
|
+
}
|
|
50
61
|
}
|
|
@@ -35,5 +35,10 @@ import type { Knex } from 'knex';
|
|
|
35
35
|
* runs (after `initSchema`, itself after `assertCompatibleVersion` rejects
|
|
36
36
|
* pre-0.13 archives) the table is always present.
|
|
37
37
|
* @param instance - The Knex query builder instance connected to the database.
|
|
38
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
39
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
40
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
41
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
42
|
+
* `console.error` when omitted (direct/test callers).
|
|
38
43
|
*/
|
|
39
|
-
export declare function migratePageMetaBodyHash(instance: Knex): Promise<void>;
|
|
44
|
+
export declare function migratePageMetaBodyHash(instance: Knex, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -34,8 +34,13 @@
|
|
|
34
34
|
* runs (after `initSchema`, itself after `assertCompatibleVersion` rejects
|
|
35
35
|
* pre-0.13 archives) the table is always present.
|
|
36
36
|
* @param instance - The Knex query builder instance connected to the database.
|
|
37
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
38
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
39
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
40
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
41
|
+
* `console.error` when omitted (direct/test callers).
|
|
37
42
|
*/
|
|
38
|
-
export async function migratePageMetaBodyHash(instance) {
|
|
43
|
+
export async function migratePageMetaBodyHash(instance, onLog) {
|
|
39
44
|
const hasPageMeta = await instance.schema.hasTable('page_meta');
|
|
40
45
|
if (!hasPageMeta) {
|
|
41
46
|
return;
|
|
@@ -45,8 +50,14 @@ export async function migratePageMetaBodyHash(instance) {
|
|
|
45
50
|
await instance.schema.table('page_meta', (t) => {
|
|
46
51
|
t.binary('body_hash');
|
|
47
52
|
});
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
const message = '[migrate] page_meta.body_hash column added';
|
|
54
|
+
if (onLog) {
|
|
55
|
+
onLog(message);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error(message);
|
|
60
|
+
}
|
|
50
61
|
}
|
|
51
62
|
await instance.raw('CREATE INDEX IF NOT EXISTS idx_page_meta_body_hash ON page_meta(body_hash)');
|
|
52
63
|
}
|
|
@@ -18,5 +18,10 @@ import type { Knex } from 'knex';
|
|
|
18
18
|
*
|
|
19
19
|
* Idempotent: adding the column is a no-op once it exists.
|
|
20
20
|
* @param instance - The Knex query builder instance connected to the database.
|
|
21
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
22
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
23
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
24
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
25
|
+
* `console.error` when omitted (direct/test callers).
|
|
21
26
|
*/
|
|
22
|
-
export declare function migratePageMetaConsoleErrorCount(instance: Knex): Promise<void>;
|
|
27
|
+
export declare function migratePageMetaConsoleErrorCount(instance: Knex, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -17,8 +17,13 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Idempotent: adding the column is a no-op once it exists.
|
|
19
19
|
* @param instance - The Knex query builder instance connected to the database.
|
|
20
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
21
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
22
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
23
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
24
|
+
* `console.error` when omitted (direct/test callers).
|
|
20
25
|
*/
|
|
21
|
-
export async function migratePageMetaConsoleErrorCount(instance) {
|
|
26
|
+
export async function migratePageMetaConsoleErrorCount(instance, onLog) {
|
|
22
27
|
const hasPageMeta = await instance.schema.hasTable('page_meta');
|
|
23
28
|
if (!hasPageMeta) {
|
|
24
29
|
return;
|
|
@@ -28,7 +33,13 @@ export async function migratePageMetaConsoleErrorCount(instance) {
|
|
|
28
33
|
await instance.schema.table('page_meta', (t) => {
|
|
29
34
|
t.integer('console_error_count');
|
|
30
35
|
});
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
const message = '[migrate] page_meta.console_error_count column added';
|
|
37
|
+
if (onLog) {
|
|
38
|
+
onLog(message);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
console.error(message);
|
|
43
|
+
}
|
|
33
44
|
}
|
|
34
45
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Adds the `page_meta.main_content_custom_element_count` column to archives
|
|
4
|
+
* created before this feature.
|
|
5
|
+
*
|
|
6
|
+
* `page_meta` is provisioned via a bare `CREATE TABLE IF NOT EXISTS` in
|
|
7
|
+
* {@link import('./create-entity-tables.js').createEntityTables}, which
|
|
8
|
+
* self-heals a *missing table* on every `initSchema` call but is a no-op
|
|
9
|
+
* against an *existing* table — adding a column to the DDL string never
|
|
10
|
+
* reaches an archive whose `page_meta` predates this change. Same catch-up
|
|
11
|
+
* shape as {@link import('./migrate-page-meta-console-error-count.js').migratePageMetaConsoleErrorCount}.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately its own file rather than an addition to
|
|
14
|
+
* {@link import('./migrate-main-contents-columns.js').migrateMainContentsColumns}:
|
|
15
|
+
* that migration early-returns once its sentinel column
|
|
16
|
+
* (`main_content_word_count`) already exists, so an archive that predates
|
|
17
|
+
* only this one column (but already has the other sixteen main-content
|
|
18
|
+
* columns) would never reach an appended `ALTER TABLE` inside it.
|
|
19
|
+
*
|
|
20
|
+
* No separate backfill: a page that has never been re-scraped since this
|
|
21
|
+
* feature shipped stays `NULL` (unknown), which is correct — it is not
|
|
22
|
+
* conflated with `0` (capture succeeded, zero custom elements found). See
|
|
23
|
+
* {@link import('./meta/compute-main-contents-denormalized.js').computeMainContentsDenormalized}.
|
|
24
|
+
*
|
|
25
|
+
* Idempotent: adding the column is a no-op once it exists.
|
|
26
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
27
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
28
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
29
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
30
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
31
|
+
* `console.error` when omitted (direct/test callers).
|
|
32
|
+
*/
|
|
33
|
+
export declare function migratePageMetaCustomElementCount(instance: Knex, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds the `page_meta.main_content_custom_element_count` column to archives
|
|
3
|
+
* created before this feature.
|
|
4
|
+
*
|
|
5
|
+
* `page_meta` is provisioned via a bare `CREATE TABLE IF NOT EXISTS` in
|
|
6
|
+
* {@link import('./create-entity-tables.js').createEntityTables}, which
|
|
7
|
+
* self-heals a *missing table* on every `initSchema` call but is a no-op
|
|
8
|
+
* against an *existing* table — adding a column to the DDL string never
|
|
9
|
+
* reaches an archive whose `page_meta` predates this change. Same catch-up
|
|
10
|
+
* shape as {@link import('./migrate-page-meta-console-error-count.js').migratePageMetaConsoleErrorCount}.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately its own file rather than an addition to
|
|
13
|
+
* {@link import('./migrate-main-contents-columns.js').migrateMainContentsColumns}:
|
|
14
|
+
* that migration early-returns once its sentinel column
|
|
15
|
+
* (`main_content_word_count`) already exists, so an archive that predates
|
|
16
|
+
* only this one column (but already has the other sixteen main-content
|
|
17
|
+
* columns) would never reach an appended `ALTER TABLE` inside it.
|
|
18
|
+
*
|
|
19
|
+
* No separate backfill: a page that has never been re-scraped since this
|
|
20
|
+
* feature shipped stays `NULL` (unknown), which is correct — it is not
|
|
21
|
+
* conflated with `0` (capture succeeded, zero custom elements found). See
|
|
22
|
+
* {@link import('./meta/compute-main-contents-denormalized.js').computeMainContentsDenormalized}.
|
|
23
|
+
*
|
|
24
|
+
* Idempotent: adding the column is a no-op once it exists.
|
|
25
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
26
|
+
* @param onLog - Called instead of `console.error` when this migration
|
|
27
|
+
* actually applies (issue #294: a bare `console.error` here can fire
|
|
28
|
+
* while a `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
29
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
30
|
+
* `console.error` when omitted (direct/test callers).
|
|
31
|
+
*/
|
|
32
|
+
export async function migratePageMetaCustomElementCount(instance, onLog) {
|
|
33
|
+
const hasPageMeta = await instance.schema.hasTable('page_meta');
|
|
34
|
+
if (!hasPageMeta) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const hasColumn = await instance.schema.hasColumn('page_meta', 'main_content_custom_element_count');
|
|
38
|
+
if (!hasColumn) {
|
|
39
|
+
await instance.schema.table('page_meta', (t) => {
|
|
40
|
+
t.integer('main_content_custom_element_count');
|
|
41
|
+
});
|
|
42
|
+
const message = '[migrate] page_meta.main_content_custom_element_count column added';
|
|
43
|
+
if (onLog) {
|
|
44
|
+
onLog(message);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// eslint-disable-next-line no-console
|
|
48
|
+
console.error(message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Converts every `page_tags` row (the removed Wappalyzer-only detection
|
|
4
|
+
* table) into `technology_signals` + `page_technologies` rows, then drops
|
|
5
|
+
* `page_tags` — for archives crawled before this feature shipped, which
|
|
6
|
+
* still carry the old table.
|
|
7
|
+
*
|
|
8
|
+
* Unlike every other `migrate*` function in this directory (which add a
|
|
9
|
+
* nullable column and never touch existing tables), this one both writes
|
|
10
|
+
* to two NEW tables and DROPS an old one — because `page_technologies`
|
|
11
|
+
* supersedes `page_tags` outright rather than extending it. The conversion
|
|
12
|
+
* itself is shared with `retarget-legacy-fk-tables.ts`'s special-cased
|
|
13
|
+
* `page_tags` handling (0.10→0.13 upgrade path) via
|
|
14
|
+
* `convertLegacyPageTagsToInserts` — a `page_tags` row converts identically
|
|
15
|
+
* regardless of which migration path produced it.
|
|
16
|
+
*
|
|
17
|
+
* No separate backfill step is needed (v0.x policy: read-time migration
|
|
18
|
+
* only, no retroactive re-crawl): only the `wappalyzer` signal survives the
|
|
19
|
+
* conversion — the structural/`js-license-comment` signals this feature
|
|
20
|
+
* also detects require re-crawling to populate, which is the same
|
|
21
|
+
* "existing archives need a re-crawl for new signal types" trade-off
|
|
22
|
+
* already accepted for this feature.
|
|
23
|
+
*
|
|
24
|
+
* Idempotent: a no-op once `page_tags` is gone (either because this
|
|
25
|
+
* function already ran, or because the archive was crawled after this
|
|
26
|
+
* feature shipped and never had `page_tags` at all).
|
|
27
|
+
*
|
|
28
|
+
* Logs a row count before starting (issue #294): on a large legacy archive
|
|
29
|
+
* this SELECT-all-convert-drop runs as one transaction with no per-chunk
|
|
30
|
+
* progress, so without an upfront count it looks indistinguishable from a
|
|
31
|
+
* hang for however long the conversion actually takes.
|
|
32
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
33
|
+
* @param onLog - Called instead of `console.error` for both the start and
|
|
34
|
+
* end notices (issue #294: a bare `console.error` here can fire while a
|
|
35
|
+
* `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
36
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
37
|
+
* `console.error` when omitted (direct/test callers).
|
|
38
|
+
*/
|
|
39
|
+
export declare function migratePageTagsToPageTechnologies(instance: Knex, onLog?: (message: string) => void): Promise<void>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { eachSplitted } from '../utils/array/each-splitted.js';
|
|
2
|
+
import { convertLegacyPageTagsToInserts } from './meta/technologies/convert-legacy-page-tags-to-inserts.js';
|
|
3
|
+
/**
|
|
4
|
+
* Converts every `page_tags` row (the removed Wappalyzer-only detection
|
|
5
|
+
* table) into `technology_signals` + `page_technologies` rows, then drops
|
|
6
|
+
* `page_tags` — for archives crawled before this feature shipped, which
|
|
7
|
+
* still carry the old table.
|
|
8
|
+
*
|
|
9
|
+
* Unlike every other `migrate*` function in this directory (which add a
|
|
10
|
+
* nullable column and never touch existing tables), this one both writes
|
|
11
|
+
* to two NEW tables and DROPS an old one — because `page_technologies`
|
|
12
|
+
* supersedes `page_tags` outright rather than extending it. The conversion
|
|
13
|
+
* itself is shared with `retarget-legacy-fk-tables.ts`'s special-cased
|
|
14
|
+
* `page_tags` handling (0.10→0.13 upgrade path) via
|
|
15
|
+
* `convertLegacyPageTagsToInserts` — a `page_tags` row converts identically
|
|
16
|
+
* regardless of which migration path produced it.
|
|
17
|
+
*
|
|
18
|
+
* No separate backfill step is needed (v0.x policy: read-time migration
|
|
19
|
+
* only, no retroactive re-crawl): only the `wappalyzer` signal survives the
|
|
20
|
+
* conversion — the structural/`js-license-comment` signals this feature
|
|
21
|
+
* also detects require re-crawling to populate, which is the same
|
|
22
|
+
* "existing archives need a re-crawl for new signal types" trade-off
|
|
23
|
+
* already accepted for this feature.
|
|
24
|
+
*
|
|
25
|
+
* Idempotent: a no-op once `page_tags` is gone (either because this
|
|
26
|
+
* function already ran, or because the archive was crawled after this
|
|
27
|
+
* feature shipped and never had `page_tags` at all).
|
|
28
|
+
*
|
|
29
|
+
* Logs a row count before starting (issue #294): on a large legacy archive
|
|
30
|
+
* this SELECT-all-convert-drop runs as one transaction with no per-chunk
|
|
31
|
+
* progress, so without an upfront count it looks indistinguishable from a
|
|
32
|
+
* hang for however long the conversion actually takes.
|
|
33
|
+
* @param instance - The Knex query builder instance connected to the database.
|
|
34
|
+
* @param onLog - Called instead of `console.error` for both the start and
|
|
35
|
+
* end notices (issue #294: a bare `console.error` here can fire while a
|
|
36
|
+
* `@d-zero/dealer` `Lanes`/`TaskList` display is mid-redraw during
|
|
37
|
+
* `Archive.open`, corrupting its cursor tracking). Falls back to
|
|
38
|
+
* `console.error` when omitted (direct/test callers).
|
|
39
|
+
*/
|
|
40
|
+
export async function migratePageTagsToPageTechnologies(instance, onLog) {
|
|
41
|
+
const hasPageTags = await instance.schema.hasTable('page_tags');
|
|
42
|
+
if (!hasPageTags)
|
|
43
|
+
return;
|
|
44
|
+
const [row] = await instance('page_tags').count({ total: '*' });
|
|
45
|
+
const total = row?.total ?? 0;
|
|
46
|
+
const startMessage = `[migrate] converting ${total} page_tags row(s) to technology_signals/page_technologies…`;
|
|
47
|
+
if (onLog) {
|
|
48
|
+
onLog(startMessage);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.error(startMessage);
|
|
53
|
+
}
|
|
54
|
+
await instance.transaction(async (trx) => {
|
|
55
|
+
const rows = await trx
|
|
56
|
+
.select('pageId', 'provider', 'version', 'confidence', 'categories')
|
|
57
|
+
.from('page_tags');
|
|
58
|
+
const { signalInserts, technologyInserts } = convertLegacyPageTagsToInserts(rows);
|
|
59
|
+
if (signalInserts.length > 0) {
|
|
60
|
+
await eachSplitted(signalInserts, 100, async (chunk) => {
|
|
61
|
+
await trx('technology_signals').insert(chunk);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (technologyInserts.length > 0) {
|
|
65
|
+
await eachSplitted(technologyInserts, 100, async (chunk) => {
|
|
66
|
+
await trx('page_technologies').insert(chunk);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
await trx.schema.dropTable('page_tags');
|
|
70
|
+
});
|
|
71
|
+
const endMessage = '[migrate] page_tags converted to technology_signals/page_technologies and dropped';
|
|
72
|
+
if (onLog) {
|
|
73
|
+
onLog(endMessage);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// eslint-disable-next-line no-console
|
|
77
|
+
console.error(endMessage);
|
|
78
|
+
}
|
|
79
|
+
}
|
package/lib/archive/page.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ArchiveAccessor } from './archive-accessor.js';
|
|
2
|
-
import type { JsonLdRow, JsonLdSummary,
|
|
2
|
+
import type { JsonLdRow, JsonLdSummary, PageTechnologyRow, TechnologySignalRow } from './meta/types.js';
|
|
3
3
|
import type { Anchor, Redirect, Referrer, DB_Anchor, DB_Page, DB_Redirect, DB_Referrer } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Subset of {@link DB_Page} that maps to the flat meta columns derived from
|
|
@@ -18,9 +18,10 @@ declare const FLAT_META_COLUMNS: readonly ["lang", "dir", "charset", "baseHref",
|
|
|
18
18
|
* iterable view over all ~47 flat meta columns and {@link Page.metaExtras} for
|
|
19
19
|
* the JSON catch-all of nested sub-objects not flattened to columns.
|
|
20
20
|
*
|
|
21
|
-
* JSON-LD entries and
|
|
22
|
-
* fetched on demand via {@link Page.getJsonLd} /
|
|
23
|
-
*
|
|
21
|
+
* JSON-LD entries and detected technology signals live in dedicated tables
|
|
22
|
+
* and are fetched on demand via {@link Page.getJsonLd} /
|
|
23
|
+
* {@link Page.getTechnologySignals} / {@link Page.getPageTechnologies} (lazy
|
|
24
|
+
* reads, same pattern as {@link Page.getAnchors}).
|
|
24
25
|
*
|
|
25
26
|
* Instances are created by {@link ArchiveAccessor.getPages} or
|
|
26
27
|
* {@link ArchiveAccessor.getPagesWithRefs}.
|
|
@@ -155,6 +156,12 @@ export default class Page {
|
|
|
155
156
|
* Number of canvases within the main region (denormalised aggregate), or null.
|
|
156
157
|
*/
|
|
157
158
|
get mainContentCanvasCount(): number | null;
|
|
159
|
+
/**
|
|
160
|
+
* Number of Web Components (custom elements) within the main region
|
|
161
|
+
* (denormalised aggregate), `null` when unknown (page not rendered, or
|
|
162
|
+
* nitpicker's own capture failed — see `compute-main-contents-denormalized.ts`).
|
|
163
|
+
*/
|
|
164
|
+
get mainContentCustomElementCount(): number | null;
|
|
158
165
|
/**
|
|
159
166
|
* Iterable view over every flat meta column (~47 fields). Returns a frozen
|
|
160
167
|
* record so consumers can pick fields by name without re-enumerating
|
|
@@ -251,8 +258,9 @@ export default class Page {
|
|
|
251
258
|
get tagCount(): number | null;
|
|
252
259
|
/**
|
|
253
260
|
* Sorted unique Wappalyzer provider names, comma-separated, empty string
|
|
254
|
-
* when no tags. Denormalised aggregate; for the structured form
|
|
255
|
-
*
|
|
261
|
+
* when no tags. Denormalised aggregate; for the structured form (with
|
|
262
|
+
* confidence, category, and every raw signal) fetch
|
|
263
|
+
* {@link Page.getTechnologySignals} / {@link Page.getPageTechnologies} (lazy).
|
|
256
264
|
*/
|
|
257
265
|
get tagsProvidersCsv(): string;
|
|
258
266
|
/**
|
|
@@ -304,6 +312,13 @@ export default class Page {
|
|
|
304
312
|
* @returns Ordered canvas rows.
|
|
305
313
|
*/
|
|
306
314
|
getCanvases(): Promise<import("./meta/types.js").MainContentCanvasRow[]>;
|
|
315
|
+
/**
|
|
316
|
+
* Retrieves the Web Components (custom elements) within this page's
|
|
317
|
+
* detected main content region from `page_main_content_custom_elements`.
|
|
318
|
+
* Lazy — runs a single SELECT per call.
|
|
319
|
+
* @returns Ordered custom-element rows.
|
|
320
|
+
*/
|
|
321
|
+
getCustomElements(): Promise<import("./meta/types.js").MainContentCustomElementRow[]>;
|
|
307
322
|
/**
|
|
308
323
|
* Retrieves the headings within this page's detected main content region
|
|
309
324
|
* from `page_main_content_headings`. Lazy — runs a single SELECT per call.
|
|
@@ -349,6 +364,12 @@ export default class Page {
|
|
|
349
364
|
* @returns Ordered table rows.
|
|
350
365
|
*/
|
|
351
366
|
getMainContentTables(): Promise<import("./meta/types.js").MainContentTableRow[]>;
|
|
367
|
+
/**
|
|
368
|
+
* Retrieves the confidence-combined technology roll-up for this page
|
|
369
|
+
* from `page_technologies`. Lazy — runs a single SELECT per call.
|
|
370
|
+
* @returns Technology rows, highest confidence first.
|
|
371
|
+
*/
|
|
372
|
+
getPageTechnologies(): Promise<readonly PageTechnologyRow[]>;
|
|
352
373
|
/**
|
|
353
374
|
* Retrieves the referrers (incoming links) pointing to this page.
|
|
354
375
|
* Uses pre-loaded data if available, otherwise queries the database.
|
|
@@ -362,11 +383,11 @@ export default class Page {
|
|
|
362
383
|
*/
|
|
363
384
|
getRequests(): Promise<Referrer[]>;
|
|
364
385
|
/**
|
|
365
|
-
* Retrieves the
|
|
366
|
-
* Lazy — runs a single SELECT per call.
|
|
367
|
-
* @returns Ordered
|
|
386
|
+
* Retrieves the raw technology-detection signals for this page from
|
|
387
|
+
* `technology_signals`. Lazy — runs a single SELECT per call.
|
|
388
|
+
* @returns Ordered signal rows.
|
|
368
389
|
*/
|
|
369
|
-
|
|
390
|
+
getTechnologySignals(): Promise<readonly TechnologySignalRow[]>;
|
|
370
391
|
/**
|
|
371
392
|
* Retrieves the videos within this page's detected main content region
|
|
372
393
|
* from `page_main_content_videos`. Lazy — runs a single SELECT per call.
|
|
@@ -385,8 +406,11 @@ export default class Page {
|
|
|
385
406
|
isPage(): boolean;
|
|
386
407
|
/**
|
|
387
408
|
* Serializes the page data to a plain JSON object including the full flat
|
|
388
|
-
* meta column set, the `meta_extras` catch-all,
|
|
389
|
-
* JSON-LD and
|
|
409
|
+
* meta column set, the `meta_extras` catch-all, a **summary** of the
|
|
410
|
+
* JSON-LD rows, and the confidence-combined technology roll-up
|
|
411
|
+
* (`page_technologies` — already compact, one row per detected
|
|
412
|
+
* technology, so no further summarization is needed the way `page_tags`'
|
|
413
|
+
* per-`externalId` granularity used to require).
|
|
390
414
|
*
|
|
391
415
|
* Summaries (not raw entries) are inlined so a Page detail payload stays
|
|
392
416
|
* token-bounded for MCP / LLM consumers — the full `raw` JSON-LD payload
|
|
@@ -400,7 +424,7 @@ export default class Page {
|
|
|
400
424
|
toJSON(): Promise<{
|
|
401
425
|
metaExtras: Record<string, unknown>;
|
|
402
426
|
jsonLd: JsonLdSummary;
|
|
403
|
-
|
|
427
|
+
technologies: readonly PageTechnologyRow[];
|
|
404
428
|
redirectFrom: Redirect[];
|
|
405
429
|
isPage: boolean;
|
|
406
430
|
isInternalPage: boolean;
|
package/lib/archive/page.js
CHANGED
|
@@ -2,7 +2,6 @@ import { tryParseUrl as parseUrl } from '@d-zero/shared/parse-url';
|
|
|
2
2
|
import { isHtmlContentType } from '../crawler/is-html-content-type.js';
|
|
3
3
|
import { parseResponseHeaders } from '../utils/object/parse-response-headers.js';
|
|
4
4
|
import { summarizeJsonLd } from './meta/summarize-jsonld.js';
|
|
5
|
-
import { summarizeTags } from './meta/summarize-tags.js';
|
|
6
5
|
/**
|
|
7
6
|
* Subset of {@link DB_Page} that maps to the flat meta columns derived from
|
|
8
7
|
* beholder's nested Meta. Used by {@link Page.metaFlat} so consumers can
|
|
@@ -86,9 +85,10 @@ const FLAT_META_COLUMNS = [
|
|
|
86
85
|
* iterable view over all ~47 flat meta columns and {@link Page.metaExtras} for
|
|
87
86
|
* the JSON catch-all of nested sub-objects not flattened to columns.
|
|
88
87
|
*
|
|
89
|
-
* JSON-LD entries and
|
|
90
|
-
* fetched on demand via {@link Page.getJsonLd} /
|
|
91
|
-
*
|
|
88
|
+
* JSON-LD entries and detected technology signals live in dedicated tables
|
|
89
|
+
* and are fetched on demand via {@link Page.getJsonLd} /
|
|
90
|
+
* {@link Page.getTechnologySignals} / {@link Page.getPageTechnologies} (lazy
|
|
91
|
+
* reads, same pattern as {@link Page.getAnchors}).
|
|
92
92
|
*
|
|
93
93
|
* Instances are created by {@link ArchiveAccessor.getPages} or
|
|
94
94
|
* {@link ArchiveAccessor.getPagesWithRefs}.
|
|
@@ -286,6 +286,14 @@ export default class Page {
|
|
|
286
286
|
get mainContentCanvasCount() {
|
|
287
287
|
return this.#raw.main_content_canvas_count;
|
|
288
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Number of Web Components (custom elements) within the main region
|
|
291
|
+
* (denormalised aggregate), `null` when unknown (page not rendered, or
|
|
292
|
+
* nitpicker's own capture failed — see `compute-main-contents-denormalized.ts`).
|
|
293
|
+
*/
|
|
294
|
+
get mainContentCustomElementCount() {
|
|
295
|
+
return this.#raw.main_content_custom_element_count;
|
|
296
|
+
}
|
|
289
297
|
/**
|
|
290
298
|
* Iterable view over every flat meta column (~47 fields). Returns a frozen
|
|
291
299
|
* record so consumers can pick fields by name without re-enumerating
|
|
@@ -436,8 +444,9 @@ export default class Page {
|
|
|
436
444
|
}
|
|
437
445
|
/**
|
|
438
446
|
* Sorted unique Wappalyzer provider names, comma-separated, empty string
|
|
439
|
-
* when no tags. Denormalised aggregate; for the structured form
|
|
440
|
-
*
|
|
447
|
+
* when no tags. Denormalised aggregate; for the structured form (with
|
|
448
|
+
* confidence, category, and every raw signal) fetch
|
|
449
|
+
* {@link Page.getTechnologySignals} / {@link Page.getPageTechnologies} (lazy).
|
|
441
450
|
*/
|
|
442
451
|
get tagsProvidersCsv() {
|
|
443
452
|
return this.#raw.tags_providers_csv ?? '';
|
|
@@ -530,6 +539,15 @@ export default class Page {
|
|
|
530
539
|
async getCanvases() {
|
|
531
540
|
return this.#archive.getCanvasesOfPage(this.#raw.id);
|
|
532
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* Retrieves the Web Components (custom elements) within this page's
|
|
544
|
+
* detected main content region from `page_main_content_custom_elements`.
|
|
545
|
+
* Lazy — runs a single SELECT per call.
|
|
546
|
+
* @returns Ordered custom-element rows.
|
|
547
|
+
*/
|
|
548
|
+
async getCustomElements() {
|
|
549
|
+
return this.#archive.getCustomElementsOfPage(this.#raw.id);
|
|
550
|
+
}
|
|
533
551
|
/**
|
|
534
552
|
* Retrieves the headings within this page's detected main content region
|
|
535
553
|
* from `page_main_content_headings`. Lazy — runs a single SELECT per call.
|
|
@@ -587,6 +605,14 @@ export default class Page {
|
|
|
587
605
|
async getMainContentTables() {
|
|
588
606
|
return this.#archive.getMainContentTablesOfPage(this.#raw.id);
|
|
589
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* Retrieves the confidence-combined technology roll-up for this page
|
|
610
|
+
* from `page_technologies`. Lazy — runs a single SELECT per call.
|
|
611
|
+
* @returns Technology rows, highest confidence first.
|
|
612
|
+
*/
|
|
613
|
+
async getPageTechnologies() {
|
|
614
|
+
return this.#archive.getPageTechnologiesOfPage(this.#raw.id);
|
|
615
|
+
}
|
|
590
616
|
/**
|
|
591
617
|
* Retrieves the referrers (incoming links) pointing to this page.
|
|
592
618
|
* Uses pre-loaded data if available, otherwise queries the database.
|
|
@@ -627,12 +653,12 @@ export default class Page {
|
|
|
627
653
|
}));
|
|
628
654
|
}
|
|
629
655
|
/**
|
|
630
|
-
* Retrieves the
|
|
631
|
-
* Lazy — runs a single SELECT per call.
|
|
632
|
-
* @returns Ordered
|
|
656
|
+
* Retrieves the raw technology-detection signals for this page from
|
|
657
|
+
* `technology_signals`. Lazy — runs a single SELECT per call.
|
|
658
|
+
* @returns Ordered signal rows.
|
|
633
659
|
*/
|
|
634
|
-
async
|
|
635
|
-
return this.#archive.
|
|
660
|
+
async getTechnologySignals() {
|
|
661
|
+
return this.#archive.getTechnologySignalsOfPage(this.#raw.id);
|
|
636
662
|
}
|
|
637
663
|
/**
|
|
638
664
|
* Retrieves the videos within this page's detected main content region
|
|
@@ -658,8 +684,11 @@ export default class Page {
|
|
|
658
684
|
}
|
|
659
685
|
/**
|
|
660
686
|
* Serializes the page data to a plain JSON object including the full flat
|
|
661
|
-
* meta column set, the `meta_extras` catch-all,
|
|
662
|
-
* JSON-LD and
|
|
687
|
+
* meta column set, the `meta_extras` catch-all, a **summary** of the
|
|
688
|
+
* JSON-LD rows, and the confidence-combined technology roll-up
|
|
689
|
+
* (`page_technologies` — already compact, one row per detected
|
|
690
|
+
* technology, so no further summarization is needed the way `page_tags`'
|
|
691
|
+
* per-`externalId` granularity used to require).
|
|
663
692
|
*
|
|
664
693
|
* Summaries (not raw entries) are inlined so a Page detail payload stays
|
|
665
694
|
* token-bounded for MCP / LLM consumers — the full `raw` JSON-LD payload
|
|
@@ -671,14 +700,13 @@ export default class Page {
|
|
|
671
700
|
* @returns A plain object containing all page metadata and relationships.
|
|
672
701
|
*/
|
|
673
702
|
async toJSON() {
|
|
674
|
-
const [anchors, referrers, jsonLdRows,
|
|
703
|
+
const [anchors, referrers, jsonLdRows, technologies] = await Promise.all([
|
|
675
704
|
this.getAnchors(),
|
|
676
705
|
this.getReferrers(),
|
|
677
706
|
this.getJsonLd(),
|
|
678
|
-
this.
|
|
707
|
+
this.getPageTechnologies(),
|
|
679
708
|
]);
|
|
680
709
|
const jsonLdSummary = summarizeJsonLd(jsonLdRows);
|
|
681
|
-
const tagsSummary = summarizeTags(tagRows);
|
|
682
710
|
return {
|
|
683
711
|
url: this.url.href,
|
|
684
712
|
status: this.status,
|
|
@@ -695,7 +723,7 @@ export default class Page {
|
|
|
695
723
|
...this.metaFlat,
|
|
696
724
|
metaExtras: this.metaExtras,
|
|
697
725
|
jsonLd: jsonLdSummary,
|
|
698
|
-
|
|
726
|
+
technologies,
|
|
699
727
|
redirectFrom: this.redirectFrom,
|
|
700
728
|
isPage: this.isPage(),
|
|
701
729
|
isInternalPage: this.isInternalPage(),
|