gscdump 0.19.0 → 0.19.2
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/query/index.mjs +25 -0
- package/dist/query/plan.d.mts +18 -1
- package/dist/query/plan.mjs +26 -1
- package/package.json +2 -2
package/dist/query/index.mjs
CHANGED
|
@@ -2042,6 +2042,30 @@ function inferDataset(dimensions, filterDims = []) {
|
|
|
2042
2042
|
if (has("device")) return "devices";
|
|
2043
2043
|
return "devices";
|
|
2044
2044
|
}
|
|
2045
|
+
const RESOLVABLE_DIMENSION_FAMILIES = [
|
|
2046
|
+
new Set([
|
|
2047
|
+
"page",
|
|
2048
|
+
"query",
|
|
2049
|
+
"queryCanonical"
|
|
2050
|
+
]),
|
|
2051
|
+
new Set(["country"]),
|
|
2052
|
+
new Set(["device"]),
|
|
2053
|
+
new Set(["searchAppearance"])
|
|
2054
|
+
];
|
|
2055
|
+
const TIME_AXIS_DIMENSIONS = new Set(["date", "hour"]);
|
|
2056
|
+
function isDatasetResolvable(dimensions, filterDims = []) {
|
|
2057
|
+
const needed = new Set([...dimensions, ...filterDims].filter((d) => !TIME_AXIS_DIMENSIONS.has(d)));
|
|
2058
|
+
if (needed.size === 0) return true;
|
|
2059
|
+
return RESOLVABLE_DIMENSION_FAMILIES.some((family) => [...needed].every((d) => family.has(d)));
|
|
2060
|
+
}
|
|
2061
|
+
var UnresolvableDatasetError = class extends Error {
|
|
2062
|
+
constructor(dimensions, filterDims = []) {
|
|
2063
|
+
const grouped = dimensions.filter((d) => !TIME_AXIS_DIMENSIONS.has(d));
|
|
2064
|
+
const filtered = filterDims.filter((d) => !TIME_AXIS_DIMENSIONS.has(d));
|
|
2065
|
+
super(`Cannot resolve a [${grouped.join(", ")}] breakdown filtered by [${filtered.join(", ")}] from stored data: these dimensions live in separate per-dimension tables. Only the live GSC API computes cross-dimension aggregates.`);
|
|
2066
|
+
this.name = "UnresolvableDatasetError";
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2045
2069
|
function requireCapability(capabilities, capability, enabled, context) {
|
|
2046
2070
|
if (enabled && !capabilities?.[capability]) throw new UnsupportedLogicalCapabilityError(capability, context);
|
|
2047
2071
|
}
|
|
@@ -2113,6 +2137,7 @@ function buildLogicalPlan(state, capabilities = {}) {
|
|
|
2113
2137
|
}
|
|
2114
2138
|
const dimensionFilterTree = buildDimensionFilterTree(normalizedFilter, capabilities);
|
|
2115
2139
|
const filterDims = dimensionFilters.map((filter) => filter.dimension);
|
|
2140
|
+
if (!isDatasetResolvable(state.dimensions, filterDims)) throw new UnresolvableDatasetError(state.dimensions, filterDims);
|
|
2116
2141
|
return {
|
|
2117
2142
|
dataset: inferDataset(state.dimensions, filterDims),
|
|
2118
2143
|
dimensions: [...state.dimensions],
|
package/dist/query/plan.d.mts
CHANGED
|
@@ -136,6 +136,23 @@ interface LogicalComparisonPlan {
|
|
|
136
136
|
declare class UnsupportedLogicalCapabilityError extends Error {
|
|
137
137
|
constructor(capability: keyof PlannerCapabilities, context: string);
|
|
138
138
|
}
|
|
139
|
+
declare function inferDataset(dimensions: readonly Dimension[], filterDims?: readonly Dimension[]): LogicalDataset;
|
|
140
|
+
/**
|
|
141
|
+
* True when every grouped + filtered dimension fits inside one stored dataset,
|
|
142
|
+
* i.e. the query is answerable from stored Parquet/D1 tables without a live
|
|
143
|
+
* GSC call. `inferDataset` always returns *some* dataset; this predicate is
|
|
144
|
+
* how callers tell a genuine match from one that will fail at column-resolve
|
|
145
|
+
* time.
|
|
146
|
+
*/
|
|
147
|
+
declare function isDatasetResolvable(dimensions: readonly Dimension[], filterDims?: readonly Dimension[]): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Thrown when a query's grouped + filtered dimensions span more than one
|
|
150
|
+
* stored dataset. Replaces the resolver's raw "unknown column" error so hosts
|
|
151
|
+
* can map it to a 4xx instead of leaking an opaque 500.
|
|
152
|
+
*/
|
|
153
|
+
declare class UnresolvableDatasetError extends Error {
|
|
154
|
+
constructor(dimensions: readonly Dimension[], filterDims?: readonly Dimension[]);
|
|
155
|
+
}
|
|
139
156
|
declare function buildLogicalPlan(state: BuilderState, capabilities?: PlannerCapabilities): LogicalQueryPlan;
|
|
140
157
|
declare function buildLogicalComparisonPlan(current: BuilderState, previous: BuilderState, capabilities?: PlannerCapabilities, comparisonFilter?: ComparisonFilter): LogicalComparisonPlan;
|
|
141
|
-
export { ComparisonFilter, LogicalComparisonPlan, LogicalDataset, LogicalDimensionFilter, LogicalFilterGroup, LogicalFilterLeaf, LogicalFilterNode, LogicalMetricFilter, LogicalQueryPlan, PlannerCapabilities, type TableName, UnsupportedLogicalCapabilityError, buildLogicalComparisonPlan, buildLogicalPlan };
|
|
158
|
+
export { ComparisonFilter, LogicalComparisonPlan, LogicalDataset, LogicalDimensionFilter, LogicalFilterGroup, LogicalFilterLeaf, LogicalFilterNode, LogicalMetricFilter, LogicalQueryPlan, PlannerCapabilities, type TableName, UnresolvableDatasetError, UnsupportedLogicalCapabilityError, buildLogicalComparisonPlan, buildLogicalPlan, inferDataset, isDatasetResolvable };
|
package/dist/query/plan.mjs
CHANGED
|
@@ -1688,6 +1688,30 @@ function inferDataset(dimensions, filterDims = []) {
|
|
|
1688
1688
|
if (has("device")) return "devices";
|
|
1689
1689
|
return "devices";
|
|
1690
1690
|
}
|
|
1691
|
+
const RESOLVABLE_DIMENSION_FAMILIES = [
|
|
1692
|
+
new Set([
|
|
1693
|
+
"page",
|
|
1694
|
+
"query",
|
|
1695
|
+
"queryCanonical"
|
|
1696
|
+
]),
|
|
1697
|
+
new Set(["country"]),
|
|
1698
|
+
new Set(["device"]),
|
|
1699
|
+
new Set(["searchAppearance"])
|
|
1700
|
+
];
|
|
1701
|
+
const TIME_AXIS_DIMENSIONS = new Set(["date", "hour"]);
|
|
1702
|
+
function isDatasetResolvable(dimensions, filterDims = []) {
|
|
1703
|
+
const needed = new Set([...dimensions, ...filterDims].filter((d) => !TIME_AXIS_DIMENSIONS.has(d)));
|
|
1704
|
+
if (needed.size === 0) return true;
|
|
1705
|
+
return RESOLVABLE_DIMENSION_FAMILIES.some((family) => [...needed].every((d) => family.has(d)));
|
|
1706
|
+
}
|
|
1707
|
+
var UnresolvableDatasetError = class extends Error {
|
|
1708
|
+
constructor(dimensions, filterDims = []) {
|
|
1709
|
+
const grouped = dimensions.filter((d) => !TIME_AXIS_DIMENSIONS.has(d));
|
|
1710
|
+
const filtered = filterDims.filter((d) => !TIME_AXIS_DIMENSIONS.has(d));
|
|
1711
|
+
super(`Cannot resolve a [${grouped.join(", ")}] breakdown filtered by [${filtered.join(", ")}] from stored data: these dimensions live in separate per-dimension tables. Only the live GSC API computes cross-dimension aggregates.`);
|
|
1712
|
+
this.name = "UnresolvableDatasetError";
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1691
1715
|
function requireCapability(capabilities, capability, enabled, context) {
|
|
1692
1716
|
if (enabled && !capabilities?.[capability]) throw new UnsupportedLogicalCapabilityError(capability, context);
|
|
1693
1717
|
}
|
|
@@ -1759,6 +1783,7 @@ function buildLogicalPlan(state, capabilities = {}) {
|
|
|
1759
1783
|
}
|
|
1760
1784
|
const dimensionFilterTree = buildDimensionFilterTree(normalizedFilter, capabilities);
|
|
1761
1785
|
const filterDims = dimensionFilters.map((filter) => filter.dimension);
|
|
1786
|
+
if (!isDatasetResolvable(state.dimensions, filterDims)) throw new UnresolvableDatasetError(state.dimensions, filterDims);
|
|
1762
1787
|
return {
|
|
1763
1788
|
dataset: inferDataset(state.dimensions, filterDims),
|
|
1764
1789
|
dimensions: [...state.dimensions],
|
|
@@ -1806,4 +1831,4 @@ function buildLogicalComparisonPlan(current, previous, capabilities = {}, compar
|
|
|
1806
1831
|
comparisonFilter
|
|
1807
1832
|
};
|
|
1808
1833
|
}
|
|
1809
|
-
export { UnsupportedLogicalCapabilityError, buildLogicalComparisonPlan, buildLogicalPlan };
|
|
1834
|
+
export { UnresolvableDatasetError, UnsupportedLogicalCapabilityError, buildLogicalComparisonPlan, buildLogicalPlan, inferDataset, isDatasetResolvable };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gscdump",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.19.
|
|
4
|
+
"version": "0.19.2",
|
|
5
5
|
"description": "Google Search Console API wrapper with typed query builder, streaming pagination, and SEO analysis functions",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"defu": "^6.1.7",
|
|
109
109
|
"ofetch": "^1.5.1",
|
|
110
110
|
"ufo": "^1.6.4",
|
|
111
|
-
"@gscdump/contracts": "0.19.
|
|
111
|
+
"@gscdump/contracts": "0.19.2"
|
|
112
112
|
},
|
|
113
113
|
"devDependencies": {
|
|
114
114
|
"@googleapis/indexing": "^6.0.1",
|