@gscdump/cloudflare 0.26.0 → 0.26.2
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/server-tail/index.mjs +93 -9
- package/package.json +9 -9
|
@@ -35,6 +35,44 @@ function deviceSource(suffix) {
|
|
|
35
35
|
sumPosition: `sum_position_${suffix}`
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
const STD_METRICS = [
|
|
39
|
+
"clicks",
|
|
40
|
+
"impressions",
|
|
41
|
+
"ctr",
|
|
42
|
+
"position"
|
|
43
|
+
];
|
|
44
|
+
function coalesceMetric(metric, src, alias) {
|
|
45
|
+
const ref = `${src}.${metric}`;
|
|
46
|
+
return metric === "clicks" || metric === "impressions" ? `CAST(COALESCE(${ref}, 0) AS DOUBLE) AS ${alias}` : `COALESCE(${ref}, 0) AS ${alias}`;
|
|
47
|
+
}
|
|
48
|
+
function prevAlias(metric) {
|
|
49
|
+
return `prev${metric.charAt(0).toUpperCase()}${metric.slice(1)}`;
|
|
50
|
+
}
|
|
51
|
+
function moverClause(movers) {
|
|
52
|
+
const curClicks = "COALESCE(c.clicks, 0)";
|
|
53
|
+
const prevClicks = "COALESCE(p.clicks, 0)";
|
|
54
|
+
const curImpr = "COALESCE(c.impressions, 0)";
|
|
55
|
+
const prevImpr = "COALESCE(p.impressions, 0)";
|
|
56
|
+
switch (movers) {
|
|
57
|
+
case "improving": return {
|
|
58
|
+
where: `${curClicks} > ${prevClicks}`,
|
|
59
|
+
order: `(${curClicks} - ${prevClicks}) DESC`
|
|
60
|
+
};
|
|
61
|
+
case "declining": return {
|
|
62
|
+
where: `${curClicks} < ${prevClicks}`,
|
|
63
|
+
order: `(${curClicks} - ${prevClicks}) ASC`
|
|
64
|
+
};
|
|
65
|
+
case "new": return {
|
|
66
|
+
where: `${prevImpr} = 0 AND ${curImpr} > 0`,
|
|
67
|
+
order: `${curClicks} DESC, ${curImpr} DESC`
|
|
68
|
+
};
|
|
69
|
+
case "lost": return {
|
|
70
|
+
where: `${curImpr} = 0 AND ${prevImpr} > 0`,
|
|
71
|
+
order: `${prevImpr} DESC`
|
|
72
|
+
};
|
|
73
|
+
default: throw new Error(`[archetype-sql] unknown movers mode: ${movers}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
38
76
|
function sqlStringLiteral(value) {
|
|
39
77
|
return `'${value.replace(/'/g, "''")}'`;
|
|
40
78
|
}
|
|
@@ -115,15 +153,36 @@ function buildEntityDailySparkline(q) {
|
|
|
115
153
|
function buildTopNBreakdown(q) {
|
|
116
154
|
const table = inferTable([q.dimension]);
|
|
117
155
|
const w = partitionWhere(q);
|
|
156
|
+
const order = `${q.orderBy.metric} ${q.orderBy.dir.toUpperCase()}`;
|
|
157
|
+
const limit = `LIMIT ${Math.max(0, Math.floor(q.limit))}`;
|
|
158
|
+
const offset = q.offset && q.offset > 0 ? ` OFFSET ${Math.floor(q.offset)}` : "";
|
|
159
|
+
const metricList = q.metrics.includes(q.orderBy.metric) ? q.metrics : [...q.metrics, q.orderBy.metric];
|
|
160
|
+
const variantSel = q.dimension === "queryCanonical" ? ", COUNT(DISTINCT query) AS variantCount" : "";
|
|
118
161
|
if (q.dimension === "device") {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
162
|
+
if (q.compareRange) {
|
|
163
|
+
const wPrev = partitionWhere({
|
|
164
|
+
...q,
|
|
165
|
+
range: q.compareRange
|
|
166
|
+
});
|
|
167
|
+
const deviceSelects = (clause, ml) => DEVICE_SUFFIXES.map((suffix) => {
|
|
168
|
+
const source = deviceSource(suffix);
|
|
169
|
+
const metrics = ml.map((m) => metricExprForSource(m, source)).join(", ");
|
|
170
|
+
return `SELECT '${suffix.toUpperCase()}' AS device, ${metrics} FROM ${TABLE_PLACEHOLDER} WHERE ${clause}`;
|
|
171
|
+
}).join(" UNION ALL ");
|
|
172
|
+
const curCols = metricList.map((m) => coalesceMetric(m, "c", m)).join(", ");
|
|
173
|
+
const prevCols = STD_METRICS.map((m) => coalesceMetric(m, "p", prevAlias(m))).join(", ");
|
|
174
|
+
const sql = `WITH cur AS (${deviceSelects(w.clause, metricList)}), prev AS (${deviceSelects(wPrev.clause, STD_METRICS)}) SELECT COALESCE(c.device, p.device) AS device, ${curCols}, ${prevCols} FROM cur c FULL OUTER JOIN prev p ON c.device = p.device ORDER BY ${order} ${limit}${offset}`;
|
|
175
|
+
return {
|
|
176
|
+
table,
|
|
177
|
+
params: [...DEVICE_SUFFIXES.flatMap(() => w.params), ...DEVICE_SUFFIXES.flatMap(() => wPrev.params)],
|
|
178
|
+
sql
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const sql = `${DEVICE_SUFFIXES.map((suffix) => {
|
|
122
182
|
const source = deviceSource(suffix);
|
|
123
183
|
const metrics = metricList.map((m) => metricExprForSource(m, source)).join(", ");
|
|
124
184
|
return `SELECT '${suffix.toUpperCase()}' AS device, ${metrics} FROM ${TABLE_PLACEHOLDER} WHERE ${w.clause}`;
|
|
125
|
-
}).join(" UNION ALL ")} ORDER BY ${order}
|
|
126
|
-
if (q.offset && q.offset > 0) sql += ` OFFSET ${Math.floor(q.offset)}`;
|
|
185
|
+
}).join(" UNION ALL ")} ORDER BY ${order} ${limit}${offset}`;
|
|
127
186
|
return {
|
|
128
187
|
table,
|
|
129
188
|
params: DEVICE_SUFFIXES.flatMap(() => w.params),
|
|
@@ -131,11 +190,34 @@ function buildTopNBreakdown(q) {
|
|
|
131
190
|
};
|
|
132
191
|
}
|
|
133
192
|
const col = dimColumn(q.dimension);
|
|
134
|
-
const metrics = (q.metrics.includes(q.orderBy.metric) ? q.metrics : [...q.metrics, q.orderBy.metric]).map(metricExpr).join(", ");
|
|
135
|
-
const order = `${q.orderBy.metric} ${q.orderBy.dir.toUpperCase()}`;
|
|
136
193
|
const facet = facetPredicate(q);
|
|
137
|
-
|
|
138
|
-
if (q.
|
|
194
|
+
const totalCol = q.includeTotal ? ", COUNT(*) OVER() AS __total" : "";
|
|
195
|
+
if (q.compareRange) {
|
|
196
|
+
const wPrev = partitionWhere({
|
|
197
|
+
...q,
|
|
198
|
+
range: q.compareRange
|
|
199
|
+
});
|
|
200
|
+
const curMetrics = metricList.map(metricExpr).join(", ");
|
|
201
|
+
const prevMetrics = STD_METRICS.map((m) => metricExpr(m)).join(", ");
|
|
202
|
+
const curCols = metricList.map((m) => coalesceMetric(m, "c", m)).join(", ");
|
|
203
|
+
const prevCols = STD_METRICS.map((m) => coalesceMetric(m, "p", prevAlias(m))).join(", ");
|
|
204
|
+
const variantOut = q.dimension === "queryCanonical" ? ", c.variantCount AS variantCount" : "";
|
|
205
|
+
const mover = q.movers ? moverClause(q.movers) : null;
|
|
206
|
+
const moverWhere = mover ? `WHERE ${mover.where} ` : "";
|
|
207
|
+
const orderSql = mover ? `ORDER BY ${mover.order}` : `ORDER BY ${order}`;
|
|
208
|
+
const sql = `WITH cur AS (SELECT ${col} AS k, ${curMetrics}${variantSel} FROM ${TABLE_PLACEHOLDER} WHERE ${w.clause}${facet.sql} GROUP BY ${col}), prev AS (SELECT ${col} AS k, ${prevMetrics} FROM ${TABLE_PLACEHOLDER} WHERE ${wPrev.clause}${facet.sql} GROUP BY ${col}) SELECT COALESCE(c.k, p.k) AS ${q.dimension}, ${curCols}, ${prevCols}${variantOut}${totalCol} FROM cur c FULL OUTER JOIN prev p ON c.k = p.k ${moverWhere}${orderSql} ${limit}${offset}`;
|
|
209
|
+
return {
|
|
210
|
+
table,
|
|
211
|
+
params: [
|
|
212
|
+
...w.params,
|
|
213
|
+
...facet.params,
|
|
214
|
+
...wPrev.params,
|
|
215
|
+
...facet.params
|
|
216
|
+
],
|
|
217
|
+
sql
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const sql = `SELECT ${col}, ${metricList.map(metricExpr).join(", ")}${variantSel}${totalCol} FROM ${TABLE_PLACEHOLDER} WHERE ${w.clause}${facet.sql} GROUP BY ${col} ORDER BY ${order} ${limit}${offset}`;
|
|
139
221
|
return {
|
|
140
222
|
table,
|
|
141
223
|
params: [...w.params, ...facet.params],
|
|
@@ -230,6 +312,8 @@ function resolveServerTailEngineResult(query) {
|
|
|
230
312
|
if (cls === "duckdb") return ok("duckdb");
|
|
231
313
|
if (query.archetype === "top-n-breakdown" && query.offset && query.offset > 0) return ok("duckdb");
|
|
232
314
|
if (query.archetype === "top-n-breakdown" && query.includeTotal) return ok("duckdb");
|
|
315
|
+
if (query.archetype === "top-n-breakdown" && query.compareRange) return ok("duckdb");
|
|
316
|
+
if (query.archetype === "top-n-breakdown" && query.dimension === "queryCanonical") return ok("duckdb");
|
|
233
317
|
const facets = query.facets;
|
|
234
318
|
if (facets && facets.length > 0) return ok("duckdb");
|
|
235
319
|
return ok("r2-sql");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/cloudflare",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.26.
|
|
4
|
+
"version": "0.26.2",
|
|
5
5
|
"description": "Cloudflare-Workers-flavored helpers for the gscdump analytics stack: AnalyticsEnv binding contract, R2 SigV4 presigner, size-hint HMAC, DuckDB Workers shims, engine factory.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -46,18 +46,18 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@uwdata/flechette": "^2.5.0",
|
|
48
48
|
"aws4fetch": "^1.0.20",
|
|
49
|
-
"@gscdump/
|
|
50
|
-
"@gscdump/
|
|
51
|
-
"@gscdump/
|
|
52
|
-
"
|
|
53
|
-
"gscdump": "0.26.
|
|
49
|
+
"@gscdump/engine": "0.26.2",
|
|
50
|
+
"@gscdump/contracts": "0.26.2",
|
|
51
|
+
"@gscdump/engine-sqlite": "0.26.2",
|
|
52
|
+
"gscdump": "0.26.2",
|
|
53
|
+
"@gscdump/sdk": "0.26.2"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@cloudflare/vitest-pool-workers": "^0.16.
|
|
57
|
-
"@cloudflare/workers-types": "^4.
|
|
56
|
+
"@cloudflare/vitest-pool-workers": "^0.16.13",
|
|
57
|
+
"@cloudflare/workers-types": "^4.20260608.1",
|
|
58
58
|
"h3": "^1.15.11",
|
|
59
59
|
"typescript": "^6.0.3",
|
|
60
|
-
"wrangler": "^4.
|
|
60
|
+
"wrangler": "^4.98.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "obuild",
|