gscdump 1.3.2 → 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.
- package/dist/api/batch.d.mts +12 -0
- package/dist/api/batch.mjs +29 -0
- package/dist/api/indexing.d.mts +37 -0
- package/dist/api/indexing.mjs +25 -0
- package/dist/api/inspection.d.mts +89 -0
- package/dist/api/inspection.mjs +128 -0
- package/dist/api/oauth.d.mts +48 -0
- package/dist/api/oauth.mjs +154 -0
- package/dist/api/sites.d.mts +45 -0
- package/dist/api/sites.mjs +34 -0
- package/dist/api/verification.d.mts +43 -0
- package/dist/api/verification.mjs +89 -0
- package/dist/contracts.d.mts +46 -1
- package/dist/core/cli-format.mjs +9 -0
- package/dist/core/client.d.mts +122 -0
- package/dist/core/client.mjs +246 -0
- package/dist/core/errors.d.mts +66 -0
- package/dist/core/errors.mjs +207 -0
- package/dist/core/indexing-issues.d.mts +60 -0
- package/dist/core/indexing-issues.mjs +139 -0
- package/dist/core/property.d.mts +41 -0
- package/dist/core/property.mjs +74 -0
- package/dist/core/quota.d.mts +2 -0
- package/dist/core/quota.mjs +2 -0
- package/dist/core/result.d.mts +19 -1
- package/dist/core/scope-values.d.mts +7 -0
- package/dist/core/scope-values.mjs +15 -0
- package/dist/core/scopes.d.mts +5 -0
- package/dist/core/scopes.mjs +11 -0
- package/dist/core/site-url.d.mts +25 -0
- package/dist/core/site-url.mjs +30 -0
- package/dist/core/types.d.mts +201 -0
- package/dist/core/types.mjs +1 -0
- package/dist/dates.d.mts +2 -2
- package/dist/dates.mjs +2 -2
- package/dist/index.d.mts +20 -814
- package/dist/index.mjs +19 -1243
- package/dist/onboarding.d.mts +2 -0
- package/dist/onboarding.mjs +2 -0
- package/dist/{_chunks → query}/builder.d.mts +2 -2
- package/dist/query/builder.mjs +86 -0
- package/dist/query/columns.d.mts +15 -0
- package/dist/query/columns.mjs +23 -0
- package/dist/query/constants.d.mts +19 -0
- package/dist/query/constants.mjs +16 -0
- package/dist/query/errors.d.mts +85 -0
- package/dist/query/errors.mjs +141 -0
- package/dist/query/index.d.mts +9 -75
- package/dist/query/index.mjs +7 -230
- package/dist/query/operator-meta.mjs +41 -0
- package/dist/query/operators.d.mts +24 -0
- package/dist/query/operators.mjs +124 -0
- package/dist/query/plan.d.mts +92 -2
- package/dist/query/plan.mjs +3 -1
- package/dist/query/resolver.d.mts +40 -0
- package/dist/query/resolver.mjs +243 -0
- package/dist/{_chunks → query}/types.d.mts +3 -25
- package/dist/query/utils/countries.d.mts +7 -0
- package/dist/{_chunks/resolver.mjs → query/utils/countries.mjs} +1 -434
- package/dist/{_chunks → query/utils}/dayjs.mjs +2 -4
- package/dist/sitemap.d.mts +26 -0
- package/dist/sitemap.mjs +70 -0
- package/dist/url.d.mts +9 -0
- package/dist/url.mjs +6 -0
- package/package.json +2 -3
- package/dist/_chunks/contracts.d.mts +0 -47
- package/dist/_chunks/plan.d.mts +0 -175
- package/dist/_chunks/result.d.mts +0 -20
- /package/dist/{_chunks → core}/gsc-dates.d.mts +0 -0
- /package/dist/{_chunks → core}/gsc-dates.mjs +0 -0
- /package/dist/{_chunks → query/utils}/dayjs.d.mts +0 -0
package/dist/query/index.mjs
CHANGED
|
@@ -1,234 +1,11 @@
|
|
|
1
|
-
import { Countries,
|
|
2
|
-
import {
|
|
1
|
+
import { Countries, Devices, SearchTypes } from "./constants.mjs";
|
|
2
|
+
import { UnresolvableDatasetError, UnsupportedLogicalCapabilityError, isQueryError, queryErrorToException, queryErrors } from "./errors.mjs";
|
|
3
|
+
import { extractDateRange, extractMetricFilters, extractSearchType, extractSpecialOperatorFilters, normalizeBuilderState, normalizeBuilderStateResult, normalizeFilter, resolveToBody, resolveToBodyResult } from "./resolver.mjs";
|
|
4
|
+
import { currentPstDate, daysAgoPst } from "./utils/dayjs.mjs";
|
|
5
|
+
import { gsc } from "./builder.mjs";
|
|
6
|
+
import { clicks, country, ctr, date, device, hour, impressions, page, position, query, queryCanonical, searchAppearance, searchType } from "./columns.mjs";
|
|
7
|
+
import { and, between, contains, eq, gt, gte, inArray, like, lt, lte, ne, not, notRegex, or, regex, topLevel } from "./operators.mjs";
|
|
3
8
|
import { buildLogicalComparisonPlan, buildLogicalComparisonPlanResult, buildLogicalPlan, buildLogicalPlanResult } from "./plan.mjs";
|
|
4
|
-
function isDimensionString(v) {
|
|
5
|
-
return typeof v === "string";
|
|
6
|
-
}
|
|
7
|
-
function isMetricColumn(v) {
|
|
8
|
-
return typeof v === "object" && v !== null && "metric" in v;
|
|
9
|
-
}
|
|
10
|
-
function isDimensionColumn(v) {
|
|
11
|
-
return typeof v === "object" && v !== null && "dimension" in v && !("metric" in v);
|
|
12
|
-
}
|
|
13
|
-
function createBuilder(state) {
|
|
14
|
-
return {
|
|
15
|
-
select(...args) {
|
|
16
|
-
const dimensions = [];
|
|
17
|
-
const metrics = [];
|
|
18
|
-
for (const arg of args) if (isDimensionString(arg)) dimensions.push(arg);
|
|
19
|
-
else if (isDimensionColumn(arg)) dimensions.push(arg.dimension);
|
|
20
|
-
else if (isMetricColumn(arg)) metrics.push(arg.metric);
|
|
21
|
-
return createBuilder({
|
|
22
|
-
...state,
|
|
23
|
-
dimensions,
|
|
24
|
-
metrics: metrics.length > 0 ? metrics : void 0
|
|
25
|
-
});
|
|
26
|
-
},
|
|
27
|
-
where(filter) {
|
|
28
|
-
return createBuilder({
|
|
29
|
-
...state,
|
|
30
|
-
filter
|
|
31
|
-
});
|
|
32
|
-
},
|
|
33
|
-
prefilter(filter) {
|
|
34
|
-
return createBuilder({
|
|
35
|
-
...state,
|
|
36
|
-
prefilter: filter
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
orderBy(col, dir) {
|
|
40
|
-
const column = isMetricColumn(col) ? col.metric : col.dimension;
|
|
41
|
-
return createBuilder({
|
|
42
|
-
...state,
|
|
43
|
-
orderBy: {
|
|
44
|
-
column,
|
|
45
|
-
dir
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
limit(n) {
|
|
50
|
-
return createBuilder({
|
|
51
|
-
...state,
|
|
52
|
-
rowLimit: n
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
offset(n) {
|
|
56
|
-
return createBuilder({
|
|
57
|
-
...state,
|
|
58
|
-
startRow: n
|
|
59
|
-
});
|
|
60
|
-
},
|
|
61
|
-
dataState(s) {
|
|
62
|
-
return createBuilder({
|
|
63
|
-
...state,
|
|
64
|
-
dataState: s
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
aggregationType(t) {
|
|
68
|
-
return createBuilder({
|
|
69
|
-
...state,
|
|
70
|
-
aggregationType: t
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
type(t) {
|
|
74
|
-
return createBuilder({
|
|
75
|
-
...state,
|
|
76
|
-
searchType: t
|
|
77
|
-
});
|
|
78
|
-
},
|
|
79
|
-
toBody() {
|
|
80
|
-
return resolveToBody(state);
|
|
81
|
-
},
|
|
82
|
-
getState() {
|
|
83
|
-
return { ...state };
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
const gsc = createBuilder({ dimensions: [] });
|
|
88
|
-
function createColumn(dimension) {
|
|
89
|
-
return { dimension };
|
|
90
|
-
}
|
|
91
|
-
function createMetricColumn(metric) {
|
|
92
|
-
return { metric };
|
|
93
|
-
}
|
|
94
|
-
function createQueryParam(param) {
|
|
95
|
-
return { param };
|
|
96
|
-
}
|
|
97
|
-
const page = createColumn("page");
|
|
98
|
-
const query = createColumn("query");
|
|
99
|
-
const queryCanonical = createColumn("queryCanonical");
|
|
100
|
-
const device = createColumn("device");
|
|
101
|
-
const country = createColumn("country");
|
|
102
|
-
const searchAppearance = createColumn("searchAppearance");
|
|
103
|
-
const date = createColumn("date");
|
|
104
|
-
const hour = createColumn("hour");
|
|
105
|
-
const clicks = createMetricColumn("clicks");
|
|
106
|
-
const impressions = createMetricColumn("impressions");
|
|
107
|
-
const ctr = createMetricColumn("ctr");
|
|
108
|
-
const position = createMetricColumn("position");
|
|
109
|
-
const searchType = createQueryParam("searchType");
|
|
110
|
-
function leafFilter(dimension, operator, expression, expression2) {
|
|
111
|
-
const filter = {
|
|
112
|
-
dimension,
|
|
113
|
-
operator,
|
|
114
|
-
expression
|
|
115
|
-
};
|
|
116
|
-
if (expression2 !== void 0) filter.expression2 = expression2;
|
|
117
|
-
return {
|
|
118
|
-
_constraints: {},
|
|
119
|
-
_filters: [filter]
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function metricOrDimFilter(column, metricOp, dimOp, expression, expression2) {
|
|
123
|
-
return "metric" in column ? leafFilter(column.metric, metricOp, expression, expression2) : leafFilter(column.dimension, dimOp, expression, expression2);
|
|
124
|
-
}
|
|
125
|
-
function eq(columnOrParam, value) {
|
|
126
|
-
return leafFilter("dimension" in columnOrParam ? columnOrParam.dimension : columnOrParam.param, "equals", String(value));
|
|
127
|
-
}
|
|
128
|
-
function ne(column, value) {
|
|
129
|
-
return leafFilter(column.dimension, "notEquals", String(value));
|
|
130
|
-
}
|
|
131
|
-
function inArray(column, values) {
|
|
132
|
-
if (values.length === 0) throw new Error(`inArray(${column.dimension}, []) requires at least one value`);
|
|
133
|
-
return {
|
|
134
|
-
_constraints: {},
|
|
135
|
-
_filters: values.map((v) => ({
|
|
136
|
-
dimension: column.dimension,
|
|
137
|
-
operator: "equals",
|
|
138
|
-
expression: String(v)
|
|
139
|
-
})),
|
|
140
|
-
_groupType: "or"
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function contains(column, pattern) {
|
|
144
|
-
return leafFilter(column.dimension, "contains", pattern);
|
|
145
|
-
}
|
|
146
|
-
function like(column, pattern) {
|
|
147
|
-
if (!/[%_]/.test(pattern)) return leafFilter(column.dimension, "contains", pattern);
|
|
148
|
-
const regex = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/%/g, ".*").replace(/_/g, ".");
|
|
149
|
-
return leafFilter(column.dimension, "includingRegex", regex);
|
|
150
|
-
}
|
|
151
|
-
function regex(column, pattern) {
|
|
152
|
-
return leafFilter(column.dimension, "includingRegex", typeof pattern === "string" ? pattern : pattern.source);
|
|
153
|
-
}
|
|
154
|
-
function notRegex(column, pattern) {
|
|
155
|
-
return leafFilter(column.dimension, "excludingRegex", typeof pattern === "string" ? pattern : pattern.source);
|
|
156
|
-
}
|
|
157
|
-
function and(...filters) {
|
|
158
|
-
const flatFilters = [];
|
|
159
|
-
const nestedGroups = [];
|
|
160
|
-
for (const f of filters) if (f._groupType === "or") nestedGroups.push(f);
|
|
161
|
-
else {
|
|
162
|
-
flatFilters.push(...f._filters);
|
|
163
|
-
if (f._nestedGroups) nestedGroups.push(...f._nestedGroups);
|
|
164
|
-
}
|
|
165
|
-
return {
|
|
166
|
-
_constraints: {},
|
|
167
|
-
_filters: flatFilters,
|
|
168
|
-
_nestedGroups: nestedGroups.length > 0 ? nestedGroups : void 0,
|
|
169
|
-
_groupType: "and"
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
function or(...filters) {
|
|
173
|
-
for (const f of filters) {
|
|
174
|
-
if (f._groupType === "and" && f._filters.length > 1) throw new Error("or() cannot contain a multi-leaf AND group: GSC filter groups do not nest. Restructure as flat OR or split into multiple queries.");
|
|
175
|
-
if (f._nestedGroups && f._nestedGroups.length > 0) throw new Error("or() cannot contain nested filter groups: GSC filter groups do not nest.");
|
|
176
|
-
for (const leaf of f._filters) {
|
|
177
|
-
if (leaf.dimension === "date") throw new Error("or() cannot contain a date filter: GSC date range is a top-level AND-applied request field, not a filter. Apply the date range outside the or() group.");
|
|
178
|
-
if (leaf.dimension === "searchType") throw new Error("or() cannot contain a searchType filter: GSC search type is a top-level AND-applied request field. Use .type() outside the or() group.");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
_constraints: {},
|
|
183
|
-
_filters: filters.flatMap((f) => f._filters),
|
|
184
|
-
_groupType: "or"
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
const INVERSIONS = {
|
|
188
|
-
equals: "notEquals",
|
|
189
|
-
notEquals: "equals",
|
|
190
|
-
contains: "notContains",
|
|
191
|
-
notContains: "contains",
|
|
192
|
-
includingRegex: "excludingRegex",
|
|
193
|
-
excludingRegex: "includingRegex"
|
|
194
|
-
};
|
|
195
|
-
function invertOperator(op) {
|
|
196
|
-
return INVERSIONS[op];
|
|
197
|
-
}
|
|
198
|
-
function not(filter) {
|
|
199
|
-
if ((filter._nestedGroups?.length ?? 0) > 0 || filter._filters.length !== 1) throw new Error("not() can only invert a single leaf filter. GSC filter groups do not support compound negation.");
|
|
200
|
-
const inverted = [];
|
|
201
|
-
for (const f of filter._filters) {
|
|
202
|
-
if (DATE_OPERATORS.includes(f.operator)) throw new Error(`not() cannot invert date operator "${f.operator}": GSC has no negated date filter.`);
|
|
203
|
-
if (!(f.operator in INVERSIONS)) throw new Error(`not() cannot invert operator "${f.operator}".`);
|
|
204
|
-
inverted.push({
|
|
205
|
-
...f,
|
|
206
|
-
operator: invertOperator(f.operator)
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
return {
|
|
210
|
-
_constraints: {},
|
|
211
|
-
_filters: inverted
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
function gte(column, value) {
|
|
215
|
-
return metricOrDimFilter(column, "metricGte", "gte", String(value));
|
|
216
|
-
}
|
|
217
|
-
function gt(column, value) {
|
|
218
|
-
return metricOrDimFilter(column, "metricGt", "gt", String(value));
|
|
219
|
-
}
|
|
220
|
-
function lte(column, value) {
|
|
221
|
-
return metricOrDimFilter(column, "metricLte", "lte", String(value));
|
|
222
|
-
}
|
|
223
|
-
function lt(column, value) {
|
|
224
|
-
return metricOrDimFilter(column, "metricLt", "lt", String(value));
|
|
225
|
-
}
|
|
226
|
-
function between(column, start, end) {
|
|
227
|
-
return metricOrDimFilter(column, "metricBetween", "between", String(start), String(end));
|
|
228
|
-
}
|
|
229
|
-
function topLevel(column) {
|
|
230
|
-
return leafFilter(column.dimension, "topLevel", "");
|
|
231
|
-
}
|
|
232
9
|
function today() {
|
|
233
10
|
return currentPstDate();
|
|
234
11
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const DATE_OPERATORS = [
|
|
2
|
+
"gte",
|
|
3
|
+
"gt",
|
|
4
|
+
"lte",
|
|
5
|
+
"lt",
|
|
6
|
+
"between"
|
|
7
|
+
];
|
|
8
|
+
const METRIC_OPERATORS = [
|
|
9
|
+
"metricGte",
|
|
10
|
+
"metricGt",
|
|
11
|
+
"metricLte",
|
|
12
|
+
"metricLt",
|
|
13
|
+
"metricBetween"
|
|
14
|
+
];
|
|
15
|
+
const SPECIAL_OPERATORS = ["topLevel"];
|
|
16
|
+
const QUERY_PARAMS = ["searchType"];
|
|
17
|
+
const FILTER_METRICS = [
|
|
18
|
+
"clicks",
|
|
19
|
+
"impressions",
|
|
20
|
+
"ctr",
|
|
21
|
+
"position"
|
|
22
|
+
];
|
|
23
|
+
function isDateOperator(op) {
|
|
24
|
+
return DATE_OPERATORS.includes(op);
|
|
25
|
+
}
|
|
26
|
+
function isMetricOperator(op) {
|
|
27
|
+
return METRIC_OPERATORS.includes(op);
|
|
28
|
+
}
|
|
29
|
+
function isSpecialOperator(op) {
|
|
30
|
+
return SPECIAL_OPERATORS.includes(op);
|
|
31
|
+
}
|
|
32
|
+
function isQueryParam(value) {
|
|
33
|
+
return QUERY_PARAMS.includes(value);
|
|
34
|
+
}
|
|
35
|
+
function isMetric(value) {
|
|
36
|
+
return FILTER_METRICS.includes(value);
|
|
37
|
+
}
|
|
38
|
+
function isRegexOperator(op) {
|
|
39
|
+
return op === "includingRegex" || op === "excludingRegex";
|
|
40
|
+
}
|
|
41
|
+
export { DATE_OPERATORS, FILTER_METRICS, METRIC_OPERATORS, QUERY_PARAMS, SPECIAL_OPERATORS, isDateOperator, isMetric, isMetricOperator, isQueryParam, isRegexOperator, isSpecialOperator };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Column, Dimension, DimensionValueMap, Filter, MergeConstraints, Metric, MetricColumn, QueryParam, QueryParamName, QueryParamValueMap } from "./types.mjs";
|
|
2
|
+
declare function eq<D extends Dimension, V extends DimensionValueMap[D]>(column: Column<D>, value: V): Filter<Record<D, V>>;
|
|
3
|
+
declare function eq<P extends QueryParamName, V extends QueryParamValueMap[P]>(param: QueryParam<P>, value: V): Filter<Record<P, V>>;
|
|
4
|
+
declare function ne<D extends Dimension>(column: Column<D>, value: DimensionValueMap[D]): Filter<object>;
|
|
5
|
+
declare function inArray<D extends Dimension, V extends DimensionValueMap[D]>(column: Column<D>, values: readonly V[]): Filter<Record<D, V>>;
|
|
6
|
+
declare function contains<D extends Dimension>(column: Column<D>, pattern: string): Filter<object>;
|
|
7
|
+
declare function like<D extends Dimension>(column: Column<D>, pattern: string): Filter<object>;
|
|
8
|
+
declare function regex<D extends Dimension>(column: Column<D>, pattern: RegExp | string): Filter<object>;
|
|
9
|
+
declare function notRegex<D extends Dimension>(column: Column<D>, pattern: RegExp | string): Filter<object>;
|
|
10
|
+
declare function and<F extends Filter<any>[]>(...filters: F): Filter<MergeConstraints<F>>;
|
|
11
|
+
declare function or<F extends Filter<any>[]>(...filters: F): Filter<object>;
|
|
12
|
+
declare function not<F extends Filter<any>>(filter: F): Filter<object>;
|
|
13
|
+
declare function gte<M extends Metric>(column: MetricColumn<M>, value: number): Filter<object>;
|
|
14
|
+
declare function gte(column: Column<'date'>, value: string): Filter<object>;
|
|
15
|
+
declare function gt<M extends Metric>(column: MetricColumn<M>, value: number): Filter<object>;
|
|
16
|
+
declare function gt(column: Column<'date'>, value: string): Filter<object>;
|
|
17
|
+
declare function lte<M extends Metric>(column: MetricColumn<M>, value: number): Filter<object>;
|
|
18
|
+
declare function lte(column: Column<'date'>, value: string): Filter<object>;
|
|
19
|
+
declare function lt<M extends Metric>(column: MetricColumn<M>, value: number): Filter<object>;
|
|
20
|
+
declare function lt(column: Column<'date'>, value: string): Filter<object>;
|
|
21
|
+
declare function between<M extends Metric>(column: MetricColumn<M>, start: number, end: number): Filter<object>;
|
|
22
|
+
declare function between(column: Column<'date'>, start: string, end: string): Filter<object>;
|
|
23
|
+
declare function topLevel(column: Column<'page'>): Filter<object>;
|
|
24
|
+
export { and, between, contains, eq, gt, gte, inArray, like, lt, lte, ne, not, notRegex, or, regex, topLevel };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DATE_OPERATORS } from "./operator-meta.mjs";
|
|
2
|
+
function leafFilter(dimension, operator, expression, expression2) {
|
|
3
|
+
const filter = {
|
|
4
|
+
dimension,
|
|
5
|
+
operator,
|
|
6
|
+
expression
|
|
7
|
+
};
|
|
8
|
+
if (expression2 !== void 0) filter.expression2 = expression2;
|
|
9
|
+
return {
|
|
10
|
+
_constraints: {},
|
|
11
|
+
_filters: [filter]
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function metricOrDimFilter(column, metricOp, dimOp, expression, expression2) {
|
|
15
|
+
return "metric" in column ? leafFilter(column.metric, metricOp, expression, expression2) : leafFilter(column.dimension, dimOp, expression, expression2);
|
|
16
|
+
}
|
|
17
|
+
function eq(columnOrParam, value) {
|
|
18
|
+
return leafFilter("dimension" in columnOrParam ? columnOrParam.dimension : columnOrParam.param, "equals", String(value));
|
|
19
|
+
}
|
|
20
|
+
function ne(column, value) {
|
|
21
|
+
return leafFilter(column.dimension, "notEquals", String(value));
|
|
22
|
+
}
|
|
23
|
+
function inArray(column, values) {
|
|
24
|
+
if (values.length === 0) throw new Error(`inArray(${column.dimension}, []) requires at least one value`);
|
|
25
|
+
return {
|
|
26
|
+
_constraints: {},
|
|
27
|
+
_filters: values.map((v) => ({
|
|
28
|
+
dimension: column.dimension,
|
|
29
|
+
operator: "equals",
|
|
30
|
+
expression: String(v)
|
|
31
|
+
})),
|
|
32
|
+
_groupType: "or"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function contains(column, pattern) {
|
|
36
|
+
return leafFilter(column.dimension, "contains", pattern);
|
|
37
|
+
}
|
|
38
|
+
function like(column, pattern) {
|
|
39
|
+
if (!/[%_]/.test(pattern)) return leafFilter(column.dimension, "contains", pattern);
|
|
40
|
+
const regex = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/%/g, ".*").replace(/_/g, ".");
|
|
41
|
+
return leafFilter(column.dimension, "includingRegex", regex);
|
|
42
|
+
}
|
|
43
|
+
function regex(column, pattern) {
|
|
44
|
+
return leafFilter(column.dimension, "includingRegex", typeof pattern === "string" ? pattern : pattern.source);
|
|
45
|
+
}
|
|
46
|
+
function notRegex(column, pattern) {
|
|
47
|
+
return leafFilter(column.dimension, "excludingRegex", typeof pattern === "string" ? pattern : pattern.source);
|
|
48
|
+
}
|
|
49
|
+
function and(...filters) {
|
|
50
|
+
const flatFilters = [];
|
|
51
|
+
const nestedGroups = [];
|
|
52
|
+
for (const f of filters) if (f._groupType === "or") nestedGroups.push(f);
|
|
53
|
+
else {
|
|
54
|
+
flatFilters.push(...f._filters);
|
|
55
|
+
if (f._nestedGroups) nestedGroups.push(...f._nestedGroups);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
_constraints: {},
|
|
59
|
+
_filters: flatFilters,
|
|
60
|
+
_nestedGroups: nestedGroups.length > 0 ? nestedGroups : void 0,
|
|
61
|
+
_groupType: "and"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function or(...filters) {
|
|
65
|
+
for (const f of filters) {
|
|
66
|
+
if (f._groupType === "and" && f._filters.length > 1) throw new Error("or() cannot contain a multi-leaf AND group: GSC filter groups do not nest. Restructure as flat OR or split into multiple queries.");
|
|
67
|
+
if (f._nestedGroups && f._nestedGroups.length > 0) throw new Error("or() cannot contain nested filter groups: GSC filter groups do not nest.");
|
|
68
|
+
for (const leaf of f._filters) {
|
|
69
|
+
if (leaf.dimension === "date") throw new Error("or() cannot contain a date filter: GSC date range is a top-level AND-applied request field, not a filter. Apply the date range outside the or() group.");
|
|
70
|
+
if (leaf.dimension === "searchType") throw new Error("or() cannot contain a searchType filter: GSC search type is a top-level AND-applied request field. Use .type() outside the or() group.");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
_constraints: {},
|
|
75
|
+
_filters: filters.flatMap((f) => f._filters),
|
|
76
|
+
_groupType: "or"
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const INVERSIONS = {
|
|
80
|
+
equals: "notEquals",
|
|
81
|
+
notEquals: "equals",
|
|
82
|
+
contains: "notContains",
|
|
83
|
+
notContains: "contains",
|
|
84
|
+
includingRegex: "excludingRegex",
|
|
85
|
+
excludingRegex: "includingRegex"
|
|
86
|
+
};
|
|
87
|
+
function invertOperator(op) {
|
|
88
|
+
return INVERSIONS[op];
|
|
89
|
+
}
|
|
90
|
+
function not(filter) {
|
|
91
|
+
if ((filter._nestedGroups?.length ?? 0) > 0 || filter._filters.length !== 1) throw new Error("not() can only invert a single leaf filter. GSC filter groups do not support compound negation.");
|
|
92
|
+
const inverted = [];
|
|
93
|
+
for (const f of filter._filters) {
|
|
94
|
+
if (DATE_OPERATORS.includes(f.operator)) throw new Error(`not() cannot invert date operator "${f.operator}": GSC has no negated date filter.`);
|
|
95
|
+
if (!(f.operator in INVERSIONS)) throw new Error(`not() cannot invert operator "${f.operator}".`);
|
|
96
|
+
inverted.push({
|
|
97
|
+
...f,
|
|
98
|
+
operator: invertOperator(f.operator)
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
_constraints: {},
|
|
103
|
+
_filters: inverted
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function gte(column, value) {
|
|
107
|
+
return metricOrDimFilter(column, "metricGte", "gte", String(value));
|
|
108
|
+
}
|
|
109
|
+
function gt(column, value) {
|
|
110
|
+
return metricOrDimFilter(column, "metricGt", "gt", String(value));
|
|
111
|
+
}
|
|
112
|
+
function lte(column, value) {
|
|
113
|
+
return metricOrDimFilter(column, "metricLte", "lte", String(value));
|
|
114
|
+
}
|
|
115
|
+
function lt(column, value) {
|
|
116
|
+
return metricOrDimFilter(column, "metricLt", "lt", String(value));
|
|
117
|
+
}
|
|
118
|
+
function between(column, start, end) {
|
|
119
|
+
return metricOrDimFilter(column, "metricBetween", "between", String(start), String(end));
|
|
120
|
+
}
|
|
121
|
+
function topLevel(column) {
|
|
122
|
+
return leafFilter(column.dimension, "topLevel", "");
|
|
123
|
+
}
|
|
124
|
+
export { and, between, contains, eq, gt, gte, inArray, like, lt, lte, ne, not, notRegex, or, regex, topLevel };
|
package/dist/query/plan.d.mts
CHANGED
|
@@ -1,3 +1,93 @@
|
|
|
1
|
-
import { TableName } from "../
|
|
2
|
-
import {
|
|
1
|
+
import { TableName } from "../contracts.mjs";
|
|
2
|
+
import { Result } from "../core/result.mjs";
|
|
3
|
+
import { BuilderState, Dimension, FilterOperator, Metric, MetricOperator, QueryParamName } from "./types.mjs";
|
|
4
|
+
import { QueryError, QueryErrorKind, UnresolvableDatasetError, UnsupportedLogicalCapabilityError } from "./errors.mjs";
|
|
5
|
+
type LogicalDataset = TableName;
|
|
6
|
+
type ComparisonFilter = 'new' | 'lost' | 'improving' | 'declining';
|
|
7
|
+
interface PlannerCapabilities {
|
|
8
|
+
regex?: boolean;
|
|
9
|
+
multiDataset?: boolean;
|
|
10
|
+
comparisonJoin?: boolean;
|
|
11
|
+
windowTotals?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface LogicalDimensionFilter {
|
|
14
|
+
dimension: Dimension;
|
|
15
|
+
operator: FilterOperator;
|
|
16
|
+
expression: string;
|
|
17
|
+
expression2?: string;
|
|
18
|
+
}
|
|
19
|
+
interface LogicalMetricFilter {
|
|
20
|
+
metric: Metric;
|
|
21
|
+
operator: MetricOperator;
|
|
22
|
+
expression: number;
|
|
23
|
+
expression2?: number;
|
|
24
|
+
}
|
|
25
|
+
interface LogicalFilterLeaf {
|
|
26
|
+
kind: 'leaf';
|
|
27
|
+
filter: LogicalDimensionFilter;
|
|
28
|
+
}
|
|
29
|
+
interface LogicalFilterGroup {
|
|
30
|
+
kind: 'group';
|
|
31
|
+
groupType: 'and' | 'or';
|
|
32
|
+
children: LogicalFilterNode[];
|
|
33
|
+
}
|
|
34
|
+
type LogicalFilterNode = LogicalFilterLeaf | LogicalFilterGroup;
|
|
35
|
+
interface LogicalQueryPlan {
|
|
36
|
+
dataset: LogicalDataset;
|
|
37
|
+
dimensions: Dimension[];
|
|
38
|
+
groupByDimensions: Dimension[];
|
|
39
|
+
hasDate: boolean;
|
|
40
|
+
metrics: Metric[];
|
|
41
|
+
dateRange: {
|
|
42
|
+
startDate: string;
|
|
43
|
+
endDate: string;
|
|
44
|
+
};
|
|
45
|
+
dimensionFilters: LogicalDimensionFilter[];
|
|
46
|
+
dimensionFilterTree?: LogicalFilterNode;
|
|
47
|
+
metricFilters: LogicalMetricFilter[];
|
|
48
|
+
prefilters: LogicalMetricFilter[];
|
|
49
|
+
specialFilters: {
|
|
50
|
+
topLevel: boolean;
|
|
51
|
+
};
|
|
52
|
+
queryParams: Partial<Record<QueryParamName, string>>;
|
|
53
|
+
orderBy?: BuilderState['orderBy'];
|
|
54
|
+
rowLimit?: number;
|
|
55
|
+
startRow?: number;
|
|
56
|
+
}
|
|
57
|
+
interface LogicalComparisonPlan {
|
|
58
|
+
current: LogicalQueryPlan;
|
|
59
|
+
previous: LogicalQueryPlan;
|
|
60
|
+
comparisonFilter?: ComparisonFilter;
|
|
61
|
+
}
|
|
62
|
+
declare function inferDataset(dimensions: readonly Dimension[], filterDims?: readonly Dimension[]): LogicalDataset;
|
|
63
|
+
/**
|
|
64
|
+
* True when every grouped + filtered dimension fits inside one stored dataset,
|
|
65
|
+
* i.e. the query is answerable from stored Parquet/D1 tables without a live
|
|
66
|
+
* GSC call. `inferDataset` always returns *some* dataset; this predicate is
|
|
67
|
+
* how callers tell a genuine match from one that will fail at column-resolve
|
|
68
|
+
* time.
|
|
69
|
+
*/
|
|
70
|
+
declare function isDatasetResolvable(dimensions: readonly Dimension[], filterDims?: readonly Dimension[]): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* `BuilderState`-level convenience for {@link isDatasetResolvable}: extracts
|
|
73
|
+
* the state's dimension filters (the same way `buildLogicalPlan` does) and
|
|
74
|
+
* checks them against the grouped dimensions. Lets routing code (e.g. the
|
|
75
|
+
* composite source) detect a cross-dimension query without rebuilding a plan.
|
|
76
|
+
*/
|
|
77
|
+
declare function isStateResolvable(state: BuilderState): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Errors-as-values core: builds the logical plan or returns a typed `QueryError`
|
|
80
|
+
* for every modelled failure (missing date range, a regex filter on an engine
|
|
81
|
+
* without regex pushdown, a cross-dimension query with no stored home).
|
|
82
|
+
* `buildLogicalPlan` is the throwing wrapper for call sites that prefer exceptions.
|
|
83
|
+
*/
|
|
84
|
+
declare function buildLogicalPlanResult(state: BuilderState, capabilities?: PlannerCapabilities): Result<LogicalQueryPlan, QueryError>;
|
|
85
|
+
declare function buildLogicalPlan(state: BuilderState, capabilities?: PlannerCapabilities): LogicalQueryPlan;
|
|
86
|
+
/**
|
|
87
|
+
* Errors-as-values core for the comparison plan: returns a typed `QueryError`
|
|
88
|
+
* when the engine lacks the comparison-join or multi-dataset capability the
|
|
89
|
+
* paired queries need, or when either side fails to plan.
|
|
90
|
+
*/
|
|
91
|
+
declare function buildLogicalComparisonPlanResult(current: BuilderState, previous: BuilderState, capabilities?: PlannerCapabilities, comparisonFilter?: ComparisonFilter): Result<LogicalComparisonPlan, QueryError>;
|
|
92
|
+
declare function buildLogicalComparisonPlan(current: BuilderState, previous: BuilderState, capabilities?: PlannerCapabilities, comparisonFilter?: ComparisonFilter): LogicalComparisonPlan;
|
|
3
93
|
export { ComparisonFilter, LogicalComparisonPlan, LogicalDataset, LogicalDimensionFilter, LogicalFilterGroup, LogicalFilterLeaf, LogicalFilterNode, LogicalMetricFilter, LogicalQueryPlan, PlannerCapabilities, type QueryError, type QueryErrorKind, type TableName, UnresolvableDatasetError, UnsupportedLogicalCapabilityError, buildLogicalComparisonPlan, buildLogicalComparisonPlanResult, buildLogicalPlan, buildLogicalPlanResult, inferDataset, isDatasetResolvable, isStateResolvable };
|
package/dist/query/plan.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { err, ok, unwrapResult } from "../core/result.mjs";
|
|
2
|
-
import { UnresolvableDatasetError, UnsupportedLogicalCapabilityError,
|
|
2
|
+
import { UnresolvableDatasetError, UnsupportedLogicalCapabilityError, queryErrorToException, queryErrors } from "./errors.mjs";
|
|
3
|
+
import { isDateOperator, isMetric, isQueryParam, isRegexOperator } from "./operator-meta.mjs";
|
|
4
|
+
import { extractDateRange, extractMetricFilters, extractSpecialOperatorFilters, normalizeFilter } from "./resolver.mjs";
|
|
3
5
|
function collectInternalFilters(filter) {
|
|
4
6
|
if (!filter || !("_filters" in filter)) return [];
|
|
5
7
|
const flat = filter._filters;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { GscSearchAnalyticsRequest } from "../contracts.mjs";
|
|
2
|
+
import { Result } from "../core/result.mjs";
|
|
3
|
+
import { SearchType } from "./constants.mjs";
|
|
4
|
+
import { BuilderState, Filter, FilterInput, InternalFilter } from "./types.mjs";
|
|
5
|
+
import { QueryError } from "./errors.mjs";
|
|
6
|
+
declare function normalizeFilter(input?: FilterInput): Filter<any> | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Errors-as-values core for {@link normalizeBuilderState}: returns an
|
|
9
|
+
* `invalid-builder-state` `QueryError` when the untrusted partner-API body is
|
|
10
|
+
* not an object, and an `invalid-filter` `QueryError` when a filter leaf lacks
|
|
11
|
+
* its string `dimension`/`operator`, instead of throwing. Also coerces
|
|
12
|
+
* alternative `orderBy` shapes into the canonical `{ column, dir }`.
|
|
13
|
+
* Receive-edge parse (parse, don't validate), so hosts can map a bad body to a
|
|
14
|
+
* 4xx and downstream consumers only ever see the canonical shape.
|
|
15
|
+
*/
|
|
16
|
+
declare function normalizeBuilderStateResult(state: unknown): Result<BuilderState, QueryError>;
|
|
17
|
+
declare function normalizeBuilderState(state: unknown): BuilderState;
|
|
18
|
+
declare function extractDateRange(input?: FilterInput): {
|
|
19
|
+
startDate?: string;
|
|
20
|
+
endDate?: string;
|
|
21
|
+
};
|
|
22
|
+
declare function extractMetricFilters(input?: FilterInput): InternalFilter[];
|
|
23
|
+
declare function extractSpecialOperatorFilters(input?: FilterInput): InternalFilter[];
|
|
24
|
+
/**
|
|
25
|
+
* Pull `searchType` out of a BuilderState filter. Returns undefined for
|
|
26
|
+
* missing/invalid shapes — callers treat that as "no scope" (cross-type read).
|
|
27
|
+
* Validated against the canonical `SearchTypes` set so unknown strings
|
|
28
|
+
* don't reach the engine.
|
|
29
|
+
*/
|
|
30
|
+
declare function extractSearchType(state: BuilderState | undefined | null): SearchType | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Errors-as-values core: turns a `BuilderState` into a GSC API request body or
|
|
33
|
+
* returns a typed `QueryError` for every modelled bad-query case (missing date
|
|
34
|
+
* range, out-of-range row limit / start row, hour/dataState mismatch, illegal
|
|
35
|
+
* aggregationType combination). `resolveToBody` is the throwing wrapper over this
|
|
36
|
+
* for `.toBody()` and the live-API client paths.
|
|
37
|
+
*/
|
|
38
|
+
declare function resolveToBodyResult(state: BuilderState): Result<GscSearchAnalyticsRequest, QueryError>;
|
|
39
|
+
declare function resolveToBody(state: BuilderState): GscSearchAnalyticsRequest;
|
|
40
|
+
export { extractDateRange, extractMetricFilters, extractSearchType, extractSpecialOperatorFilters, normalizeBuilderState, normalizeBuilderStateResult, normalizeFilter, resolveToBody, resolveToBodyResult };
|