@gscdump/analysis 0.25.4 → 0.25.6

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.
@@ -461,6 +461,13 @@ interface MoversSeriesPoint {
461
461
  }
462
462
  interface MoversResultRow extends MoverData {
463
463
  direction: 'rising' | 'declining' | 'stable';
464
+ clicks: number;
465
+ impressions: number;
466
+ ctr: number;
467
+ position: number;
468
+ prevClicks: number;
469
+ prevImpressions: number;
470
+ prevPosition: number;
464
471
  series?: MoversSeriesPoint[];
465
472
  }
466
473
  declare const moversAnalyzer: import("@gscdump/engine/analyzer").DefinedAnalyzer;
@@ -3151,6 +3151,45 @@ function parseJsonList$4(v) {
3151
3151
  }
3152
3152
  return [];
3153
3153
  }
3154
+ function withCanonicalFields(r) {
3155
+ return {
3156
+ ...r,
3157
+ clicks: r.recentClicks,
3158
+ impressions: r.recentImpressions,
3159
+ ctr: r.recentImpressions > 0 ? r.recentClicks / r.recentImpressions : 0,
3160
+ position: r.recentPosition,
3161
+ prevClicks: r.baselineClicks,
3162
+ prevImpressions: r.baselineImpressions,
3163
+ prevPosition: r.baselinePosition
3164
+ };
3165
+ }
3166
+ function selectMoversDirection(rising, declining, stable, params) {
3167
+ if (params.direction) {
3168
+ const selected = params.direction === "rising" ? rising : declining;
3169
+ const total = selected.length;
3170
+ const offset = params.offset ?? 0;
3171
+ const limit = params.limit ?? total;
3172
+ return {
3173
+ results: selected.slice(offset, offset + limit),
3174
+ meta: {
3175
+ total,
3176
+ rising: rising.length,
3177
+ declining: declining.length,
3178
+ stable: stable.length
3179
+ }
3180
+ };
3181
+ }
3182
+ const combined = [...rising, ...declining];
3183
+ return {
3184
+ results: combined,
3185
+ meta: {
3186
+ total: combined.length,
3187
+ rising: rising.length,
3188
+ declining: declining.length,
3189
+ stable: stable.length
3190
+ }
3191
+ };
3192
+ }
3154
3193
  function analyzeMovers(input, options = {}) {
3155
3194
  const { changeThreshold = .2, minImpressions = 50, sortBy = "clicksChange" } = options;
3156
3195
  const normFactor = input.normalizationFactor ?? 1;
@@ -3223,7 +3262,7 @@ const moversAnalyzer = defineAnalyzer({
3223
3262
  const { current: cur, previous: prev } = comparisonOf(params);
3224
3263
  const minImpressions = params.minImpressions ?? 50;
3225
3264
  const changeThreshold = params.changeThreshold ?? .2;
3226
- const limit = params.limit ?? 2e3;
3265
+ const limit = params.direction ? 5e3 : params.limit ?? 2e3;
3227
3266
  return {
3228
3267
  sql: `
3229
3268
  WITH cur AS (
@@ -3328,40 +3367,36 @@ const moversAnalyzer = defineAnalyzer({
3328
3367
  }
3329
3368
  };
3330
3369
  },
3331
- reduceSql(rows) {
3332
- const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
3333
- keyword: str$7(r.keyword),
3334
- page: r.page == null ? null : str$7(r.page),
3335
- recentClicks: num(r.recentClicks),
3336
- recentImpressions: num(r.recentImpressions),
3337
- recentPosition: num(r.recentPosition),
3338
- baselineClicks: Math.round(num(r.baselineClicks)),
3339
- baselineImpressions: Math.round(num(r.baselineImpressions)),
3340
- baselinePosition: num(r.baselinePosition),
3341
- clicksChange: num(r.clicksChange),
3342
- clicksChangePercent: num(r.clicksChangePercent),
3343
- impressionsChangePercent: num(r.impressionsChangePercent),
3344
- positionChange: num(r.positionChange),
3345
- direction: str$7(r.direction),
3346
- series: parseJsonList$4(r.seriesJson).map((s) => ({
3347
- week: str$7(s.week),
3348
- clicks: num(s.clicks),
3349
- impressions: num(s.impressions)
3350
- }))
3351
- }));
3352
- const rising = normalized.filter((r) => r.direction === "rising");
3353
- const declining = normalized.filter((r) => r.direction === "declining");
3354
- const stable = normalized.filter((r) => r.direction === "stable");
3355
- const combined = [...rising, ...declining];
3356
- return {
3357
- results: combined,
3358
- meta: {
3359
- total: combined.length,
3360
- rising: rising.length,
3361
- declining: declining.length,
3362
- stable: stable.length
3363
- }
3364
- };
3370
+ reduceSql(rows, params) {
3371
+ const normalized = (Array.isArray(rows) ? rows : []).map((r) => {
3372
+ const recentClicks = num(r.recentClicks);
3373
+ const recentImpressions = num(r.recentImpressions);
3374
+ const recentPosition = num(r.recentPosition);
3375
+ const baselineClicks = Math.round(num(r.baselineClicks));
3376
+ const baselineImpressions = Math.round(num(r.baselineImpressions));
3377
+ const baselinePosition = num(r.baselinePosition);
3378
+ return {
3379
+ keyword: str$7(r.keyword),
3380
+ page: r.page == null ? null : str$7(r.page),
3381
+ recentClicks,
3382
+ recentImpressions,
3383
+ recentPosition,
3384
+ baselineClicks,
3385
+ baselineImpressions,
3386
+ baselinePosition,
3387
+ clicksChange: num(r.clicksChange),
3388
+ clicksChangePercent: num(r.clicksChangePercent),
3389
+ impressionsChangePercent: num(r.impressionsChangePercent),
3390
+ positionChange: num(r.positionChange),
3391
+ direction: str$7(r.direction),
3392
+ series: parseJsonList$4(r.seriesJson).map((s) => ({
3393
+ week: str$7(s.week),
3394
+ clicks: num(s.clicks),
3395
+ impressions: num(s.impressions)
3396
+ }))
3397
+ };
3398
+ }).map(withCanonicalFields);
3399
+ return selectMoversDirection(normalized.filter((r) => r.direction === "rising"), normalized.filter((r) => r.direction === "declining"), normalized.filter((r) => r.direction === "stable"), params);
3365
3400
  },
3366
3401
  buildRows(params) {
3367
3402
  const { current, previous } = comparisonOf(params);
@@ -3382,19 +3417,13 @@ const moversAnalyzer = defineAnalyzer({
3382
3417
  changeThreshold: params.changeThreshold,
3383
3418
  minImpressions: params.minImpressions
3384
3419
  });
3385
- return {
3386
- results: [...result.rising.map((r) => ({
3387
- ...r,
3388
- direction: "rising"
3389
- })), ...result.declining.map((r) => ({
3390
- ...r,
3391
- direction: "declining"
3392
- }))],
3393
- meta: {
3394
- rising: result.rising.length,
3395
- declining: result.declining.length
3396
- }
3397
- };
3420
+ return selectMoversDirection(result.rising.map((r) => withCanonicalFields({
3421
+ ...r,
3422
+ direction: "rising"
3423
+ })), result.declining.map((r) => withCanonicalFields({
3424
+ ...r,
3425
+ direction: "declining"
3426
+ })), [], params);
3398
3427
  }
3399
3428
  });
3400
3429
  const DEFAULT_LIMIT = 1e3;
@@ -3151,6 +3151,45 @@ function parseJsonList$4(v) {
3151
3151
  }
3152
3152
  return [];
3153
3153
  }
3154
+ function withCanonicalFields(r) {
3155
+ return {
3156
+ ...r,
3157
+ clicks: r.recentClicks,
3158
+ impressions: r.recentImpressions,
3159
+ ctr: r.recentImpressions > 0 ? r.recentClicks / r.recentImpressions : 0,
3160
+ position: r.recentPosition,
3161
+ prevClicks: r.baselineClicks,
3162
+ prevImpressions: r.baselineImpressions,
3163
+ prevPosition: r.baselinePosition
3164
+ };
3165
+ }
3166
+ function selectMoversDirection(rising, declining, stable, params) {
3167
+ if (params.direction) {
3168
+ const selected = params.direction === "rising" ? rising : declining;
3169
+ const total = selected.length;
3170
+ const offset = params.offset ?? 0;
3171
+ const limit = params.limit ?? total;
3172
+ return {
3173
+ results: selected.slice(offset, offset + limit),
3174
+ meta: {
3175
+ total,
3176
+ rising: rising.length,
3177
+ declining: declining.length,
3178
+ stable: stable.length
3179
+ }
3180
+ };
3181
+ }
3182
+ const combined = [...rising, ...declining];
3183
+ return {
3184
+ results: combined,
3185
+ meta: {
3186
+ total: combined.length,
3187
+ rising: rising.length,
3188
+ declining: declining.length,
3189
+ stable: stable.length
3190
+ }
3191
+ };
3192
+ }
3154
3193
  function analyzeMovers(input, options = {}) {
3155
3194
  const { changeThreshold = .2, minImpressions = 50, sortBy = "clicksChange" } = options;
3156
3195
  const normFactor = input.normalizationFactor ?? 1;
@@ -3223,7 +3262,7 @@ const moversAnalyzer = defineAnalyzer({
3223
3262
  const { current: cur, previous: prev } = comparisonOf(params);
3224
3263
  const minImpressions = params.minImpressions ?? 50;
3225
3264
  const changeThreshold = params.changeThreshold ?? .2;
3226
- const limit = params.limit ?? 2e3;
3265
+ const limit = params.direction ? 5e3 : params.limit ?? 2e3;
3227
3266
  return {
3228
3267
  sql: `
3229
3268
  WITH cur AS (
@@ -3328,40 +3367,36 @@ const moversAnalyzer = defineAnalyzer({
3328
3367
  }
3329
3368
  };
3330
3369
  },
3331
- reduceSql(rows) {
3332
- const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
3333
- keyword: str$7(r.keyword),
3334
- page: r.page == null ? null : str$7(r.page),
3335
- recentClicks: num(r.recentClicks),
3336
- recentImpressions: num(r.recentImpressions),
3337
- recentPosition: num(r.recentPosition),
3338
- baselineClicks: Math.round(num(r.baselineClicks)),
3339
- baselineImpressions: Math.round(num(r.baselineImpressions)),
3340
- baselinePosition: num(r.baselinePosition),
3341
- clicksChange: num(r.clicksChange),
3342
- clicksChangePercent: num(r.clicksChangePercent),
3343
- impressionsChangePercent: num(r.impressionsChangePercent),
3344
- positionChange: num(r.positionChange),
3345
- direction: str$7(r.direction),
3346
- series: parseJsonList$4(r.seriesJson).map((s) => ({
3347
- week: str$7(s.week),
3348
- clicks: num(s.clicks),
3349
- impressions: num(s.impressions)
3350
- }))
3351
- }));
3352
- const rising = normalized.filter((r) => r.direction === "rising");
3353
- const declining = normalized.filter((r) => r.direction === "declining");
3354
- const stable = normalized.filter((r) => r.direction === "stable");
3355
- const combined = [...rising, ...declining];
3356
- return {
3357
- results: combined,
3358
- meta: {
3359
- total: combined.length,
3360
- rising: rising.length,
3361
- declining: declining.length,
3362
- stable: stable.length
3363
- }
3364
- };
3370
+ reduceSql(rows, params) {
3371
+ const normalized = (Array.isArray(rows) ? rows : []).map((r) => {
3372
+ const recentClicks = num(r.recentClicks);
3373
+ const recentImpressions = num(r.recentImpressions);
3374
+ const recentPosition = num(r.recentPosition);
3375
+ const baselineClicks = Math.round(num(r.baselineClicks));
3376
+ const baselineImpressions = Math.round(num(r.baselineImpressions));
3377
+ const baselinePosition = num(r.baselinePosition);
3378
+ return {
3379
+ keyword: str$7(r.keyword),
3380
+ page: r.page == null ? null : str$7(r.page),
3381
+ recentClicks,
3382
+ recentImpressions,
3383
+ recentPosition,
3384
+ baselineClicks,
3385
+ baselineImpressions,
3386
+ baselinePosition,
3387
+ clicksChange: num(r.clicksChange),
3388
+ clicksChangePercent: num(r.clicksChangePercent),
3389
+ impressionsChangePercent: num(r.impressionsChangePercent),
3390
+ positionChange: num(r.positionChange),
3391
+ direction: str$7(r.direction),
3392
+ series: parseJsonList$4(r.seriesJson).map((s) => ({
3393
+ week: str$7(s.week),
3394
+ clicks: num(s.clicks),
3395
+ impressions: num(s.impressions)
3396
+ }))
3397
+ };
3398
+ }).map(withCanonicalFields);
3399
+ return selectMoversDirection(normalized.filter((r) => r.direction === "rising"), normalized.filter((r) => r.direction === "declining"), normalized.filter((r) => r.direction === "stable"), params);
3365
3400
  },
3366
3401
  buildRows(params) {
3367
3402
  const { current, previous } = comparisonOf(params);
@@ -3382,19 +3417,13 @@ const moversAnalyzer = defineAnalyzer({
3382
3417
  changeThreshold: params.changeThreshold,
3383
3418
  minImpressions: params.minImpressions
3384
3419
  });
3385
- return {
3386
- results: [...result.rising.map((r) => ({
3387
- ...r,
3388
- direction: "rising"
3389
- })), ...result.declining.map((r) => ({
3390
- ...r,
3391
- direction: "declining"
3392
- }))],
3393
- meta: {
3394
- rising: result.rising.length,
3395
- declining: result.declining.length
3396
- }
3397
- };
3420
+ return selectMoversDirection(result.rising.map((r) => withCanonicalFields({
3421
+ ...r,
3422
+ direction: "rising"
3423
+ })), result.declining.map((r) => withCanonicalFields({
3424
+ ...r,
3425
+ direction: "declining"
3426
+ })), [], params);
3398
3427
  }
3399
3428
  });
