@gscdump/analysis 1.4.0 → 1.4.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.
Files changed (99) hide show
  1. package/dist/action-priority.d.mts +34 -0
  2. package/dist/action-priority.mjs +201 -0
  3. package/dist/analyzer/adapt-rows.mjs +12 -0
  4. package/dist/analyzer/all.mjs +61 -0
  5. package/dist/analyzer/paginate.mjs +85 -0
  6. package/dist/analyzer/row-analyzers.d.mts +3 -0
  7. package/dist/analyzer/row-analyzers.mjs +3 -0
  8. package/dist/analyzer/row-values.mjs +22 -0
  9. package/dist/analyzers/bayesian-ctr.mjs +177 -0
  10. package/dist/analyzers/bipartite-pagerank.mjs +243 -0
  11. package/dist/analyzers/brand.d.mts +33 -0
  12. package/dist/analyzers/brand.mjs +154 -0
  13. package/dist/analyzers/cannibalization.d.mts +58 -0
  14. package/dist/analyzers/cannibalization.mjs +267 -0
  15. package/dist/analyzers/change-point.mjs +197 -0
  16. package/dist/analyzers/clustering.d.mts +29 -0
  17. package/dist/analyzers/clustering.mjs +217 -0
  18. package/dist/analyzers/concentration.d.mts +42 -0
  19. package/dist/analyzers/concentration.mjs +180 -0
  20. package/dist/analyzers/content-velocity.mjs +87 -0
  21. package/dist/analyzers/ctr-anomaly.mjs +189 -0
  22. package/dist/analyzers/ctr-curve.mjs +128 -0
  23. package/dist/analyzers/dark-traffic.mjs +130 -0
  24. package/dist/analyzers/data-detail.mjs +44 -0
  25. package/dist/analyzers/data-query.mjs +44 -0
  26. package/dist/analyzers/decay.d.mts +38 -0
  27. package/dist/analyzers/decay.mjs +187 -0
  28. package/dist/analyzers/device-gap.mjs +135 -0
  29. package/dist/analyzers/intent-atlas.mjs +175 -0
  30. package/dist/analyzers/keyword-breadth.mjs +115 -0
  31. package/dist/analyzers/long-tail.mjs +150 -0
  32. package/dist/analyzers/movers.d.mts +43 -0
  33. package/dist/analyzers/movers.mjs +283 -0
  34. package/dist/analyzers/opportunity.d.mts +32 -0
  35. package/dist/analyzers/opportunity.mjs +212 -0
  36. package/dist/analyzers/position-distribution.mjs +57 -0
  37. package/dist/analyzers/position-volatility.mjs +143 -0
  38. package/dist/analyzers/query-migration.mjs +178 -0
  39. package/dist/analyzers/seasonality.d.mts +28 -0
  40. package/dist/analyzers/seasonality.mjs +136 -0
  41. package/dist/analyzers/stl-decompose.mjs +187 -0
  42. package/dist/analyzers/striking-distance.d.mts +54 -0
  43. package/dist/analyzers/striking-distance.mjs +82 -0
  44. package/dist/analyzers/survival.mjs +200 -0
  45. package/dist/analyzers/trends.mjs +156 -0
  46. package/dist/analyzers/zero-click.d.mts +17 -0
  47. package/dist/analyzers/zero-click.mjs +122 -0
  48. package/dist/browser.d.mts +5 -0
  49. package/dist/browser.mjs +11 -0
  50. package/dist/default-registry.d.mts +3 -1
  51. package/dist/default-registry.mjs +5 -1
  52. package/dist/errors.d.mts +51 -1
  53. package/dist/index.d.mts +30 -494
  54. package/dist/index.mjs +28 -49
  55. package/dist/query/analyzers.mjs +301 -0
  56. package/dist/query/index.mjs +4 -0
  57. package/dist/query/intent.d.mts +20 -0
  58. package/dist/query/intent.mjs +139 -0
  59. package/dist/query/normalize.d.mts +15 -0
  60. package/dist/query/normalize.mjs +204 -0
  61. package/dist/report/format.d.mts +7 -0
  62. package/dist/report/format.mjs +45 -0
  63. package/dist/report/index.d.mts +4 -1
  64. package/dist/report/index.mjs +4 -1
  65. package/dist/report/registry.d.mts +4 -0
  66. package/dist/report/registry.mjs +26 -0
  67. package/dist/report/reports/brand.mjs +104 -0
  68. package/dist/report/reports/growth.mjs +144 -0
  69. package/dist/report/reports/health.mjs +141 -0
  70. package/dist/report/reports/movers.mjs +203 -0
  71. package/dist/report/reports/opportunities.mjs +184 -0
  72. package/dist/report/reports/pre-publish.mjs +111 -0
  73. package/dist/report/reports/priority.mjs +97 -0
  74. package/dist/report/reports/risks.mjs +201 -0
  75. package/dist/report/reports/triage.mjs +155 -0
  76. package/dist/report/require.mjs +15 -0
  77. package/dist/report/resolve-target.d.mts +29 -0
  78. package/dist/report/resolve-target.mjs +32 -0
  79. package/dist/{_chunks/index.d.mts → report/runtime.d.mts} +2 -37
  80. package/dist/report/runtime.mjs +80 -0
  81. package/dist/report/sections.mjs +22 -0
  82. package/dist/sitemap-health.d.mts +34 -0
  83. package/dist/sitemap-health.mjs +37 -0
  84. package/dist/{_chunks/index2.d.mts → source/composite.d.mts} +2 -13
  85. package/dist/{_chunks/source.mjs → source/composite.mjs} +1 -17
  86. package/dist/source/in-memory.d.mts +14 -0
  87. package/dist/source/in-memory.mjs +17 -0
  88. package/dist/source/index.d.mts +2 -1
  89. package/dist/source/index.mjs +2 -1
  90. package/dist/sql-analyzers.d.mts +3 -0
  91. package/dist/sql-analyzers.mjs +3 -0
  92. package/dist/types.d.mts +34 -0
  93. package/dist/types.mjs +21 -0
  94. package/package.json +4 -4
  95. package/dist/_chunks/default-registry.d.mts +0 -5
  96. package/dist/_chunks/default-registry.mjs +0 -5183
  97. package/dist/_chunks/errors.d.mts +0 -52
  98. package/dist/_chunks/report.mjs +0 -1710
  99. /package/dist/{_chunks/scoring.mjs → scoring.mjs} +0 -0
