@gscdump/engine 1.0.1 → 1.0.3
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 +4 -2
- package/dist/_chunks/coerce.mjs +1 -1
- package/dist/_chunks/engine.mjs +1 -1
- package/dist/_chunks/entities.mjs +3 -91
- package/dist/_chunks/index.d.mts +44 -95
- package/dist/_chunks/resolver.mjs +105 -105
- package/dist/_chunks/schema2.mjs +2 -1
- package/dist/_chunks/sink.d.mts +2 -1
- package/dist/_chunks/snapshot.d.mts +27 -1
- package/dist/adapters/node.d.mts +6 -2
- package/dist/adapters/node.mjs +93 -2
- package/dist/entities.d.mts +3 -36
- package/dist/entities.mjs +4 -2
- package/dist/entity-keys.d.mts +27 -0
- package/dist/entity-keys.mjs +93 -0
- package/dist/iceberg/index.mjs +7 -7
- package/dist/index.d.mts +6 -107
- package/dist/index.mjs +101 -132
- package/dist/ingest-accumulator.d.mts +97 -0
- package/dist/ingest-accumulator.mjs +123 -0
- package/dist/period/index.d.mts +1 -1
- package/dist/planner.d.mts +14 -2
- package/dist/report/index.d.mts +1 -1
- package/dist/resolver/index.d.mts +53 -1
- package/dist/rollups.mjs +3 -2
- package/dist/source/index.d.mts +103 -2
- package/dist/source/index.mjs +142 -1
- package/package.json +14 -4
- package/dist/_chunks/duckdb.d.mts +0 -27
- package/dist/_chunks/index2.d.mts +0 -53
- package/dist/_chunks/pg-adapter.d.mts +0 -56
- package/dist/_chunks/planner.d.mts +0 -15
- package/dist/_chunks/source.mjs +0 -143
- package/dist/adapters/duckdb-node.d.mts +0 -7
- package/dist/adapters/duckdb-node.mjs +0 -94
- package/dist/schedule.mjs +0 -100
- /package/dist/{schedule.d.mts → _chunks/schedule.d.mts} +0 -0
package/dist/adapters/node.mjs
CHANGED
|
@@ -1,10 +1,101 @@
|
|
|
1
1
|
import { engineErrors } from "../errors.mjs";
|
|
2
2
|
import { createDuckDBCodec, createDuckDBExecutor, createStorageEngine } from "../_chunks/engine.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { arrowToRows } from "../arrow-utils.mjs";
|
|
4
4
|
import { createFilesystemDataSource, createFilesystemManifestStore } from "./filesystem.mjs";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import { err, ok, unwrapResult } from "gscdump/result";
|
|
6
|
-
import
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
import { unlinkSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import path, { join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { ConsoleLogger, NODE_RUNTIME, VoidLogger, createDuckDB } from "@duckdb/duckdb-wasm/dist/duckdb-node-blocking.cjs";
|
|
7
13
|
import { encodeSiteId } from "gscdump/tenant";
|
|
14
|
+
const require_ = createRequire(typeof __filename !== "undefined" ? __filename : typeof import.meta !== "undefined" ? fileURLToPath(import.meta.url) : process.cwd());
|
|
15
|
+
let singleton = null;
|
|
16
|
+
let singletonOpts = null;
|
|
17
|
+
function bundles() {
|
|
18
|
+
return {
|
|
19
|
+
mvp: {
|
|
20
|
+
mainModule: require_.resolve("@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm"),
|
|
21
|
+
mainWorker: null
|
|
22
|
+
},
|
|
23
|
+
eh: {
|
|
24
|
+
mainModule: require_.resolve("@duckdb/duckdb-wasm/dist/duckdb-eh.wasm"),
|
|
25
|
+
mainWorker: null
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function initialize(opts) {
|
|
30
|
+
const logger = opts.verbose ? new ConsoleLogger() : new VoidLogger();
|
|
31
|
+
const db = await createDuckDB(bundles(), logger, NODE_RUNTIME);
|
|
32
|
+
await db.instantiate();
|
|
33
|
+
return {
|
|
34
|
+
db,
|
|
35
|
+
conn: db.connect()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function getSingleton(opts) {
|
|
39
|
+
if (!singleton) {
|
|
40
|
+
singleton = initialize(opts);
|
|
41
|
+
singletonOpts = opts;
|
|
42
|
+
}
|
|
43
|
+
return singleton;
|
|
44
|
+
}
|
|
45
|
+
function createNodeDuckDBHandle(opts = {}) {
|
|
46
|
+
if (singleton && opts.verbose !== void 0 && opts.verbose !== (singletonOpts?.verbose ?? false)) console.warn(`[gscdump] createNodeDuckDBHandle: ignoring verbose=${opts.verbose} — a shared DuckDB instance was already initialized with verbose=${singletonOpts?.verbose ?? false}. Call resetNodeDuckDB() before re-initializing to change it.`);
|
|
47
|
+
getSingleton(opts);
|
|
48
|
+
return {
|
|
49
|
+
async query(sql, params) {
|
|
50
|
+
const { conn } = await getSingleton(opts);
|
|
51
|
+
if (!params || params.length === 0) return arrowToRows(conn.query(sql));
|
|
52
|
+
const stmt = conn.prepare(sql);
|
|
53
|
+
try {
|
|
54
|
+
return arrowToRows(stmt.query(...params));
|
|
55
|
+
} finally {
|
|
56
|
+
stmt.close();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async registerFileBuffer(name, bytes) {
|
|
60
|
+
const { db } = await getSingleton(opts);
|
|
61
|
+
db.registerFileBuffer(name, bytes);
|
|
62
|
+
},
|
|
63
|
+
async copyFileToBuffer(name) {
|
|
64
|
+
const { db } = await getSingleton(opts);
|
|
65
|
+
return db.copyFileToBuffer(name);
|
|
66
|
+
},
|
|
67
|
+
async dropFiles(names) {
|
|
68
|
+
const { db } = await getSingleton(opts);
|
|
69
|
+
for (const name of names) {
|
|
70
|
+
try {
|
|
71
|
+
db.dropFile(name);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
74
|
+
if (!/not found|does not exist|unknown file/i.test(message)) throw error;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
unlinkSync(name);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error.code !== "ENOENT") throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
makeTempPath(ext) {
|
|
84
|
+
return join(tmpdir(), `gscdump-${Math.random().toString(36).slice(2, 10)}.${ext}`);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function resetNodeDuckDB() {
|
|
89
|
+
const pending = singleton;
|
|
90
|
+
singleton = null;
|
|
91
|
+
singletonOpts = null;
|
|
92
|
+
pending?.then(({ db, conn }) => {
|
|
93
|
+
conn.close();
|
|
94
|
+
db.reset();
|
|
95
|
+
}).catch((err) => {
|
|
96
|
+
console.warn("[gscdump] resetNodeDuckDB: failed to release DuckDB instance", err);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
8
99
|
function createNodeHarness(opts) {
|
|
9
100
|
const dataDir = opts.dataDir;
|
|
10
101
|
const userId = opts.userId ?? "local";
|
package/dist/entities.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataSource } from "./_chunks/storage.mjs";
|
|
2
|
-
import { ScheduleState } from "./schedule.mjs";
|
|
2
|
+
import { ScheduleState } from "./_chunks/schedule.mjs";
|
|
3
|
+
import { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix } from "./entity-keys.mjs";
|
|
3
4
|
import { TenantCtx } from "@gscdump/contracts";
|
|
4
5
|
interface QueryDimRecord {
|
|
5
6
|
query: string;
|
|
@@ -18,8 +19,6 @@ interface QueryDimMeta {
|
|
|
18
19
|
normalizerVersion: number;
|
|
19
20
|
intentVersion: number;
|
|
20
21
|
}
|
|
21
|
-
declare function queryDimParquetKey(ctx: TenantCtx): string;
|
|
22
|
-
declare function queryDimMetaKey(ctx: TenantCtx): string;
|
|
23
22
|
/**
|
|
24
23
|
* Injected derivation. `engine` never imports `@gscdump/analysis`; the host
|
|
25
24
|
* passes `normalizeQuery` / `classifyIntentCode` (e.g. `encodeIntent ∘
|
|
@@ -109,32 +108,11 @@ interface InspectionHistoryShard {
|
|
|
109
108
|
/** Records persisted in this batch. */
|
|
110
109
|
records: InspectionRecord[];
|
|
111
110
|
}
|
|
112
|
-
declare function inspectionIndexKey(ctx: TenantCtx): string;
|
|
113
|
-
declare function emptyTypesKey(ctx: TenantCtx): string;
|
|
114
|
-
declare function inspectionParquetKey(ctx: TenantCtx): string;
|
|
115
|
-
/** Directory prefix holding a tenant's immutable inspection-event parquets. */
|
|
116
|
-
declare function inspectionEventsPrefix(ctx: TenantCtx): string;
|
|
117
|
-
/**
|
|
118
|
-
* Object key for one immutable inspection-event batch, partitioned by the
|
|
119
|
-
* `YYYY-MM` of the records' `inspectedAt`. The `batchId` is caller-supplied so
|
|
120
|
-
* a job retry re-writes the SAME key (idempotent whole-file overwrite).
|
|
121
|
-
*/
|
|
122
|
-
declare function inspectionEventKey(ctx: TenantCtx, yearMonth: string, batchId: string): string;
|
|
123
|
-
/** Compacted latest-per-url base produced by `compactInspections`. */
|
|
124
|
-
declare function inspectionBaseKey(ctx: TenantCtx): string;
|
|
125
111
|
/**
|
|
126
112
|
* Directory prefix for a month's history shards. Each shard is a UUID-keyed
|
|
127
113
|
* blob under this prefix; `appendHistory` writes one per call, `loadHistory`
|
|
128
114
|
* lists + concatenates.
|
|
129
115
|
*/
|
|
130
|
-
declare function inspectionHistoryPrefix(ctx: TenantCtx, yearMonth: string): string;
|
|
131
|
-
declare function inspectionHistoryShardKey(ctx: TenantCtx, yearMonth: string, batchId: string): string;
|
|
132
|
-
/**
|
|
133
|
-
* Stable URL hash used as the index key. Short, URL-safe, deterministic.
|
|
134
|
-
* Uses a 64-bit FNV-1a; collisions vanishingly unlikely at the scales we
|
|
135
|
-
* care about (≤100k URLs/site).
|
|
136
|
-
*/
|
|
137
|
-
declare function hashUrl(url: string): string;
|
|
138
116
|
/**
|
|
139
117
|
* Row shape for the inspections parquet sidecar. Caller-side schema for
|
|
140
118
|
* `materialize` — D1 is the source of truth in the 2026-05-19 redesign, so
|
|
@@ -332,11 +310,6 @@ interface SitemapHistoryDoc {
|
|
|
332
310
|
capturedAt: string;
|
|
333
311
|
record: SitemapRecord;
|
|
334
312
|
}
|
|
335
|
-
declare function sitemapIndexKey(ctx: TenantCtx): string;
|
|
336
|
-
declare function sitemapHistoryKey(ctx: TenantCtx, feedpathHash: string, capturedAtMs: number): string;
|
|
337
|
-
declare function sitemapUrlsIndexPrefix(ctx: TenantCtx): string;
|
|
338
|
-
declare function sitemapUrlsIndexKey(ctx: TenantCtx, feedpathHash: string): string;
|
|
339
|
-
declare function sitemapUrlsDeltaKey(ctx: TenantCtx, feedpathHash: string, date: string): string;
|
|
340
313
|
/** Parsed URL entry from a sitemap XML. */
|
|
341
314
|
interface ParsedUrl {
|
|
342
315
|
loc: string;
|
|
@@ -410,11 +383,6 @@ interface DateRange {
|
|
|
410
383
|
interface LoadUrlsOptions {
|
|
411
384
|
includeRemoved?: boolean;
|
|
412
385
|
}
|
|
413
|
-
/**
|
|
414
|
-
* Hash a URL list for change detection. Sorts then folds via FNV-1a so it's
|
|
415
|
-
* deterministic, locale-free, and cheap on Workers.
|
|
416
|
-
*/
|
|
417
|
-
declare function hashUrlList(urls: readonly ParsedUrl[]): string;
|
|
418
386
|
interface SitemapStore {
|
|
419
387
|
/**
|
|
420
388
|
* Persist a snapshot run. Updates the index + writes one immutable
|
|
@@ -484,7 +452,6 @@ interface IndexingMetadataIndex {
|
|
|
484
452
|
version: 1;
|
|
485
453
|
records: Record<string, IndexingMetadataRecord>;
|
|
486
454
|
}
|
|
487
|
-
declare function indexingMetadataIndexKey(ctx: TenantCtx): string;
|
|
488
455
|
interface IndexingMetadataStore {
|
|
489
456
|
writeBatch: (ctx: TenantCtx, records: readonly IndexingMetadataRecord[]) => Promise<void>;
|
|
490
457
|
loadIndex: (ctx: TenantCtx) => Promise<IndexingMetadataIndex>;
|
|
@@ -514,4 +481,4 @@ interface CreateEmptyTypesStoreOptions {
|
|
|
514
481
|
now?: () => number;
|
|
515
482
|
}
|
|
516
483
|
declare function createEmptyTypesStore(opts: CreateEmptyTypesStoreOptions): EmptyTypesStore;
|
|
517
|
-
export { CompactUrlsOptions, CompactUrlsResult, CreateEmptyTypesStoreOptions, CreateIndexingMetadataStoreOptions, CreateInspectionStoreOptions, CreateSitemapStoreOptions, DateRange, DeltaEntry, EmptyTypesDoc, EmptyTypesStore, INSPECTION_HISTORY_MAX_BYTES, IndexingMetadataIndex, IndexingMetadataRecord, IndexingMetadataStore, InspectionEventRow, InspectionHistoryShard, InspectionIndex, InspectionParquetRow, InspectionRecord, InspectionStore, LoadUrlsOptions, ParsedUrl, QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, ReconcileResult, SitemapHistoryDoc, SitemapIndex, SitemapRecord, SitemapStore, SitemapUrlRecord, SnapshotUrlsResult, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore, emptyTypesKey, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix };
|
|
484
|
+
export { CompactUrlsOptions, CompactUrlsResult, CreateEmptyTypesStoreOptions, CreateIndexingMetadataStoreOptions, CreateInspectionStoreOptions, CreateSitemapStoreOptions, DateRange, DeltaEntry, EmptyTypesDoc, EmptyTypesStore, INSPECTION_HISTORY_MAX_BYTES, IndexingMetadataIndex, IndexingMetadataRecord, IndexingMetadataStore, InspectionEventRow, InspectionHistoryShard, InspectionIndex, InspectionParquetRow, InspectionRecord, InspectionStore, LoadUrlsOptions, ParsedUrl, QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, ReconcileResult, SitemapHistoryDoc, SitemapIndex, SitemapRecord, SitemapStore, SitemapUrlRecord, SnapshotUrlsResult, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore, emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix };
|
package/dist/entities.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix } from "./entity-keys.mjs";
|
|
2
|
+
import "./adapters/hyparquet.mjs";
|
|
3
|
+
import { INSPECTION_HISTORY_MAX_BYTES, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore } from "./_chunks/entities.mjs";
|
|
4
|
+
export { INSPECTION_HISTORY_MAX_BYTES, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore, emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TenantCtx } from "@gscdump/contracts";
|
|
2
|
+
declare function inspectionIndexKey(ctx: TenantCtx): string;
|
|
3
|
+
declare function emptyTypesKey(ctx: TenantCtx): string;
|
|
4
|
+
declare function inspectionParquetKey(ctx: TenantCtx): string;
|
|
5
|
+
declare function inspectionEventsPrefix(ctx: TenantCtx): string;
|
|
6
|
+
declare function inspectionEventKey(ctx: TenantCtx, yearMonth: string, batchId: string): string;
|
|
7
|
+
declare function inspectionBaseKey(ctx: TenantCtx): string;
|
|
8
|
+
declare function inspectionHistoryPrefix(ctx: TenantCtx, yearMonth: string): string;
|
|
9
|
+
declare function inspectionHistoryShardKey(ctx: TenantCtx, yearMonth: string, batchId: string): string;
|
|
10
|
+
/** Stable, URL-safe FNV-1a hash used for entity keys. */
|
|
11
|
+
declare function hashUrl(url: string): string;
|
|
12
|
+
declare function sitemapIndexKey(ctx: TenantCtx): string;
|
|
13
|
+
declare function sitemapHistoryKey(ctx: TenantCtx, feedpathHash: string, capturedAtMs: number): string;
|
|
14
|
+
declare function sitemapUrlsPrefix(ctx: TenantCtx): string;
|
|
15
|
+
declare function sitemapUrlsIndexPrefix(ctx: TenantCtx): string;
|
|
16
|
+
declare function sitemapUrlsIndexKey(ctx: TenantCtx, feedpathHash: string): string;
|
|
17
|
+
declare function sitemapUrlsDeltaKey(ctx: TenantCtx, feedpathHash: string, date: string): string;
|
|
18
|
+
/** Hash a URL list for deterministic change detection. */
|
|
19
|
+
declare function hashUrlList(urls: readonly {
|
|
20
|
+
loc: string;
|
|
21
|
+
}[]): string;
|
|
22
|
+
/** Hash sorted URL strings as though joined by a newline, without allocating the join. */
|
|
23
|
+
declare function hashSortedUrlList(locs: readonly string[]): string;
|
|
24
|
+
declare function indexingMetadataIndexKey(ctx: TenantCtx): string;
|
|
25
|
+
declare function queryDimParquetKey(ctx: TenantCtx): string;
|
|
26
|
+
declare function queryDimMetaKey(ctx: TenantCtx): string;
|
|
27
|
+
export { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
function tenantEntityPrefix(ctx) {
|
|
2
|
+
return ctx.siteId ? `u_${ctx.userId}/${ctx.siteId}/entities` : `u_${ctx.userId}/entities`;
|
|
3
|
+
}
|
|
4
|
+
function inspectionIndexKey(ctx) {
|
|
5
|
+
return `${tenantEntityPrefix(ctx)}/inspections/index.json`;
|
|
6
|
+
}
|
|
7
|
+
function emptyTypesKey(ctx) {
|
|
8
|
+
return `${tenantEntityPrefix(ctx)}/empty-types.json`;
|
|
9
|
+
}
|
|
10
|
+
function inspectionParquetKey(ctx) {
|
|
11
|
+
return `${tenantEntityPrefix(ctx)}/inspections/index.parquet`;
|
|
12
|
+
}
|
|
13
|
+
function inspectionEventsPrefix(ctx) {
|
|
14
|
+
return `${tenantEntityPrefix(ctx)}/inspections/events`;
|
|
15
|
+
}
|
|
16
|
+
function inspectionEventKey(ctx, yearMonth, batchId) {
|
|
17
|
+
return `${inspectionEventsPrefix(ctx)}/${yearMonth}/${batchId}.parquet`;
|
|
18
|
+
}
|
|
19
|
+
function inspectionBaseKey(ctx) {
|
|
20
|
+
return `${tenantEntityPrefix(ctx)}/inspections/base.parquet`;
|
|
21
|
+
}
|
|
22
|
+
function inspectionHistoryPrefix(ctx, yearMonth) {
|
|
23
|
+
return `${tenantEntityPrefix(ctx)}/inspections/history/${yearMonth}`;
|
|
24
|
+
}
|
|
25
|
+
function inspectionHistoryShardKey(ctx, yearMonth, batchId) {
|
|
26
|
+
return `${inspectionHistoryPrefix(ctx, yearMonth)}/${batchId}.json`;
|
|
27
|
+
}
|
|
28
|
+
function hashUrl(url) {
|
|
29
|
+
let hi = 2166136261;
|
|
30
|
+
let lo = 3421674724;
|
|
31
|
+
for (let i = 0; i < url.length; i++) {
|
|
32
|
+
const c = url.charCodeAt(i);
|
|
33
|
+
lo ^= c;
|
|
34
|
+
const loMul = Math.imul(lo, 435) >>> 0;
|
|
35
|
+
const carry = Math.floor(lo * 435 / 4294967296);
|
|
36
|
+
const hiMul = Math.imul(hi, 435) + Math.imul(lo, 1) + carry >>> 0;
|
|
37
|
+
lo = loMul;
|
|
38
|
+
hi = hiMul;
|
|
39
|
+
}
|
|
40
|
+
return (hi >>> 0).toString(16).padStart(8, "0") + (lo >>> 0).toString(16).padStart(8, "0");
|
|
41
|
+
}
|
|
42
|
+
function sitemapIndexKey(ctx) {
|
|
43
|
+
return `${tenantEntityPrefix(ctx)}/sitemaps/index.json`;
|
|
44
|
+
}
|
|
45
|
+
function sitemapHistoryKey(ctx, feedpathHash, capturedAtMs) {
|
|
46
|
+
return `${tenantEntityPrefix(ctx)}/sitemaps/history/${feedpathHash}__${capturedAtMs}.json`;
|
|
47
|
+
}
|
|
48
|
+
function sitemapUrlsPrefix(ctx) {
|
|
49
|
+
return `${tenantEntityPrefix(ctx)}/sitemaps/urls`;
|
|
50
|
+
}
|
|
51
|
+
function sitemapUrlsIndexPrefix(ctx) {
|
|
52
|
+
return `${sitemapUrlsPrefix(ctx)}/by-feed`;
|
|
53
|
+
}
|
|
54
|
+
function sitemapUrlsIndexKey(ctx, feedpathHash) {
|
|
55
|
+
return `${sitemapUrlsIndexPrefix(ctx)}/${feedpathHash}/index.parquet`;
|
|
56
|
+
}
|
|
57
|
+
function sitemapUrlsDeltaKey(ctx, feedpathHash, date) {
|
|
58
|
+
return `${sitemapUrlsPrefix(ctx)}/deltas/${date}__${feedpathHash}.parquet`;
|
|
59
|
+
}
|
|
60
|
+
function hashUrlList(urls) {
|
|
61
|
+
return hashSortedUrlList(urls.map((url) => url.loc).sort());
|
|
62
|
+
}
|
|
63
|
+
function hashSortedUrlList(locs) {
|
|
64
|
+
let hi = 2166136261;
|
|
65
|
+
let lo = 3421674724;
|
|
66
|
+
for (let locIndex = 0; locIndex < locs.length; locIndex++) {
|
|
67
|
+
const loc = locs[locIndex];
|
|
68
|
+
const length = loc.length + (locIndex < locs.length - 1 ? 1 : 0);
|
|
69
|
+
for (let i = 0; i < length; i++) {
|
|
70
|
+
const c = i < loc.length ? loc.charCodeAt(i) : 10;
|
|
71
|
+
lo ^= c;
|
|
72
|
+
const loMul = Math.imul(lo, 435) >>> 0;
|
|
73
|
+
const carry = Math.floor(lo * 435 / 4294967296);
|
|
74
|
+
const hiMul = Math.imul(hi, 435) + Math.imul(lo, 1) + carry >>> 0;
|
|
75
|
+
lo = loMul;
|
|
76
|
+
hi = hiMul;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return (hi >>> 0).toString(16).padStart(8, "0") + (lo >>> 0).toString(16).padStart(8, "0");
|
|
80
|
+
}
|
|
81
|
+
function indexingMetadataIndexKey(ctx) {
|
|
82
|
+
return `${tenantEntityPrefix(ctx)}/indexing/index.json`;
|
|
83
|
+
}
|
|
84
|
+
function queryDimPrefix(ctx) {
|
|
85
|
+
return `${tenantEntityPrefix(ctx)}/query_dim`;
|
|
86
|
+
}
|
|
87
|
+
function queryDimParquetKey(ctx) {
|
|
88
|
+
return `${queryDimPrefix(ctx)}/index.parquet`;
|
|
89
|
+
}
|
|
90
|
+
function queryDimMetaKey(ctx) {
|
|
91
|
+
return `${queryDimPrefix(ctx)}/index.json`;
|
|
92
|
+
}
|
|
93
|
+
export { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix };
|
package/dist/iceberg/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { engineErrors } from "../errors.mjs";
|
|
2
2
|
import { TABLE_METADATA } from "../_chunks/schema.mjs";
|
|
3
|
-
import { DEFAULT_PARTITION_KEY_ENCODING
|
|
3
|
+
import { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, gscDataset, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable } from "../_chunks/schema2.mjs";
|
|
4
4
|
import { icebergCreateTable } from "../_chunks/libs/icebird.mjs";
|
|
5
5
|
import { err, ok } from "gscdump/result";
|
|
6
6
|
import { ICEBERG_TYPE_MAP, connectIcebergCatalog, dropIcebergTables as dropIcebergTables$1, resolveIcebergDataFiles } from "@gscdump/lakehouse";
|
|
7
7
|
import { icebergAppendRetrying } from "@gscdump/lakehouse/unsafe-raw";
|
|
8
|
-
function icebergSchemaFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING
|
|
8
|
+
function icebergSchemaFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
|
|
9
9
|
return {
|
|
10
10
|
"type": "struct",
|
|
11
11
|
"schema-id": 0,
|
|
@@ -17,7 +17,7 @@ function icebergSchemaFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING$1) {
|
|
|
17
17
|
}))
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
function icebergPartitionSpecFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING
|
|
20
|
+
function icebergPartitionSpecFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
|
|
21
21
|
const fields = icebergSchemasFor(encoding)[table].columns;
|
|
22
22
|
const fieldId = (name) => {
|
|
23
23
|
const col = fields.find((c) => c.name === name);
|
|
@@ -34,7 +34,7 @@ function icebergPartitionSpecFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODIN
|
|
|
34
34
|
}))
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
function icebergSortOrderFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING
|
|
37
|
+
function icebergSortOrderFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
|
|
38
38
|
const fields = icebergSchemasFor(encoding)[table].columns;
|
|
39
39
|
const fieldId = (name) => {
|
|
40
40
|
const col = fields.find((c) => c.name === name);
|
|
@@ -51,7 +51,7 @@ function icebergSortOrderFor(table, encoding = DEFAULT_PARTITION_KEY_ENCODING$1)
|
|
|
51
51
|
}))
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
async function createIcebergTables(conn, tables = ICEBERG_TABLES, encoding = DEFAULT_PARTITION_KEY_ENCODING
|
|
54
|
+
async function createIcebergTables(conn, tables = ICEBERG_TABLES, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
|
|
55
55
|
const results = [];
|
|
56
56
|
for (const table of tables) await icebergCreateTable({
|
|
57
57
|
catalog: conn.catalog,
|
|
@@ -76,7 +76,7 @@ async function dropIcebergTables(conn, tables) {
|
|
|
76
76
|
}));
|
|
77
77
|
}
|
|
78
78
|
async function listIcebergDataFiles(conn, opts) {
|
|
79
|
-
const matches = (opts.encoding ?? DEFAULT_PARTITION_KEY_ENCODING
|
|
79
|
+
const matches = (opts.encoding ?? DEFAULT_PARTITION_KEY_ENCODING) === "string" ? [{
|
|
80
80
|
field: "site_id",
|
|
81
81
|
value: opts.siteId,
|
|
82
82
|
encoding: "string"
|
|
@@ -178,7 +178,7 @@ function toRecords(slice, rows, encoding) {
|
|
|
178
178
|
}
|
|
179
179
|
function createIcebergAppendSink(options) {
|
|
180
180
|
let connection;
|
|
181
|
-
const encoding = options.encoding ?? DEFAULT_PARTITION_KEY_ENCODING
|
|
181
|
+
const encoding = options.encoding ?? DEFAULT_PARTITION_KEY_ENCODING;
|
|
182
182
|
const buffers = /* @__PURE__ */ new Map();
|
|
183
183
|
function connect() {
|
|
184
184
|
connection ??= connectIcebergCatalog(options.catalog, options.connect);
|
package/dist/index.d.mts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import { CodecCtx, CompactionThresholds, CompactionTier, DataSource, EngineOptions, FileSetRef, GcCtx, Grain, ListLiveFilter, LockScope, ManifestEntry, ManifestPurgeResult, ManifestStore, ParquetCodec, PurgeFilter, PurgeResult, PurgeUrlsResult, QueryCtx, QueryExecuteOptions, QueryExecuteResult, QueryExecutor, QueryProfiler, QueryResult, QuerySpan, Row, RunSQLOptions, SearchType, StorageEngine, SyncState, SyncStateDetail, SyncStateFilter, SyncStateKind, SyncStateScope, TableName, TenantCtx, Watermark, WatermarkFilter, WatermarkScope, WriteCtx, WriteResult
|
|
2
|
-
import { DuckDBFactory, DuckDBHandle, createDuckDBCodec, createDuckDBExecutor } from "./_chunks/
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { EngineError, EngineErrorKind, engineErrorToException, engineErrors, isEngineError } from "./_chunks/errors.mjs";
|
|
6
|
-
import { InspectionVerdict, SchedulePolicy, ScheduleState, fixedPolicy, inspectionPolicy, sitemapPolicy } from "./schedule.mjs";
|
|
1
|
+
import { CodecCtx, CompactionThresholds, CompactionTier, DataSource, EngineOptions, FileSetRef, GcCtx, Grain, ListLiveFilter, LockScope, ManifestEntry, ManifestPurgeResult, ManifestStore, ParquetCodec, PurgeFilter, PurgeResult, PurgeUrlsResult, QueryCtx, QueryExecuteOptions, QueryExecuteResult, QueryExecutor, QueryProfiler, QueryResult, QuerySpan, Row, RunSQLOptions, SearchType, StorageEngine, SyncState, SyncStateDetail, SyncStateFilter, SyncStateKind, SyncStateScope, TableName, TenantCtx, Watermark, WatermarkFilter, WatermarkScope, WriteCtx, WriteResult } from "./_chunks/storage.mjs";
|
|
2
|
+
import { DuckDBFactory, DuckDBHandle, SnapshotIndex, createDuckDBCodec, createDuckDBExecutor } from "./_chunks/snapshot.mjs";
|
|
3
|
+
import { EngineError } from "./_chunks/errors.mjs";
|
|
4
|
+
import { InspectionVerdict, SchedulePolicy, ScheduleState, fixedPolicy, inspectionPolicy, sitemapPolicy } from "./_chunks/schedule.mjs";
|
|
7
5
|
import { IcebergTableName, Sink, SinkCapabilities, SinkCloseResult, SinkOptions, SinkSlice, SinkWriteResult } from "./_chunks/sink.mjs";
|
|
8
|
-
import { GscApiRow, IngestOptions, RowAccumulator, RowAccumulatorOptions, assembleDatesRow, createRowAccumulator, toPath, toSumPosition, transformGscRow } from "./ingest.mjs";
|
|
9
|
-
import { FILES_PLACEHOLDER, ResolvedQuery, resolveParquetSQL, substituteNamedFiles } from "./_chunks/planner.mjs";
|
|
10
|
-
import { createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "./_chunks/pg-adapter.mjs";
|
|
11
|
-
import { rebuildDailyFromHourly } from "./rollups.mjs";
|
|
12
|
-
import { ENGINE_QUERY_CAPABILITIES, createSqlQuerySource } from "./_chunks/index.mjs";
|
|
13
|
-
import { bindLiterals, formatLiteral } from "./sql-bind.mjs";
|
|
14
6
|
import { Result } from "gscdump/result";
|
|
15
7
|
import { SearchType as SearchType$1 } from "gscdump/query";
|
|
16
|
-
import {
|
|
8
|
+
import { Row as Row$1, TableName as TableName$1, TenantCtx as TenantCtx$1 } from "@gscdump/contracts";
|
|
17
9
|
interface R2ObjectMetadata {
|
|
18
10
|
etag: string;
|
|
19
11
|
}
|
|
@@ -98,99 +90,6 @@ declare function coerceRow(row: Row$1): Row$1;
|
|
|
98
90
|
declare function coerceRows(rows: readonly Row$1[]): Row$1[];
|
|
99
91
|
declare const MAX_DAY_BYTES: number;
|
|
100
92
|
declare function createStorageEngine(opts: EngineOptions): StorageEngine;
|
|
101
|
-
interface IngestAccumulatorEngine {
|
|
102
|
-
writeDay: (scope: TenantCtx & {
|
|
103
|
-
table: TableName$1;
|
|
104
|
-
date: string;
|
|
105
|
-
searchType?: SearchType;
|
|
106
|
-
}, rows: Row$1[]) => Promise<void>;
|
|
107
|
-
/**
|
|
108
|
-
* Routed when the accumulator's `ctx.grain === 'hour'`. Same scope shape as
|
|
109
|
-
* `writeDay`; `date` is the PT calendar day, rows carry `hour` + `date`.
|
|
110
|
-
* Optional so hosts that never opt into hourly need not implement it.
|
|
111
|
-
*/
|
|
112
|
-
writeHour?: (scope: TenantCtx & {
|
|
113
|
-
table: TableName$1;
|
|
114
|
-
date: string;
|
|
115
|
-
searchType?: SearchType;
|
|
116
|
-
}, rows: Row$1[]) => Promise<void>;
|
|
117
|
-
setSyncState: (scope: TenantCtx & {
|
|
118
|
-
table: TableName$1;
|
|
119
|
-
date: string;
|
|
120
|
-
searchType?: SearchType;
|
|
121
|
-
}, state: 'done' | 'failed', info?: {
|
|
122
|
-
error?: string;
|
|
123
|
-
}) => Promise<void>;
|
|
124
|
-
}
|
|
125
|
-
interface IngestAccumulatorCtx {
|
|
126
|
-
userId: string | number;
|
|
127
|
-
siteId: string;
|
|
128
|
-
searchType?: SearchType;
|
|
129
|
-
/**
|
|
130
|
-
* Temporal granularity for this accumulator. `'day'` (default) routes
|
|
131
|
-
* flushed buckets to `engine.writeDay`. `'hour'` routes to
|
|
132
|
-
* `engine.writeHour` and requires the engine implementation to be set.
|
|
133
|
-
*/
|
|
134
|
-
grain?: Grain$1;
|
|
135
|
-
}
|
|
136
|
-
interface IngestAccumulatorHooks {
|
|
137
|
-
/**
|
|
138
|
-
* Called once per (table, date) when the job must abandon in-memory rows
|
|
139
|
-
* (overflow or `hasMore` continuation). Host queues a forced re-sync from
|
|
140
|
-
* the source. Return true iff a recovery job was actually queued.
|
|
141
|
-
*/
|
|
142
|
-
onRecover: (table: TableName$1, date: string) => Promise<boolean>;
|
|
143
|
-
/**
|
|
144
|
-
* Called when an engine.writeDay fails or recovery itself errors. Host
|
|
145
|
-
* logs to its error sink (e.g. `r2_write_errors` D1 table).
|
|
146
|
-
*/
|
|
147
|
-
onWriteError: (info: {
|
|
148
|
-
table: TableName$1 | null;
|
|
149
|
-
date: string | null;
|
|
150
|
-
error: unknown;
|
|
151
|
-
}) => Promise<void>;
|
|
152
|
-
/**
|
|
153
|
-
* Called after a successful writeDay for a (table, date). Host typically
|
|
154
|
-
* busts the manifest cache here so the next read sees the new parquet.
|
|
155
|
-
*/
|
|
156
|
-
onWritten?: (info: {
|
|
157
|
-
table: TableName$1;
|
|
158
|
-
date: string;
|
|
159
|
-
rowCount: number;
|
|
160
|
-
}) => void | Promise<void>;
|
|
161
|
-
/**
|
|
162
|
-
* Called once at end of `finalize`, only when at least one (table, date)
|
|
163
|
-
* actually landed. Host queues rollup rebuild + compaction.
|
|
164
|
-
*/
|
|
165
|
-
onJobComplete?: (info: {
|
|
166
|
-
flushed: number;
|
|
167
|
-
rowsWritten: number;
|
|
168
|
-
}) => Promise<void>;
|
|
169
|
-
}
|
|
170
|
-
interface FinalizeOptions {
|
|
171
|
-
/**
|
|
172
|
-
* The GSC `hasMore` flag for the whole job. When true, in-memory buckets
|
|
173
|
-
* only reflect this job's slice; we re-queue forced single-day re-syncs
|
|
174
|
-
* via `onRecover` so R2 stays authoritative.
|
|
175
|
-
*/
|
|
176
|
-
hasMore: boolean;
|
|
177
|
-
}
|
|
178
|
-
interface FinalizeResult {
|
|
179
|
-
flushed: number;
|
|
180
|
-
recovered: number;
|
|
181
|
-
failed: number;
|
|
182
|
-
rowsWritten: number;
|
|
183
|
-
}
|
|
184
|
-
interface IngestAccumulator {
|
|
185
|
-
push: (table: TableName$1, rows: readonly GscApiRow[]) => boolean;
|
|
186
|
-
finalize: (opts: FinalizeOptions) => Promise<FinalizeResult>;
|
|
187
|
-
}
|
|
188
|
-
interface CreateIngestAccumulatorOptions extends RowAccumulatorOptions {
|
|
189
|
-
engine: IngestAccumulatorEngine;
|
|
190
|
-
ctx: IngestAccumulatorCtx;
|
|
191
|
-
hooks: IngestAccumulatorHooks;
|
|
192
|
-
}
|
|
193
|
-
declare function createIngestAccumulator(opts: CreateIngestAccumulatorOptions): IngestAccumulator;
|
|
194
93
|
declare function dayPartition(date: string): string;
|
|
195
94
|
/**
|
|
196
95
|
* Hourly partition keyed by the PT calendar day (`YYYY-MM-DD`). One parquet
|
|
@@ -289,4 +188,4 @@ declare const MIN_SYNC_IMPRESSIONS = 1;
|
|
|
289
188
|
declare const MIN_COUNTRY_IMPRESSIONS = 10;
|
|
290
189
|
declare const MAX_SITEMAP_URLS_PER_SITE = 50000;
|
|
291
190
|
declare const MAX_TRACKED_URLS_PER_SITE = 200000;
|
|
292
|
-
export { type CodecCtx, type
|
|
191
|
+
export { type CodecCtx, type CompactionThresholds, type CompactionTier, type CreateR2ManifestStoreOptions, DEFAULT_SEARCH_TYPE, type DataSource, type DateWeight, type DuckDBFactory, type DuckDBHandle, type EngineOptions, type FileSetRef, type GcCtx, type Grain, type InMemorySink, type InspectionVerdict, type ListLiveFilter, type LockScope, MAX_DAY_BYTES, MAX_GSC_PAGES_R2, MAX_SITEMAP_URLS_PER_SITE, MAX_TRACKED_URLS_PER_SITE, MIN_COUNTRY_IMPRESSIONS, MIN_SYNC_IMPRESSIONS, type ManifestEntry, type ManifestPurgeResult, type ManifestStore, type ParquetCodec, type PurgeFilter, type PurgeResult, type PurgeUrlsResult, type QueryCtx, type QueryExecuteOptions, type QueryExecuteResult, type QueryExecutor, type QueryProfiler, type QueryResult, type QuerySpan, type R2ManifestBucketLike, type R2ManifestEvent, ROW_LIMIT_R2, type Row, type RunSQLOptions, type SchedulePolicy, type ScheduleState, type SearchType, type Sink, type SinkCapabilities, type SinkCloseResult, type SinkOptions, type SinkSlice, type SinkWriteResult, type SnapshotIndex, type StorageEngine, type StoredRow, type SyncState, type SyncStateDetail, type SyncStateFilter, type SyncStateKind, type SyncStateScope, type SyncTableName, TABLES_BY_SEARCH_TYPE, TABLE_TIERS, TIER_PRIORITY, type TableName, type TableTier, type TenantCtx, type TieredTableName, WEIGHT_PRIORITY, type Watermark, type WatermarkFilter, type WatermarkScope, type WriteCtx, type WriteResult, coerceRow, coerceRows, collectSpans, createDuckDBCodec, createDuckDBExecutor, createInMemorySink, createQueryProfiler, createR2ManifestStore, createStorageEngine, dayPartition, fixedPolicy, getDateWeight, getTableTier, getTablesForTier, hourPartition, inferLegacyTier, inferSearchType, inspectionPolicy, objectKey, parseEnabledSearchTypes, sitemapPolicy, validateEnabledSearchTypes, validateEnabledSearchTypesResult };
|