@gscdump/engine 1.4.2 → 1.4.4
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 +2 -1
- package/dist/adapters/node-harness.mjs +12 -1
- package/dist/drizzle-schema.d.mts +20 -20
- package/dist/entities.d.mts +57 -29
- package/dist/entities.mjs +698 -325
- package/dist/entity-keys.d.mts +18 -2
- package/dist/entity-keys.mjs +38 -3
- package/dist/errors.d.mts +1 -1
- package/dist/rollups.d.mts +2 -2
- package/dist/rollups.mjs +71 -20
- package/dist/sitemap-projection.d.mts +20 -0
- package/dist/sitemap-projection.mjs +57 -0
- package/dist/sync-config.d.mts +5 -5
- package/package.json +4 -4
package/dist/entity-keys.d.mts
CHANGED
|
@@ -14,7 +14,23 @@ declare function sitemapHistoryKey(ctx: TenantCtx, feedpathHash: string, capture
|
|
|
14
14
|
declare function sitemapUrlsPrefix(ctx: TenantCtx): string;
|
|
15
15
|
declare function sitemapUrlsIndexPrefix(ctx: TenantCtx): string;
|
|
16
16
|
declare function sitemapUrlsIndexKey(ctx: TenantCtx, feedpathHash: string): string;
|
|
17
|
-
declare function
|
|
17
|
+
declare function sitemapUrlsProjectionManifestKey(ctx: TenantCtx): string;
|
|
18
|
+
interface SitemapGenerationKey {
|
|
19
|
+
id: string;
|
|
20
|
+
observedAt: number;
|
|
21
|
+
}
|
|
22
|
+
declare function sitemapUrlsDeltaKey(ctx: TenantCtx, feedpathHash: string, generation: SitemapGenerationKey): string;
|
|
23
|
+
declare function parseSitemapUrlsDeltaKey(key: string): {
|
|
24
|
+
date: string;
|
|
25
|
+
feedpathHash: string;
|
|
26
|
+
} | undefined;
|
|
27
|
+
declare function sitemapUrlsEventsPrefix(ctx: TenantCtx): string;
|
|
28
|
+
declare function sitemapUrlsEventKey(ctx: TenantCtx, feedpathHash: string, generation: SitemapGenerationKey): string;
|
|
29
|
+
declare function sitemapUrlsEventSeedKey(ctx: TenantCtx, feedpathHash: string): string;
|
|
30
|
+
declare function sitemapUrlsGenerationKey(ctx: TenantCtx, feedpathHash: string): string;
|
|
31
|
+
declare function sitemapUrlsPendingGenerationsPrefix(ctx: TenantCtx): string;
|
|
32
|
+
declare function sitemapUrlsPendingGenerationKey(ctx: TenantCtx, feedpathHash: string): string;
|
|
33
|
+
declare function sitemapUrlsReconcileGenerationKey(ctx: TenantCtx): string;
|
|
18
34
|
/** Hash a URL list for deterministic change detection. */
|
|
19
35
|
declare function hashUrlList(urls: readonly {
|
|
20
36
|
loc: string;
|
|
@@ -24,4 +40,4 @@ declare function hashSortedUrlList(locs: readonly string[]): string;
|
|
|
24
40
|
declare function indexingMetadataIndexKey(ctx: TenantCtx): string;
|
|
25
41
|
declare function queryDimParquetKey(ctx: TenantCtx): string;
|
|
26
42
|
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 };
|
|
43
|
+
export { SitemapGenerationKey, emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, parseSitemapUrlsDeltaKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsEventKey, sitemapUrlsEventSeedKey, sitemapUrlsEventsPrefix, sitemapUrlsGenerationKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPendingGenerationKey, sitemapUrlsPendingGenerationsPrefix, sitemapUrlsPrefix, sitemapUrlsProjectionManifestKey, sitemapUrlsReconcileGenerationKey };
|
package/dist/entity-keys.mjs
CHANGED
|
@@ -54,8 +54,43 @@ function sitemapUrlsIndexPrefix(ctx) {
|
|
|
54
54
|
function sitemapUrlsIndexKey(ctx, feedpathHash) {
|
|
55
55
|
return `${sitemapUrlsIndexPrefix(ctx)}/${feedpathHash}/index.parquet`;
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
return `${sitemapUrlsPrefix(ctx)}/
|
|
57
|
+
function sitemapUrlsProjectionManifestKey(ctx) {
|
|
58
|
+
return `${sitemapUrlsPrefix(ctx)}/projection.json`;
|
|
59
|
+
}
|
|
60
|
+
function sitemapGenerationDate(generation) {
|
|
61
|
+
return new Date(generation.observedAt).toISOString().slice(0, 10);
|
|
62
|
+
}
|
|
63
|
+
function sitemapUrlsDeltaKey(ctx, feedpathHash, generation) {
|
|
64
|
+
return `${sitemapUrlsPrefix(ctx)}/deltas/${sitemapGenerationDate(generation)}__${feedpathHash}__${String(generation.observedAt).padStart(13, "0")}__${hashUrl(generation.id)}.parquet`;
|
|
65
|
+
}
|
|
66
|
+
const SITEMAP_URLS_DELTA_KEY_RE = /\/urls\/deltas\/(\d{4}-\d{2}-\d{2})__([0-9a-f]+)(?:__\d+__[0-9a-f]+)?\.parquet$/;
|
|
67
|
+
function parseSitemapUrlsDeltaKey(key) {
|
|
68
|
+
const match = SITEMAP_URLS_DELTA_KEY_RE.exec(key);
|
|
69
|
+
return match?.[1] && match[2] ? {
|
|
70
|
+
date: match[1],
|
|
71
|
+
feedpathHash: match[2]
|
|
72
|
+
} : void 0;
|
|
73
|
+
}
|
|
74
|
+
function sitemapUrlsEventsPrefix(ctx) {
|
|
75
|
+
return `${sitemapUrlsPrefix(ctx)}/events`;
|
|
76
|
+
}
|
|
77
|
+
function sitemapUrlsEventKey(ctx, feedpathHash, generation) {
|
|
78
|
+
return `${sitemapUrlsEventsPrefix(ctx)}/${sitemapGenerationDate(generation)}__${feedpathHash}__${String(generation.observedAt).padStart(13, "0")}__${hashUrl(generation.id)}.parquet`;
|
|
79
|
+
}
|
|
80
|
+
function sitemapUrlsEventSeedKey(ctx, feedpathHash) {
|
|
81
|
+
return `${sitemapUrlsPrefix(ctx)}/event-seeds/${feedpathHash}.json`;
|
|
82
|
+
}
|
|
83
|
+
function sitemapUrlsGenerationKey(ctx, feedpathHash) {
|
|
84
|
+
return `${sitemapUrlsPrefix(ctx)}/generations/by-feed/${feedpathHash}.json`;
|
|
85
|
+
}
|
|
86
|
+
function sitemapUrlsPendingGenerationsPrefix(ctx) {
|
|
87
|
+
return `${sitemapUrlsPrefix(ctx)}/generations/pending`;
|
|
88
|
+
}
|
|
89
|
+
function sitemapUrlsPendingGenerationKey(ctx, feedpathHash) {
|
|
90
|
+
return `${sitemapUrlsPendingGenerationsPrefix(ctx)}/${feedpathHash}.json`;
|
|
91
|
+
}
|
|
92
|
+
function sitemapUrlsReconcileGenerationKey(ctx) {
|
|
93
|
+
return `${sitemapUrlsPrefix(ctx)}/generations/reconcile.json`;
|
|
59
94
|
}
|
|
60
95
|
function hashUrlList(urls) {
|
|
61
96
|
return hashSortedUrlList(urls.map((url) => url.loc).sort());
|
|
@@ -90,4 +125,4 @@ function queryDimParquetKey(ctx) {
|
|
|
90
125
|
function queryDimMetaKey(ctx) {
|
|
91
126
|
return `${queryDimPrefix(ctx)}/index.json`;
|
|
92
127
|
}
|
|
93
|
-
export { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix };
|
|
128
|
+
export { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, parseSitemapUrlsDeltaKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsEventKey, sitemapUrlsEventSeedKey, sitemapUrlsEventsPrefix, sitemapUrlsGenerationKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPendingGenerationKey, sitemapUrlsPendingGenerationsPrefix, sitemapUrlsPrefix, sitemapUrlsProjectionManifestKey, sitemapUrlsReconcileGenerationKey };
|
package/dist/errors.d.mts
CHANGED
|
@@ -97,7 +97,7 @@ declare const engineErrors: {
|
|
|
97
97
|
readonly invalidYearMonth: (value: string) => EngineError;
|
|
98
98
|
readonly missingAttachUrl: (fileName: string) => EngineError;
|
|
99
99
|
readonly manifestCasRoundLost: (siteId: string, table: string, attempt: number) => EngineError;
|
|
100
|
-
readonly icebergTableOpFailed: (op:
|
|
100
|
+
readonly icebergTableOpFailed: (op: "create" | "drop", table: string, cause: unknown) => EngineError;
|
|
101
101
|
readonly sinkTableFlushFailed: (table: string, cause: unknown) => EngineError;
|
|
102
102
|
readonly rollupBuildFailed: (id: string, cause: unknown) => EngineError;
|
|
103
103
|
readonly lockAcquireTimeout: (scope: string, timeoutMs: number) => EngineError;
|
package/dist/rollups.d.mts
CHANGED
|
@@ -423,8 +423,8 @@ declare const sitemapHealthRollup: RollupDef;
|
|
|
423
423
|
/**
|
|
424
424
|
* Trailing-28-day sitemap URL changes: per-day per-feedpath {added, removed}
|
|
425
425
|
* counts plus rolling top-200 added and removed URLs. Streams from
|
|
426
|
-
* `
|
|
427
|
-
*
|
|
426
|
+
* retained `SitemapReadStore.loadEvents()` history. State compaction cannot
|
|
427
|
+
* erase analytics input; memory scales independently of total site state.
|
|
428
428
|
*/
|
|
429
429
|
declare const sitemapChanges28dRollup: RollupDef;
|
|
430
430
|
/**
|
package/dist/rollups.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { engineErrors } from "./errors.mjs";
|
|
2
2
|
import "./layout.mjs";
|
|
3
|
-
import { inspectionParquetKey, sitemapUrlsIndexPrefix } from "./entity-keys.mjs";
|
|
3
|
+
import { inspectionParquetKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix, sitemapUrlsProjectionManifestKey } from "./entity-keys.mjs";
|
|
4
4
|
import { encodeRowsToParquetFlex } from "./adapters/hyparquet.mjs";
|
|
5
|
+
import { readOptional } from "./adapters/read-optional.mjs";
|
|
6
|
+
import { decodeSitemapProjectionManifest, selectSitemapProjectionFiles } from "./sitemap-projection.mjs";
|
|
5
7
|
import { createQueryDimStore } from "./query-dim.mjs";
|
|
6
|
-
import { createIndexingMetadataStore,
|
|
8
|
+
import { createIndexingMetadataStore, createSitemapReadStore } from "./entities.mjs";
|
|
7
9
|
import { MS_PER_DAY } from "gscdump/dates";
|
|
8
10
|
import { encodeJsonBigintSafe } from "@gscdump/lakehouse/bigint";
|
|
9
11
|
function rollupPrefix(ctx, searchType) {
|
|
@@ -1003,16 +1005,71 @@ const indexingHealthRollup = {
|
|
|
1003
1005
|
})) };
|
|
1004
1006
|
}
|
|
1005
1007
|
};
|
|
1008
|
+
function currentSitemapProjectionRelation(hasIndexes, hasDeltas) {
|
|
1009
|
+
const sources = [];
|
|
1010
|
+
if (hasIndexes) sources.push(`
|
|
1011
|
+
SELECT
|
|
1012
|
+
feedpath_hash,
|
|
1013
|
+
url_hash,
|
|
1014
|
+
loc,
|
|
1015
|
+
removed_at,
|
|
1016
|
+
greatest(
|
|
1017
|
+
coalesce(removed_at, 0),
|
|
1018
|
+
coalesce(last_seen_at, 0),
|
|
1019
|
+
coalesce(first_seen_at, 0)
|
|
1020
|
+
)::BIGINT AS observed_at,
|
|
1021
|
+
0::INTEGER AS source_order
|
|
1022
|
+
FROM read_parquet({{URLS_INDEX}}, union_by_name = true)
|
|
1023
|
+
`);
|
|
1024
|
+
if (hasDeltas) sources.push(`
|
|
1025
|
+
SELECT
|
|
1026
|
+
feedpath_hash,
|
|
1027
|
+
url_hash,
|
|
1028
|
+
loc,
|
|
1029
|
+
CASE WHEN op = 'removed' THEN "at" ELSE NULL END AS removed_at,
|
|
1030
|
+
"at"::BIGINT AS observed_at,
|
|
1031
|
+
1::INTEGER AS source_order
|
|
1032
|
+
FROM read_parquet({{URLS_DELTA}}, union_by_name = true)
|
|
1033
|
+
WHERE op IN ('added', 'removed')
|
|
1034
|
+
`);
|
|
1035
|
+
return `(
|
|
1036
|
+
WITH membership_events AS (
|
|
1037
|
+
${sources.join("\nUNION ALL\n")}
|
|
1038
|
+
)
|
|
1039
|
+
SELECT feedpath_hash, url_hash, loc, removed_at
|
|
1040
|
+
FROM membership_events
|
|
1041
|
+
QUALIFY row_number() OVER (
|
|
1042
|
+
PARTITION BY feedpath_hash, url_hash
|
|
1043
|
+
ORDER BY observed_at DESC, source_order DESC
|
|
1044
|
+
) = 1
|
|
1045
|
+
)`;
|
|
1046
|
+
}
|
|
1006
1047
|
const indexPercentRollup = {
|
|
1007
1048
|
id: "index_percent",
|
|
1008
1049
|
windowDays: 90,
|
|
1009
1050
|
sliceOrthogonal: true,
|
|
1010
1051
|
async build({ engine, ctx, dataSource, windowAnchorMs, searchType }) {
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1052
|
+
const [listedIndexKeys, listedDeltaKeys, manifestBytes] = await Promise.all([
|
|
1053
|
+
dataSource.list(sitemapUrlsIndexPrefix(ctx)),
|
|
1054
|
+
dataSource.list(`${sitemapUrlsPrefix(ctx)}/deltas/`),
|
|
1055
|
+
readOptional(dataSource, sitemapUrlsProjectionManifestKey(ctx))
|
|
1056
|
+
]);
|
|
1057
|
+
const projection = selectSitemapProjectionFiles(listedIndexKeys, listedDeltaKeys, manifestBytes ? decodeSitemapProjectionManifest(new TextDecoder().decode(manifestBytes)) : void 0);
|
|
1058
|
+
if (projection.indexKeys.length === 0 && projection.deltaKeys.length === 0) return {
|
|
1013
1059
|
totalSitemapUrls: 0,
|
|
1014
1060
|
days: []
|
|
1015
1061
|
};
|
|
1062
|
+
const sitemapFileSets = {
|
|
1063
|
+
...projection.indexKeys.length > 0 ? { URLS_INDEX: {
|
|
1064
|
+
table: "pages",
|
|
1065
|
+
keys: projection.indexKeys
|
|
1066
|
+
} } : {},
|
|
1067
|
+
...projection.deltaKeys.length > 0 ? { URLS_DELTA: {
|
|
1068
|
+
table: "pages",
|
|
1069
|
+
keys: projection.deltaKeys
|
|
1070
|
+
} } : {}
|
|
1071
|
+
};
|
|
1072
|
+
const currentMembership = currentSitemapProjectionRelation(projection.indexKeys.length > 0, projection.deltaKeys.length > 0);
|
|
1016
1073
|
const cutoff = utcDateMinusDays(windowAnchorMs, 90);
|
|
1017
1074
|
const factSearchType = searchType ?? "web";
|
|
1018
1075
|
const pagesPartitions = partitionsInRange(await engine.listPartitions({
|
|
@@ -1028,10 +1085,7 @@ const indexPercentRollup = {
|
|
|
1028
1085
|
table: "pages",
|
|
1029
1086
|
partitions: pagesPartitions
|
|
1030
1087
|
},
|
|
1031
|
-
|
|
1032
|
-
table: "pages",
|
|
1033
|
-
keys: urlsKeys
|
|
1034
|
-
}
|
|
1088
|
+
...sitemapFileSets
|
|
1035
1089
|
},
|
|
1036
1090
|
searchType: factSearchType,
|
|
1037
1091
|
sql: `
|
|
@@ -1039,7 +1093,7 @@ const indexPercentRollup = {
|
|
|
1039
1093
|
p.date AS date,
|
|
1040
1094
|
COUNT(DISTINCT p.url)::BIGINT AS clicked_urls
|
|
1041
1095
|
FROM read_parquet({{PAGES}}, union_by_name = true) p
|
|
1042
|
-
INNER JOIN
|
|
1096
|
+
INNER JOIN ${currentMembership} s
|
|
1043
1097
|
ON s.loc = p.url AND s.removed_at IS NULL
|
|
1044
1098
|
WHERE p.clicks > 0 AND p.date >= '${cutoff}'
|
|
1045
1099
|
GROUP BY p.date
|
|
@@ -1049,13 +1103,10 @@ const indexPercentRollup = {
|
|
|
1049
1103
|
const denom = await engine.runSQL({
|
|
1050
1104
|
ctx,
|
|
1051
1105
|
table: "pages",
|
|
1052
|
-
fileSets:
|
|
1053
|
-
table: "pages",
|
|
1054
|
-
keys: urlsKeys
|
|
1055
|
-
} },
|
|
1106
|
+
fileSets: sitemapFileSets,
|
|
1056
1107
|
sql: `
|
|
1057
|
-
SELECT COUNT(
|
|
1058
|
-
FROM
|
|
1108
|
+
SELECT COUNT(DISTINCT loc)::BIGINT AS total
|
|
1109
|
+
FROM ${currentMembership}
|
|
1059
1110
|
WHERE removed_at IS NULL
|
|
1060
1111
|
`
|
|
1061
1112
|
});
|
|
@@ -1079,7 +1130,7 @@ const sitemapHealthRollup = {
|
|
|
1079
1130
|
windowDays: 90,
|
|
1080
1131
|
sliceOrthogonal: true,
|
|
1081
1132
|
async build({ dataSource, ctx, windowAnchorMs }) {
|
|
1082
|
-
const index = await
|
|
1133
|
+
const index = await createSitemapReadStore({ dataSource }).loadIndex(ctx);
|
|
1083
1134
|
const records = Object.values(index.records);
|
|
1084
1135
|
const cutoff = utcDateMinusDays(windowAnchorMs, 90);
|
|
1085
1136
|
const byDay = /* @__PURE__ */ new Map();
|
|
@@ -1157,7 +1208,7 @@ const sitemapChanges28dRollup = {
|
|
|
1157
1208
|
windowDays: 28,
|
|
1158
1209
|
sliceOrthogonal: true,
|
|
1159
1210
|
async build({ dataSource, ctx, windowAnchorMs }) {
|
|
1160
|
-
const store =
|
|
1211
|
+
const store = createSitemapReadStore({ dataSource });
|
|
1161
1212
|
const from = utcDateMinusDays(windowAnchorMs, 28);
|
|
1162
1213
|
const to = utcDateMinusDays(windowAnchorMs, 0);
|
|
1163
1214
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -1167,11 +1218,11 @@ const sitemapChanges28dRollup = {
|
|
|
1167
1218
|
function key(k) {
|
|
1168
1219
|
return `${k.day}\x00${k.feedpath}`;
|
|
1169
1220
|
}
|
|
1170
|
-
for await (const d of store.
|
|
1221
|
+
for await (const d of store.loadEvents(ctx, {
|
|
1171
1222
|
from,
|
|
1172
1223
|
to
|
|
1173
1224
|
})) {
|
|
1174
|
-
const day = new Date(d.
|
|
1225
|
+
const day = new Date(d.observedAt).toISOString().slice(0, 10);
|
|
1175
1226
|
const k = key({
|
|
1176
1227
|
day,
|
|
1177
1228
|
feedpath: d.feedpath
|
|
@@ -1185,7 +1236,7 @@ const sitemapChanges28dRollup = {
|
|
|
1185
1236
|
const change = {
|
|
1186
1237
|
loc: d.loc,
|
|
1187
1238
|
feedpath: d.feedpath,
|
|
1188
|
-
at: d.
|
|
1239
|
+
at: d.observedAt,
|
|
1189
1240
|
sequence: sequence++
|
|
1190
1241
|
};
|
|
1191
1242
|
if (d.op === "added") {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const SITEMAP_PROJECTION_GRACE_MS: number;
|
|
2
|
+
interface SitemapProjectionFeed {
|
|
3
|
+
compactedThrough: string;
|
|
4
|
+
/** Unix epoch milliseconds when readers started filtering through this key. */
|
|
5
|
+
publishedAt: number;
|
|
6
|
+
}
|
|
7
|
+
interface SitemapProjectionManifest {
|
|
8
|
+
version: 1;
|
|
9
|
+
feeds: Record<string, SitemapProjectionFeed>;
|
|
10
|
+
}
|
|
11
|
+
interface SitemapProjectionFiles {
|
|
12
|
+
indexKeys: string[];
|
|
13
|
+
deltaKeys: string[];
|
|
14
|
+
}
|
|
15
|
+
declare function emptySitemapProjectionManifest(): SitemapProjectionManifest;
|
|
16
|
+
declare function parseSitemapProjectionManifest(value: unknown): SitemapProjectionManifest;
|
|
17
|
+
declare function decodeSitemapProjectionManifest(text: string): SitemapProjectionManifest;
|
|
18
|
+
declare function selectSitemapProjectionFiles(indexKeys: readonly string[], deltaKeys: readonly string[], manifest: SitemapProjectionManifest | undefined): SitemapProjectionFiles;
|
|
19
|
+
declare function withSitemapProjectionFeed(manifest: SitemapProjectionManifest, feedpathHash: string, feed: SitemapProjectionFeed): SitemapProjectionManifest;
|
|
20
|
+
export { SITEMAP_PROJECTION_GRACE_MS, SitemapProjectionFeed, SitemapProjectionFiles, SitemapProjectionManifest, decodeSitemapProjectionManifest, emptySitemapProjectionManifest, parseSitemapProjectionManifest, selectSitemapProjectionFiles, withSitemapProjectionFeed };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { parseSitemapUrlsDeltaKey } from "./entity-keys.mjs";
|
|
2
|
+
const SITEMAP_PROJECTION_GRACE_MS = 900 * 1e3;
|
|
3
|
+
function emptySitemapProjectionManifest() {
|
|
4
|
+
return {
|
|
5
|
+
version: 1,
|
|
6
|
+
feeds: {}
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function objectRecord(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
11
|
+
}
|
|
12
|
+
function parseSitemapProjectionManifest(value) {
|
|
13
|
+
const manifest = objectRecord(value);
|
|
14
|
+
const rawFeeds = objectRecord(manifest?.feeds);
|
|
15
|
+
if (manifest?.version !== 1 || !rawFeeds) throw new Error("invalid sitemap projection manifest");
|
|
16
|
+
const feeds = {};
|
|
17
|
+
for (const [feedpathHash, rawFeed] of Object.entries(rawFeeds)) {
|
|
18
|
+
const feed = objectRecord(rawFeed);
|
|
19
|
+
const compactedThrough = feed?.compactedThrough;
|
|
20
|
+
const publishedAt = feed?.publishedAt;
|
|
21
|
+
if (typeof compactedThrough !== "string") throw new Error(`invalid sitemap projection manifest feed: ${feedpathHash}`);
|
|
22
|
+
const parsedKey = parseSitemapUrlsDeltaKey(compactedThrough);
|
|
23
|
+
if (!/^[0-9a-f]+$/.test(feedpathHash) || parsedKey?.feedpathHash !== feedpathHash || !Number.isSafeInteger(publishedAt) || Number(publishedAt) < 0) throw new Error(`invalid sitemap projection manifest feed: ${feedpathHash}`);
|
|
24
|
+
feeds[feedpathHash] = {
|
|
25
|
+
compactedThrough,
|
|
26
|
+
publishedAt: Number(publishedAt)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
version: 1,
|
|
31
|
+
feeds
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function decodeSitemapProjectionManifest(text) {
|
|
35
|
+
return parseSitemapProjectionManifest(JSON.parse(text));
|
|
36
|
+
}
|
|
37
|
+
function selectSitemapProjectionFiles(indexKeys, deltaKeys, manifest) {
|
|
38
|
+
return {
|
|
39
|
+
indexKeys: indexKeys.filter((key) => key.endsWith("/index.parquet")),
|
|
40
|
+
deltaKeys: deltaKeys.filter((key) => {
|
|
41
|
+
const parsed = parseSitemapUrlsDeltaKey(key);
|
|
42
|
+
if (!parsed) return false;
|
|
43
|
+
const feed = manifest?.feeds[parsed.feedpathHash];
|
|
44
|
+
return !feed || key > feed.compactedThrough;
|
|
45
|
+
})
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function withSitemapProjectionFeed(manifest, feedpathHash, feed) {
|
|
49
|
+
return {
|
|
50
|
+
version: 1,
|
|
51
|
+
feeds: {
|
|
52
|
+
...manifest.feeds,
|
|
53
|
+
[feedpathHash]: feed
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export { SITEMAP_PROJECTION_GRACE_MS, decodeSitemapProjectionManifest, emptySitemapProjectionManifest, parseSitemapProjectionManifest, selectSitemapProjectionFiles, withSitemapProjectionFeed };
|
package/dist/sync-config.d.mts
CHANGED
|
@@ -13,11 +13,11 @@ declare function parseEnabledSearchTypes(raw: string | null | undefined): Search
|
|
|
13
13
|
declare function validateEnabledSearchTypesResult(value: unknown): Result<SearchType[], EngineError>;
|
|
14
14
|
declare function validateEnabledSearchTypes(value: unknown): SearchType[];
|
|
15
15
|
declare const TABLE_TIERS: {
|
|
16
|
-
readonly pages:
|
|
17
|
-
readonly queries:
|
|
18
|
-
readonly countries:
|
|
19
|
-
readonly dates:
|
|
20
|
-
readonly page_queries:
|
|
16
|
+
readonly pages: "critical";
|
|
17
|
+
readonly queries: "critical";
|
|
18
|
+
readonly countries: "standard";
|
|
19
|
+
readonly dates: "standard";
|
|
20
|
+
readonly page_queries: "extended";
|
|
21
21
|
};
|
|
22
22
|
type TieredTableName = keyof typeof TABLE_TIERS;
|
|
23
23
|
type TableTier = 'critical' | 'standard' | 'extended';
|
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.4",
|
|
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
|
-
"gscdump": "^1.4.
|
|
186
|
-
"@gscdump/
|
|
184
|
+
"@gscdump/lakehouse": "^1.4.4",
|
|
185
|
+
"gscdump": "^1.4.4",
|
|
186
|
+
"@gscdump/contracts": "^1.4.4"
|
|
187
187
|
},
|
|
188
188
|
"devDependencies": {
|
|
189
189
|
"@duckdb/duckdb-wasm": "1.33.1-dev57.0",
|