package/dist/errors.d.mts CHANGED
@@ -1,2 +1,52 @@
1
- import { AnalysisError, AnalysisErrorKind, analysisErrorToException, analysisErrors, formatAnalysisError, isAnalysisError } from "./_chunks/errors.mjs";
1
+ type AnalysisErrorKind = 'missing-report-param' | 'missing-comparison-window' | 'missing-brand-terms' | 'unknown-report' | 'unknown-analyzer' | 'required-step-failed';
2
+ type AnalysisError = {
3
+ kind: 'missing-report-param';
4
+ report: string;
5
+ param: string;
6
+ message: string;
7
+ } | {
8
+ kind: 'missing-comparison-window';
9
+ report: string;
10
+ message: string;
11
+ } | {
12
+ kind: 'missing-brand-terms';
13
+ message: string;
14
+ } | {
15
+ kind: 'unknown-report';
16
+ report: string;
17
+ available: readonly string[];
18
+ message: string;
19
+ } | {
20
+ kind: 'unknown-analyzer';
21
+ analyzer: string;
22
+ message: string;
23
+ } | {
24
+ kind: 'required-step-failed';
25
+ report: string;
26
+ stepKey: string;
27
+ stepError: string;
28
+ message: string;
29
+ cause?: unknown;
30
+ };
31
+ declare const analysisErrors: {
32
+ readonly missingReportParam: (report: string, param: string, message: string) => AnalysisError;
33
+ readonly missingComparisonWindow: (report: string, message: string) => AnalysisError;
34
+ readonly missingBrandTerms: () => AnalysisError;
35
+ readonly unknownReport: (report: string, available: readonly string[]) => AnalysisError;
36
+ readonly unknownAnalyzer: (analyzer: string) => AnalysisError;
37
+ readonly requiredStepFailed: (report: string, stepKey: string, stepError: string, cause?: unknown) => AnalysisError;
38
+ };
39
+ declare function isAnalysisError(value: unknown): value is AnalysisError;
40
+ /** The human-readable rendering of an `AnalysisError`, for logs and string sinks. */
41
+ declare function formatAnalysisError(error: AnalysisError): string;
42
+ /**
43
+ * Re-raises an `AnalysisError` value as a generic `Error`, stashing the union
44
+ * under `.analysisError` for stack-walking and preserving the original `cause`
45
+ * (so a `required-step-failed` keeps the underlying thrown value reachable).
46
+ * Used by the throwing wrappers over the `Result`-returning cores. The thrown
47
+ * `.message` is kept verbatim from the union, so existing message-regex
48
+ * assertions (`/--target/`, `/comparison window/`, `/required step "k"/`)
49
+ * continue to match.
50
+ */
51
+ declare function analysisErrorToException(error: AnalysisError): Error;
2
52
  export { AnalysisError, AnalysisErrorKind, analysisErrorToException, analysisErrors, formatAnalysisError, isAnalysisError };
