@gscdump/analysis 0.35.12 → 0.36.1

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.
@@ -2011,24 +2011,31 @@ function coerceNumericCols(row) {
2011
2011
  function shapeDataQuery(rows, extras, opts) {
2012
2012
  let totalCount;
2013
2013
  let cleaned;
2014
+ let totals;
2014
2015
  if (opts.hasPrev) {
2015
2016
  cleaned = rows.map(coerceNumericCols);
2016
2017
  totalCount = Number((extras?.count?.[0])?.total ?? cleaned.length);
2018
+ const totalsRow = extras?.totals?.[0] ?? {};
2019
+ totals = {
2020
+ clicks: Number(totalsRow.clicks ?? 0),
2021
+ impressions: Number(totalsRow.impressions ?? 0),
2022
+ ctr: Number(totalsRow.ctr ?? 0),
2023
+ position: Number(totalsRow.position ?? 0)
2024
+ };
2017
2025
  } else {
2018
2026
  const first = rows[0];
2019
2027
  totalCount = Number(first?.totalCount ?? 0);
2028
+ totals = {
2029
+ clicks: Number(first?.totalClicks ?? 0),
2030
+ impressions: Number(first?.totalImpressions ?? 0),
2031
+ ctr: Number(first?.totalCtr ?? 0),
2032
+ position: Number(first?.totalPosition ?? 0)
2033
+ };
2020
2034
  cleaned = rows.map((raw) => {
2021
2035
  const { totalCount: _tc, totalClicks: _tclk, totalImpressions: _timp, totalCtr: _tctr, totalPosition: _tpos, sum_position: _sp, ...rest } = raw;
2022
2036
  return coerceNumericCols(rest);
2023
2037
  });
2024
2038
  }
2025
- const totalsRow = extras?.totals?.[0] ?? {};
2026
- const totals = {
2027
- clicks: Number(totalsRow.clicks ?? 0),
2028
- impressions: Number(totalsRow.impressions ?? 0),
2029
- ctr: Number(totalsRow.ctr ?? 0),
2030
- position: Number(totalsRow.position ?? 0)
2031
- };
2032
2039
  const extrasResults = [];
2033
2040
  if (extras?.canonicalExtras) extrasResults.push({
2034
2041
  key: "canonicalExtras",
@@ -2046,20 +2053,20 @@ function buildDataQueryPlan(params, options) {
2046
2053
  const state = requireBuilderState(params.q, "data-query");
2047
2054
  if (state.dimensions.includes("date")) throw new Error("data-query: date dimension not supported; use data-detail");
2048
2055
  const prev = optionalBuilderState(params.qc, "data-query", "qc");
2049
- const totals = buildTotalsSql(state, options);
2050
- const extras = buildExtrasQueries(state, options);
2051
- const extraQueries = [{
2052
- name: "totals",
2053
- sql: totals.sql,
2054
- params: totals.params
2055
- }, ...extras.map((extra) => ({
2056
+ const extraQueries = [...buildExtrasQueries(state, options).map((extra) => ({
2056
2057
  name: extra.key,
2057
2058
  sql: extra.sql,
2058
2059
  params: extra.params
2059
2060
  }))];
2060
2061
  const tableKey = options.adapter.inferTable(state.dimensions);
2061
2062
  if (prev) {
2063
+ const totals = buildTotalsSql(state, options);
2062
2064
  const comparison = resolveComparisonSQL(state, prev, options, params.comparisonFilter);
2065
+ extraQueries.unshift({
2066
+ name: "totals",
2067
+ sql: totals.sql,
2068
+ params: totals.params
2069
+ });
2063
2070
  extraQueries.push({
2064
2071
  name: "count",
2065
2072
  sql: comparison.countSql,
@@ -2122,7 +2129,7 @@ function shapeDataQueryRowResults(rowMap, params) {
2122
2129
  };
2123
2130
  const state = requireBuilderState(params.q, "data-query");
2124
2131
  const dims = state.dimensions;
2125
- const keyOf = (row) => dims.map((d) => String(row[d] ?? "")).join("\0");
2132
+ const keyOf = (row) => dims.map((d) => String(row[d] ?? "")).join("");
2126
2133
  const prevByKey = /* @__PURE__ */ new Map();
2127
2134
  for (const r of rowMap.prevMain ?? []) prevByKey.set(keyOf(r), r);
2128
2135
  const filter = params.comparisonFilter;
@@ -2011,24 +2011,31 @@ function coerceNumericCols(row) {
2011
2011
  function shapeDataQuery(rows, extras, opts) {
2012
2012
  let totalCount;
2013
2013
  let cleaned;
2014
+ let totals;
2014
2015
  if (opts.hasPrev) {
2015
2016
  cleaned = rows.map(coerceNumericCols);
2016
2017
  totalCount = Number((extras?.count?.[0])?.total ?? cleaned.length);
2018
+ const totalsRow = extras?.totals?.[0] ?? {};
2019
+ totals = {
2020
+ clicks: Number(totalsRow.clicks ?? 0),
2021
+ impressions: Number(totalsRow.impressions ?? 0),
2022
+ ctr: Number(totalsRow.ctr ?? 0),
2023
+ position: Number(totalsRow.position ?? 0)
2024
+ };
2017
2025
  } else {
2018
2026
  const first = rows[0];
2019
2027
  totalCount = Number(first?.totalCount ?? 0);
2028
+ totals = {
2029
+ clicks: Number(first?.totalClicks ?? 0),
2030
+ impressions: Number(first?.totalImpressions ?? 0),
2031
+ ctr: Number(first?.totalCtr ?? 0),
2032
+ position: Number(first?.totalPosition ?? 0)
2033
+ };
2020
2034
  cleaned = rows.map((raw) => {
2021
2035
  const { totalCount: _tc, totalClicks: _tclk, totalImpressions: _timp, totalCtr: _tctr, totalPosition: _tpos, sum_position: _sp, ...rest } = raw;
2022
2036
  return coerceNumericCols(rest);
2023
2037
  });
2024
2038
  }
2025
- const totalsRow = extras?.totals?.[0] ?? {};
2026
- const totals = {
2027
- clicks: Number(totalsRow.clicks ?? 0),
2028
- impressions: Number(totalsRow.impressions ?? 0),
2029
- ctr: Number(totalsRow.ctr ?? 0),
2030
- position: Number(totalsRow.position ?? 0)
2031
- };
2032
2039
  const extrasResults = [];
2033
2040
  if (extras?.canonicalExtras) extrasResults.push({
2034
2041
  key: "canonicalExtras",
@@ -2046,20 +2053,20 @@ function buildDataQueryPlan(params, options) {
2046
2053
  const state = requireBuilderState(params.q, "data-query");
2047
2054
  if (state.dimensions.includes("date")) throw new Error("data-query: date dimension not supported; use data-detail");
2048
2055
  const prev = optionalBuilderState(params.qc, "data-query", "qc");
2049
- const totals = buildTotalsSql(state, options);
2050
- const extras = buildExtrasQueries(state, options);
2051
- const extraQueries = [{
2052
- name: "totals",
2053
- sql: totals.sql,
2054
- params: totals.params
2055
- }, ...extras.map((extra) => ({
2056
+ const extraQueries = [...buildExtrasQueries(state, options).map((extra) => ({
2056
2057
  name: extra.key,
2057
2058
  sql: extra.sql,
2058
2059
  params: extra.params
2059
2060
  }))];
2060
2061
  const tableKey = options.adapter.inferTable(state.dimensions);
2061
2062
  if (prev) {
2063
+ const totals = buildTotalsSql(state, options);
2062
2064
  const comparison = resolveComparisonSQL(state, prev, options, params.comparisonFilter);
2065
+ extraQueries.unshift({
2066
+ name: "totals",
2067
+ sql: totals.sql,
2068
+ params: totals.params
2069
+ });
2063
2070
  extraQueries.push({
2064
2071
  name: "count",
2065
2072
  sql: comparison.countSql,
@@ -2122,7 +2129,7 @@ function shapeDataQueryRowResults(rowMap, params) {
2122
2129
  };
2123
2130
  const state = requireBuilderState(params.q, "data-query");
2124
2131
  const dims = state.dimensions;
2125
- const keyOf = (row) => dims.map((d) => String(row[d] ?? "")).join("\0");
2132
+ const keyOf = (row) => dims.map((d) => String(row[d] ?? "")).join("");
2126
2133
  const prevByKey = /* @__PURE__ */ new Map();
2127
2134
  for (const r of rowMap.prevMain ?? []) prevByKey.set(keyOf(r), r);
2128
2135
  const filter = params.comparisonFilter;
package/dist/index.mjs CHANGED
@@ -2243,24 +2243,31 @@ function coerceNumericCols(row) {
2243
2243
  function shapeDataQuery(rows, extras, opts) {
2244
2244
  let totalCount;
2245
2245
  let cleaned;
2246
+ let totals;
2246
2247
  if (opts.hasPrev) {
2247
2248
  cleaned = rows.map(coerceNumericCols);
2248
2249
  totalCount = Number((extras?.count?.[0])?.total ?? cleaned.length);
2250
+ const totalsRow = extras?.totals?.[0] ?? {};
2251
+ totals = {
2252
+ clicks: Number(totalsRow.clicks ?? 0),
2253
+ impressions: Number(totalsRow.impressions ?? 0),
2254
+ ctr: Number(totalsRow.ctr ?? 0),
2255
+ position: Number(totalsRow.position ?? 0)
2256
+ };
2249
2257
  } else {
2250
2258
  const first = rows[0];
2251
2259
  totalCount = Number(first?.totalCount ?? 0);
2260
+ totals = {
2261
+ clicks: Number(first?.totalClicks ?? 0),
2262
+ impressions: Number(first?.totalImpressions ?? 0),
2263
+ ctr: Number(first?.totalCtr ?? 0),
2264
+ position: Number(first?.totalPosition ?? 0)
2265
+ };
2252
2266
  cleaned = rows.map((raw) => {
2253
2267
  const { totalCount: _tc, totalClicks: _tclk, totalImpressions: _timp, totalCtr: _tctr, totalPosition: _tpos, sum_position: _sp, ...rest } = raw;
2254
2268
  return coerceNumericCols(rest);
2255
2269
  });
2256
2270
  }
2257
- const totalsRow = extras?.totals?.[0] ?? {};
2258
- const totals = {
2259
- clicks: Number(totalsRow.clicks ?? 0),
2260
- impressions: Number(totalsRow.impressions ?? 0),
2261
- ctr: Number(totalsRow.ctr ?? 0),
2262
- position: Number(totalsRow.position ?? 0)
2263
- };
2264
2271
  const extrasResults = [];
2265
2272
  if (extras?.canonicalExtras) extrasResults.push({
2266
2273
  key: "canonicalExtras",
@@ -2278,20 +2285,20 @@ function buildDataQueryPlan(params, options) {
2278
2285
  const state = requireBuilderState(params.q, "data-query");
2279
2286
  if (state.dimensions.includes("date")) throw new Error("data-query: date dimension not supported; use data-detail");
2280
2287
  const prev = optionalBuilderState(params.qc, "data-query", "qc");
2281
- const totals = buildTotalsSql(state, options);
2282
- const extras = buildExtrasQueries(state, options);
2283
- const extraQueries = [{
2284
- name: "totals",
2285
- sql: totals.sql,
2286
- params: totals.params
2287
- }, ...extras.map((extra) => ({
2288
+ const extraQueries = [...buildExtrasQueries(state, options).map((extra) => ({
2288
2289
  name: extra.key,
2289
2290
  sql: extra.sql,
2290
2291
  params: extra.params
2291
2292
  }))];
2292
2293
  const tableKey = options.adapter.inferTable(state.dimensions);
2293
2294
  if (prev) {
2295
+ const totals = buildTotalsSql(state, options);
2294
2296
  const comparison = resolveComparisonSQL(state, prev, options, params.comparisonFilter);
2297
+ extraQueries.unshift({
2298
+ name: "totals",
2299
+ sql: totals.sql,
2300
+ params: totals.params
2301
+ });
2295
2302
  extraQueries.push({
2296
2303
  name: "count",
2297
2304
  sql: comparison.countSql,
@@ -2354,7 +2361,7 @@ function shapeDataQueryRowResults(rowMap, params) {
2354
2361
  };
2355
2362
  const state = requireBuilderState(params.q, "data-query");
2356
2363
  const dims = state.dimensions;
2357
- const keyOf = (row) => dims.map((d) => String(row[d] ?? "")).join("\0");
2364
+ const keyOf = (row) => dims.map((d) => String(row[d] ?? "")).join("");
2358
2365
  const prevByKey = /* @__PURE__ */ new Map();
2359
2366
  for (const r of rowMap.prevMain ?? []) prevByKey.set(keyOf(r), r);
2360
2367
  const filter = params.comparisonFilter;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/analysis",
3
3
  "type": "module",
4
- "version": "0.35.12",
4
+ "version": "0.36.1",
5
5
  "description": "GSC analyzers — striking-distance, opportunity, movers, decay, brand, clustering, concentration, seasonality. Pure row-based + DuckDB-native.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -76,9 +76,9 @@
76
76
  "dependencies": {
77
77
  "drizzle-orm": "1.0.0-rc.3",
78
78
  "pluralize": "^8.0.0",
79
- "@gscdump/engine": "0.35.12",
80
- "@gscdump/engine-gsc-api": "0.35.12",
81
- "gscdump": "0.35.12"
79
+ "@gscdump/engine": "0.36.1",
80
+ "@gscdump/engine-gsc-api": "0.36.1",
81
+ "gscdump": "0.36.1"
82
82
  },
83
83
  "devDependencies": {
84
84
  "vitest": "^4.1.9"