@gscdump/cloudflare 0.25.10 → 0.25.12

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.
@@ -86,8 +86,8 @@ declare function createDuckDbIcebergExecutor(config: DuckDbIcebergExecutorConfig
86
86
  interface R2SqlClientConfig {
87
87
  /** Cloudflare account id. */
88
88
  accountId: string;
89
- /** R2 Data Catalog warehouse name (`<bucket>` or `<account>_<bucket>`). */
90
- warehouse: string;
89
+ /** R2 bucket backing the Iceberg catalog R2 SQL addresses the catalog by bucket. */
90
+ bucket: string;
91
91
  /** Iceberg namespace the 5 fact tables live in. */
92
92
  namespace: string;
93
93
  /** Cloudflare API token with R2 Data Catalog read scope. */
@@ -14,14 +14,6 @@ function metricExpr(metric) {
14
14
  case "position": return "SUM(sum_position) / NULLIF(SUM(impressions), 0) AS position";
15
15
  }
16
16
  }
17
- function orderMetricExpr(metric) {
18
- switch (metric) {
19
- case "clicks": return "SUM(clicks)";
20
- case "impressions": return "SUM(impressions)";
21
- case "ctr": return "SUM(clicks) / NULLIF(SUM(impressions), 0)";
22
- case "position": return "SUM(sum_position) / NULLIF(SUM(impressions), 0)";
23
- }
24
- }
25
17
  const DEVICE_SUFFIXES = [
26
18
  "desktop",
27
19
  "mobile",
@@ -138,8 +130,8 @@ function buildTopNBreakdown(q) {
138
130
  };
139
131
  }
140
132
  const col = dimColumn(q.dimension);
141
- const metrics = q.metrics.map(metricExpr).join(", ");
142
- const order = `${orderMetricExpr(q.orderBy.metric)} ${q.orderBy.dir.toUpperCase()}`;
133
+ const metrics = (q.metrics.includes(q.orderBy.metric) ? q.metrics : [...q.metrics, q.orderBy.metric]).map(metricExpr).join(", ");
134
+ const order = `${q.orderBy.metric} ${q.orderBy.dir.toUpperCase()}`;
143
135
  const facet = facetPredicate(q);
144
136
  let sql = `SELECT ${col}, ${metrics} FROM ${TABLE_PLACEHOLDER} WHERE ${w.clause}${facet.sql} GROUP BY ${col} ORDER BY ${order} LIMIT ${Math.max(0, Math.floor(q.limit))}`;
145
137
  if (q.offset && q.offset > 0) sql += ` OFFSET ${Math.floor(q.offset)}`;
@@ -203,8 +195,8 @@ function buildTwoDimensionDetail(q) {
203
195
  const facet = facetPredicate(q);
204
196
  clause += facet.sql;
205
197
  params.push(...facet.params);
206
- let sql = `SELECT url, query, ${q.metrics.map(metricExpr).join(", ")} FROM ${TABLE_PLACEHOLDER} WHERE ${clause} GROUP BY url, query`;
207
- if (q.orderBy) sql += ` ORDER BY ${orderMetricExpr(q.orderBy.metric)} ${q.orderBy.dir.toUpperCase()}`;
198
+ let sql = `SELECT url, query, ${(q.orderBy && !q.metrics.includes(q.orderBy.metric) ? [...q.metrics, q.orderBy.metric] : q.metrics).map(metricExpr).join(", ")} FROM ${TABLE_PLACEHOLDER} WHERE ${clause} GROUP BY url, query`;
199
+ if (q.orderBy) sql += ` ORDER BY ${q.orderBy.metric} ${q.orderBy.dir.toUpperCase()}`;
208
200
  if (q.limit && q.limit > 0) sql += ` LIMIT ${Math.floor(q.limit)}`;
209
201
  return {
210
202
  table: "page_queries",
@@ -349,8 +341,12 @@ var R2SqlTimeoutError = class extends Error {
349
341
  super(`R2 SQL query exceeded ${timeoutMs}ms deadline`);
350
342
  }
351
343
  };
352
- const DEFAULT_API_BASE = "https://api.cloudflare.com/client/v4";
344
+ const DEFAULT_API_BASE = "https://api.sql.cloudflarestorage.com/api/v1";
353
345
  const DEFAULT_TIMEOUT_MS = 25e3;
346
+ const PARTITION_PREDICATE_RE = /\b(site_id|search_type)(\s*=)/g;
347
+ function workaroundPartitionEquality(sql) {
348
+ return sql.replace(PARTITION_PREDICATE_RE, (_m, col, eq) => `CONCAT(${col}, '')${eq}`);
349
+ }
354
350
  function escapeSqlValue(value) {
355
351
  if (value === null || value === void 0) return "NULL";
356
352
  if (typeof value === "number") {
@@ -406,7 +402,7 @@ function createR2SqlClient(config) {
406
402
  const fetchImpl = config.fetchImpl ?? globalThis.fetch;
407
403
  const apiBase = config.apiBase ?? DEFAULT_API_BASE;
408
404
  const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
409
- const endpoint = `${apiBase}/accounts/${config.accountId}/r2-catalog/${config.warehouse}/sql`;
405
+ const endpoint = `${apiBase}/accounts/${config.accountId}/r2-sql/query/${config.bucket}`;
410
406
  async function query(sql) {
411
407
  const started = Date.now();
412
408
  const controller = new AbortController();
@@ -417,7 +413,8 @@ function createR2SqlClient(config) {
417
413
  method: "POST",
418
414
  headers: {
419
415
  "authorization": `Bearer ${config.token}`,
420
- "content-type": "application/json"
416
+ "content-type": "application/json",
417
+ "user-agent": "gscdump-cloudflare-r2sql/1.0"
421
418
  },
422
419
  body: JSON.stringify({ query: sql }),
423
420
  signal: controller.signal
@@ -442,7 +439,7 @@ function createR2SqlClient(config) {
442
439
  }
443
440
  function runPlan(plan) {
444
441
  const tableRef = r2TableRef(config.namespace, plan.table);
445
- return query(inlineParams(plan.sql.split(TABLE_PLACEHOLDER).join(tableRef), plan.params));
442
+ return query(workaroundPartitionEquality(inlineParams(plan.sql.split(TABLE_PLACEHOLDER).join(tableRef), plan.params)));
446
443
  }
447
444
  function runArchetype(archetypeQuery) {
448
445
  return runPlan(buildArchetypeSql(archetypeQuery));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/cloudflare",
3
3
  "type": "module",
4
- "version": "0.25.10",
4
+ "version": "0.25.12",
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,11 +46,11 @@
46
46
  "dependencies": {
47
47
  "@uwdata/flechette": "^2.5.0",
48
48
  "aws4fetch": "^1.0.20",
49
- "@gscdump/contracts": "0.25.10",
50
- "@gscdump/engine-sqlite": "0.25.10",
51
- "@gscdump/sdk": "0.25.10",
52
- "gscdump": "0.25.10",
53
- "@gscdump/engine": "0.25.10"
49
+ "@gscdump/engine": "0.25.12",
50
+ "@gscdump/engine-sqlite": "0.25.12",
51
+ "@gscdump/sdk": "0.25.12",
52
+ "@gscdump/contracts": "0.25.12",
53
+ "gscdump": "0.25.12"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@cloudflare/vitest-pool-workers": "^0.16.10",