package/dist/index.d.mts CHANGED
@@ -1,497 +1,33 @@
1
- import { ROW_ANALYZERS, SQL_ANALYZERS, defaultAnalyzerRegistry } from "./_chunks/default-registry.mjs";
2
- import { AnalysisError, AnalysisErrorKind, analysisErrorToException, analysisErrors, formatAnalysisError, isAnalysisError } from "./_chunks/errors.mjs";
3
- import { DryRunReportResult, FormatReportOptions, REPORTS, RunReportOptions, defaultReportRegistry, dryRunReport, formatReport, runReport, runReportResult } from "./_chunks/index.mjs";
4
- import { CompositeSourceOptions, IN_MEMORY_DEFAULT_CAPABILITIES, InMemoryQuerySourceOptions, createCompositeSource, createInMemoryQuerySource } from "./_chunks/index2.mjs";
5
- import { Analyzer, AnalyzerCapabilityError, AnalyzerRegistry, AnalyzerRegistry as AnalyzerRegistry$1, AnalyzerRegistryInit, AnalyzerVariants, DefineAnalyzerOptions, DefinedAnalyzer, Plan, ReduceContext, ReduceCtx, Reducer, RequiredCapability, RowQueriesPlan, SqlExtraQuery, SqlPlan, SqlPlanSpec, TypedRowQuery, createAnalyzerRegistry, defineAnalyzer, runAnalyzerFromSource } from "@gscdump/engine/analyzer";
1
+ import { ROW_ANALYZERS } from "./analyzer/row-analyzers.mjs";
2
+ import { SQL_ANALYZERS } from "./sql-analyzers.mjs";
3
+ import { defaultAnalyzerRegistry } from "./default-registry.mjs";
4
+ import { AnalysisError, AnalysisErrorKind, analysisErrorToException, analysisErrors, formatAnalysisError, isAnalysisError } from "./errors.mjs";
5
+ import { ActionPriorityResult, ActionPrioritySourceState, ActionPrioritySourceStatus, ActionSource, DEFAULT_PRIORITY_SOURCES, Effort, PriorityAction, mergePriorityActions, normalizePriorityActions, scorePriorityActions } from "./action-priority.mjs";
6
+ import { BaseMetrics, DateRow, PageRow, QueriesRow, QueryPageRow, SortOrder, createSorter } from "./types.mjs";
7
+ import { BrandSegmentationOptions, BrandSegmentationResult, BrandSegmentationRow, BrandSummary, analyzeBrandSegmentation } from "./analyzers/brand.mjs";
8
+ import { CannibalizationCompetitor, CannibalizationEvent, CannibalizationOptions, CannibalizationPage, CannibalizationResult, CannibalizationSortMetric, analyzeCannibalization } from "./analyzers/cannibalization.mjs";
9
+ import { ClusterType, ClusteringOptions, ClusteringResult, KeywordCluster, analyzeClustering } from "./analyzers/clustering.mjs";
10
+ import { ConcentrationInput, ConcentrationItem, ConcentrationOptions, ConcentrationResult, ConcentrationRiskLevel, analyzeConcentration, analyzeKeywordConcentration, analyzePageConcentration } from "./analyzers/concentration.mjs";
11
+ import { DecayInput, DecayOptions, DecayResult, DecaySeriesPoint, DecaySortMetric, analyzeDecay } from "./analyzers/decay.mjs";
12
+ import { MoverData, MoversInput, MoversOptions, MoversResult, MoversSortMetric, analyzeMovers } from "./analyzers/movers.mjs";
13
+ import { OpportunityFactors, OpportunityOptions, OpportunityResult, OpportunitySortMetric, OpportunityWeights, analyzeOpportunity } from "./analyzers/opportunity.mjs";
14
+ import { MonthlyData, SeasonalityMetric, SeasonalityOptions, SeasonalityResult, analyzeSeasonality } from "./analyzers/seasonality.mjs";
15
+ import { StrikingDistanceInputRow, StrikingDistanceOptions, StrikingDistanceResult, analyzeStrikingDistance } from "./analyzers/striking-distance.mjs";
16
+ import { ZeroClickOptions, ZeroClickResult, analyzeZeroClick } from "./analyzers/zero-click.mjs";
17
+ import { AnalyzerRunner, BrowserAnalyzeOptions, analyzeInBrowser } from "./browser.mjs";
18
+ import { INTENT_CLASSIFIER_VERSION, IntentClassification, SEARCH_INTENT_CODE, SearchIntent, classifyQueryIntent, encodeIntent } from "./query/intent.mjs";
19
+ import { NORMALIZER_VERSION, normalizeQuery } from "./query/normalize.mjs";
20
+ import { FormatReportOptions, formatReport } from "./report/format.mjs";
21
+ import { REPORTS, defaultReportRegistry } from "./report/registry.mjs";
22
+ import { DryRunReportResult, RunReportOptions, dryRunReport, runReport, runReportResult } from "./report/runtime.mjs";
23
+ import "./report/index.mjs";
24
+ import { SitemapDelta, SitemapHealthDiff, SitemapHealthInput, SitemapHealthRow, SitemapHealthTotals, diffSitemapHealth } from "./sitemap-health.mjs";
25
+ import { CompositeSourceOptions, createCompositeSource } from "./source/composite.mjs";
26
+ import { IN_MEMORY_DEFAULT_CAPABILITIES, InMemoryQuerySourceOptions, createInMemoryQuerySource } from "./source/in-memory.mjs";
27
+ import "./source/index.mjs";
28
+ import { Analyzer, AnalyzerCapabilityError, AnalyzerRegistry, AnalyzerRegistryInit, AnalyzerVariants, DefineAnalyzerOptions, DefinedAnalyzer, Plan, ReduceContext, ReduceCtx, Reducer, RequiredCapability, RowQueriesPlan, SqlExtraQuery, SqlPlan, SqlPlanSpec, TypedRowQuery, createAnalyzerRegistry, defineAnalyzer, runAnalyzerFromSource } from "@gscdump/engine/analyzer";
6
29
  import { AnalysisPeriod, ComparisonMode, ComparisonPeriod, PadTimeseriesOptions, ResolveWindowOptions, ResolvedWindow, WindowPreset, comparisonOf, padTimeseries, periodOf, resolveWindow } from "@gscdump/engine/period";
7
- import { AnalysisParams, AnalysisParams as AnalysisParams$1, AnalysisResult, AnalysisResult as AnalysisResult$1, AnalysisTool, num } from "@gscdump/engine/analysis-types";
8
- import { AnalysisQuerySource, AnalysisSourceKind, AttachedTableRunner, AttachedTableRunner as AnalyzerRunner, AttachedTableSourceOptions, AttachedTableSourceOptions as BrowserAnalyzeOptions, ENGINE_QUERY_CAPABILITIES, EngineQuerySourceOptions, ExecuteSqlOptions, FileSet, QueryRow, SourceCapabilities, createEngineQuerySource, queryRows, runAnalyzerWithEngine } from "@gscdump/engine/source";
30
+ import { AnalysisParams, AnalysisResult, AnalysisTool, num } from "@gscdump/engine/analysis-types";
31
+ import { AnalysisQuerySource, AnalysisSourceKind, ENGINE_QUERY_CAPABILITIES, EngineQuerySourceOptions, ExecuteSqlOptions, FileSet, QueryRow, SourceCapabilities, createEngineQuerySource, queryRows, runAnalyzerWithEngine } from "@gscdump/engine/source";
9
32
  import { DefineReportOptions, DefinedReport, ReportAction, ReportContext, ReportFinding, ReportPlanStep, ReportResult, ReportSection } from "@gscdump/engine/report";
