@pythia-software/query-table-core 0.9.9 → 1.0.0

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/index.js CHANGED
@@ -1201,6 +1201,7 @@ function parseHostQueryState(input) {
1201
1201
  if (new TextEncoder().encode(JSON.stringify(state)).length > MAX_HOST_QUERY_STATE_BYTES) fail("payload too large.");
1202
1202
  return state;
1203
1203
  }
1204
+ var FIT_MODES = /* @__PURE__ */ new Set(["content", "screen"]);
1204
1205
  var EMPTY_QUERY = {
1205
1206
  select: [],
1206
1207
  where: [],
@@ -1390,7 +1391,11 @@ function normalizeQuery(input, fallback, links) {
1390
1391
  limit: boundedInteger(raw.limit, fallback.limit, 1, MAX_QUERY_LIMIT),
1391
1392
  offset: boundedInteger(raw.offset, fallback.offset, 0, MAX_QUERY_OFFSET)
1392
1393
  };
1393
- if (raw.fitToScreen === true) out.fitToScreen = true;
1394
+ if (typeof raw.fit === "string" && FIT_MODES.has(raw.fit)) out.fit = raw.fit;
1395
+ else if (raw.fitToScreen === true) {
1396
+ out.fit = "screen";
1397
+ for (const column of select) delete column.width;
1398
+ }
1394
1399
  if (links && raw.hostState !== void 0) {
1395
1400
  try {
1396
1401
  out.hostState = parseHostQueryState(raw.hostState);
@@ -1561,7 +1566,7 @@ function normalizeGroupingState(value, links) {
1561
1566
  return { keys, summaries, representative, source: normalizedSource, ...diagnostics.length ? { diagnostics: [...new Set(diagnostics)] } : {} };
1562
1567
  }
1563
1568
  var QUERY_FACETS = /* @__PURE__ */ new Set(["filters", "columns", "order", "grouping", "metrics", "limit"]);
1564
- var PATCH_QUERY_KEYS = ["select", "where", "orderBy", "limit", "aggregations", "grouping", "fitToScreen"];
1569
+ var PATCH_QUERY_KEYS = ["select", "where", "orderBy", "limit", "aggregations", "grouping", "fit"];
1565
1570
  var MAX_LINK_PARAMS = 50;
1566
1571
  function normalizeLinkSpec(value) {
1567
1572
  if (!isRecord(value) || !isRecord(value.to)) return void 0;
@@ -1613,14 +1618,15 @@ function normalizeLinkPatch(value) {
1613
1618
  if (raw.aggregations !== void 0 && (!Array.isArray(raw.aggregations) || raw.aggregations.length !== (normalized.aggregations?.length ?? 0) || normalized.aggregations?.some((clause) => clause.diagnostics?.length))) return void 0;
1614
1619
  const query = {};
1615
1620
  for (const key of PATCH_QUERY_KEYS) {
1616
- if (raw[key] !== void 0 && normalized[key] !== void 0) query[key] = normalized[key];
1621
+ const supplied = raw[key] !== void 0 || key === "fit" && raw.fitToScreen !== void 0;
1622
+ if (supplied && normalized[key] !== void 0) query[key] = normalized[key];
1617
1623
  }
1618
1624
  return { id: value.id, owns: [...new Set(value.owns)], query };
1619
1625
  }
1620
1626
  function queriesEqual(a, b) {
1621
1627
  if (a === b) return true;
1622
1628
  if (a.limit !== b.limit || a.offset !== b.offset) return false;
1623
- if (a.fitToScreen === true !== (b.fitToScreen === true)) return false;
1629
+ if (a.fit !== b.fit) return false;
1624
1630
  if (!sameArray(a.where, b.where)) return false;
1625
1631
  if (!sameArray(a.orderBy, b.orderBy)) return false;
1626
1632
  if (!sameArray(a.select, b.select)) return false;
@@ -1679,6 +1685,183 @@ function isGroupable(field) {
1679
1685
  // src/encode.ts
1680
1686
  import { deflateSync, Inflate } from "fflate";
1681
1687
 
1688
+ // src/wire.generated.ts
1689
+ var ERROR_CODES = [
1690
+ "invalid_json",
1691
+ "invalid_request",
1692
+ "method_not_allowed",
1693
+ "forbidden",
1694
+ "not_found",
1695
+ "request_too_large",
1696
+ "conflict",
1697
+ "quota_exceeded",
1698
+ "timeout",
1699
+ "cancelled",
1700
+ "unavailable",
1701
+ "internal",
1702
+ "version",
1703
+ "invalid_query",
1704
+ "profile_mismatch",
1705
+ "definition_changed",
1706
+ "execution_expired",
1707
+ "definition_unavailable",
1708
+ "definition_invalid",
1709
+ "datetime_value",
1710
+ "residual_query",
1711
+ "dataset",
1712
+ "schema",
1713
+ "source_required",
1714
+ "resolver_required",
1715
+ "resolver_scope",
1716
+ "snapshot_transaction_required",
1717
+ "snapshot_validation_required",
1718
+ "plan_token_validation_required",
1719
+ "numeric_domain_required",
1720
+ "unknown_field",
1721
+ "select_required",
1722
+ "filter",
1723
+ "filter_disabled",
1724
+ "sort",
1725
+ "collation_required",
1726
+ "unsupported_regex",
1727
+ "unsupported_literal",
1728
+ "unsupported_type",
1729
+ "invalid_source_value",
1730
+ "numeric_range",
1731
+ "encoding",
1732
+ "identity_required",
1733
+ "duplicate_identity",
1734
+ "scope",
1735
+ "window_required",
1736
+ "syntax",
1737
+ "unsupported_syntax",
1738
+ "unsupported_function",
1739
+ "arity",
1740
+ "type_error",
1741
+ "aggregate_required",
1742
+ "aggregate_context",
1743
+ "aggregate_disabled",
1744
+ "paired_expression_required",
1745
+ "dependency_cycle",
1746
+ "invalid_metric",
1747
+ "duplicate_metric",
1748
+ "metric_sort",
1749
+ "distribution",
1750
+ "date_grouping",
1751
+ "unsupported_date_grouping",
1752
+ "min_count_grouping",
1753
+ "min_count_field",
1754
+ "min_count_distribution",
1755
+ "group_disabled",
1756
+ "grouping",
1757
+ "representative",
1758
+ "resource_limit",
1759
+ "complexity_limit",
1760
+ "dependency_limit",
1761
+ "expansion_limit",
1762
+ "source_limit",
1763
+ "stage_limit",
1764
+ "schema_limit",
1765
+ "capability_unsupported",
1766
+ "unexpanded_series",
1767
+ "invalid",
1768
+ "too_large",
1769
+ "unsupported_version"
1770
+ ];
1771
+
1772
+ // src/errors.ts
1773
+ var KNOWN = new Set(ERROR_CODES);
1774
+ var isErrorCode = (value) => typeof value === "string" && KNOWN.has(value);
1775
+ var QueryError = class extends Error {
1776
+ constructor(code, message, options2 = {}) {
1777
+ super(message);
1778
+ this.name = "QueryError";
1779
+ this.code = code;
1780
+ for (const key of ["field", "offset", "metricId", "status", "capability", "cause"])
1781
+ if (options2[key] !== void 0) this[key] = options2[key];
1782
+ }
1783
+ /** The QueryErrorDetail wire shape of this error. */
1784
+ toJSON() {
1785
+ return {
1786
+ code: this.code,
1787
+ message: this.message,
1788
+ ...this.field !== void 0 ? { field: this.field } : {},
1789
+ ...this.offset !== void 0 ? { offset: this.offset } : {},
1790
+ ...this.metricId !== void 0 ? { metricId: this.metricId } : {}
1791
+ };
1792
+ }
1793
+ };
1794
+ var isQueryError = (error) => error instanceof QueryError;
1795
+ function errorCodeForStatus(status) {
1796
+ switch (status) {
1797
+ case 400:
1798
+ return "invalid_request";
1799
+ case 401:
1800
+ case 403:
1801
+ return "forbidden";
1802
+ case 404:
1803
+ return "not_found";
1804
+ case 405:
1805
+ return "method_not_allowed";
1806
+ case 408:
1807
+ return "cancelled";
1808
+ case 409:
1809
+ return "conflict";
1810
+ case 413:
1811
+ return "request_too_large";
1812
+ case 422:
1813
+ case 429:
1814
+ return "resource_limit";
1815
+ case 502:
1816
+ case 503:
1817
+ return "unavailable";
1818
+ case 504:
1819
+ return "timeout";
1820
+ case 507:
1821
+ return "quota_exceeded";
1822
+ }
1823
+ return "internal";
1824
+ }
1825
+ function parseQueryErrorBody(body) {
1826
+ const error = body?.error;
1827
+ if (!error || typeof error !== "object" || typeof error.code !== "string" || !error.code || typeof error.message !== "string") return null;
1828
+ return {
1829
+ code: error.code,
1830
+ message: error.message,
1831
+ ...typeof error.field === "string" ? { field: error.field } : {},
1832
+ ...Number.isSafeInteger(error.offset) ? { offset: error.offset } : {},
1833
+ ...typeof error.metricId === "string" ? { metricId: error.metricId } : {}
1834
+ };
1835
+ }
1836
+ function queryErrorFromBody(body, status) {
1837
+ const detail = parseQueryErrorBody(body);
1838
+ if (!detail) return null;
1839
+ const { code, message, ...rest } = detail;
1840
+ return new QueryError(code, message, { ...rest, ...status !== void 0 ? { status } : {} });
1841
+ }
1842
+ async function queryErrorFromResponse(response) {
1843
+ let text2 = "";
1844
+ try {
1845
+ text2 = await response.text();
1846
+ } catch {
1847
+ }
1848
+ let body;
1849
+ try {
1850
+ body = JSON.parse(text2);
1851
+ } catch {
1852
+ body = void 0;
1853
+ }
1854
+ const parsed = queryErrorFromBody(body, response.status);
1855
+ if (parsed) return parsed;
1856
+ const legacy = typeof body?.error === "string" ? body.error : text2.trim();
1857
+ const message = legacy && legacy.length <= 500 && !/^\s*</.test(legacy) ? legacy : `Request failed with HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}.`;
1858
+ return new QueryError(errorCodeForStatus(response.status), message, { status: response.status });
1859
+ }
1860
+ function failedMetric(id, error, fallback) {
1861
+ const code = error instanceof QueryError ? error.code : fallback;
1862
+ return { id, buckets: [], error: error instanceof Error ? error.message : String(error), errorCode: code };
1863
+ }
1864
+
1682
1865
  // src/setFilter.ts
1683
1866
  function setFilterTarget(field) {
1684
1867
  if (field.filter?.editor !== "set") return null;
@@ -1768,16 +1951,15 @@ function compactQuery(q) {
1768
1951
  if (q.offset) c.f = q.offset;
1769
1952
  if (q.aggregations?.length) c.g = q.aggregations;
1770
1953
  if (q.itemDetails) c.d = q.itemDetails;
1771
- if (q.fitToScreen) c.fs = true;
1954
+ if (q.fit) c.ft = q.fit;
1772
1955
  if (q.grouping) c.b = q.grouping;
1773
1956
  if (q.hostState) c.h = q.hostState;
1774
1957
  return c;
1775
1958
  }
1776
1959
  var MAX_QUERY_STATE_BYTES = 4 * 1024 * 1024;
1777
- var QueryDecodeError = class extends Error {
1960
+ var QueryDecodeError = class extends QueryError {
1778
1961
  constructor(code, message) {
1779
- super(message);
1780
- this.code = code;
1962
+ super(code, message);
1781
1963
  this.name = "QueryDecodeError";
1782
1964
  }
1783
1965
  };
@@ -1911,6 +2093,7 @@ function decodeToken(token, strict) {
1911
2093
  offset: c.f,
1912
2094
  aggregations: c.g,
1913
2095
  itemDetails: c.d,
2096
+ fit: c.ft,
1914
2097
  fitToScreen: c.fs,
1915
2098
  grouping: c.b,
1916
2099
  hostState: c.h
@@ -1986,29 +2169,6 @@ function termIsPushdown(term, byName, resolveField2) {
1986
2169
  return f2 != null && isPushdownFilter(f2);
1987
2170
  });
1988
2171
  }
1989
- function toAggregationQuery(q, schema) {
1990
- if (q.grouping) throw new Error("Metrics over grouped rows require grouped-query execution.");
1991
- const suppliedCount = q.where.reduce((n, t) => n + predicatesOf(t).length, 0);
1992
- q = normalizeQueryState(q);
1993
- if (q.aggregations?.some((a) => a.dateGrouping)) throw new Error("Date grouping currently requires local metric execution.");
1994
- if (q.aggregations?.some((a) => a.diagnostics?.length || a.expression !== void 0 || a.expressionY !== void 0 || a.distribution || a.scope === "shownRows" || a.sort?.length || a.groupLimit || a.where?.length || a.minCount !== void 0)) throw new Error("Metric definition requires the version 2 metric transport.");
1995
- const byName = indexFields(schema);
1996
- const resolveField2 = (name) => resolveFieldName(schema, name) ?? name;
1997
- const where = q.where.filter((term) => termIsPushdown(term, byName, resolveField2)).map(stripSetFilter);
1998
- const isBackend = (name) => {
1999
- if (name == null) return true;
2000
- const field = resolveField2(name);
2001
- const f2 = byName.get(field);
2002
- return f2 != null && f2.source.kind === "backend";
2003
- };
2004
- const aggregations = (q.aggregations ?? []).filter(
2005
- (a) => isBackend(a.field) && a.groupBy.every((g) => isBackend(g))
2006
- );
2007
- const diagnostics = [];
2008
- for (const c of q.aggregations ?? []) if (!aggregations.includes(c)) diagnostics.push({ metricId: c.id, code: "unsupported_metric", message: "Metric requires unavailable backend fields.", from: 0, to: 1 });
2009
- if (where.length !== q.where.length || suppliedCount !== q.where.reduce((n, t) => n + predicatesOf(t).length, 0)) diagnostics.push({ code: "residual_filter", message: "All-matching metrics are unavailable with residual client-only filters.", from: 0, to: 1 });
2010
- return { where, aggregations, ...diagnostics.length ? { diagnostics } : {} };
2011
- }
2012
2172
 
2013
2173
  // src/queryLinks.ts
2014
2174
  var codePattern = /^[A-Za-z0-9_-]{22}$/;
@@ -2383,14 +2543,22 @@ function httpComputedColumnStore(base, fetcher = fetch) {
2383
2543
  const listeners = /* @__PURE__ */ new Map();
2384
2544
  const url = (dataset) => `${base}${base.includes("?") ? "&" : "?"}dataset=${encodeURIComponent(dataset)}`;
2385
2545
  async function read(response) {
2386
- if (response.status === 409)
2387
- throw new Error(
2388
- "This definition changed. Reload it before saving again."
2389
- );
2390
- if (!response.ok)
2391
- throw new Error(
2392
- `Computed column store request failed (${response.status}).`
2546
+ if (!response.ok) {
2547
+ const error = await queryErrorFromResponse(response);
2548
+ if (response.status !== 409) throw error;
2549
+ const { code, field, offset, metricId, status } = error;
2550
+ throw new QueryError(
2551
+ code,
2552
+ "This definition changed. Reload it before saving again.",
2553
+ {
2554
+ ...field !== void 0 ? { field } : {},
2555
+ ...offset !== void 0 ? { offset } : {},
2556
+ ...metricId !== void 0 ? { metricId } : {},
2557
+ ...status !== void 0 ? { status } : {},
2558
+ cause: error
2559
+ }
2393
2560
  );
2561
+ }
2394
2562
  return response.json();
2395
2563
  }
2396
2564
  return {
@@ -4024,7 +4192,7 @@ function planMetricSeries(query, schema, options2 = {}) {
4024
4192
  return prepared.map((item) => {
4025
4193
  const c = item.clause;
4026
4194
  if (item.kind === "error")
4027
- return { id: c.id, buckets: [], error: item.error };
4195
+ return { id: c.id, buckets: [], error: item.error, errorCode: "invalid_metric" };
4028
4196
  if (item.kind === "empty") {
4029
4197
  const none = {
4030
4198
  id: c.id,
@@ -4039,20 +4207,20 @@ function planMetricSeries(query, schema, options2 = {}) {
4039
4207
  (part) => byId.get(part.id) ?? {
4040
4208
  id: part.id,
4041
4209
  buckets: [],
4042
- error: "Server omitted this metric."
4210
+ error: "Server omitted this metric.",
4211
+ errorCode: "internal"
4043
4212
  }
4044
4213
  );
4045
4214
  if (item.kind === "plain") return { ...entries[0], id: c.id };
4215
+ const failed = entries.find((entry) => entry.error);
4216
+ if (failed)
4217
+ return { id: c.id, buckets: [], error: failed.error, ...failed.errorCode ? { errorCode: failed.errorCode } : {} };
4046
4218
  try {
4047
4219
  if (item.kind === "occurrence")
4048
4220
  return combineOccurrences(c, entries[0]);
4049
4221
  return combineSeries(item, entries);
4050
4222
  } catch (e) {
4051
- return {
4052
- id: c.id,
4053
- buckets: [],
4054
- error: e instanceof Error ? e.message : String(e)
4055
- };
4223
+ return failedMetric(c.id, e, "invalid_metric");
4056
4224
  }
4057
4225
  });
4058
4226
  }
@@ -5205,7 +5373,7 @@ function distinctAccumulator(options2) {
5205
5373
  if (v === null || values.has(v)) return;
5206
5374
  const size = 64 + (typeof v === "string" ? v.length * 2 : 8);
5207
5375
  if (values.size >= cardinality || size > byteLimit - bytes)
5208
- throw Error("Metric distinct retained-memory budget exceeded.");
5376
+ throw new QueryError("resource_limit", "Metric distinct retained-memory budget exceeded.");
5209
5377
  values.add(v);
5210
5378
  bytes += size;
5211
5379
  },
@@ -5756,15 +5924,15 @@ function assertMetricQueryState(q, schema) {
5756
5924
  throw Error("Malformed metric query state cannot be executed exactly.");
5757
5925
  }
5758
5926
  function selectMetricShownRows(rows, q, schema, options2 = {}) {
5759
- if (options2.signal?.aborted) throw Error("Metric evaluation cancelled.");
5927
+ if (options2.signal?.aborted) throw new QueryError("cancelled", "Metric evaluation cancelled.");
5760
5928
  assertMetricQueryState(q, schema);
5761
5929
  if (options2.shownRows) return options2.shownRows;
5762
5930
  const orderBy = q.orderBy.length ? q.orderBy : schema.defaultSort ?? [];
5763
5931
  assertMetricPopulation(q, schema, orderBy);
5764
5932
  if (rows.length > (options2.maxRows ?? 1e6))
5765
- throw Error("Metric row budget exceeded.");
5933
+ throw new QueryError("resource_limit", "Metric row budget exceeded.");
5766
5934
  if ((options2.maxOperations ?? 1e7) <= 0)
5767
- throw Error("Metric operation budget exceeded.");
5935
+ throw new QueryError("resource_limit", "Metric operation budget exceeded.");
5768
5936
  return applyQuery(
5769
5937
  rows,
5770
5938
  // Local tables use a stable sort: ties retain input order. An extra ID
@@ -5805,7 +5973,7 @@ var scalar = (v) => {
5805
5973
  function groupStringLength(value, limit) {
5806
5974
  let length = value.length + 2;
5807
5975
  if (length > limit)
5808
- throw Error("Metric grouping key length budget exceeded.");
5976
+ throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
5809
5977
  for (let i = 0; i < value.length; i++) {
5810
5978
  const code = value.charCodeAt(i);
5811
5979
  if (code === 34 || code === 92 || code === 8 || code === 9 || code === 10 || code === 12 || code === 13)
@@ -5815,7 +5983,7 @@ function groupStringLength(value, limit) {
5815
5983
  i++;
5816
5984
  else if (code >= 55296 && code <= 57343) length += 5;
5817
5985
  if (length > limit)
5818
- throw Error("Metric grouping key length budget exceeded.");
5986
+ throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
5819
5987
  }
5820
5988
  return length;
5821
5989
  }
@@ -5855,8 +6023,8 @@ function sortMetricBuckets(buckets, c) {
5855
6023
  function evaluateMetrics(rows, q, schema, options2 = {}) {
5856
6024
  if (q.grouping) {
5857
6025
  try {
5858
- if (options2.signal?.aborted) throw new Error("Metric evaluation cancelled.");
5859
- if (rows.length > (options2.maxRows ?? 1e6)) throw new Error("Metric row budget exceeded.");
6026
+ if (options2.signal?.aborted) throw new QueryError("cancelled", "Metric evaluation cancelled.");
6027
+ if (rows.length > (options2.maxRows ?? 1e6)) throw new QueryError("resource_limit", "Metric row budget exceeded.");
5860
6028
  const now2 = evaluationTime(options2);
5861
6029
  const prepared = prepareGrouping(rows, schema, q.grouping, { now: now2 });
5862
6030
  return evaluateMetrics(prepared.rows, withoutGrouping(q), prepared.schema, {
@@ -5864,7 +6032,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
5864
6032
  now: now2
5865
6033
  });
5866
6034
  } catch (error) {
5867
- return { metrics: (q.aggregations ?? []).map((metric) => ({ id: metric.id, buckets: [], error: error instanceof Error ? error.message : String(error) })) };
6035
+ return { metrics: (q.aggregations ?? []).map((metric) => failedMetric(metric.id, error, "grouping")) };
5868
6036
  }
5869
6037
  }
5870
6038
  const now = evaluationTime(options2), fields = indexFields(schema);
@@ -5886,19 +6054,19 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
5886
6054
  metrics: (q.aggregations ?? []).map((c) => {
5887
6055
  let budget = options2.maxOperations ?? 1e7;
5888
6056
  const tick = () => {
5889
- if (--budget < 0) throw Error("Metric operation budget exceeded.");
6057
+ if (--budget < 0) throw new QueryError("resource_limit", "Metric operation budget exceeded.");
5890
6058
  if (options2.signal?.aborted)
5891
- throw Error("Metric evaluation cancelled.");
6059
+ throw new QueryError("cancelled", "Metric evaluation cancelled.");
5892
6060
  };
5893
6061
  try {
5894
6062
  if (options2.signal?.aborted)
5895
- throw Error("Metric evaluation cancelled.");
6063
+ throw new QueryError("cancelled", "Metric evaluation cancelled.");
5896
6064
  assertMetricQueryState(q, schema);
5897
6065
  if ((q.aggregations?.length ?? 0) > 20)
5898
- throw Error("At most 20 metrics are allowed.");
6066
+ throw new QueryError("resource_limit", "At most 20 metrics are allowed.");
5899
6067
  const diagnostics = validateMetric(c, schema, options2.resolveComputed);
5900
6068
  if (diagnostics.length)
5901
- throw Error(diagnostics.map((d) => d.message).join(" "));
6069
+ throw new QueryError("invalid_metric", diagnostics.map((d) => d.message).join(" "), { metricId: c.id });
5902
6070
  const ps = metricPlans(c, schema, options2.resolveComputed);
5903
6071
  if (ps.some(hasRegex) && !options2.evaluateInput)
5904
6072
  throw Error(
@@ -5911,7 +6079,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
5911
6079
  if (!matching) {
5912
6080
  assertMetricPopulation(q, schema, []);
5913
6081
  if (rows.length > (options2.maxRows ?? 1e6))
5914
- throw Error("Metric row budget exceeded.");
6082
+ throw new QueryError("resource_limit", "Metric row budget exceeded.");
5915
6083
  matching = applyQuery(
5916
6084
  rows,
5917
6085
  { ...q, orderBy: [], offset: 0, limit: Number.MAX_SAFE_INTEGER },
@@ -5922,7 +6090,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
5922
6090
  inputRows = matching;
5923
6091
  }
5924
6092
  if (inputRows.length > (options2.maxRows ?? 1e6))
5925
- throw Error("Metric row budget exceeded.");
6093
+ throw new QueryError("resource_limit", "Metric row budget exceeded.");
5926
6094
  const population = inputRows.length;
5927
6095
  if (c.where?.length)
5928
6096
  inputRows = applyQuery(
@@ -5942,11 +6110,11 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
5942
6110
  let groupBytes = 0;
5943
6111
  const reserve = (bytes) => {
5944
6112
  if (bytes > byteLimit - groupBytes)
5945
- throw Error("Metric grouping retained-memory budget exceeded.");
6113
+ throw new QueryError("resource_limit", "Metric grouping retained-memory budget exceeded.");
5946
6114
  };
5947
6115
  if (!c.groupBy.length) {
5948
6116
  if (keyLimit < 2)
5949
- throw Error("Metric grouping key length budget exceeded.");
6117
+ throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
5950
6118
  reserve(256);
5951
6119
  groupBytes = 256;
5952
6120
  groups.set("[]", { keys: [], rows: [] });
@@ -5995,7 +6163,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
5995
6163
  keyLength += 1 + (typeof value === "string" ? groupStringLength(value, keyLimit - keyLength - 1) : value === null ? 4 : typeof value === "boolean" ? value ? 4 : 5 : String(value).length);
5996
6164
  payloadBytes += typeof value === "string" ? 2 * value.length : 8;
5997
6165
  if (keyLength > keyLimit)
5998
- throw Error("Metric grouping key length budget exceeded.");
6166
+ throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
5999
6167
  }
6000
6168
  const retainedBytes = 256 + 16 * keys.length + payloadBytes + 2 * keyLength;
6001
6169
  reserve(retainedBytes + 2 * keyLength + 16);
@@ -6003,7 +6171,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
6003
6171
  let g = groups.get(key);
6004
6172
  if (!g) {
6005
6173
  if (groups.size >= (options2.maxGroups ?? 1e4))
6006
- throw Error("Metric group budget exceeded.");
6174
+ throw new QueryError("resource_limit", "Metric group budget exceeded.");
6007
6175
  g = { keys, rows: [] };
6008
6176
  groups.set(key, g);
6009
6177
  groupBytes += retainedBytes;
@@ -6043,10 +6211,10 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
6043
6211
  const keys = [slot, ...tuple];
6044
6212
  const key = JSON.stringify(keys);
6045
6213
  if (key.length > keyLimit)
6046
- throw Error("Metric grouping key length budget exceeded.");
6214
+ throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
6047
6215
  if (!groups.has(key)) {
6048
6216
  if (groups.size >= (options2.maxGroups ?? 1e4))
6049
- throw Error("Metric group budget exceeded.");
6217
+ throw new QueryError("resource_limit", "Metric group budget exceeded.");
6050
6218
  const bytes = 256 + 16 * keys.length + 4 * key.length;
6051
6219
  reserve(bytes);
6052
6220
  groupBytes += bytes;
@@ -6071,10 +6239,10 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
6071
6239
  ];
6072
6240
  const key = JSON.stringify(keys);
6073
6241
  if (key.length > keyLimit)
6074
- throw Error("Metric grouping key length budget exceeded.");
6242
+ throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
6075
6243
  if (!groups.has(key)) {
6076
6244
  if (groups.size >= (options2.maxGroups ?? 1e4))
6077
- throw Error("Metric group budget exceeded.");
6245
+ throw new QueryError("resource_limit", "Metric group budget exceeded.");
6078
6246
  const bytes = 256 + 16 * keys.length + 4 * key.length;
6079
6247
  reserve(bytes);
6080
6248
  groupBytes += bytes;
@@ -6087,7 +6255,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
6087
6255
  config.timeZone
6088
6256
  );
6089
6257
  if (!Number.isFinite(next) || next <= time)
6090
- throw Error("Date grouping exceeds calendar limits.");
6258
+ throw new QueryError("resource_limit", "Date grouping exceeds calendar limits.");
6091
6259
  time = next;
6092
6260
  }
6093
6261
  }
@@ -6258,11 +6426,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
6258
6426
  ...other ? { other } : {}
6259
6427
  };
6260
6428
  } catch (e) {
6261
- return {
6262
- id: c.id,
6263
- buckets: [],
6264
- error: e instanceof Error ? e.message : String(e)
6265
- };
6429
+ return failedMetric(c.id, e, "invalid_metric");
6266
6430
  }
6267
6431
  })
6268
6432
  };
@@ -6377,7 +6541,8 @@ function applyAggregations(rows, q, schema, options2 = {}) {
6377
6541
  return {
6378
6542
  id: agg.id,
6379
6543
  buckets: [],
6380
- error: `Field ${field.name} does not allow ${agg.op}.`
6544
+ error: `Field ${field.name} does not allow ${agg.op}.`,
6545
+ errorCode: "aggregate_disabled"
6381
6546
  };
6382
6547
  return {
6383
6548
  id: agg.id,
@@ -7205,6 +7370,8 @@ function resolveGroupLink(query, value, targets = {}) {
7205
7370
  }
7206
7371
 
7207
7372
  // src/queryV3.ts
7373
+ var MAX_DISTINCT_LIMIT = 500;
7374
+ var MAX_DISTINCT_SEARCH_LENGTH = 2e3;
7208
7375
  function sourceExecutionSchema(schema, execution, catalogue = []) {
7209
7376
  const fields = new Map(schema.fields.map((field) => [field.name, field]));
7210
7377
  for (const [name, capability] of Object.entries(execution?.fields ?? {})) {
@@ -7225,73 +7392,97 @@ function sourceExecutionSchema(schema, execution, catalogue = []) {
7225
7392
  }
7226
7393
  return { ...schema, fields: [...fields.values()] };
7227
7394
  }
7395
+ var profileUnavailable = (capabilities, execution) => capabilities.version !== 3 || !!execution && execution.profile !== capabilities.profile;
7396
+ var unsupported = (capability, message, field) => ({ code: "capability_unsupported", message, capability, ...field !== void 0 ? { field } : {} });
7397
+ function refuse(refusals) {
7398
+ const [first] = refusals;
7399
+ if (!first) return;
7400
+ const { code, message: _message, ...details } = first;
7401
+ throw new QueryError(code, [...new Set(refusals.map((refusal) => refusal.message))].join(" "), details);
7402
+ }
7403
+ function requestEnvelope(capabilities, execution, now, refusals) {
7404
+ const revisions = /* @__PURE__ */ Object.create(null);
7405
+ for (const [id, revision] of Object.entries(execution?.resolvedRevisions ?? {})) {
7406
+ if (!revision || id.length > 256 || revision.length > 256) refusals.push({ code: "definition_changed", message: "Invalid computed revision envelope." });
7407
+ else revisions[id.startsWith("@computed/") ? id.slice(10) : id] = revision;
7408
+ }
7409
+ return {
7410
+ version: 3,
7411
+ profile: capabilities.profile,
7412
+ now,
7413
+ expectedRevisions: revisions,
7414
+ ...execution?.planToken ? { planToken: execution.planToken } : {},
7415
+ ...execution?.snapshot !== void 0 ? { snapshot: execution.snapshot } : {}
7416
+ };
7417
+ }
7418
+ var profileRefusal = { code: "profile_mismatch", message: "The negotiated query profile is unavailable." };
7228
7419
  function toQueryRequestV3(query, baseSchema, capabilities, execution, options2 = {}) {
7229
7420
  const now = evaluationTime(options2), q = normalizeQueryState(query);
7230
7421
  const sourceSchema = sourceExecutionSchema(baseSchema, execution);
7231
7422
  const source = q.grouping?.source ?? withoutGrouping(q);
7232
- const diagnostics = [];
7233
- if ((query.aggregations?.length ?? 0) !== (q.aggregations?.length ?? 0)) diagnostics.push("The query exceeds its metric budget or contains malformed metric state.");
7234
- if (capabilities.version !== 3 || execution && execution.profile !== capabilities.profile) diagnostics.push("The negotiated query profile is unavailable.");
7423
+ const refusals = [];
7424
+ if ((query.aggregations?.length ?? 0) !== (q.aggregations?.length ?? 0))
7425
+ refusals.push({ code: "invalid_metric", message: "The query exceeds its metric budget or contains malformed metric state." });
7426
+ if (profileUnavailable(capabilities, execution)) refusals.push(profileRefusal);
7235
7427
  let resultSchema = sourceSchema;
7236
7428
  if (q.grouping) {
7237
- if (!capabilities.groupedRows) diagnostics.push("This server does not support grouped rows.");
7429
+ if (!capabilities.groupedRows) refusals.push(unsupported("groupedRows", "This server does not support grouped rows."));
7238
7430
  else if (q.grouping.keys.some(isCalendarGroupKey) && !capabilities.calendarGroupKeys)
7239
- diagnostics.push("This server does not support grouping keys by calendar interval.");
7431
+ refusals.push(unsupported("calendarGroupKeys", "This server does not support grouping keys by calendar interval."));
7432
+ for (const message of q.grouping.diagnostics ?? []) refusals.push({ code: "grouping", message });
7240
7433
  try {
7241
7434
  const model = createGroupingModel(sourceSchema);
7242
7435
  model.validate(q.grouping);
7243
7436
  resultSchema = model.resultSchema(q.grouping.keys, q.grouping.summaries);
7244
7437
  } catch (error) {
7245
- diagnostics.push(error instanceof Error ? error.message : String(error));
7438
+ refusals.push({ code: "grouping", message: error instanceof Error ? error.message : String(error) });
7246
7439
  }
7247
7440
  }
7248
7441
  const sourceRequest = toServerQuery(withoutGrouping(source), sourceSchema);
7249
7442
  for (const term of source.where) for (const predicate of predicatesOf(term)) if (["matches_regex", "not_matches_regex"].includes(predicate.op) && !capabilities.regexSourceFilters)
7250
- diagnostics.push("This server profile does not support isolated regex source filtering.");
7443
+ refusals.push(unsupported("regexSourceFilters", "This server profile does not support isolated regex source filtering.", predicate.field));
7251
7444
  const result = toServerQuery(withoutGrouping(q), resultSchema);
7252
- if (sourceRequest.where.length !== source.where.length || result.where.length !== q.where.length) diagnostics.push("The query contains unsupported source or result predicates.");
7253
- if (result.orderBy.length !== q.orderBy.length) diagnostics.push("The query contains unsupported result ordering.");
7445
+ if (sourceRequest.where.length !== source.where.length || result.where.length !== q.where.length)
7446
+ refusals.push({ code: "residual_query", message: "The query contains unsupported source or result predicates." });
7447
+ if (result.orderBy.length !== q.orderBy.length) refusals.push({ code: "residual_query", message: "The query contains unsupported result ordering." });
7254
7448
  if (q.grouping) {
7255
7449
  for (const term of q.grouping.source.where) for (const predicate of predicatesOf(term))
7256
- if (predicate.field.startsWith("@computed/") && !capabilities.computedSourceFilters) diagnostics.push("This server does not support computed source predicates.");
7450
+ if (predicate.field.startsWith("@computed/") && !capabilities.computedSourceFilters)
7451
+ refusals.push(unsupported("computedSourceFilters", "This server does not support computed source predicates.", predicate.field));
7257
7452
  }
7258
7453
  const metricQuery = toMetricQuery(withoutGrouping(q), resultSchema, execution);
7259
- diagnostics.push(...metricQuery.diagnostics.filter((issue) => issue.code === "residual_metric_filter" || issue.code === "unexpanded_series").map((issue) => issue.message));
7260
- const metrics = metricQuery.metrics.map((metric) => {
7261
- const dateGrouping = q.aggregations?.find((clause) => clause.id === metric.id)?.dateGrouping;
7262
- return dateGrouping ? { ...metric, dateGrouping } : metric;
7263
- });
7264
- for (const metric of metrics) {
7265
- diagnostics.push(...validateMetric(metric, resultSchema).map((issue) => issue.message));
7266
- if (metric.dateGrouping && !capabilities.calendarGrouping) diagnostics.push("This server does not support calendar grouping.");
7267
- else if (isMetricCycle(metric.dateGrouping?.interval) && !capabilities.calendarCycles)
7268
- diagnostics.push("This server does not support hour-of-day or day-of-week grouping.");
7269
- }
7270
- const revisions = /* @__PURE__ */ Object.create(null);
7271
- for (const [id, revision] of Object.entries(execution?.resolvedRevisions ?? {})) {
7272
- if (!revision || id.length > 256 || revision.length > 256) diagnostics.push("Invalid computed revision envelope.");
7273
- else revisions[id.startsWith("@computed/") ? id.slice(10) : id] = revision;
7274
- }
7454
+ for (const issue of metricQuery.diagnostics)
7455
+ if (issue.code === "residual_metric_filter" || issue.code === "unexpanded_series")
7456
+ refusals.push({ code: issue.code === "unexpanded_series" ? "unexpanded_series" : "residual_query", message: issue.message, ...issue.metricId ? { metricId: issue.metricId } : {} });
7457
+ const metrics = [];
7458
+ for (const { unsupportedOp, ...clause } of metricQuery.metrics) {
7459
+ const { diagnostics: _diagnostics, ...projected } = clause;
7460
+ if (unsupportedOp)
7461
+ refusals.push({ code: "invalid_metric", message: `Metric ${projected.id} uses an unsupported operation: ${unsupportedOp}.`, metricId: projected.id });
7462
+ const dateGrouping = q.aggregations?.find((c) => c.id === projected.id)?.dateGrouping;
7463
+ for (const issue of validateMetric({ ...clause, ...dateGrouping ? { dateGrouping } : {} }, resultSchema))
7464
+ refusals.push({ code: "invalid_metric", message: issue.message, metricId: projected.id });
7465
+ if (dateGrouping && !capabilities.calendarGrouping)
7466
+ refusals.push({ ...unsupported("calendarGrouping", "This server does not support calendar grouping."), metricId: projected.id });
7467
+ else if (isMetricCycle(dateGrouping?.interval) && !capabilities.calendarCycles)
7468
+ refusals.push({ ...unsupported("calendarCycles", "This server does not support hour-of-day or day-of-week grouping."), metricId: projected.id });
7469
+ metrics.push(dateGrouping ? { ...projected, dateGrouping } : projected);
7470
+ }
7471
+ const envelope = requestEnvelope(capabilities, execution, now, refusals);
7472
+ refuse(refusals);
7275
7473
  return {
7276
- version: 3,
7277
- profile: capabilities.profile,
7278
- now,
7279
- expectedRevisions: revisions,
7280
- ...execution?.planToken ? { planToken: execution.planToken } : {},
7281
- ...execution?.snapshot !== void 0 ? { snapshot: execution.snapshot } : {},
7474
+ ...envelope,
7282
7475
  source: sourceRequest,
7283
7476
  result,
7284
7477
  ...q.grouping ? { grouping: {
7285
7478
  keys: q.grouping.keys,
7286
7479
  // Summary display is presentation only and link columns are computed on
7287
7480
  // the client; the wire contract is unchanged.
7288
- summaries: withoutLinkSummaries(q.grouping.summaries).map(({ display: _display, ...summary }) => summary),
7289
- representative: q.grouping.representative,
7290
- ...q.grouping.diagnostics ? { diagnostics: q.grouping.diagnostics } : {}
7481
+ summaries: withoutLinkSummaries(q.grouping.summaries).map(({ display: _display, link: _link, ...summary }) => summary),
7482
+ representative: q.grouping.representative
7291
7483
  } } : {},
7292
7484
  rows: options2.rows !== false,
7293
- metrics,
7294
- diagnostics: [...new Set(diagnostics)]
7485
+ metrics
7295
7486
  };
7296
7487
  }
7297
7488
  function validateQueryResultV3(result, request, schema) {
@@ -7301,6 +7492,11 @@ function validateQueryResultV3(result, request, schema) {
7301
7492
  if (!Array.isArray(result.schema.fields) || result.schema.fields.some((field) => expected.get(field.name) !== field.type) || request.rows && request.result.select.some((name) => !result.schema.fields.some((field) => field.name === name))) throw Error("The server returned a different projected schema.");
7302
7493
  for (const [id, revision] of Object.entries(request.expectedRevisions)) if (result.execution.resolvedRevisions[id] !== revision)
7303
7494
  throw Error("Computed definitions changed. Refresh the server execution handshake.");
7495
+ if (request.distinct) {
7496
+ const distinct = result.distinct;
7497
+ if (!distinct || !Array.isArray(distinct.values) || distinct.values.length > request.distinct.limit || distinct.values.some((value) => typeof value !== "string") || typeof distinct.hasMore !== "boolean" || typeof distinct.hasNull !== "boolean")
7498
+ throw Error("Invalid distinct values response.");
7499
+ }
7304
7500
  const seen = /* @__PURE__ */ new Set();
7305
7501
  for (const row of result.rows) {
7306
7502
  const id = row[schema.idField];
@@ -7308,6 +7504,98 @@ function validateQueryResultV3(result, request, schema) {
7308
7504
  seen.add(id);
7309
7505
  }
7310
7506
  }
7507
+ var emptyStage = (where = []) => ({ select: [], where, orderBy: [], limit: 1, offset: 0 });
7508
+ function auxiliaryRequest(capabilities, source, result, options2, part, refusals = []) {
7509
+ if (profileUnavailable(capabilities, options2.execution)) refusals.unshift(profileRefusal);
7510
+ const envelope = requestEnvelope(capabilities, options2.execution, evaluationTime(options2), refusals);
7511
+ refuse(refusals);
7512
+ return {
7513
+ ...envelope,
7514
+ source,
7515
+ result,
7516
+ ...part,
7517
+ ...options2.hostState ? { hostState: options2.hostState } : {}
7518
+ };
7519
+ }
7520
+ async function execute(transport, request, signal) {
7521
+ const result = await transport.fetchQueryV3(request, signal);
7522
+ if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
7523
+ return result;
7524
+ }
7525
+ function toDistinctRequestV3(field, search, capabilities, options2 = {}) {
7526
+ const limit = options2.limit ?? 50, refusals = [];
7527
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_DISTINCT_LIMIT) refusals.push({ code: "invalid_query", message: `Distinct value limit must be between 1 and ${MAX_DISTINCT_LIMIT}.` });
7528
+ if (search.length > MAX_DISTINCT_SEARCH_LENGTH) refusals.push({ code: "invalid_query", message: "Distinct search exceeds its budget." });
7529
+ return auxiliaryRequest(capabilities, emptyStage(options2.where), emptyStage(), options2, { rows: false, metrics: [], distinct: { field, search, limit } }, refusals);
7530
+ }
7531
+ function readDistinctResultV3(result, request, schema) {
7532
+ validateQueryResultV3(result, request, schema);
7533
+ const { values, hasMore, hasNull } = result.distinct;
7534
+ return { values: [...values], hasMore, hasNull };
7535
+ }
7536
+ async function fetchDistinctValuesV3(transport, schema, field, search, options2 = {}, signal) {
7537
+ const request = toDistinctRequestV3(field, search, transport.queryCapabilities, options2);
7538
+ return readDistinctResultV3(await execute(transport, request, signal), request, schema);
7539
+ }
7540
+ function rowLookupFields(schema) {
7541
+ return [.../* @__PURE__ */ new Set([schema.idField, ...schema.fields.filter((field) => field.source.kind === "backend" && isSelectable(field) && !field.name.startsWith("@computed/")).map((field) => field.name)])].sort();
7542
+ }
7543
+ function toRowRequestV3(schema, id, capabilities, options2 = {}) {
7544
+ const stage = {
7545
+ select: options2.select ? [.../* @__PURE__ */ new Set([schema.idField, ...options2.select])].sort() : rowLookupFields(schema),
7546
+ where: [{ field: schema.idField, op: "=", value: String(id) }],
7547
+ orderBy: [],
7548
+ limit: Math.max(1, Math.min(2, capabilities.maxRows)),
7549
+ offset: 0
7550
+ };
7551
+ return auxiliaryRequest(capabilities, stage, stage, options2, { rows: true, metrics: [] });
7552
+ }
7553
+ function readRowResultV3(result, request, schema) {
7554
+ if (Array.isArray(result?.rows) && result.rows.length > 1 || result?.total > 1) throw Error("Row identity must be unique.");
7555
+ validateQueryResultV3(result, request, schema);
7556
+ const row = result.rows[0];
7557
+ if (row && String(row[schema.idField]) !== request.result.where[0]?.value)
7558
+ throw Error("The server returned a different row.");
7559
+ return row ?? null;
7560
+ }
7561
+ async function fetchRowV3(transport, schema, id, options2 = {}, signal) {
7562
+ const request = toRowRequestV3(schema, id, transport.queryCapabilities, options2);
7563
+ return readRowResultV3(await execute(transport, request, signal), request, schema);
7564
+ }
7565
+ var STAT_OPS = [{ op: "count_distinct", key: "distinct" }, { op: "min", key: "min" }, { op: "max", key: "max" }];
7566
+ function toFieldStatsRequestV3(schema, fields, capabilities, options2 = {}) {
7567
+ const metrics = [];
7568
+ for (const name of new Set(fields)) {
7569
+ const field = schema.fields.find((f2) => f2.name === name);
7570
+ if (!field || field.source.kind !== "backend" || !isMeasurable(field)) continue;
7571
+ const ops = aggOpsForField(field);
7572
+ for (const { op, key } of STAT_OPS) if (ops.includes(op)) metrics.push({ id: `${name}:${key}`, op, field: name, groupBy: [] });
7573
+ }
7574
+ return auxiliaryRequest(capabilities, emptyStage(), emptyStage(), options2, { rows: false, metrics });
7575
+ }
7576
+ function readFieldStatsResultV3(result, request, schema) {
7577
+ validateQueryResultV3(result, request, schema);
7578
+ const byId = new Map(result.metrics.map((metric) => [metric.id, metric]));
7579
+ const stats = /* @__PURE__ */ new Map();
7580
+ for (const metric of request.metrics) {
7581
+ const entry = byId.get(metric.id), value = entry && !entry.error ? entry.buckets[0]?.value : void 0;
7582
+ const key = metric.id.slice(metric.field.length + 1);
7583
+ const item = stats.get(metric.field) ?? {};
7584
+ stats.set(metric.field, item);
7585
+ if (key === "distinct" ? typeof value === "number" : typeof value === "number" || typeof value === "string") item[key] = value;
7586
+ }
7587
+ return Object.fromEntries(stats);
7588
+ }
7589
+ async function fetchFieldStatsV3(transport, schema, fields, options2 = {}, signal) {
7590
+ const all = toFieldStatsRequestV3(schema, fields, transport.queryCapabilities, options2);
7591
+ const stats = /* @__PURE__ */ new Map();
7592
+ for (let i = 0; i < all.metrics.length; i += MAX_AGGREGATIONS) {
7593
+ const request = { ...all, metrics: all.metrics.slice(i, i + MAX_AGGREGATIONS) };
7594
+ for (const [name, item] of Object.entries(readFieldStatsResultV3(await execute(transport, request, signal), request, schema)))
7595
+ stats.set(name, { ...stats.get(name), ...item });
7596
+ }
7597
+ return Object.fromEntries(stats);
7598
+ }
7311
7599
 
7312
7600
  // src/drilldown.ts
7313
7601
  function metricSchema(query, schema) {
@@ -7551,6 +7839,7 @@ export {
7551
7839
  AGG_OPS_NEEDING_FIELD,
7552
7840
  COMPUTED_PREFIX,
7553
7841
  EMPTY_QUERY,
7842
+ ERROR_CODES,
7554
7843
  FORMULA_FUNCTIONS,
7555
7844
  FormulaError,
7556
7845
  GROUP_ID_FIELD,
@@ -7558,6 +7847,8 @@ export {
7558
7847
  GROUP_LINK_RENDER,
7559
7848
  ITEM_DETAILS_COLUMN,
7560
7849
  MAX_AGGREGATIONS,
7850
+ MAX_DISTINCT_LIMIT,
7851
+ MAX_DISTINCT_SEARCH_LENGTH,
7561
7852
  MAX_GROUP_BY_FIELDS,
7562
7853
  MAX_HOST_QUERY_PARAMS,
7563
7854
  MAX_HOST_QUERY_STATE_BYTES,
@@ -7585,6 +7876,7 @@ export {
7585
7876
  OPS_BY_TYPE,
7586
7877
  OP_LABEL,
7587
7878
  QueryDecodeError,
7879
+ QueryError,
7588
7880
  QuerySnapshotHttpError,
7589
7881
  aggOpAllowedForType,
7590
7882
  aggOpNeedsField,
@@ -7610,9 +7902,14 @@ export {
7610
7902
  decodeQueryStrict,
7611
7903
  discoverFieldRelationships,
7612
7904
  encodeQuery,
7905
+ errorCodeForStatus,
7613
7906
  evaluateMetricReductions,
7614
7907
  evaluateMetrics,
7615
7908
  exitGrouping,
7909
+ failedMetric,
7910
+ fetchDistinctValuesV3,
7911
+ fetchFieldStatsV3,
7912
+ fetchRowV3,
7616
7913
  filterValues,
7617
7914
  formulaRuntime,
7618
7915
  groupKeyField,
@@ -7632,6 +7929,7 @@ export {
7632
7929
  isCalendarGroupKey,
7633
7930
  isComputedCellError,
7634
7931
  isComputedField,
7932
+ isErrorCode,
7635
7933
  isFilterable,
7636
7934
  isGroupLinkField,
7637
7935
  isGroupLinkValue,
@@ -7646,6 +7944,7 @@ export {
7646
7944
  isOrOfAndsGroup,
7647
7945
  isPropertyFieldLoaded,
7648
7946
  isPushdownFilter,
7947
+ isQueryError,
7649
7948
  isSelectable,
7650
7949
  isSortable,
7651
7950
  isWhereConjunction,
@@ -7696,20 +7995,27 @@ export {
7696
7995
  parseHostQueryState,
7697
7996
  parseJsonInspectionState,
7698
7997
  parseJsonInspections,
7998
+ parseQueryErrorBody,
7699
7999
  parseRelativeDuration,
7700
8000
  percentile,
7701
8001
  planMetricSeries,
7702
8002
  predicatesOf,
7703
8003
  prepareGrouping,
7704
8004
  queriesEqual,
8005
+ queryErrorFromBody,
8006
+ queryErrorFromResponse,
8007
+ readDistinctResultV3,
8008
+ readFieldStatsResultV3,
7705
8009
  readFieldValue,
7706
8010
  readObjectReference,
8011
+ readRowResultV3,
7707
8012
  readSetFilter,
7708
8013
  reduceMetricValues,
7709
8014
  removePatch,
7710
8015
  renderItemPageTemplate,
7711
8016
  resolveFieldName,
7712
8017
  resolveGroupLink,
8018
+ rowLookupFields,
7713
8019
  selectMetricShownRows,
7714
8020
  selectedFields,
7715
8021
  setFilterModes,
@@ -7718,11 +8024,11 @@ export {
7718
8024
  sourceDependencies,
7719
8025
  sourceExecutionSchema,
7720
8026
  stripSetFilter,
7721
- toAggregationQuery,
7722
- toMetricQuery,
8027
+ toDistinctRequestV3,
8028
+ toFieldStatsRequestV3,
7723
8029
  toQueryRequestV3,
8030
+ toRowRequestV3,
7724
8031
  toServerQuery,
7725
- toServerQueryV2,
7726
8032
  validateComputedColumn,
7727
8033
  validateDateGrouping,
7728
8034
  validateItemPageTemplate,