3400
3429
  const DEFAULT_LIMIT = 1e3;
package/dist/index.mjs CHANGED
@@ -3513,6 +3513,45 @@ function parseJsonList$4(v) {
3513
3513
  }
3514
3514
  return [];
3515
3515
  }
3516
+ function withCanonicalFields(r) {
3517
+ return {
3518
+ ...r,
3519
+ clicks: r.recentClicks,
3520
+ impressions: r.recentImpressions,
3521
+ ctr: r.recentImpressions > 0 ? r.recentClicks / r.recentImpressions : 0,
3522
+ position: r.recentPosition,
3523
+ prevClicks: r.baselineClicks,
3524
+ prevImpressions: r.baselineImpressions,
3525
+ prevPosition: r.baselinePosition
3526
+ };
3527
+ }
3528
+ function selectMoversDirection(rising, declining, stable, params) {
3529
+ if (params.direction) {
3530
+ const selected = params.direction === "rising" ? rising : declining;
3531
+ const total = selected.length;
3532
+ const offset = params.offset ?? 0;
3533
+ const limit = params.limit ?? total;
3534
+ return {
3535
+ results: selected.slice(offset, offset + limit),
3536
+ meta: {
3537
+ total,
3538
+ rising: rising.length,
3539
+ declining: declining.length,
3540
+ stable: stable.length
3541
+ }
3542
+ };
3543
+ }
3544
+ const combined = [...rising, ...declining];
3545
+ return {
3546
+ results: combined,
3547
+ meta: {
3548
+ total: combined.length,
3549
+ rising: rising.length,
3550
+ declining: declining.length,
3551
+ stable: stable.length
3552
+ }
3553
+ };
3554
+ }
3516
3555
  function analyzeMovers(input, options = {}) {
3517
3556
  const { changeThreshold = .2, minImpressions = 50, sortBy = "clicksChange" } = options;
3518
3557
  const normFactor = input.normalizationFactor ?? 1;
@@ -3585,7 +3624,7 @@ const moversAnalyzer = defineAnalyzer$1({
3585
3624
  const { current: cur, previous: prev } = comparisonOf$1(params);
3586
3625
  const minImpressions = params.minImpressions ?? 50;
3587
3626
  const changeThreshold = params.changeThreshold ?? .2;
3588
- const limit = params.limit ?? 2e3;
3627
+ const limit = params.direction ? 5e3 : params.limit ?? 2e3;
3589
3628
  return {
3590
3629
  sql: `
3591
3630
  WITH cur AS (
@@ -3690,40 +3729,36 @@ const moversAnalyzer = defineAnalyzer$1({
3690
3729
  }
3691
3730
  };
3692
3731
  },
3693
- reduceSql(rows) {
3694
- const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
3695
- keyword: str$7(r.keyword),
3696
- page: r.page == null ? null : str$7(r.page),
3697
- recentClicks: num$1(r.recentClicks),
3698
- recentImpressions: num$1(r.recentImpressions),
3699
- recentPosition: num$1(r.recentPosition),
3700
- baselineClicks: Math.round(num$1(r.baselineClicks)),
3701
- baselineImpressions: Math.round(num$1(r.baselineImpressions)),
3702
- baselinePosition: num$1(r.baselinePosition),
3703
- clicksChange: num$1(r.clicksChange),
3704
- clicksChangePercent: num$1(r.clicksChangePercent),
3705
- impressionsChangePercent: num$1(r.impressionsChangePercent),
3706
- positionChange: num$1(r.positionChange),
3707
- direction: str$7(r.direction),
3708
- series: parseJsonList$4(r.seriesJson).map((s) => ({
3709
- week: str$7(s.week),
3710
- clicks: num$1(s.clicks),
3711
- impressions: num$1(s.impressions)
3712
- }))
3713
- }));
3714
- const rising = normalized.filter((r) => r.direction === "rising");
3715
- const declining = normalized.filter((r) => r.direction === "declining");
3716
- const stable = normalized.filter((r) => r.direction === "stable");
3717
- const combined = [...rising, ...declining];
3718
- return {
3719
- results: combined,
3720
- meta: {
3721
- total: combined.length,
3722
- rising: rising.length,
3723
- declining: declining.length,
3724
- stable: stable.length
3725
- }
3726
- };
3732
+ reduceSql(rows, params) {
3733
+ const normalized = (Array.isArray(rows) ? rows : []).map((r) => {
3734
+ const recentClicks = num$1(r.recentClicks);
3735
+ const recentImpressions = num$1(r.recentImpressions);
3736
+ const recentPosition = num$1(r.recentPosition);
3737
+ const baselineClicks = Math.round(num$1(r.baselineClicks));
3738
+ const baselineImpressions = Math.round(num$1(r.baselineImpressions));
3739
+ const baselinePosition = num$1(r.baselinePosition);
3740
+ return {
3741
+ keyword: str$7(r.keyword),
3742
+ page: r.page == null ? null : str$7(r.page),
3743
+ recentClicks,
3744
+ recentImpressions,
3745
+ recentPosition,
3746
+ baselineClicks,
3747
+ baselineImpressions,
3748
+ baselinePosition,
3749
+ clicksChange: num$1(r.clicksChange),
3750
+ clicksChangePercent: num$1(r.clicksChangePercent),
3751
+ impressionsChangePercent: num$1(r.impressionsChangePercent),
3752
+ positionChange: num$1(r.positionChange),
3753
+ direction: str$7(r.direction),
3754
+ series: parseJsonList$4(r.seriesJson).map((s) => ({
3755
+ week: str$7(s.week),
3756
+ clicks: num$1(s.clicks),
3757
+ impressions: num$1(s.impressions)
3758
+ }))
3759
+ };
3760
+ }).map(withCanonicalFields);
3761
+ return selectMoversDirection(normalized.filter((r) => r.direction === "rising"), normalized.filter((r) => r.direction === "declining"), normalized.filter((r) => r.direction === "stable"), params);
3727
3762
  },
3728
3763
  buildRows(params) {
3729
3764
  const { current, previous } = comparisonOf$1(params);
@@ -3744,19 +3779,13 @@ const moversAnalyzer = defineAnalyzer$1({
3744
3779
  changeThreshold: params.changeThreshold,
3745
3780
  minImpressions: params.minImpressions
3746
3781
  });
3747
- return {
3748
- results: [...result.rising.map((r) => ({
3749
- ...r,
3750
- direction: "rising"
3751
- })), ...result.declining.map((r) => ({
3752
- ...r,
3753
- direction: "declining"
3754
- }))],
3755
- meta: {
3756
- rising: result.rising.length,
3757
- declining: result.declining.length
3758
- }
3759
- };
3782
+ return selectMoversDirection(result.rising.map((r) => withCanonicalFields({
3783
+ ...r,
3784
+ direction: "rising"
3785
+ })), result.declining.map((r) => withCanonicalFields({
3786
+ ...r,
3787
+ direction: "declining"
3788
+ })), [], params);
3760
3789
  }
3761
3790
  });
3762
3791
  const DEFAULT_LIMIT$1 = 1e3;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/analysis",
3
3
  "type": "module",
4
- "version": "0.25.4",
4
+ "version": "0.25.6",
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",
@@ -70,9 +70,9 @@
70
70
  },
71
71
  "dependencies": {
72
72
  "drizzle-orm": "1.0.0-rc.3",
73
- "@gscdump/engine": "0.25.4",
74
- "gscdump": "0.25.4",
75
- "@gscdump/engine-gsc-api": "0.25.4"
73
+ "@gscdump/engine": "0.25.6",
74
+ "@gscdump/engine-gsc-api": "0.25.6",
75
+ "gscdump": "0.25.6"
76
76
  },
77
77
  "devDependencies": {
78
78
  "vitest": "^4.1.7"