10
- type ActionSource = 'cannibalization' | 'striking-distance' | 'ctr-anomaly' | 'change-point' | 'opportunity';
11
- type Effort = 'low' | 'medium' | 'high';
12
- interface PriorityAction {
13
- id: string;
14
- title: string;
15
- keyword: string;
16
- page: string;
17
- sources: ActionSource[];
18
- severity: number;
19
- impressions: number;
20
- impact: number;
21
- why: string;
22
- effort: Effort;
23
- priorityScore: number;
24
- data: Partial<Record<ActionSource, Record<string, unknown>>>;
25
- }
26
- type ActionPrioritySourceStatus = 'pending' | 'running' | 'done' | 'skipped' | 'error';
27
- interface ActionPrioritySourceState {
28
- source: ActionSource;
29
- status: ActionPrioritySourceStatus;
30
- count: number;
31
- error?: string;
32
- }
33
- interface ActionPriorityResult {
34
- actions: PriorityAction[];
35
- totalSignals: number;
36
- sources: ActionPrioritySourceState[];
37
- }
38
- declare const DEFAULT_PRIORITY_SOURCES: ActionSource[];
39
- declare function normalizePriorityActions(source: ActionSource, result: AnalysisResult$1): PriorityAction[];
40
- declare function mergePriorityActions(all: PriorityAction[]): PriorityAction[];
41
- declare function scorePriorityActions(actions: PriorityAction[]): PriorityAction[];
42
- /**
43
- * Domain row shapes + analysis utilities. Analyzer-call contracts
44
- * (`AnalysisParams`, `AnalysisResult`, `AnalysisTool`, `num`) live in
45
- * `@gscdump/engine/analysis-types`.
46
- */
47
- type SortOrder = 'asc' | 'desc';
48
- /** Base search metrics */
49
- interface BaseMetrics {
50
- clicks: number;
51
- impressions: number;
52
- ctr: number;
53
- position: number;
54
- }
55
- /** Keyword row from query */
56
- interface QueriesRow extends BaseMetrics {
57
- query: string;
58
- page?: string;
59
- }
60
- /** Page row from query */
61
- interface PageRow extends BaseMetrics {
62
- page: string;
63
- }
64
- /** Row with both query and page dimensions, both required */
65
- interface QueryPageRow extends BaseMetrics {
66
- query: string;
67
- page: string;
68
- }
69
- /** Date row from query */
70
- interface DateRow extends BaseMetrics {
71
- date: string;
72
- }
73
- /** Create a generic sorter for any metric type */
74
- declare function createSorter<T, M extends string>(getValue: (item: T, metric: M) => number, defaultMetric: M, defaultOrder?: SortOrder): (items: T[], sortBy?: M, sortOrder?: SortOrder) => T[];
75
- interface BrandSegmentationOptions {
76
- /** Brand terms to match against keywords (case-insensitive) */
77
- brandTerms: string[];
78
- /** Minimum impressions for a keyword to be included. Default: 10 */
79
- minImpressions?: number;
80
- }
81
- interface BrandSegmentationRow {
82
- query?: string;
83
- keyword?: string;
84
- variants?: readonly string[];
85
- clicks?: number | null;
86
- impressions?: number | null;
87
- }
88
- interface BrandSummary {
89
- brandClicks: number;
90
- nonBrandClicks: number;
91
- brandShare: number;
92
- brandImpressions: number;
93
- nonBrandImpressions: number;
94
- }
95
- interface BrandSegmentationResult<T extends BrandSegmentationRow = QueriesRow> {
96
- brand: T[];
97
- nonBrand: T[];
98
- summary: BrandSummary;
99
- }
100
- /**
101
- * Pure helper: segment keywords into brand and non-brand based on provided
102
- * brand terms. Re-exported from `@gscdump/analysis` for portable callers.
103
- */
104
- declare function analyzeBrandSegmentation(keywords: QueriesRow[], options: BrandSegmentationOptions): BrandSegmentationResult<QueriesRow>;
105
- declare function analyzeBrandSegmentation<T extends BrandSegmentationRow>(keywords: T[], options: BrandSegmentationOptions): BrandSegmentationResult<T>;
106
- type CannibalizationSortMetric = 'clicks' | 'impressions' | 'positionSpread' | 'pageCount';
107
- interface CannibalizationOptions {
108
- /** Minimum impressions for a query to be considered. Default: 10 */
109
- minImpressions?: number;
110
- /** Maximum position spread to flag as cannibalization. Default: 10 */
111
- maxPositionSpread?: number;
112
- /** Minimum number of pages ranking for same query. Default: 2 */
113
- minPages?: number;
114
- /** Sort metric. Default: clicks */
115
- sortBy?: CannibalizationSortMetric;
116
- /** Sort order. Default: desc */
117
- sortOrder?: SortOrder;
118
- }
119
- interface CannibalizationPage {
120
- page: string;
121
- clicks: number;
122
- impressions: number;
123
- ctr: number;
124
- position: number;
125
- }
126
- interface CannibalizationResult {
127
- query: string;
128
- pages: CannibalizationPage[];
129
- totalClicks: number;
130
- totalImpressions: number;
131
- positionSpread: number;
132
- }
133
- interface CannibalizationCompetitor {
134
- url: string;
135
- clicks: number;
136
- impressions: number;
137
- ctr: number;
138
- position: number;
139
- share: number;
140
- rank: number;
141
- }
142
- interface CannibalizationEvent {
143
- keyword: string;
144
- totalImpressions: number;
145
- totalClicks: number;
146
- competitorCount: number;
147
- leaderUrl: string;
148
- leaderCtr: number;
149
- leaderPosition: number;
150
- hhi: number;
151
- fragmentation: number;
152
- stolenClicks: number;
153
- severity: number;
154
- competitors: CannibalizationCompetitor[];
155
- }
156
- /**
157
- * Pure helper: detects keyword cannibalization from row data — queries
158
- * ranking for multiple pages within a small position spread. Re-exported
159
- * from `@gscdump/analysis` for portable callers.
160
- */
161
- declare function analyzeCannibalization(rows: QueryPageRow[], options?: CannibalizationOptions): CannibalizationResult[];
162
- type ClusterType = 'prefix' | 'intent' | 'both';
163
- interface ClusteringOptions {
164
- /** Minimum keywords for a cluster to be reported. Default: 2 */
165
- minClusterSize?: number;
166
- /** Minimum impressions for a keyword to be included. Default: 10 */
167
- minImpressions?: number;
168
- /** Clustering method. Default: 'both' */
169
- clusterBy?: ClusterType;
170
- }
171
- interface KeywordCluster {
172
- clusterName: string;
173
- clusterType: 'prefix' | 'intent';
174
- keywords: QueriesRow[];
175
- totalClicks: number;
176
- totalImpressions: number;
177
- avgPosition: number;
178
- keywordCount: number;
179
- }
180
- interface ClusteringResult {
181
- clusters: KeywordCluster[];
182
- unclustered: QueriesRow[];
183
- }
184
- /**
185
- * Pure helper: clusters keywords by intent prefix or common word prefix.
186
- * Re-exported from `@gscdump/analysis` for portable callers.
187
- */
188
- declare function analyzeClustering(keywords: QueriesRow[], options?: ClusteringOptions): ClusteringResult;
189
- type ConcentrationRiskLevel = 'low' | 'medium' | 'high';
190
- interface ConcentrationOptions {
191
- /** Number of top items to report. Default: 10 */
192
- topN?: number;
193
- }
194
- interface ConcentrationItem {
195
- key: string;
196
- clicks: number;
197
- share: number;
198
- }
199
- interface ConcentrationInput {
200
- key: string;
201
- clicks: number;
202
- }
203
- interface ConcentrationResult {
204
- /** Gini coefficient: 0 = equal distribution, 1 = fully concentrated */
205
- giniCoefficient: number;
206
- /** Herfindahl-Hirschman Index: 0-10000, >2500 = highly concentrated */
207
- hhi: number;
208
- /** Percentage of total clicks from top N items */
209
- topNConcentration: number;
210
- topNItems: ConcentrationItem[];
211
- totalItems: number;
212
- totalClicks: number;
213
- /** Risk level derived from HHI: <1500 low, 1500-2500 medium, >2500 high */
214
- riskLevel: ConcentrationRiskLevel;
215
- }
216
- /**
217
- * Pure helper: analyze traffic concentration across items (pages or keywords).
218
- * Re-exported from `@gscdump/analysis` for portable callers.
219
- */
220
- declare function analyzeConcentration(items: ConcentrationInput[], options?: ConcentrationOptions): ConcentrationResult;
221
- /**
222
- * Page concentration analysis.
223
- */
224
- declare function analyzePageConcentration(pages: PageRow[], options?: ConcentrationOptions): ConcentrationResult;
225
- /**
226
- * Keyword concentration analysis.
227
- */
228
- declare function analyzeKeywordConcentration(keywords: QueriesRow[], options?: ConcentrationOptions): ConcentrationResult;
229
- type DecaySortMetric = 'lostClicks' | 'declinePercent' | 'currentClicks';
230
- interface DecayOptions {
231
- /** Minimum clicks in previous period to consider. Default: 50 */
232
- minPreviousClicks?: number;
233
- /** Minimum decline percentage (0-1). Default: 0.2 (20%) */
234
- threshold?: number;
235
- /** Metric to sort results by. Default: lostClicks */
236
- sortBy?: DecaySortMetric;
237
- }
238
- interface DecayInput {
239
- current: PageRow[];
240
- previous: PageRow[];
241
- }
242
- interface DecaySeriesPoint {
243
- week: string;
244
- clicks: number;
245
- impressions: number;
246
- }
247
- interface DecayResult {
248
- page: string;
249
- currentClicks: number;
250
- previousClicks: number;
251
- lostClicks: number;
252
- declinePercent: number;
253
- /** `null` when the page had no ranking data in the current period (not "position 0"). */
254
- currentPosition: number | null;
255
- previousPosition: number;
256
- /** `null` unless both currentPosition and previousPosition exist. */
257
- positionDrop: number | null;
258
- series?: DecaySeriesPoint[];
259
- }
260
- /**
261
- * Pure helper: identify "decaying" content — pages that have lost
262
- * significant traffic between two periods.
263
- */
264
- declare function analyzeDecay(input: DecayInput, options?: DecayOptions): DecayResult[];
265
- type MoversSortMetric = 'clicks' | 'impressions' | 'clicksChange' | 'impressionsChange' | 'positionChange';
266
- interface MoversOptions {
267
- /** Minimum change threshold to flag. Default: 0.2 (20%) */
268
- changeThreshold?: number;
269
- /** Minimum impressions in recent period. Default: 50 */
270
- minImpressions?: number;
271
- /** Metric to sort results by. Default: clicksChange */
272
- sortBy?: MoversSortMetric;
273
- }
274
- interface MoversInput {
275
- current: QueriesRow[];
276
- previous: QueriesRow[];
277
- /** If periods have different lengths, provide normalization factor (previous/current) */
278
- normalizationFactor?: number;
279
- }
280
- interface MoverData {
281
- keyword: string;
282
- page: string | null;
283
- recentClicks: number;
284
- recentImpressions: number;
285
- recentPosition: number;
286
- baselineClicks: number;
287
- baselineImpressions: number;
288
- /** `null` when the keyword has no previous-period data (a genuinely new query), not "position 0". */
289
- baselinePosition: number | null;
290
- clicksChange: number;
291
- clicksChangePercent: number;
292
- impressionsChangePercent: number;
293
- /** `null` unless both recentPosition and baselinePosition exist. */
294
- positionChange: number | null;
295
- }
296
- interface MoversResult {
297
- rising: MoverData[];
298
- declining: MoverData[];
299
- stable: MoverData[];
300
- }
301
- /**
302
- * Pure helper: identify "movers and shakers" — keywords with significant
303
- * recent changes between two periods.
304
- */
305
- declare function analyzeMovers(input: MoversInput, options?: MoversOptions): MoversResult;
306
- type OpportunitySortMetric = 'opportunityScore' | 'potentialClicks' | 'impressions' | 'position';
307
- interface OpportunityFactors {
308
- positionScore: number;
309
- impressionScore: number;
310
- ctrGapScore: number;
311
- }
312
- interface OpportunityResult {
313
- keyword: string;
314
- page: string | null;
315
- clicks: number;
316
- impressions: number;
317
- ctr: number;
318
- position: number;
319
- opportunityScore: number;
320
- potentialClicks: number;
321
- factors: OpportunityFactors;
322
- }
323
- interface OpportunityWeights {
324
- position?: number;
325
- impressions?: number;
326
- ctrGap?: number;
327
- }
328
- interface OpportunityOptions {
329
- minImpressions?: number;
330
- weights?: OpportunityWeights;
331
- sortBy?: OpportunitySortMetric;
332
- limit?: number;
333
- }
334
- /** Pure opportunity scorer shared by hosted and package analyzer callers. */
335
- declare function analyzeOpportunity(keywords: QueriesRow[], options?: OpportunityOptions): OpportunityResult[];
336
- type SeasonalityMetric = 'clicks' | 'impressions';
337
- interface SeasonalityOptions {
338
- /** Metric to analyze for seasonality. Default: clicks */
339
- metric?: SeasonalityMetric;
340
- }
341
- interface MonthlyData {
342
- month: string;
343
- value: number;
344
- vsAverage: number;
345
- isPeak: boolean;
346
- isTrough: boolean;
347
- }
348
- interface SeasonalityResult {
349
- hasSeasonality: boolean;
350
- /** Coefficient of variation: std dev / mean. Higher = more seasonal. */
351
- strength: number;
352
- peakMonths: string[];
353
- troughMonths: string[];
354
- monthlyBreakdown: MonthlyData[];
355
- insufficientData: boolean;
356
- }
357
- /**
358
- * Pure helper: detects seasonality patterns by analyzing monthly traffic
359
- * variation. Re-exported from `@gscdump/analysis` for portable callers.
360
- */
361
- declare function analyzeSeasonality(dates: DateRow[], options?: SeasonalityOptions): SeasonalityResult;
362
- /**
363
- * Unified `striking-distance` analyzer. Spike for the `defineAnalyzer`
364
- * pattern: one file owns the SQL plan, the row plan, the typed `InputRow`
365
- * contract, and the shared reducer.
366
- *
367
- * SQL and row plans both emit rows matching `StrikingDistanceInputRow`.
368
- * Filtering (`minPosition`/`maxPosition`/`minImpressions`/`maxCtr`),
369
- * derivation (`potentialClicks`), and sorting all live in the reducer, so
370
- * drift between plans cannot silently change results. A SQL-side `LIMIT`
371
- * push-down can be added later as an optional optimization, but the
372
- * correctness contract stays with the reducer.
373
- */
374
- /**
375
- * Row shape both plans produce. Field names match GSC's native columns so
376
- * the row-source path needs no rename step; the SQL plan aliases `url → page`.
377
- */
378
- interface StrikingDistanceInputRow {
379
- query: string;
380
- page: string | null;
381
- clicks: number;
382
- impressions: number;
383
- ctr: number;
384
- position: number;
385
- }
386
- interface StrikingDistanceResult {
387
- keyword: string;
388
- page: string | null;
389
- clicks: number;
390
- impressions: number;
391
- ctr: number;
392
- position: number;
393
- /** Estimated clicks at ~15% CTR (the average for positions 1–3). */
394
- potentialClicks: number;
395
- }
396
- interface StrikingDistanceFilterOptions {
397
- minPosition?: number;
398
- maxPosition?: number;
399
- minImpressions?: number;
400
- maxCtr?: number;
401
- }
402
- interface StrikingDistanceOptions extends StrikingDistanceFilterOptions {
403
- limit?: number;
404
- offset?: number;
405
- }
406
- /** Pure striking-distance analysis for consumers that already own the rows. */
407
- declare function analyzeStrikingDistance(rows: readonly {
408
- query: unknown;
409
- page?: unknown;
410
- clicks: unknown;
411
- impressions: unknown;
412
- ctr: unknown;
413
- position: unknown;
414
- }[], options?: StrikingDistanceOptions): StrikingDistanceResult[];
415
- interface ZeroClickResult {
416
- query: string;
417
- page: string;
418
- clicks: number;
419
- impressions: number;
420
- ctr: number;
421
- position: number;
422
- }
423
- interface ZeroClickOptions {
424
- minImpressions?: number;
425
- maxCtr?: number;
426
- maxPosition?: number;
427
- }
428
- /** Pure zero-click detector shared by hosted and package analyzer callers. */
429
- declare function analyzeZeroClick(rows: QueryPageRow[], options?: ZeroClickOptions): ZeroClickResult[];
430
- declare function analyzeInBrowser(runner: AttachedTableRunner, opts: AttachedTableSourceOptions, params: AnalysisParams$1, registry: AnalyzerRegistry$1): Promise<AnalysisResult$1>;
431
- declare const INTENT_CLASSIFIER_VERSION = 1;
432
- type SearchIntent = 'transactional' | 'commercial' | 'informational' | 'unknown';
433
- interface IntentClassification {
434
- /** Primary intent, by priority transactional > commercial > informational. */
435
- intent: SearchIntent;
436
- /** Procedural sub-signal of informational ("how to …"). */
437
- howTo: boolean;
438
- /** Matched cue tokens/phrases, for explainability + debugging. */
439
- signals: readonly string[];
440
- }
441
- declare const SEARCH_INTENT_CODE: Record<SearchIntent, number>;
442
- /** Pack a classification into one small int for cheap dimension storage. */
443
- declare function encodeIntent(c: Pick<IntentClassification, 'intent' | 'howTo'>): number;
444
- /**
445
- * Classify the search intent of a raw query from lexical cues. Deterministic;
446
- * version it (`INTENT_CLASSIFIER_VERSION`) and bump on any rule change so a
447
- * materialized `intent` can detect staleness, like the canonical normalizer.
448
- */
449
- declare function classifyQueryIntent(query: string): IntentClassification;
450
- /**
451
- * Algorithm version. Bump on ANY behaviour change (synonyms, depluralize,
452
- * folding) so downstream stores can record which version produced a
453
- * `query_canonical` and detect/repair staleness on a rule change instead of
454
- * silently mixing old and new keys. v1 = the original ASCII heuristic;
455
- * v2 adds Unicode folding, the empty-canonical guard, and `pluralize`-based
456
- * singularization.
457
- */
458
- declare const NORMALIZER_VERSION = 2;
459
- /**
460
- * Produce a canonical form of a search query for grouping near-duplicates.
461
- * Idempotent: `normalizeQuery(normalizeQuery(q)) === normalizeQuery(q)`.
462
- */
463
- declare function normalizeQuery(query: string): string;
464
- interface SitemapHealthRow {
465
- path: string;
466
- errors: number;
467
- warnings: number;
468
- }
469
- interface SitemapHealthInput extends SitemapHealthRow {
470
- isPending?: boolean;
471
- type?: string;
472
- contents?: Array<{
473
- submitted: number | string;
474
- }>;
475
- }
476
- interface SitemapDelta {
477
- path: string;
478
- errorDelta: number;
479
- warningDelta: number;
480
- newErrors: number;
481
- newWarnings: number;
482
- }
483
- interface SitemapHealthTotals {
484
- totalSitemaps: number;
485
- totalUrls: number;
486
- totalErrors: number;
487
- totalWarnings: number;
488
- pendingCount: number;
489
- indexSitemaps: number;
490
- }
491
- interface SitemapHealthDiff {
492
- changed: boolean;
493
- deltas: SitemapDelta[];
494
- totals: SitemapHealthTotals;
495
- }
496
- declare function diffSitemapHealth(prev: readonly SitemapHealthRow[], curr: readonly SitemapHealthInput[]): SitemapHealthDiff;
497
33
  export { type ActionPriorityResult, type ActionPrioritySourceState, type ActionPrioritySourceStatus, type ActionSource, type AnalysisError, type AnalysisErrorKind, type AnalysisParams, type AnalysisPeriod, type AnalysisQuerySource, type AnalysisResult, type AnalysisSourceKind, type AnalysisTool, type Analyzer, AnalyzerCapabilityError, type AnalyzerRegistry, type AnalyzerRegistryInit, type AnalyzerRunner, type AnalyzerVariants, type BaseMetrics, type BrandSegmentationOptions, type BrandSegmentationResult, type BrandSegmentationRow, type BrandSummary, type BrowserAnalyzeOptions, type CannibalizationCompetitor, type CannibalizationEvent, type CannibalizationOptions, type CannibalizationPage, type CannibalizationResult, type CannibalizationSortMetric, type ClusterType, type ClusteringOptions, type ClusteringResult, type ComparisonMode, type ComparisonPeriod, type CompositeSourceOptions, type ConcentrationInput, type ConcentrationItem, type ConcentrationOptions, type ConcentrationResult, type ConcentrationRiskLevel, DEFAULT_PRIORITY_SOURCES, type DateRow, type DecayInput, type DecayOptions, type DecayResult, type DecaySeriesPoint, type DecaySortMetric, type DefineAnalyzerOptions, type DefineReportOptions, type DefinedAnalyzer, type DefinedReport, type DryRunReportResult, ENGINE_QUERY_CAPABILITIES, type Effort, type EngineQuerySourceOptions, type ExecuteSqlOptions, type FileSet, type FormatReportOptions, INTENT_CLASSIFIER_VERSION, IN_MEMORY_DEFAULT_CAPABILITIES, type InMemoryQuerySourceOptions, type IntentClassification, type KeywordCluster, type MonthlyData, type MoverData, type MoversInput, type MoversOptions, type MoversResult, type MoversSortMetric, NORMALIZER_VERSION, type OpportunityFactors, type OpportunityOptions, type OpportunityResult, type OpportunitySortMetric, type OpportunityWeights, type PadTimeseriesOptions, type PageRow, type Plan, type PriorityAction, type QueriesRow, type QueryPageRow, type QueryRow, REPORTS, ROW_ANALYZERS, type ReduceContext, type ReduceCtx, type Reducer, type ReportAction, type ReportContext, type ReportFinding, type ReportPlanStep, type ReportResult, type ReportSection, type RequiredCapability, type ResolveWindowOptions, type ResolvedWindow, type RowQueriesPlan, type RunReportOptions, SEARCH_INTENT_CODE, SQL_ANALYZERS, type SearchIntent, type SeasonalityMetric, type SeasonalityOptions, type SeasonalityResult, type SitemapDelta, type SitemapHealthDiff, type SitemapHealthInput, type SitemapHealthRow, type SitemapHealthTotals, type SortOrder, type SourceCapabilities, type SqlExtraQuery, type SqlPlan, type SqlPlanSpec, type StrikingDistanceInputRow, type StrikingDistanceOptions, type StrikingDistanceResult, type TypedRowQuery, type WindowPreset, type ZeroClickOptions, type ZeroClickResult, analysisErrorToException, analysisErrors, analyzeBrandSegmentation, analyzeCannibalization, analyzeClustering, analyzeConcentration, analyzeDecay, analyzeInBrowser, analyzeKeywordConcentration, analyzeMovers, analyzeOpportunity, analyzePageConcentration, analyzeSeasonality, analyzeStrikingDistance, analyzeZeroClick, classifyQueryIntent, comparisonOf, createAnalyzerRegistry, createCompositeSource, createEngineQuerySource, createInMemoryQuerySource, createSorter, defaultAnalyzerRegistry, defaultReportRegistry, defineAnalyzer, diffSitemapHealth, dryRunReport, encodeIntent, formatAnalysisError, formatReport, isAnalysisError, mergePriorityActions, normalizePriorityActions, normalizeQuery, num, padTimeseries, periodOf, queryRows, resolveWindow, runAnalyzerFromSource, runAnalyzerWithEngine, runReport, runReportResult, scorePriorityActions };
