@gscdump/engine 1.4.7 → 1.4.10
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/dist/adapters/node-harness.d.mts +1 -1
- package/dist/entities.d.mts +12 -0
- package/dist/entities.mjs +50 -11
- package/dist/layout.d.mts +1 -1
- package/dist/resolver/run-query.d.mts +1 -1
- package/dist/rollups.d.mts +1 -1
- package/dist/rollups.mjs +1 -1
- package/dist/source/source-types.d.mts +1 -1
- package/dist/storage.d.mts +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DataSource, StorageEngine } from "../storage.mjs";
|
|
2
|
-
import { SearchType } from "gscdump/query";
|
|
3
2
|
import { Row, TableName, TenantCtx } from "@gscdump/contracts";
|
|
3
|
+
import { SearchType } from "gscdump/query";
|
|
4
4
|
interface NodeHarnessOptions {
|
|
5
5
|
dataDir: string;
|
|
6
6
|
/** Tenant user id. Defaults to `'local'` for single-user CLI installs. */
|
package/dist/entities.d.mts
CHANGED
|
@@ -4,6 +4,7 @@ import { SitemapGenerationKey, emptyTypesKey, hashSortedUrlList, hashUrl, hashUr
|
|
|
4
4
|
import { QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, buildQueryDimRecords, createQueryDimStore } from "./query-dim.mjs";
|
|
5
5
|
import { SITEMAP_PROJECTION_GRACE_MS, SitemapProjectionFeed, SitemapProjectionFiles, SitemapProjectionManifest, decodeSitemapProjectionManifest, emptySitemapProjectionManifest, parseSitemapProjectionManifest, selectSitemapProjectionFiles, withSitemapProjectionFeed } from "./sitemap-projection.mjs";
|
|
6
6
|
import { TenantCtx } from "@gscdump/contracts";
|
|
7
|
+
import { CanonicalDifferenceKind } from "gscdump";
|
|
7
8
|
/**
|
|
8
9
|
* GSC URL inspection result fields we persist. Mirrors the
|
|
9
10
|
* `searchconsole_v1.Schema$UrlInspectionResult` shape but as plain JSON
|
|
@@ -106,6 +107,8 @@ interface InspectionParquetRow {
|
|
|
106
107
|
* newest-by-`inspectedAt` event.
|
|
107
108
|
*/
|
|
108
109
|
interface InspectionEventRow extends InspectionParquetRow {
|
|
110
|
+
/** Declared-vs-selected canonical classification, computed at inspection ingest. */
|
|
111
|
+
canonicalMismatchKind: CanonicalDifferenceKind;
|
|
109
112
|
crawlingUserAgent: string | null;
|
|
110
113
|
/** JSON-encoded `RichResultsItem[]`. */
|
|
111
114
|
richResultsItems: string | null;
|
|
@@ -212,6 +215,15 @@ interface InspectionStore {
|
|
|
212
215
|
eventFilesDeleted: number;
|
|
213
216
|
transitionsWritten: number;
|
|
214
217
|
}>;
|
|
218
|
+
/**
|
|
219
|
+
* Rewrite a legacy latest-only base with the canonical kind derived from the
|
|
220
|
+
* canonical pair it already retains. Outstanding events must be compacted first.
|
|
221
|
+
*/
|
|
222
|
+
backfillCanonicalMismatchKinds: (ctx: TenantCtx) => Promise<{
|
|
223
|
+
baseRowCount: number;
|
|
224
|
+
rowsBackfilled: number;
|
|
225
|
+
rewritten: boolean;
|
|
226
|
+
}>;
|
|
215
227
|
/**
|
|
216
228
|
* DuckDB-resolvable URI for the materialised parquet sidecar, or
|
|
217
229
|
* `undefined` if the underlying `DataSource` has no native URI shape
|
package/dist/entities.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import { readOptional } from "./adapters/read-optional.mjs";
|
|
|
4
4
|
import { SITEMAP_PROJECTION_GRACE_MS, decodeSitemapProjectionManifest, emptySitemapProjectionManifest, parseSitemapProjectionManifest, selectSitemapProjectionFiles, withSitemapProjectionFeed } from "./sitemap-projection.mjs";
|
|
5
5
|
import { buildQueryDimRecords, createQueryDimStore } from "./query-dim.mjs";
|
|
6
6
|
import { encodeJsonBigintSafe } from "@gscdump/lakehouse/bigint";
|
|
7
|
+
import { GSCDUMP_INDEXING_TRANSITION_FIELDS } from "@gscdump/contracts";
|
|
8
|
+
import { classifyCanonicalDifference } from "gscdump";
|
|
7
9
|
const YEAR_MONTH_RE = /^(\d{4})-(\d{2})-/;
|
|
8
10
|
const ENTITY_IO_CONCURRENCY = 8;
|
|
9
11
|
async function mapEntityIo(items, fn) {
|
|
@@ -106,6 +108,11 @@ const INSPECTION_PARQUET_COLUMNS = [
|
|
|
106
108
|
];
|
|
107
109
|
const INSPECTION_EVENT_COLUMNS = [
|
|
108
110
|
...INSPECTION_PARQUET_COLUMNS,
|
|
111
|
+
{
|
|
112
|
+
name: "canonicalMismatchKind",
|
|
113
|
+
type: "VARCHAR",
|
|
114
|
+
nullable: false
|
|
115
|
+
},
|
|
109
116
|
{
|
|
110
117
|
name: "crawlingUserAgent",
|
|
111
118
|
type: "VARCHAR",
|
|
@@ -157,14 +164,19 @@ const INSPECTION_EVENT_COLUMNS = [
|
|
|
157
164
|
nullable: true
|
|
158
165
|
}
|
|
159
166
|
];
|
|
160
|
-
const
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
const CANONICAL_DIFFERENCE_KINDS = /* @__PURE__ */ new Set([
|
|
168
|
+
"none",
|
|
169
|
+
"formatting",
|
|
170
|
+
"path",
|
|
171
|
+
"cross_domain"
|
|
172
|
+
]);
|
|
173
|
+
function populateCanonicalMismatchKind(row) {
|
|
174
|
+
const current = row.canonicalMismatchKind;
|
|
175
|
+
const expected = classifyCanonicalDifference(typeof row.userCanonical === "string" ? row.userCanonical : null, typeof row.googleCanonical === "string" ? row.googleCanonical : null);
|
|
176
|
+
const missingOrInvalid = typeof current !== "string" || !CANONICAL_DIFFERENCE_KINDS.has(current);
|
|
177
|
+
row.canonicalMismatchKind = expected;
|
|
178
|
+
return missingOrInvalid || current !== expected;
|
|
179
|
+
}
|
|
168
180
|
const INSPECTION_TRANSITION_COLUMNS = [
|
|
169
181
|
{
|
|
170
182
|
name: "urlHash",
|
|
@@ -186,7 +198,7 @@ const INSPECTION_TRANSITION_COLUMNS = [
|
|
|
186
198
|
type: "VARCHAR",
|
|
187
199
|
nullable: false
|
|
188
200
|
},
|
|
189
|
-
...
|
|
201
|
+
...GSCDUMP_INDEXING_TRANSITION_FIELDS.flatMap((field) => {
|
|
190
202
|
const capped = field[0].toUpperCase() + field.slice(1);
|
|
191
203
|
return [{
|
|
192
204
|
name: `from${capped}`,
|
|
@@ -200,7 +212,7 @@ const INSPECTION_TRANSITION_COLUMNS = [
|
|
|
200
212
|
})
|
|
201
213
|
];
|
|
202
214
|
function isStateTransition(before, after) {
|
|
203
|
-
return
|
|
215
|
+
return GSCDUMP_INDEXING_TRANSITION_FIELDS.some((field) => (before[field] ?? null) !== (after[field] ?? null));
|
|
204
216
|
}
|
|
205
217
|
function buildTransitionRow(before, after) {
|
|
206
218
|
const row = {
|
|
@@ -209,7 +221,7 @@ function buildTransitionRow(before, after) {
|
|
|
209
221
|
changedAfter: String(before.inspectedAt ?? ""),
|
|
210
222
|
changedBefore: String(after.inspectedAt ?? "")
|
|
211
223
|
};
|
|
212
|
-
for (const field of
|
|
224
|
+
for (const field of GSCDUMP_INDEXING_TRANSITION_FIELDS) {
|
|
213
225
|
const capped = field[0].toUpperCase() + field.slice(1);
|
|
214
226
|
row[`from${capped}`] = before[field] ?? null;
|
|
215
227
|
row[`to${capped}`] = after[field] ?? null;
|
|
@@ -396,6 +408,7 @@ function createInspectionStore(opts) {
|
|
|
396
408
|
for (const [h, row] of latest) {
|
|
397
409
|
const fc = earliestChecked.get(h);
|
|
398
410
|
if (fc !== void 0) row.firstCheckedAt = fc;
|
|
411
|
+
populateCanonicalMismatchKind(row);
|
|
399
412
|
merged.push(row);
|
|
400
413
|
}
|
|
401
414
|
const bytes = encodeRowsToParquetFlex(merged, {
|
|
@@ -412,6 +425,32 @@ function createInspectionStore(opts) {
|
|
|
412
425
|
transitionsWritten
|
|
413
426
|
};
|
|
414
427
|
},
|
|
428
|
+
async backfillCanonicalMismatchKinds(ctx) {
|
|
429
|
+
const baseKey = inspectionBaseKey(ctx);
|
|
430
|
+
const baseBytes = await readOptional(ds, baseKey);
|
|
431
|
+
if (!baseBytes) return {
|
|
432
|
+
baseRowCount: 0,
|
|
433
|
+
rowsBackfilled: 0,
|
|
434
|
+
rewritten: false
|
|
435
|
+
};
|
|
436
|
+
const rows = await decodeParquetToRows(baseBytes);
|
|
437
|
+
let rowsBackfilled = 0;
|
|
438
|
+
for (const row of rows) if (populateCanonicalMismatchKind(row)) rowsBackfilled++;
|
|
439
|
+
if (rowsBackfilled === 0) return {
|
|
440
|
+
baseRowCount: rows.length,
|
|
441
|
+
rowsBackfilled: 0,
|
|
442
|
+
rewritten: false
|
|
443
|
+
};
|
|
444
|
+
await ds.write(baseKey, encodeRowsToParquetFlex(rows, {
|
|
445
|
+
columns: INSPECTION_EVENT_COLUMNS,
|
|
446
|
+
sortKey: ["urlHash"]
|
|
447
|
+
}));
|
|
448
|
+
return {
|
|
449
|
+
baseRowCount: rows.length,
|
|
450
|
+
rowsBackfilled,
|
|
451
|
+
rewritten: true
|
|
452
|
+
};
|
|
453
|
+
},
|
|
415
454
|
parquetUri(ctx) {
|
|
416
455
|
return ds.uri?.(inspectionParquetKey(ctx));
|
|
417
456
|
}
|
package/dist/layout.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CompactionTier, ManifestEntry } from "./storage.mjs";
|
|
2
|
-
import { SearchType } from "gscdump/query";
|
|
3
2
|
import { TableName, TenantCtx } from "@gscdump/contracts";
|
|
3
|
+
import { SearchType } from "gscdump/query";
|
|
4
4
|
declare function dayPartition(date: string): string;
|
|
5
5
|
/**
|
|
6
6
|
* Hourly partition keyed by the PT calendar day (`YYYY-MM-DD`). One parquet
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SearchType as SearchType$1, TableName as TableName$1 } from "../storage.mjs";
|
|
2
2
|
import { ComparisonFilter } from "./types.mjs";
|
|
3
|
-
import { BuilderState } from "gscdump/query";
|
|
4
3
|
import { Grain } from "@gscdump/contracts";
|
|
4
|
+
import { BuilderState } from "gscdump/query";
|
|
5
5
|
interface RunQueryCtx {
|
|
6
6
|
userId: string;
|
|
7
7
|
siteId: string;
|
package/dist/rollups.d.mts
CHANGED
|
@@ -2,8 +2,8 @@ import { DataSource, FileSetRef, Row as Row$1, TableName as TableName$1 } from "
|
|
|
2
2
|
import { ColumnDef } from "./schema.mjs";
|
|
3
3
|
import { EngineError } from "./errors.mjs";
|
|
4
4
|
import "./contracts.mjs";
|
|
5
|
-
import { SearchType } from "gscdump/query";
|
|
6
5
|
import { TenantCtx } from "@gscdump/contracts";
|
|
6
|
+
import { SearchType } from "gscdump/query";
|
|
7
7
|
interface RollupCtx extends TenantCtx {
|
|
8
8
|
/** When the rollup was built. Stamped into payload + filename. */
|
|
9
9
|
builtAt: number;
|
package/dist/rollups.mjs
CHANGED
|
@@ -978,7 +978,7 @@ const indexingHealthRollup = {
|
|
|
978
978
|
SUM(CASE WHEN CAST(pageFetchState AS VARCHAR) = 'NOT_FOUND' THEN 1 ELSE 0 END)::BIGINT AS not_found,
|
|
979
979
|
SUM(CASE WHEN CAST(mobileUsabilityVerdict AS VARCHAR) = 'PASS' THEN 1 ELSE 0 END)::BIGINT AS mobile_passes,
|
|
980
980
|
SUM(CASE WHEN CAST(richResultsVerdict AS VARCHAR) = 'PASS' THEN 1 ELSE 0 END)::BIGINT AS rich_results_passes,
|
|
981
|
-
SUM(CASE WHEN
|
|
981
|
+
SUM(CASE WHEN canonicalMismatchKind IN ('path', 'cross_domain') THEN 1 ELSE 0 END)::BIGINT AS canonical_mismatches
|
|
982
982
|
FROM read_parquet({{INSPECTIONS}}, union_by_name = true)
|
|
983
983
|
WHERE substr(CAST(inspectedAt AS VARCHAR), 1, 10) >= '${utcDateMinusDays(windowAnchorMs, 90)}'
|
|
984
984
|
GROUP BY 1
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ResolverAdapter } from "../resolver/types.mjs";
|
|
2
2
|
import { PlannerCapabilities } from "gscdump/query/plan";
|
|
3
|
-
import { BuilderState } from "gscdump/query";
|
|
4
3
|
import { TableName } from "@gscdump/contracts";
|
|
4
|
+
import { BuilderState } from "gscdump/query";
|
|
5
5
|
type QueryRow = Record<string, unknown>;
|
|
6
6
|
interface FileSet {
|
|
7
7
|
table: TableName;
|
package/dist/storage.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CompactionThresholds } from "./compaction.mjs";
|
|
2
2
|
import { ParquetQueryFilter } from "./node_modules/.pnpm/hyparquet@1.26.2/node_modules/hyparquet/types/index.mjs";
|
|
3
3
|
import "./node_modules/.pnpm/hyparquet@1.26.2/node_modules/hyparquet/types/node.mjs";
|
|
4
|
-
import { BuilderState, SearchType, SearchType as SearchType$1 } from "gscdump/query";
|
|
5
4
|
import { Grain, Grain as Grain$1, Row, Row as Row$1, TableName, TableName as TableName$1, TenantCtx, TenantCtx as TenantCtx$1 } from "@gscdump/contracts";
|
|
5
|
+
import { BuilderState, SearchType, SearchType as SearchType$1 } from "gscdump/query";
|
|
6
6
|
interface WriteCtx extends TenantCtx {
|
|
7
7
|
table: TableName;
|
|
8
8
|
date?: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/engine",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.10",
|
|
5
5
|
"description": "Append-only Parquet/DuckDB storage engine + planner + adapters for the gscdump pipeline. Node + edge runtimes; opt-in heavy peers.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -181,9 +181,9 @@
|
|
|
181
181
|
"dependencies": {
|
|
182
182
|
"drizzle-orm": "1.0.0-rc.3",
|
|
183
183
|
"proper-lockfile": "^4.1.2",
|
|
184
|
-
"@gscdump/
|
|
185
|
-
"
|
|
186
|
-
"gscdump": "^1.4.
|
|
184
|
+
"@gscdump/lakehouse": "^1.4.10",
|
|
185
|
+
"gscdump": "^1.4.10",
|
|
186
|
+
"@gscdump/contracts": "^1.4.10"
|
|
187
187
|
},
|
|
188
188
|
"devDependencies": {
|
|
189
189
|
"@duckdb/duckdb-wasm": "1.33.1-dev57.0",
|