package/dist/index.mjs CHANGED
@@ -1,53 +1,32 @@
1
- import { DEFAULT_PRIORITY_SOURCES, REPORTS, defaultReportRegistry, dryRunReport, formatReport, mergePriorityActions, normalizePriorityActions, runReport, runReportResult, scorePriorityActions } from "./_chunks/report.mjs";
2
- import { INTENT_CLASSIFIER_VERSION, NORMALIZER_VERSION, ROW_ANALYZERS, SEARCH_INTENT_CODE, SQL_ANALYZERS, analyzeBrandSegmentation, analyzeCannibalization, analyzeClustering, analyzeConcentration, analyzeDecay, analyzeKeywordConcentration, analyzeMovers, analyzeOpportunity, analyzePageConcentration, analyzeSeasonality, analyzeStrikingDistance, analyzeZeroClick, classifyQueryIntent, createSorter, defaultAnalyzerRegistry, encodeIntent, normalizeQuery } from "./_chunks/default-registry.mjs";
1
+ import { DEFAULT_PRIORITY_SOURCES, mergePriorityActions, normalizePriorityActions, scorePriorityActions } from "./action-priority.mjs";
3
2
  import { analysisErrorToException, analysisErrors, formatAnalysisError, isAnalysisError } from "./errors.mjs";
4
- import { IN_MEMORY_DEFAULT_CAPABILITIES, createCompositeSource, createInMemoryQuerySource } from "./_chunks/source.mjs";
5
- import { AnalyzerCapabilityError, createAnalyzerRegistry, defineAnalyzer, runAnalyzerFromSource, runAnalyzerFromSource as runAnalyzerFromSource$1 } from "@gscdump/engine/analyzer";
3
+ import { analyzeBrandSegmentation } from "./analyzers/brand.mjs";
4
+ import { createSorter } from "./types.mjs";
5
+ import { analyzeCannibalization } from "./analyzers/cannibalization.mjs";
6
+ import { analyzeClustering } from "./analyzers/clustering.mjs";
7
+ import { analyzeConcentration, analyzeKeywordConcentration, analyzePageConcentration } from "./analyzers/concentration.mjs";
8
+ import { INTENT_CLASSIFIER_VERSION, SEARCH_INTENT_CODE, classifyQueryIntent, encodeIntent } from "./query/intent.mjs";
9
+ import { NORMALIZER_VERSION, normalizeQuery } from "./query/normalize.mjs";
10
+ import { analyzeDecay } from "./analyzers/decay.mjs";
11
+ import { analyzeMovers } from "./analyzers/movers.mjs";
12
+ import { analyzeOpportunity } from "./analyzers/opportunity.mjs";
13
+ import { analyzeSeasonality } from "./analyzers/seasonality.mjs";
14
+ import { analyzeStrikingDistance } from "./analyzers/striking-distance.mjs";
15
+ import { analyzeZeroClick } from "./analyzers/zero-click.mjs";
16
+ import { ROW_ANALYZERS } from "./analyzer/row-analyzers.mjs";
17
+ import { analyzeInBrowser } from "./browser.mjs";
18
+ import { SQL_ANALYZERS } from "./sql-analyzers.mjs";
19
+ import { defaultAnalyzerRegistry } from "./default-registry.mjs";
20
+ import { formatReport } from "./report/format.mjs";
21
+ import { REPORTS, defaultReportRegistry } from "./report/registry.mjs";
22
+ import { dryRunReport, runReport, runReportResult } from "./report/runtime.mjs";
23
+ import "./report/index.mjs";
24
+ import { diffSitemapHealth } from "./sitemap-health.mjs";
25
+ import { createCompositeSource } from "./source/composite.mjs";
26
+ import { IN_MEMORY_DEFAULT_CAPABILITIES, createInMemoryQuerySource } from "./source/in-memory.mjs";
27
+ import "./source/index.mjs";
28
+ import { AnalyzerCapabilityError, createAnalyzerRegistry, defineAnalyzer, runAnalyzerFromSource } from "@gscdump/engine/analyzer";
6
29
  import { comparisonOf, padTimeseries, periodOf, resolveWindow } from "@gscdump/engine/period";
7
30
  import { num } from "@gscdump/engine/analysis-types";
8
- import { pgResolverAdapter } from "@gscdump/engine/resolver";
9
- import { ENGINE_QUERY_CAPABILITIES, createAttachedTableSource, createEngineQuerySource, queryRows, runAnalyzerWithEngine } from "@gscdump/engine/source";
10
- async function analyzeInBrowser(runner, opts, params, registry) {
11
- opts.signal?.throwIfAborted();
12
- return runAnalyzerFromSource$1(createAttachedTableSource(runner, {
13
- adapter: pgResolverAdapter,
14
- ...opts
15
- }), params, registry);
16
- }
17
- function urlCountOf(sitemap) {
18
- return sitemap.contents?.reduce((sum, c) => sum + (Number(c.submitted) || 0), 0) || 0;
19
- }
20
- function diffSitemapHealth(prev, curr) {
21
- const previousByPath = new Map(prev.map((r) => [r.path, r]));
22
- const deltas = [];
23
- let changed = false;
24
- for (const sitemap of curr) {
25
- const prior = previousByPath.get(sitemap.path);
26
- const errorDelta = prior ? sitemap.errors - (prior.errors || 0) : 0;
27
- const warningDelta = prior ? sitemap.warnings - (prior.warnings || 0) : 0;
28
- if (errorDelta !== 0 || warningDelta !== 0) {
29
- changed = true;
30
- deltas.push({
31
- path: sitemap.path,
32
- errorDelta,
33
- warningDelta,
34
- newErrors: sitemap.errors,
35
- newWarnings: sitemap.warnings
36
- });
37
- }
38
- }
39
- const totals = {
40
- totalSitemaps: curr.length,
41
- totalUrls: curr.reduce((sum, s) => sum + urlCountOf(s), 0),
42
- totalErrors: curr.reduce((sum, s) => sum + s.errors, 0),
43
- totalWarnings: curr.reduce((sum, s) => sum + s.warnings, 0),
44
- pendingCount: curr.filter((s) => s.isPending).length,
45
- indexSitemaps: curr.filter((s) => s.type === "sitemapIndex").length
46
- };
47
- return {
48
- changed,
49
- deltas,
50
- totals
51
- };
52
- }
31
+ import { ENGINE_QUERY_CAPABILITIES, createEngineQuerySource, queryRows, runAnalyzerWithEngine } from "@gscdump/engine/source";
53
32
  export { AnalyzerCapabilityError, DEFAULT_PRIORITY_SOURCES, ENGINE_QUERY_CAPABILITIES, INTENT_CLASSIFIER_VERSION, IN_MEMORY_DEFAULT_CAPABILITIES, NORMALIZER_VERSION, REPORTS, ROW_ANALYZERS, SEARCH_INTENT_CODE, SQL_ANALYZERS, analysisErrorToException, analysisErrors, analyzeBrandSegmentation, analyzeCannibalization, analyzeClustering, analyzeConcentration, analyzeDecay, analyzeInBrowser, analyzeKeywordConcentration, analyzeMovers, analyzeOpportunity, analyzePageConcentration, analyzeSeasonality, analyzeStrikingDistance, analyzeZeroClick, classifyQueryIntent, comparisonOf, createAnalyzerRegistry, createCompositeSource, createEngineQuerySource, createInMemoryQuerySource, createSorter, defaultAnalyzerRegistry, defaultReportRegistry, defineAnalyzer, diffSitemapHealth, dryRunReport, encodeIntent, formatAnalysisError, formatReport, isAnalysisError, mergePriorityActions, normalizePriorityActions, normalizeQuery, num, padTimeseries, periodOf, queryRows, resolveWindow, runAnalyzerFromSource, runAnalyzerWithEngine, runReport, runReportResult, scorePriorityActions };