@quillsql/react 2.16.77 → 2.16.79

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
@@ -17280,7 +17280,8 @@ async function generatePivotWithSQL({
17280
17280
  getToken,
17281
17281
  reportBuilderState,
17282
17282
  // Add reportBuilderState parameter
17283
- overwriteCache = false
17283
+ overwriteCache = false,
17284
+ abortSignal
17284
17285
  }) {
17285
17286
  const databaseType = client.databaseType || "postgresql";
17286
17287
  pivot = normalizeLegacyPivotSortField(pivot);
@@ -17344,7 +17345,8 @@ async function generatePivotWithSQL({
17344
17345
  overwriteCache,
17345
17346
  dateBucket: resolvedDateBucket
17346
17347
  },
17347
- getToken
17348
+ getToken,
17349
+ abortSignal
17348
17350
  });
17349
17351
  if (resp.data?.success === false) {
17350
17352
  throw resp.data.errorMessage;
@@ -17354,9 +17356,7 @@ async function generatePivotWithSQL({
17354
17356
  const rowCount = resp.queries?.queryResults?.[1]?.rows?.[0]?.row_count || 0;
17355
17357
  parseValueFromBigQueryDates(queryResponseRows, queryResponseFields);
17356
17358
  const resultRowField = processColumnName(
17357
- String(
17358
- resp.data?.config?.rowField ?? pivot.rowField ?? ""
17359
- )
17359
+ String(resp.data?.config?.rowField ?? pivot.rowField ?? "")
17360
17360
  );
17361
17361
  const responseRows = queryResponseRows;
17362
17362
  const responseFields = queryResponseFields;
@@ -17522,7 +17522,8 @@ async function generatePivotTable({
17522
17522
  pivotQuery,
17523
17523
  reportBuilderState,
17524
17524
  // Add reportBuilderState parameter
17525
- overwriteCache
17525
+ overwriteCache,
17526
+ abortSignal
17526
17527
  }) {
17527
17528
  try {
17528
17529
  if (report && client) {
@@ -17541,11 +17542,15 @@ async function generatePivotTable({
17541
17542
  getToken,
17542
17543
  reportBuilderState,
17543
17544
  // Pass reportBuilderState
17544
- overwriteCache
17545
+ overwriteCache,
17546
+ abortSignal
17545
17547
  });
17546
17548
  return pivotTable;
17547
17549
  }
17548
17550
  } catch (e) {
17551
+ if (e instanceof Error && e.name === "AbortError") {
17552
+ throw e;
17553
+ }
17549
17554
  eventTracking?.logError?.({
17550
17555
  type: "bug",
17551
17556
  // TODO: determine type
@@ -19912,7 +19917,7 @@ var init_tableProcessing = __esm({
19912
19917
  }
19913
19918
  return { rows, columns, rowCount, error };
19914
19919
  };
19915
- fetchTableByState = async (reportBuilderState, client, getToken, tenants, eventTracking, dashboardName, processing, customFields, rowsOnly, rowCountOnly, reportId, draftSessionId) => {
19920
+ fetchTableByState = async (reportBuilderState, client, getToken, tenants, eventTracking, dashboardName, processing, customFields, rowsOnly, rowCountOnly, reportId, draftSessionId, abortSignal, filters) => {
19916
19921
  let rows = [];
19917
19922
  let columns = [];
19918
19923
  let rowCount;
@@ -19925,8 +19930,7 @@ var init_tableProcessing = __esm({
19925
19930
  processing,
19926
19931
  // report-builder-query reads `additionalProcessing` (same shape as /query); keep `processing` for compatibility
19927
19932
  additionalProcessing: processing,
19928
- filters: void 0,
19929
- // No dashboard filters for ReportBuilder table fetching
19933
+ filters,
19930
19934
  dateField: void 0,
19931
19935
  rowsOnly,
19932
19936
  rowCountOnly,
@@ -19945,7 +19949,8 @@ var init_tableProcessing = __esm({
19945
19949
  client,
19946
19950
  task: "report-builder-query",
19947
19951
  metadata: requestMetadata,
19948
- getToken
19952
+ getToken,
19953
+ abortSignal
19949
19954
  });
19950
19955
  const resp = await parseFetchResponse(
19951
19956
  client,
@@ -19979,6 +19984,9 @@ var init_tableProcessing = __esm({
19979
19984
  });
19980
19985
  }
19981
19986
  } catch (e) {
19987
+ if (e instanceof Error && e.name === "AbortError") {
19988
+ throw e;
19989
+ }
19982
19990
  eventTracking?.logError?.({
19983
19991
  type: "bug",
19984
19992
  // TODO: determine type
@@ -20008,8 +20016,8 @@ var init_tableProcessing = __esm({
20008
20016
  function setChangelogHandler(handler) {
20009
20017
  changelogHandler = handler;
20010
20018
  }
20011
- function notifyChangelogs(entries) {
20012
- changelogHandler?.(entries);
20019
+ function notifyChangelogs(entries2) {
20020
+ changelogHandler?.(entries2);
20013
20021
  }
20014
20022
  var changelogHandler;
20015
20023
  var init_changelogNotify = __esm({
@@ -20019,6 +20027,147 @@ var init_changelogNotify = __esm({
20019
20027
  }
20020
20028
  });
20021
20029
 
20030
+ // src/utils/reportRequestPool.ts
20031
+ function createAbortError(signal) {
20032
+ if (signal?.reason instanceof Error) return signal.reason;
20033
+ return Object.assign(new Error("The operation was aborted"), {
20034
+ name: "AbortError"
20035
+ });
20036
+ }
20037
+ function stableStringify(value) {
20038
+ if (value === void 0) return "undefined";
20039
+ if (value === null || typeof value !== "object") {
20040
+ return JSON.stringify(value);
20041
+ }
20042
+ if (Array.isArray(value)) {
20043
+ return `[${value.map(stableStringify).join(",")}]`;
20044
+ }
20045
+ const object = value;
20046
+ return `{${Object.keys(object).filter((key) => object[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
20047
+ }
20048
+ function normalizeAdditionalProcessing(value) {
20049
+ if (!value || typeof value !== "object") return value ?? null;
20050
+ const additionalProcessing = value;
20051
+ const page = additionalProcessing.page;
20052
+ if (!page || typeof page !== "object") return additionalProcessing;
20053
+ const pagination = page;
20054
+ return {
20055
+ ...additionalProcessing,
20056
+ page: {
20057
+ ...pagination,
20058
+ page: pagination.page ?? 0,
20059
+ rowsPerPage: pagination.rowsPerPage ?? pagination.rowsPerRequest ?? void 0
20060
+ }
20061
+ };
20062
+ }
20063
+ function createSharedRequestKey({
20064
+ client,
20065
+ task,
20066
+ metadata,
20067
+ token
20068
+ }) {
20069
+ return stableStringify({
20070
+ endpoint: client.queryEndpoint,
20071
+ clientId: client.id ?? client.clientId,
20072
+ headers: client.queryHeaders,
20073
+ withCredentials: client.withCredentials,
20074
+ token,
20075
+ task,
20076
+ metadata: {
20077
+ ...metadata,
20078
+ draftSessionId: void 0,
20079
+ overwriteCache: void 0,
20080
+ // Initial editor requests send the saved pivot/state explicitly while
20081
+ // dashboard requests ask the backend to resolve the same saved values.
20082
+ pivot: void 0,
20083
+ reportBuilderState: void 0,
20084
+ additionalProcessing: normalizeAdditionalProcessing(
20085
+ metadata?.additionalProcessing
20086
+ )
20087
+ }
20088
+ });
20089
+ }
20090
+ function cloneResult(result) {
20091
+ return JSON.parse(JSON.stringify(result));
20092
+ }
20093
+ async function getOrFetchSharedRequest({
20094
+ key,
20095
+ signal,
20096
+ fetcher
20097
+ }) {
20098
+ if (signal?.aborted) throw createAbortError(signal);
20099
+ let entry = entries.get(key);
20100
+ if (!entry) {
20101
+ const controller = new AbortController();
20102
+ entry = {
20103
+ controller,
20104
+ subscribers: 0,
20105
+ settled: false,
20106
+ promise: Promise.resolve({})
20107
+ };
20108
+ entry.promise = fetcher(controller.signal).then(
20109
+ (result) => {
20110
+ entry.settled = true;
20111
+ if (result.error || result.status === "error") {
20112
+ if (entries.get(key) === entry) entries.delete(key);
20113
+ return result;
20114
+ }
20115
+ entry.expiryTimer = setTimeout(() => {
20116
+ if (entries.get(key) === entry) entries.delete(key);
20117
+ }, COMPLETED_TTL_MS);
20118
+ return result;
20119
+ },
20120
+ (error) => {
20121
+ if (entries.get(key) === entry) entries.delete(key);
20122
+ throw error;
20123
+ }
20124
+ );
20125
+ entries.set(key, entry);
20126
+ }
20127
+ clearTimeout(entry.abortTimer);
20128
+ entry.subscribers += 1;
20129
+ return new Promise((resolve, reject) => {
20130
+ let released = false;
20131
+ const release = () => {
20132
+ if (released) return;
20133
+ released = true;
20134
+ signal?.removeEventListener("abort", onAbort);
20135
+ entry.subscribers -= 1;
20136
+ if (entry.settled || entry.subscribers > 0) return;
20137
+ entry.abortTimer = setTimeout(() => {
20138
+ if (!entry.settled && entry.subscribers === 0) {
20139
+ entry.controller.abort();
20140
+ entries.delete(key);
20141
+ }
20142
+ }, ORPHAN_GRACE_MS);
20143
+ };
20144
+ const onAbort = () => {
20145
+ release();
20146
+ reject(createAbortError(signal));
20147
+ };
20148
+ signal?.addEventListener("abort", onAbort, { once: true });
20149
+ entry.promise.then(
20150
+ (result) => {
20151
+ release();
20152
+ resolve(cloneResult(result));
20153
+ },
20154
+ (error) => {
20155
+ release();
20156
+ reject(error);
20157
+ }
20158
+ );
20159
+ });
20160
+ }
20161
+ var ORPHAN_GRACE_MS, COMPLETED_TTL_MS, entries;
20162
+ var init_reportRequestPool = __esm({
20163
+ "src/utils/reportRequestPool.ts"() {
20164
+ "use strict";
20165
+ ORPHAN_GRACE_MS = 100;
20166
+ COMPLETED_TTL_MS = 15e3;
20167
+ entries = /* @__PURE__ */ new Map();
20168
+ }
20169
+ });
20170
+
20022
20171
  // src/utils/dataFetcher.tsx
20023
20172
  var dataFetcher_exports = {};
20024
20173
  __export(dataFetcher_exports, {
@@ -20491,7 +20640,7 @@ async function fetchSqlQueryFromState(reportBuilderState, client, getToken, data
20491
20640
  return { query: "", error: `Failed to generate query: ${error.message}` };
20492
20641
  }
20493
20642
  }
20494
- async function fetchQueryDateRangesFromState(reportBuilderState, columns, client, getToken, databaseType, tenants, customFields, dashboardName, eventTracking) {
20643
+ async function fetchQueryDateRangesFromState(reportBuilderState, columns, client, getToken, databaseType, tenants, customFields, dashboardName, eventTracking, abortSignal) {
20495
20644
  try {
20496
20645
  const { data } = await quillFetch({
20497
20646
  client,
@@ -20505,7 +20654,8 @@ async function fetchQueryDateRangesFromState(reportBuilderState, columns, client
20505
20654
  customFields,
20506
20655
  dashboardName
20507
20656
  },
20508
- getToken
20657
+ getToken,
20658
+ abortSignal
20509
20659
  });
20510
20660
  if (!data || !data.rows) {
20511
20661
  return null;
@@ -20525,11 +20675,17 @@ async function fetchQueryDateRangesFromState(reportBuilderState, columns, client
20525
20675
  );
20526
20676
  return results;
20527
20677
  } catch (error) {
20678
+ if (error instanceof Error && error.name === "AbortError") {
20679
+ throw error;
20680
+ }
20528
20681
  return null;
20529
20682
  }
20530
20683
  }
20531
- async function fetchRelevantInfoFromState(reportBuilderState, tables, columns, aliasedColumns, reportBuilderColumns, client, getToken, databaseType, tenants, customFields, dashboardName, eventTracking) {
20684
+ async function fetchRelevantInfoFromState(reportBuilderState, tables, columns, aliasedColumns, reportBuilderColumns, client, getToken, databaseType, tenants, customFields, dashboardName, eventTracking, abortSignal) {
20532
20685
  try {
20686
+ if (abortSignal?.aborted) {
20687
+ throw new DOMException("Aborted", "AbortError");
20688
+ }
20533
20689
  const result = await getRelevantInfoFromQuery({
20534
20690
  tables,
20535
20691
  columns,
@@ -20552,6 +20708,9 @@ async function fetchRelevantInfoFromState(reportBuilderState, tables, columns, a
20552
20708
  error: result.error
20553
20709
  };
20554
20710
  } catch (error) {
20711
+ if (abortSignal?.aborted || error instanceof Error && error.name === "AbortError") {
20712
+ throw error;
20713
+ }
20555
20714
  return { error: error.message };
20556
20715
  }
20557
20716
  }
@@ -20564,6 +20723,7 @@ var init_dataFetcher = __esm({
20564
20723
  init_tableProcessing();
20565
20724
  init_dates();
20566
20725
  init_changelogNotify();
20726
+ init_reportRequestPool();
20567
20727
  quillFetch = async ({
20568
20728
  client,
20569
20729
  task,
@@ -20572,55 +20732,67 @@ var init_dataFetcher = __esm({
20572
20732
  abortSignal,
20573
20733
  credentials = "omit",
20574
20734
  urlParameters,
20735
+ shareRequest = false,
20575
20736
  getToken
20576
20737
  }) => {
20577
- const token = await getToken();
20578
20738
  const queryString = urlParameters ?? `task=${task}`;
20579
20739
  const endpoint = client.queryEndpoint ? `${client.queryEndpoint}?${queryString}` : `${QUILL_SERVER}${QUILL_QUERY_ENDPOINT}?${queryString}`;
20580
- try {
20581
- const response = await fetch(endpoint, {
20582
- method,
20583
- headers: {
20584
- ...token ? { Authorization: `Bearer ${token}` } : {},
20585
- ...client.queryHeaders,
20586
- "Content-Type": "application/json"
20587
- },
20588
- body: JSON.stringify({
20589
- metadata: {
20590
- task,
20591
- clientId: client.id ?? client.clientId,
20592
- ...metadata
20593
- }
20594
- }),
20595
- credentials: client.withCredentials ? "include" : credentials,
20596
- signal: abortSignal
20597
- });
20598
- if (!response.ok) {
20599
- throw new Error("API request failed");
20600
- }
20601
- let result = await response.json();
20602
- if (result.data?.data && (result.data.queries || result.data.status || result.data.error)) {
20603
- result = result.data;
20604
- }
20605
- const normalizedData = normalizeChangelogData(result.data);
20606
- if (task !== "fetch-changelog-list" && Array.isArray(normalizedData?.changelogs)) {
20607
- notifyChangelogs(normalizedData.changelogs);
20608
- }
20609
- return {
20610
- data: normalizedData,
20611
- queries: result.queries,
20612
- status: result.status,
20613
- error: result.error
20614
- };
20615
- } catch (e) {
20616
- if (e instanceof Error && e.name === "AbortError") {
20617
- throw e;
20618
- }
20619
- if (task !== "set-section-order") {
20620
- console.error("Failed to fetch:", e);
20740
+ const execute = async (signal, resolvedToken) => {
20741
+ const token2 = resolvedToken ?? await getToken();
20742
+ try {
20743
+ const response = await fetch(endpoint, {
20744
+ method,
20745
+ headers: {
20746
+ ...token2 ? { Authorization: `Bearer ${token2}` } : {},
20747
+ ...client.queryHeaders,
20748
+ "Content-Type": "application/json"
20749
+ },
20750
+ body: JSON.stringify({
20751
+ metadata: {
20752
+ task,
20753
+ clientId: client.id ?? client.clientId,
20754
+ ...metadata
20755
+ }
20756
+ }),
20757
+ credentials: client.withCredentials ? "include" : credentials,
20758
+ signal
20759
+ });
20760
+ if (!response.ok) {
20761
+ throw new Error("API request failed");
20762
+ }
20763
+ let result = await response.json();
20764
+ if (result.data?.data && (result.data.queries || result.data.status || result.data.error)) {
20765
+ result = result.data;
20766
+ }
20767
+ const normalizedData = normalizeChangelogData(result.data);
20768
+ if (task !== "fetch-changelog-list" && Array.isArray(normalizedData?.changelogs)) {
20769
+ notifyChangelogs(normalizedData.changelogs);
20770
+ }
20771
+ return {
20772
+ data: normalizedData,
20773
+ queries: result.queries,
20774
+ status: result.status,
20775
+ error: result.error
20776
+ };
20777
+ } catch (e) {
20778
+ if (e instanceof Error && e.name === "AbortError") {
20779
+ throw e;
20780
+ }
20781
+ if (task !== "set-section-order") {
20782
+ console.error("Failed to fetch:", e);
20783
+ }
20784
+ return { error: "Failed to fetch data" };
20621
20785
  }
20622
- return { error: "Failed to fetch data" };
20786
+ };
20787
+ if (!shareRequest) {
20788
+ return execute(abortSignal);
20623
20789
  }
20790
+ const token = await getToken();
20791
+ return getOrFetchSharedRequest({
20792
+ key: createSharedRequestKey({ client, task, metadata, token }),
20793
+ signal: abortSignal,
20794
+ fetcher: (signal) => execute(signal, token)
20795
+ });
20624
20796
  };
20625
20797
  parseFetchResponse = async (client, task, response, getToken, useInMemory = false) => {
20626
20798
  try {
@@ -21929,13 +22101,15 @@ async function requestPivotTemplateTask({
21929
22101
  client,
21930
22102
  metadata,
21931
22103
  getToken,
21932
- abortSignal
22104
+ abortSignal,
22105
+ shareRequest = false
21933
22106
  }) {
21934
22107
  return quillFetch({
21935
22108
  client,
21936
22109
  task: "pivot-template",
21937
22110
  metadata,
21938
22111
  abortSignal,
22112
+ shareRequest,
21939
22113
  getToken
21940
22114
  });
21941
22115
  }
@@ -22103,7 +22277,8 @@ async function fetchPivotTemplateReportForUseForm({
22103
22277
  abortSignal,
22104
22278
  baseReport,
22105
22279
  responseSelection,
22106
- draftSessionId
22280
+ draftSessionId,
22281
+ shareRequest = false
22107
22282
  }) {
22108
22283
  let fetchStep = "create-metadata";
22109
22284
  try {
@@ -22125,7 +22300,8 @@ async function fetchPivotTemplateReportForUseForm({
22125
22300
  client,
22126
22301
  metadata,
22127
22302
  getToken,
22128
- abortSignal
22303
+ abortSignal,
22304
+ shareRequest
22129
22305
  });
22130
22306
  fetchStep = "parse-task-response";
22131
22307
  const parsedResponse = await parsePivotTemplateTaskResponse({
@@ -22276,7 +22452,8 @@ async function fetchReport({
22276
22452
  pivot,
22277
22453
  reportBuilderState,
22278
22454
  skipPivotFetch,
22279
- draftSessionId
22455
+ draftSessionId,
22456
+ shareRequest = false
22280
22457
  }) {
22281
22458
  let reportInfo = void 0;
22282
22459
  let errorMessage = void 0;
@@ -22320,6 +22497,7 @@ async function fetchReport({
22320
22497
  ...resolvedDateBucket ? { dateBucket: resolvedDateBucket } : {}
22321
22498
  },
22322
22499
  abortSignal,
22500
+ shareRequest,
22323
22501
  getToken
22324
22502
  });
22325
22503
  const resp = await parseFetchResponse(client, task, fetchResp, getToken);
@@ -22987,7 +23165,8 @@ var fetchReportBuilderDataFromState = async ({
22987
23165
  dashboardName,
22988
23166
  getToken,
22989
23167
  eventTracking,
22990
- draftSessionId
23168
+ draftSessionId,
23169
+ abortSignal
22991
23170
  }) => {
22992
23171
  let newRows = [];
22993
23172
  let newColumns = [];
@@ -23061,7 +23240,8 @@ var fetchReportBuilderDataFromState = async ({
23061
23240
  skipRowCount,
23062
23241
  rowCountOnly,
23063
23242
  curReport?.id ? String(curReport.id).trim() : void 0,
23064
- draftSessionId ? String(draftSessionId).trim() : void 0
23243
+ draftSessionId ? String(draftSessionId).trim() : void 0,
23244
+ abortSignal
23065
23245
  );
23066
23246
  if (tableData.error) {
23067
23247
  throw new Error(tableData.error);
@@ -23116,7 +23296,9 @@ var fetchReportBuilderDataFromState = async ({
23116
23296
  client.databaseType?.toLowerCase() || "postgresql",
23117
23297
  tenants,
23118
23298
  customFields,
23119
- dashboardName
23299
+ dashboardName,
23300
+ void 0,
23301
+ abortSignal
23120
23302
  );
23121
23303
  const uniqueStrings = uniqueStringsByTable;
23122
23304
  const columnUniqueValues = uniqueStringsByColumn;
@@ -23136,7 +23318,9 @@ var fetchReportBuilderDataFromState = async ({
23136
23318
  client.databaseType?.toLowerCase() || "postgresql",
23137
23319
  tenants,
23138
23320
  customFields ?? [],
23139
- dashboardName
23321
+ dashboardName,
23322
+ void 0,
23323
+ abortSignal
23140
23324
  );
23141
23325
  if (dateRanges === null) {
23142
23326
  throw new Error("Couldn't fetch date ranges");
@@ -23186,8 +23370,9 @@ var fetchReportBuilderDataFromState = async ({
23186
23370
  getToken,
23187
23371
  pivotQuery: skipPivotColumnFetch ? curReport?.pivotQuery : void 0,
23188
23372
  eventTracking,
23189
- reportBuilderState
23373
+ reportBuilderState,
23190
23374
  // Pass reportBuilderState for ReportBuilder context
23375
+ abortSignal
23191
23376
  });
23192
23377
  newPivot = pivot;
23193
23378
  }
@@ -23213,6 +23398,9 @@ var fetchReportBuilderDataFromState = async ({
23213
23398
  newRows = tableData.rows;
23214
23399
  newColumns = tableData.columns;
23215
23400
  } catch (e) {
23401
+ if (e instanceof Error && e.name === "AbortError") {
23402
+ throw e;
23403
+ }
23216
23404
  eventTracking?.logError?.({
23217
23405
  type: "bug",
23218
23406
  // TODO: determine type
@@ -24166,15 +24354,226 @@ var applyFiltersInMemory = (rows, filters, options) => {
24166
24354
  // src/utils/cacheCab.ts
24167
24355
  init_inMemoryPivotEngine();
24168
24356
  import { openDB } from "idb";
24169
- import { endOfDay as endOfDay3, max as maxDate, subMonths as subMonths4, min as minDate, startOfDay as startOfDay4 } from "date-fns";
24357
+ import {
24358
+ endOfDay as endOfDay3,
24359
+ max as maxDate,
24360
+ subMonths as subMonths4,
24361
+ min as minDate,
24362
+ startOfDay as startOfDay4
24363
+ } from "date-fns";
24170
24364
  import { utcToZonedTime as utcToZonedTime2, zonedTimeToUtc } from "date-fns-tz";
24365
+
24366
+ // src/utils/cloudCacheValidation.ts
24367
+ var LIMIT_CLAUSE_REGEX = /^limit\b\s+(?:all|\d+|\$\d+|:[a-zA-Z_][a-zA-Z0-9_]*|\?)/i;
24368
+ var SQL_CONTENT_TO_IGNORE_REGEX = /'(?:''|[^'])*'|"(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|--[^\n]*|\/\*[\s\S]*?\*\//g;
24369
+ function isWordChar(char) {
24370
+ if (!char) {
24371
+ return false;
24372
+ }
24373
+ return /[A-Za-z0-9_]/.test(char);
24374
+ }
24375
+ function getFinalSqlStatement(query) {
24376
+ const statements = query.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
24377
+ return statements[statements.length - 1] ?? "";
24378
+ }
24379
+ function hasTopLevelLimitClause(statement) {
24380
+ let depth = 0;
24381
+ for (let i = 0; i < statement.length; i += 1) {
24382
+ const char = statement[i];
24383
+ if (char === "(") {
24384
+ depth += 1;
24385
+ continue;
24386
+ }
24387
+ if (char === ")") {
24388
+ depth = Math.max(0, depth - 1);
24389
+ continue;
24390
+ }
24391
+ if (depth !== 0) {
24392
+ continue;
24393
+ }
24394
+ const previousChar = i > 0 ? statement[i - 1] : void 0;
24395
+ if (isWordChar(previousChar)) {
24396
+ continue;
24397
+ }
24398
+ if (!LIMIT_CLAUSE_REGEX.test(statement.slice(i))) {
24399
+ continue;
24400
+ }
24401
+ return true;
24402
+ }
24403
+ return false;
24404
+ }
24405
+ function unwrapIdentifier(identifier) {
24406
+ if (!identifier) {
24407
+ return "";
24408
+ }
24409
+ const trimmed = identifier.trim();
24410
+ if (trimmed.length < 2) {
24411
+ return trimmed;
24412
+ }
24413
+ const startsAndEndsWithDoubleQuote = trimmed.startsWith('"') && trimmed.endsWith('"');
24414
+ const startsAndEndsWithSingleQuote = trimmed.startsWith("'") && trimmed.endsWith("'");
24415
+ const startsAndEndsWithBackticks = trimmed.startsWith("`") && trimmed.endsWith("`");
24416
+ const startsAndEndsWithBrackets = trimmed.startsWith("[") && trimmed.endsWith("]");
24417
+ if (startsAndEndsWithDoubleQuote || startsAndEndsWithSingleQuote || startsAndEndsWithBackticks || startsAndEndsWithBrackets) {
24418
+ return trimmed.slice(1, -1);
24419
+ }
24420
+ return trimmed;
24421
+ }
24422
+ function normalizeIdentifier(identifier) {
24423
+ return unwrapIdentifier(identifier).toLowerCase();
24424
+ }
24425
+ function hasRowFieldWithIdentifier(row, normalizedField) {
24426
+ if (!row || !normalizedField) {
24427
+ return false;
24428
+ }
24429
+ return Object.keys(row).some(
24430
+ (fieldName) => normalizeIdentifier(fieldName) === normalizedField
24431
+ );
24432
+ }
24433
+ function reportReferencesField(report, field, table) {
24434
+ const normalizedField = normalizeIdentifier(field);
24435
+ if (!normalizedField) {
24436
+ return false;
24437
+ }
24438
+ const referencedColumns = report?.referencedColumns ?? {};
24439
+ let entries2 = Object.entries(referencedColumns);
24440
+ if (entries2.length === 0) {
24441
+ return false;
24442
+ }
24443
+ const normalizedTable = normalizeIdentifier(table);
24444
+ if (normalizedTable) {
24445
+ entries2 = entries2.filter(
24446
+ ([tableName]) => normalizeIdentifier(tableName) === normalizedTable
24447
+ );
24448
+ }
24449
+ const referencedFields = entries2.flatMap(([, fields]) => fields ?? []);
24450
+ if (referencedFields.length === 0) {
24451
+ return false;
24452
+ }
24453
+ return referencedFields.some((referencedField) => {
24454
+ const normalizedReferencedField = normalizeIdentifier(referencedField);
24455
+ return normalizedReferencedField === normalizedField || normalizedReferencedField === "*";
24456
+ });
24457
+ }
24458
+ function isDateFieldMissingInReport(report) {
24459
+ if (!report?.dateField?.field || !report.dateField.table) {
24460
+ return false;
24461
+ }
24462
+ const dateField = report.dateField;
24463
+ if (!reportReferencesField(report, dateField.field, dateField.table)) {
24464
+ return true;
24465
+ }
24466
+ const firstRow = report.rows?.[0];
24467
+ if (!firstRow) {
24468
+ return false;
24469
+ }
24470
+ const normalizedDateField = normalizeIdentifier(dateField.field);
24471
+ const dateFieldExistsInFirstRow = hasRowFieldWithIdentifier(
24472
+ firstRow,
24473
+ normalizedDateField
24474
+ );
24475
+ return !dateFieldExistsInFirstRow;
24476
+ }
24477
+ function hasLimitClause(query) {
24478
+ if (!query) {
24479
+ return false;
24480
+ }
24481
+ const sanitizedQuery = query.replace(SQL_CONTENT_TO_IGNORE_REGEX, " ");
24482
+ const finalStatement = getFinalSqlStatement(sanitizedQuery);
24483
+ if (!finalStatement) {
24484
+ return false;
24485
+ }
24486
+ return hasTopLevelLimitClause(finalStatement);
24487
+ }
24488
+ function reportUsesLimitClause(report) {
24489
+ const queriesToInspect = report?.itemQuery && report.itemQuery.length > 0 ? report.itemQuery : report?.queryString ? [report.queryString] : [];
24490
+ return queriesToInspect.some(hasLimitClause);
24491
+ }
24492
+ function getMissingDashboardFilterFields({
24493
+ rows = [],
24494
+ dashboardFilters = []
24495
+ }) {
24496
+ const dateIndex = dashboardFilters.findIndex(
24497
+ (filter) => filter.filterType === "date_range"
24498
+ );
24499
+ const requiredFields = dashboardFilters.flatMap((filter, index) => {
24500
+ if (index === dateIndex || !filter.field) {
24501
+ return [];
24502
+ }
24503
+ const normalizedField = normalizeIdentifier(filter.field);
24504
+ if (!normalizedField) {
24505
+ return [];
24506
+ }
24507
+ return [
24508
+ {
24509
+ displayField: unwrapIdentifier(filter.field),
24510
+ normalizedField
24511
+ }
24512
+ ];
24513
+ });
24514
+ const dedupedRequiredFields = requiredFields.filter(
24515
+ (field, index, fields) => fields.findIndex(
24516
+ (candidate) => candidate.normalizedField === field.normalizedField
24517
+ ) === index
24518
+ );
24519
+ return dedupedRequiredFields.filter(
24520
+ ({ normalizedField }) => rows.some((row) => !hasRowFieldWithIdentifier(row, normalizedField))
24521
+ ).map(({ displayField }) => displayField);
24522
+ }
24523
+
24524
+ // src/utils/cacheCab.ts
24525
+ init_tableProcessing();
24526
+
24527
+ // src/utils/cacheCabSource.ts
24528
+ var stableStringify2 = (value) => {
24529
+ if (value === null || typeof value !== "object") {
24530
+ return JSON.stringify(value);
24531
+ }
24532
+ if (Array.isArray(value)) {
24533
+ return `[${value.map(stableStringify2).join(",")}]`;
24534
+ }
24535
+ const object = value;
24536
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify2(object[key])}`).join(",")}}`;
24537
+ };
24538
+ var createCacheCabSource = (reportBuilderState, config) => {
24539
+ const tableNames = [...new Set(config.referencedTables)].sort();
24540
+ const tables = reportBuilderState.tables;
24541
+ return {
24542
+ key: stableStringify2({
24543
+ version: 1,
24544
+ clientId: config.clientId,
24545
+ databaseType: config.databaseType,
24546
+ tenants: config.tenants ?? null,
24547
+ flags: config.flags ?? null,
24548
+ tables: tableNames,
24549
+ dateField: config.dateField
24550
+ }),
24551
+ dateField: config.dateField,
24552
+ state: {
24553
+ tables,
24554
+ columns: reportBuilderState.columns,
24555
+ filterStack: [],
24556
+ pivot: null,
24557
+ sort: [],
24558
+ limit: null
24559
+ }
24560
+ };
24561
+ };
24562
+
24563
+ // src/utils/cacheCab.ts
24171
24564
  var TZ = "America/Los_Angeles";
24565
+ var MAX_CACHECAB_ROWS = 1e5;
24172
24566
  var CacheCab = class {
24173
24567
  fetchedRange = {};
24174
24568
  cachedReportIds = [];
24175
24569
  uncacheableReportIDs = [];
24176
24570
  uncacheableInFlight = /* @__PURE__ */ new Map();
24571
+ reportMemoryCache = /* @__PURE__ */ new Map();
24572
+ reportLoadInFlight = /* @__PURE__ */ new Map();
24573
+ sourceAliases = /* @__PURE__ */ new Map();
24574
+ sourceFetchInFlight = /* @__PURE__ */ new Map();
24177
24575
  storage;
24576
+ metaReady;
24178
24577
  META_KEY = "cachecab:meta";
24179
24578
  DATA_PREFIX = "cachecab:data:";
24180
24579
  UNCACHEABLE_PREFIX = "cachecab:uncacheable:";
@@ -24182,13 +24581,38 @@ var CacheCab = class {
24182
24581
  storageType = "memory"
24183
24582
  } = {}) {
24184
24583
  this.storage = storageType !== "idb" ? new MemoryStorage() : new IdbStorage();
24185
- void this.loadMetaFromStorage();
24584
+ this.metaReady = this.loadMetaFromStorage();
24186
24585
  }
24187
- async get(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, pageSize, getToken, eventTracking, forceRefresh) {
24586
+ async get(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, pageSize, getToken, eventTracking, forceRefresh, sourceConfig) {
24587
+ await this.metaReady;
24188
24588
  if (this.isCached(reportId, tenants) && !forceRefresh) {
24189
- return this.getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking);
24589
+ return this.getFromCache(
24590
+ reportId,
24591
+ dashboardFilters,
24592
+ customFilters,
24593
+ pivot,
24594
+ client,
24595
+ tenants,
24596
+ flags,
24597
+ getToken,
24598
+ eventTracking,
24599
+ sourceConfig
24600
+ );
24190
24601
  } else {
24191
- return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true, forceRefresh);
24602
+ return this.addToCache(
24603
+ reportId,
24604
+ dashboardFilters,
24605
+ customFilters,
24606
+ pivot,
24607
+ client,
24608
+ tenants,
24609
+ flags,
24610
+ getToken,
24611
+ eventTracking,
24612
+ true,
24613
+ forceRefresh,
24614
+ sourceConfig
24615
+ );
24192
24616
  }
24193
24617
  }
24194
24618
  isCached(reportId, tenants) {
@@ -24197,6 +24621,47 @@ var CacheCab = class {
24197
24621
  isCacheable(reportId) {
24198
24622
  return !this.uncacheableReportIDs.includes(reportId);
24199
24623
  }
24624
+ /**
24625
+ * Returns CacheCab's raw report without fetching or applying dashboard state.
24626
+ * Consumers must only run report-builder operations locally when `complete`
24627
+ * is true; otherwise the cached rows are an unsafe sample of the report.
24628
+ */
24629
+ async getReportSnapshot(reportId, tenants) {
24630
+ await this.metaReady;
24631
+ const cacheKey = this.getCacheKey(reportId, tenants);
24632
+ if (!this.cachedReportIds.includes(cacheKey)) return null;
24633
+ const cachedReport = await this.readCachedReport(cacheKey);
24634
+ if (!cachedReport) return null;
24635
+ try {
24636
+ const report = { ...cachedReport, id: reportId };
24637
+ const rowCount = report.rows?.length ?? 0;
24638
+ const hasQueryLimit = Boolean(report.reportBuilderState?.limit) || reportUsesLimitClause(report);
24639
+ const hitRowLimit = rowCount >= MAX_CACHECAB_ROWS;
24640
+ const fetchedRange = this.fetchedRange[cacheKey];
24641
+ const coversAllTime = fetchedRange?.allTimeDoneBefore === true;
24642
+ const isSourceEntry = cacheKey.startsWith("source:");
24643
+ return {
24644
+ report,
24645
+ complete: (coversAllTime || isSourceEntry) && !hasQueryLimit && !hitRowLimit,
24646
+ reason: hasQueryLimit ? "query_limit" : hitRowLimit ? "row_limit" : !coversAllTime && !isSourceEntry ? "partial_date_range" : "complete",
24647
+ rowCount,
24648
+ fetchedRange
24649
+ };
24650
+ } catch {
24651
+ return null;
24652
+ }
24653
+ }
24654
+ async evictReport(reportId, tenants) {
24655
+ await this.metaReady;
24656
+ const cacheKey = this.getCacheKey(reportId, tenants);
24657
+ this.cachedReportIds = this.cachedReportIds.filter(
24658
+ (cachedKey) => cachedKey !== cacheKey
24659
+ );
24660
+ delete this.fetchedRange[cacheKey];
24661
+ this.reportMemoryCache.delete(cacheKey);
24662
+ await this.storage.removeItem(`${this.DATA_PREFIX}${cacheKey}`);
24663
+ await this.persistMetaToStorage();
24664
+ }
24200
24665
  async getUncacheableResult(reportId, client, tenants, flags, filters, additionalProcessing, pivot) {
24201
24666
  const key = this.getUncacheableRequestKey(
24202
24667
  reportId,
@@ -24242,7 +24707,10 @@ var CacheCab = class {
24242
24707
  if (!forceRefresh) {
24243
24708
  const raw = await this.storage.getItem(key);
24244
24709
  if (raw) {
24245
- return { report: JSON.parse(raw), fromCache: true };
24710
+ return {
24711
+ report: JSON.parse(raw),
24712
+ fromCache: true
24713
+ };
24246
24714
  }
24247
24715
  const inFlight = this.uncacheableInFlight.get(key);
24248
24716
  if (inFlight) {
@@ -24266,48 +24734,86 @@ var CacheCab = class {
24266
24734
  this.uncacheableInFlight.delete(key);
24267
24735
  }
24268
24736
  }
24269
- async addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, firstTime = true, forceRefresh = false) {
24737
+ async addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, firstTime = true, forceRefresh = false, sourceConfig) {
24270
24738
  const tenantPart = tenants ? JSON.stringify(tenants) : "";
24271
- const cacheKey = this.getCacheKey(reportId, tenants);
24739
+ let cacheKey = this.getCacheKey(reportId, tenants);
24272
24740
  if (firstTime) {
24273
- const result = await this.fetchInitialReport({ reportId, client, tenants, flags, filters: dashboardFilters, getToken, eventTracking, forceRefresh });
24741
+ const result = await this.fetchInitialReport({
24742
+ reportId,
24743
+ client,
24744
+ tenants,
24745
+ flags,
24746
+ filters: dashboardFilters,
24747
+ getToken,
24748
+ eventTracking,
24749
+ forceRefresh,
24750
+ sourceConfig
24751
+ });
24752
+ cacheKey = this.getCacheKey(reportId, tenants);
24274
24753
  try {
24275
- await this.storage.setItem(this.DATA_PREFIX + cacheKey, JSON.stringify(result));
24754
+ await this.writeCachedReport(cacheKey, result);
24276
24755
  this.cachedReportIds.push(cacheKey);
24277
24756
  await this.persistMetaToStorage();
24278
24757
  } catch {
24279
24758
  console.warn(`Failed to cache report: ${reportId}. Cache full?`);
24280
24759
  }
24281
- const newRows = await this.applyPivotsAndFilters(result, dashboardFilters, customFilters, pivot, tenantPart, false);
24760
+ const newRows = await this.applyPivotsAndFilters(
24761
+ result,
24762
+ dashboardFilters,
24763
+ customFilters,
24764
+ pivot,
24765
+ tenantPart,
24766
+ false
24767
+ );
24282
24768
  if (pivot) {
24283
24769
  const pivotColumns = generatePivotColumnsInMemory({
24284
24770
  pivot,
24285
24771
  pivotRows: newRows,
24286
24772
  sourceColumns: result.columnInternal ?? result.columns
24287
24773
  });
24288
- return { ...result, pivotRows: newRows, pivotColumns: pivotColumns ?? result.pivotColumns, pivotRowCount: newRows.length };
24774
+ return {
24775
+ ...result,
24776
+ pivotRows: newRows,
24777
+ pivotColumns: pivotColumns ?? result.pivotColumns,
24778
+ pivotRowCount: newRows.length
24779
+ };
24289
24780
  }
24290
24781
  return { ...result, rows: newRows, rowCount: newRows.length };
24291
24782
  } else {
24292
- const dbDateFilter = dashboardFilters.find((x) => x.filterType === "date_range");
24783
+ const dbDateFilter = dashboardFilters.find(
24784
+ (x) => x.filterType === "date_range"
24785
+ );
24293
24786
  const { start: requestedStart, end: requestedEnd } = normalizePSTRanges(
24294
24787
  dbDateFilter?.startDate ? new Date(dbDateFilter.startDate) : void 0,
24295
24788
  dbDateFilter?.endDate ? new Date(dbDateFilter.endDate) : void 0
24296
24789
  );
24297
24790
  if (!requestedStart || !requestedEnd) {
24298
- return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true);
24791
+ return this.addToCache(
24792
+ reportId,
24793
+ dashboardFilters,
24794
+ customFilters,
24795
+ pivot,
24796
+ client,
24797
+ tenants,
24798
+ flags,
24799
+ getToken,
24800
+ eventTracking,
24801
+ true,
24802
+ false,
24803
+ sourceConfig
24804
+ );
24299
24805
  }
24300
24806
  const currentRange = this.fetchedRange[cacheKey];
24301
24807
  const rangeStart = currentRange?.start ?? requestedStart;
24302
24808
  const rangeEnd = currentRange?.end ?? endOfDayPST(/* @__PURE__ */ new Date());
24303
- const existingRaw = await this.storage.getItem(this.DATA_PREFIX + cacheKey);
24304
24809
  let existing;
24305
24810
  let fetchedRows = [];
24306
24811
  try {
24307
- existing = JSON.parse(existingRaw);
24308
- if (!existing.rows) {
24812
+ const cachedReport = await this.readCachedReport(cacheKey);
24813
+ if (!cachedReport?.rows) {
24309
24814
  throw new Error("Invalid Cache!");
24310
24815
  }
24816
+ existing = cachedReport;
24311
24817
  if (requestedStart < rangeStart) {
24312
24818
  const olderReport = await this.fetchReport({
24313
24819
  reportId,
@@ -24364,19 +24870,33 @@ var CacheCab = class {
24364
24870
  const filteredNew = filterNewRows(boundaryRows, fetchedRows);
24365
24871
  mergedRows = filteredNew.concat(existing.rows);
24366
24872
  } else {
24367
- mergedRows = filterNewRows(existing.rows, fetchedRows).concat(existing.rows);
24873
+ mergedRows = filterNewRows(existing.rows, fetchedRows).concat(
24874
+ existing.rows
24875
+ );
24368
24876
  }
24369
24877
  const merged = { ...existing, rows: mergedRows };
24370
- await this.storage.setItem(this.DATA_PREFIX + cacheKey, JSON.stringify(merged));
24878
+ await this.writeCachedReport(cacheKey, merged);
24371
24879
  await this.persistMetaToStorage();
24372
- const newRows = await this.applyPivotsAndFilters(merged, dashboardFilters, customFilters, pivot, tenantPart, false);
24880
+ const newRows = await this.applyPivotsAndFilters(
24881
+ merged,
24882
+ dashboardFilters,
24883
+ customFilters,
24884
+ pivot,
24885
+ tenantPart,
24886
+ false
24887
+ );
24373
24888
  if (pivot) {
24374
24889
  const pivotColumns = generatePivotColumnsInMemory({
24375
24890
  pivot,
24376
24891
  pivotRows: newRows,
24377
24892
  sourceColumns: merged.columnInternal ?? merged.columns
24378
24893
  });
24379
- return { ...merged, pivotRows: newRows, pivotColumns: pivotColumns ?? merged.pivotColumns, pivotRowCount: newRows.length };
24894
+ return {
24895
+ ...merged,
24896
+ pivotRows: newRows,
24897
+ pivotColumns: pivotColumns ?? merged.pivotColumns,
24898
+ pivotRowCount: newRows.length
24899
+ };
24380
24900
  }
24381
24901
  return { ...merged, rows: newRows, rowCount: newRows.length };
24382
24902
  }
@@ -24384,37 +24904,88 @@ var CacheCab = class {
24384
24904
  /**
24385
24905
  * Returns cache entry (or re-queries if stale)
24386
24906
  */
24387
- async getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking) {
24388
- const dateRangeFilter = dashboardFilters.find((x) => x.filterType === "date_range");
24907
+ async getFromCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, sourceConfig) {
24908
+ const dateRangeFilter = dashboardFilters.find(
24909
+ (x) => x.filterType === "date_range"
24910
+ );
24389
24911
  const cacheKey = this.getCacheKey(reportId, tenants);
24390
24912
  if (!dateRangeFilter || dateRangeFilter?.primaryRange.value === "ALL_TIME") {
24391
24913
  const fetchInfo = this.fetchedRange[cacheKey];
24392
24914
  if (!fetchInfo || fetchInfo?.allTimeDoneBefore === false || Date.now() - fetchInfo.fetchedAtUTCMS >= MS_IN_DAY) {
24393
- return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, true);
24915
+ return this.addToCache(
24916
+ reportId,
24917
+ dashboardFilters,
24918
+ customFilters,
24919
+ pivot,
24920
+ client,
24921
+ tenants,
24922
+ flags,
24923
+ getToken,
24924
+ eventTracking,
24925
+ true,
24926
+ false,
24927
+ sourceConfig
24928
+ );
24394
24929
  }
24395
24930
  } else if (this.weShouldReQuery(cacheKey, dateRangeFilter)) {
24396
- return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, false);
24931
+ return this.addToCache(
24932
+ reportId,
24933
+ dashboardFilters,
24934
+ customFilters,
24935
+ pivot,
24936
+ client,
24937
+ tenants,
24938
+ flags,
24939
+ getToken,
24940
+ eventTracking,
24941
+ false,
24942
+ false,
24943
+ sourceConfig
24944
+ );
24397
24945
  }
24398
24946
  try {
24399
- const raw = await this.storage.getItem(this.DATA_PREFIX + cacheKey);
24400
- if (!raw) {
24947
+ const parsed = await this.readCachedReport(cacheKey);
24948
+ if (!parsed) {
24401
24949
  throw new Error("Invalid Cache!");
24402
24950
  }
24403
- const parsed = JSON.parse(raw);
24404
24951
  const tenantPart = tenants ? JSON.stringify(tenants) : "";
24405
- const newRows = await this.applyPivotsAndFilters(parsed, dashboardFilters, customFilters, pivot, tenantPart);
24952
+ const newRows = await this.applyPivotsAndFilters(
24953
+ parsed,
24954
+ dashboardFilters,
24955
+ customFilters,
24956
+ pivot,
24957
+ tenantPart
24958
+ );
24406
24959
  if (pivot) {
24407
24960
  const pivotColumns = generatePivotColumnsInMemory({
24408
24961
  pivot,
24409
24962
  pivotRows: newRows,
24410
24963
  sourceColumns: parsed.columnInternal ?? parsed.columns
24411
24964
  });
24412
- return { ...parsed, pivotRows: newRows, pivotColumns: pivotColumns ?? parsed.pivotColumns, pivotRowCount: newRows.length };
24965
+ return {
24966
+ ...parsed,
24967
+ pivotRows: newRows,
24968
+ pivotColumns: pivotColumns ?? parsed.pivotColumns,
24969
+ pivotRowCount: newRows.length
24970
+ };
24413
24971
  }
24414
24972
  return { ...parsed, rows: newRows, rowCount: newRows.length };
24415
24973
  } catch (err) {
24416
24974
  console.error(err);
24417
- return this.addToCache(reportId, dashboardFilters, customFilters, pivot, client, tenants, flags, getToken, eventTracking, false);
24975
+ return this.addToCache(
24976
+ reportId,
24977
+ dashboardFilters,
24978
+ customFilters,
24979
+ pivot,
24980
+ client,
24981
+ tenants,
24982
+ flags,
24983
+ getToken,
24984
+ eventTracking,
24985
+ false,
24986
+ false,
24987
+ sourceConfig
24988
+ );
24418
24989
  }
24419
24990
  }
24420
24991
  weShouldReQuery(cacheKey, dateRangeFilter) {
@@ -24435,13 +25006,17 @@ var CacheCab = class {
24435
25006
  filters = [],
24436
25007
  getToken,
24437
25008
  eventTracking,
24438
- forceRefresh
25009
+ forceRefresh,
25010
+ sourceConfig
24439
25011
  }) {
24440
25012
  let start;
24441
25013
  let end;
24442
25014
  const dateRangeFilter = filters.find((x) => x.filterType === "date_range");
24443
25015
  const cacheRange = dateRangeFilter?.initialCacheDateRange;
24444
- if (cacheRange && cacheRange.startDate) {
25016
+ if (sourceConfig) {
25017
+ start = sourceConfig.dateRange.startDate;
25018
+ end = sourceConfig.dateRange.endDate;
25019
+ } else if (cacheRange && cacheRange.startDate) {
24445
25020
  const startDate = dateRangeFilter?.startDate;
24446
25021
  start = startDate ? minDate([new Date(cacheRange.startDate), new Date(startDate)]) : new Date(cacheRange.startDate);
24447
25022
  end = endOfDayPST(/* @__PURE__ */ new Date());
@@ -24462,7 +25037,8 @@ var CacheCab = class {
24462
25037
  eventTracking,
24463
25038
  start: normalized.start,
24464
25039
  end: normalized.end,
24465
- forceRefresh
25040
+ forceRefresh,
25041
+ sourceConfig
24466
25042
  });
24467
25043
  }
24468
25044
  async fetchReport({
@@ -24475,7 +25051,8 @@ var CacheCab = class {
24475
25051
  eventTracking,
24476
25052
  start,
24477
25053
  end,
24478
- forceRefresh
25054
+ forceRefresh,
25055
+ sourceConfig
24479
25056
  }) {
24480
25057
  let reportInfo = void 0;
24481
25058
  try {
@@ -24483,100 +25060,225 @@ var CacheCab = class {
24483
25060
  const adjusted = [...filters].map((x) => {
24484
25061
  return { ...x, options: void 0 };
24485
25062
  });
24486
- const dateFilterIndex = adjusted.findIndex((x) => x.filterType === "date_range");
24487
- const isAllTime = dateFilterIndex !== -1 && adjusted[dateFilterIndex].primaryRange?.value === "ALL_TIME";
25063
+ const dateFilterIndex = adjusted.findIndex(
25064
+ (x) => x.filterType === "date_range"
25065
+ );
25066
+ const isAllTime = sourceConfig ? adjusted[dateFilterIndex]?.primaryRange?.value === "ALL_TIME" : dateFilterIndex === -1 || adjusted[dateFilterIndex].primaryRange?.value === "ALL_TIME";
24488
25067
  if (!isAllTime) {
24489
- const dateFilter = adjusted[dateFilterIndex];
24490
- if (dateFilter && dateFilter.startDate && dateFilter.endDate) {
24491
- adjusted[dateFilterIndex] = { ...dateFilter, startDate: s, endDate: e };
25068
+ const dateFilter2 = adjusted[dateFilterIndex];
25069
+ if (dateFilter2 && dateFilter2.startDate && dateFilter2.endDate) {
25070
+ adjusted[dateFilterIndex] = {
25071
+ ...dateFilter2,
25072
+ startDate: s,
25073
+ endDate: e
25074
+ };
24492
25075
  }
24493
25076
  }
24494
- const fetchResp = await quillFetch({
25077
+ const stateResponse = await quillFetch({
24495
25078
  client,
24496
- task: "report",
25079
+ task: "report-builder-state",
24497
25080
  metadata: {
24498
25081
  reportId,
24499
- clientId: client.id,
24500
- databaseType: client.databaseType,
24501
- filters: adjusted,
24502
- additionalProcessing: { page: { rowsPerPage: 1e3, rowsPerRequest: 1e5 } },
24503
- useNewNodeSql: true,
24504
- tenants,
24505
- flags,
24506
- overwriteCache: forceRefresh ?? false
25082
+ tenants
24507
25083
  },
24508
25084
  getToken
24509
25085
  });
24510
- const resp = await parseFetchResponse(
24511
- client,
24512
- "report",
24513
- fetchResp,
24514
- getToken,
24515
- true
24516
- );
24517
- reportInfo = await processReportResponse({
24518
- resp,
24519
- client,
24520
- filters: adjusted,
24521
- dateBucket: resp?.dateBucket,
24522
- additionalProcessing: { page: { rowsPerPage: 1e3, rowsPerRequest: 1e5 } },
24523
- getToken,
24524
- eventTracking,
24525
- tenants,
24526
- // CacheCab fetches with task: 'report'; keep pivot processing local.
24527
- skipPivotFetch: true,
24528
- overwriteCache: forceRefresh ?? false
25086
+ if (stateResponse.error || stateResponse.status === "error") {
25087
+ throw new Error(
25088
+ stateResponse.error || "Could not load report builder state"
25089
+ );
25090
+ }
25091
+ const reportBuilderState = stateResponse.data?.reportBuilderState;
25092
+ if (!reportBuilderState) {
25093
+ throw new Error("Report builder state was not returned");
25094
+ }
25095
+ const source = sourceConfig ? createCacheCabSource(reportBuilderState, sourceConfig) : void 0;
25096
+ const sourceCacheKey = source ? this.bindReportToSource(reportId, tenants, source.key) : this.getCacheKey(reportId, tenants);
25097
+ const sourceDateFilter = source ? {
25098
+ filterType: "date_range",
25099
+ field: source.dateField.field,
25100
+ table: source.dateField.table,
25101
+ dateField: [source.dateField],
25102
+ label: "CacheCab date range",
25103
+ dashboardName: "",
25104
+ startDate: s,
25105
+ endDate: e,
25106
+ primaryRange: { label: "Custom", value: "CUSTOM" },
25107
+ preset: { label: "Custom", value: "CUSTOM" },
25108
+ presetOptions: [],
25109
+ defaultPresetRanges: [],
25110
+ options: void 0
25111
+ } : void 0;
25112
+ const filtersForSource = source ? isAllTime ? [] : [sourceDateFilter] : adjusted;
25113
+ const builderFilters = filtersForSource.map((filter) => {
25114
+ if ("table" in filter && filter.table) return filter;
25115
+ const matchingTables = [
25116
+ ...new Set(
25117
+ reportBuilderState.columns.filter((column) => column.field === filter.field && column.table).map((column) => column.table)
25118
+ )
25119
+ ];
25120
+ return matchingTables.length === 1 ? { ...filter, table: matchingTables[0] } : filter;
24529
25121
  });
25122
+ const sourceState = source?.state ?? {
25123
+ ...reportBuilderState,
25124
+ pivot: null
25125
+ };
25126
+ const fetchSource = async () => {
25127
+ const existingRange = this.fetchedRange[sourceCacheKey];
25128
+ const rangeCovered = existingRange && (existingRange.allTimeDoneBefore || existingRange.start <= s && existingRange.end >= e);
25129
+ if (source && rangeCovered && !forceRefresh) {
25130
+ const existing = await this.readCachedReport(sourceCacheKey);
25131
+ if (existing) return existing;
25132
+ }
25133
+ const tableData = await fetchTableByState(
25134
+ sourceState,
25135
+ client,
25136
+ getToken,
25137
+ tenants,
25138
+ eventTracking,
25139
+ "",
25140
+ {
25141
+ page: {
25142
+ page: 0,
25143
+ rowsPerPage: 1e3,
25144
+ rowsPerRequest: 1e5
25145
+ }
25146
+ },
25147
+ void 0,
25148
+ true,
25149
+ false,
25150
+ reportId,
25151
+ void 0,
25152
+ void 0,
25153
+ builderFilters
25154
+ );
25155
+ if (tableData.error) {
25156
+ throw new Error(tableData.error);
25157
+ }
25158
+ const referencedColumns = sourceState.columns.reduce((result, column) => {
25159
+ const table = column.table ?? "";
25160
+ if (!result[table]) result[table] = [];
25161
+ if (!result[table].includes(column.field)) {
25162
+ result[table].push(column.field);
25163
+ }
25164
+ return result;
25165
+ }, {});
25166
+ return {
25167
+ ...EMPTY_INTERNAL_REPORT,
25168
+ id: reportId,
25169
+ name: reportId,
25170
+ rows: tableData.rows,
25171
+ rowCount: tableData.rows.length,
25172
+ columns: tableData.columns.map((column) => ({
25173
+ field: column.field,
25174
+ format: column.format,
25175
+ label: column.label
25176
+ })),
25177
+ columnInternal: tableData.columns,
25178
+ referencedTables: sourceState.tables.map((table) => table.name),
25179
+ referencedColumns,
25180
+ reportBuilderState,
25181
+ pivot: reportBuilderState.pivot,
25182
+ dateField: source?.dateField
25183
+ };
25184
+ };
25185
+ const existingSourceRequest = source ? this.sourceFetchInFlight.get(sourceCacheKey) : void 0;
25186
+ if (existingSourceRequest && !forceRefresh) {
25187
+ return { ...await existingSourceRequest, id: reportId };
25188
+ }
25189
+ const sourceRequest = fetchSource();
25190
+ if (source) this.sourceFetchInFlight.set(sourceCacheKey, sourceRequest);
25191
+ try {
25192
+ reportInfo = await sourceRequest;
25193
+ } finally {
25194
+ if (source) this.sourceFetchInFlight.delete(sourceCacheKey);
25195
+ }
25196
+ const dateFilter = builderFilters[dateFilterIndex];
25197
+ const dateFilterTable = dateFilter && "table" in dateFilter ? dateFilter.table : void 0;
25198
+ reportInfo.dateField = source?.dateField ?? (dateFilter?.field && dateFilterTable ? { field: dateFilter.field, table: dateFilterTable } : void 0);
24530
25199
  const dateField = reportInfo.dateField?.field;
24531
25200
  if (!isAllTime && dateField !== void 0 && reportInfo.rows.length > 0) {
24532
25201
  const cleanedDateField = removeQuotes(dateField);
24533
- const missingDateFieldCount = reportInfo.rows.some((row) => row[cleanedDateField] === void 0);
25202
+ const missingDateFieldCount = reportInfo.rows.some(
25203
+ (row) => row[cleanedDateField] === void 0
25204
+ );
24534
25205
  if (missingDateFieldCount) {
24535
25206
  this.uncacheableReportIDs.push(reportId);
25207
+ if (source) {
25208
+ throw new Error(
25209
+ `CacheCab source is missing date field ${dateField}`
25210
+ );
25211
+ }
24536
25212
  return EMPTY_INTERNAL_REPORT;
24537
25213
  }
24538
25214
  }
24539
25215
  const requiredFilterFields = adjusted.flatMap((x, idx) => {
24540
- if (idx === dateFilterIndex || !x.values && !x.selectedValue) return [];
25216
+ if (idx === dateFilterIndex || !x.values && !x.selectedValue)
25217
+ return [];
24541
25218
  return removeQuotes(x.field);
24542
25219
  });
24543
- const missingFields = [...new Set(requiredFilterFields.filter(
24544
- (field) => reportInfo?.rows.some((row) => row[field] === void 0)
24545
- ))];
25220
+ const missingFields = [
25221
+ ...new Set(
25222
+ requiredFilterFields.filter(
25223
+ (field) => reportInfo?.rows.some((row) => row[field] === void 0)
25224
+ )
25225
+ )
25226
+ ];
24546
25227
  if (missingFields.length > 0) {
24547
25228
  this.uncacheableReportIDs.push(reportId);
25229
+ if (source) {
25230
+ throw new Error(
25231
+ `CacheCab source is missing required fields: ${missingFields.join(", ")}`
25232
+ );
25233
+ }
24548
25234
  return EMPTY_INTERNAL_REPORT;
24549
25235
  }
24550
25236
  const cacheKey = this.getCacheKey(reportId, tenants);
24551
25237
  if (isAllTime) {
24552
- this.fetchedRange[cacheKey] = { start: /* @__PURE__ */ new Date(0), end: /* @__PURE__ */ new Date(), fetchedAtUTCMS: Date.now(), allTimeDoneBefore: true };
25238
+ this.fetchedRange[cacheKey] = {
25239
+ start: /* @__PURE__ */ new Date(0),
25240
+ end: /* @__PURE__ */ new Date(),
25241
+ fetchedAtUTCMS: Date.now(),
25242
+ allTimeDoneBefore: true
25243
+ };
24553
25244
  } else {
24554
25245
  const existing = this.fetchedRange[cacheKey];
24555
25246
  if (existing && !forceRefresh) {
24556
25247
  const oldStart = existing.start;
24557
25248
  const oldEnd = existing.end;
24558
- this.fetchedRange[cacheKey] = { start: getMinDate(oldStart, s), end: getMaxDate(oldEnd, e), fetchedAtUTCMS: Date.now(), allTimeDoneBefore: false };
25249
+ this.fetchedRange[cacheKey] = {
25250
+ start: getMinDate(oldStart, s),
25251
+ end: getMaxDate(oldEnd, e),
25252
+ fetchedAtUTCMS: Date.now(),
25253
+ allTimeDoneBefore: false
25254
+ };
24559
25255
  } else {
24560
- this.fetchedRange[cacheKey] = { start: s, end: e, fetchedAtUTCMS: Date.now(), allTimeDoneBefore: false };
25256
+ this.fetchedRange[cacheKey] = {
25257
+ start: s,
25258
+ end: e,
25259
+ fetchedAtUTCMS: Date.now(),
25260
+ allTimeDoneBefore: false
25261
+ };
24561
25262
  }
24562
25263
  }
24563
25264
  } catch (error) {
24564
25265
  console.warn(error);
24565
- if (error instanceof Error && error.name === "AbortError") {
25266
+ if (sourceConfig || error instanceof Error && error.name === "AbortError") {
24566
25267
  throw error;
24567
25268
  }
24568
25269
  }
24569
25270
  return reportInfo || EMPTY_INTERNAL_REPORT;
24570
25271
  }
24571
25272
  async applyPivotsAndFilters(report, dashboardFilters, customFilters, pivot, tenantPart, useCache = true) {
24572
- const datasetVersion = this.fetchedRange[report.id + tenantPart]?.fetchedAtUTCMS ?? 0;
25273
+ const reportCacheKey = this.sourceAliases.get(report.id + tenantPart) ?? report.id + tenantPart;
25274
+ const datasetVersion = this.fetchedRange[reportCacheKey]?.fetchedAtUTCMS ?? 0;
24573
25275
  const keyParts = [
24574
25276
  report.queryString,
24575
25277
  tenantPart,
24576
25278
  datasetVersion,
24577
25279
  // prevents fetching stale filters on hard refresh
24578
- hashString(stableStringify(dashboardFilters)),
24579
- hashString(stableStringify(customFilters))
25280
+ hashString(stableStringify3(dashboardFilters)),
25281
+ hashString(stableStringify3(customFilters))
24580
25282
  ];
24581
25283
  const queryKey = this.DATA_PREFIX + keyParts.join("|");
24582
25284
  let filtersApplied;
@@ -24590,19 +25292,30 @@ var CacheCab = class {
24590
25292
  }
24591
25293
  }
24592
25294
  if (!filtersApplied) {
24593
- const dateIndex = dashboardFilters.findIndex((x) => x.filterType === "date_range");
24594
- const requiredFilterFields = dashboardFilters.flatMap((x, idx) => {
24595
- if (idx === dateIndex || !x.values?.length && !x.selectedValue) return [];
24596
- return removeQuotes(x.field);
24597
- });
24598
- const missingFields = [...new Set(requiredFilterFields.filter(
24599
- (field) => report.rows.some((row) => row[field] === void 0)
24600
- ))];
25295
+ const dateIndex = dashboardFilters.findIndex(
25296
+ (x) => x.filterType === "date_range"
25297
+ );
25298
+ const requiredFilterFields = dashboardFilters.flatMap(
25299
+ (x, idx) => {
25300
+ if (idx === dateIndex || !x.values?.length && !x.selectedValue)
25301
+ return [];
25302
+ return removeQuotes(x.field);
25303
+ }
25304
+ );
25305
+ const missingFields = [
25306
+ ...new Set(
25307
+ requiredFilterFields.filter(
25308
+ (field) => report.rows.some((row) => row[field] === void 0)
25309
+ )
25310
+ )
25311
+ ];
24601
25312
  if (missingFields.length > 0) {
24602
25313
  this.uncacheableReportIDs.push(report.id);
24603
25314
  return [];
24604
25315
  }
24605
- const dbDateFilter = dashboardFilters.find((x) => x.filterType === "date_range");
25316
+ const dbDateFilter = dashboardFilters.find(
25317
+ (x) => x.filterType === "date_range"
25318
+ );
24606
25319
  if (report.dateField && dbDateFilter?.startDate) {
24607
25320
  const { start: startDate, end: endDate } = normalizePSTRanges(
24608
25321
  new Date(dbDateFilter.startDate),
@@ -24613,22 +25326,82 @@ var CacheCab = class {
24613
25326
  const rowDate = new Date(x[fieldToUse]);
24614
25327
  return startDate <= rowDate && (endDate ? rowDate <= endDate : true);
24615
25328
  });
24616
- filtersApplied = applyFiltersInMemory(dateFilteredRows, [], { dashboardFilters, customFilters });
25329
+ filtersApplied = applyFiltersInMemory(dateFilteredRows, [], {
25330
+ dashboardFilters,
25331
+ customFilters
25332
+ });
24617
25333
  } else {
24618
- filtersApplied = applyFiltersInMemory(report.rows, [], { dashboardFilters, customFilters });
25334
+ filtersApplied = applyFiltersInMemory(report.rows, [], {
25335
+ dashboardFilters,
25336
+ customFilters
25337
+ });
24619
25338
  }
24620
25339
  }
24621
25340
  try {
24622
25341
  await this.storage.setItem(queryKey, JSON.stringify(filtersApplied));
24623
25342
  } catch {
24624
25343
  }
24625
- const withPivot = applyPivotInMemory(filtersApplied, pivot, dashboardFilters.concat(customFilters));
25344
+ const withPivot = applyPivotInMemory(
25345
+ filtersApplied,
25346
+ pivot,
25347
+ dashboardFilters.concat(customFilters)
25348
+ );
24626
25349
  return withPivot;
24627
25350
  }
25351
+ /**
25352
+ * Reads and parses a persisted report at most once per CacheCab instance.
25353
+ * Concurrent callers share the same storage read and JSON.parse operation.
25354
+ */
25355
+ async readCachedReport(cacheKey) {
25356
+ const cached = this.reportMemoryCache.get(cacheKey);
25357
+ if (cached) return cached;
25358
+ const existingLoad = this.reportLoadInFlight.get(cacheKey);
25359
+ if (existingLoad) return existingLoad;
25360
+ const load = (async () => {
25361
+ const raw = await this.storage.getItem(`${this.DATA_PREFIX}${cacheKey}`);
25362
+ if (!raw) return null;
25363
+ try {
25364
+ const report = JSON.parse(raw);
25365
+ this.reportMemoryCache.set(cacheKey, report);
25366
+ return report;
25367
+ } catch {
25368
+ return null;
25369
+ }
25370
+ })();
25371
+ this.reportLoadInFlight.set(cacheKey, load);
25372
+ try {
25373
+ return await load;
25374
+ } finally {
25375
+ this.reportLoadInFlight.delete(cacheKey);
25376
+ }
25377
+ }
25378
+ /**
25379
+ * Persists a report and updates the parsed in-memory copy atomically from
25380
+ * CacheCab's perspective.
25381
+ */
25382
+ async writeCachedReport(cacheKey, report) {
25383
+ await this.storage.setItem(
25384
+ `${this.DATA_PREFIX}${cacheKey}`,
25385
+ JSON.stringify(report)
25386
+ );
25387
+ this.reportMemoryCache.set(cacheKey, report);
25388
+ }
24628
25389
  getCacheKey(reportId, tenants) {
24629
- const tenantPart = tenants ? JSON.stringify(tenants) : "";
25390
+ const aliasKey = this.getReportAliasKey(reportId, tenants);
25391
+ return this.sourceAliases.get(aliasKey) ?? aliasKey;
25392
+ }
25393
+ getReportAliasKey(reportId, tenants) {
25394
+ const tenantPart = tenants ? stableStringify3(tenants) : "";
24630
25395
  return reportId + tenantPart;
24631
25396
  }
25397
+ bindReportToSource(reportId, tenants, sourceKey) {
25398
+ const cacheKey = `source:${hashString(sourceKey)}`;
25399
+ this.sourceAliases.set(
25400
+ this.getReportAliasKey(reportId, tenants),
25401
+ cacheKey
25402
+ );
25403
+ return cacheKey;
25404
+ }
24632
25405
  getUncacheableRequestKey(reportId, client, tenants, flags, filters, additionalProcessing, pivot) {
24633
25406
  const canonicalFilters = filters.map(
24634
25407
  (f) => canonicalizeFilterForUncacheableKey(f)
@@ -24637,11 +25410,13 @@ var CacheCab = class {
24637
25410
  reportId,
24638
25411
  client.id,
24639
25412
  client.databaseType,
24640
- hashString(stableStringify(canonicalizeForKey(tenants ?? null))),
24641
- hashString(stableStringify(canonicalizeForKey(flags ?? null))),
24642
- hashString(stableStringify(canonicalFilters)),
24643
- hashString(stableStringify(canonicalizeForKey(additionalProcessing ?? null))),
24644
- hashString(stableStringify(canonicalizeForKey(pivot ?? null)))
25413
+ hashString(stableStringify3(canonicalizeForKey(tenants ?? null))),
25414
+ hashString(stableStringify3(canonicalizeForKey(flags ?? null))),
25415
+ hashString(stableStringify3(canonicalFilters)),
25416
+ hashString(
25417
+ stableStringify3(canonicalizeForKey(additionalProcessing ?? null))
25418
+ ),
25419
+ hashString(stableStringify3(canonicalizeForKey(pivot ?? null)))
24645
25420
  ];
24646
25421
  return this.UNCACHEABLE_PREFIX + keyParts.join("|");
24647
25422
  }
@@ -24652,6 +25427,9 @@ var CacheCab = class {
24652
25427
  const parsed = JSON.parse(meta);
24653
25428
  this.cachedReportIds = parsed.cached || [];
24654
25429
  this.uncacheableReportIDs = parsed.cannotBeCached;
25430
+ this.sourceAliases = new Map(
25431
+ Object.entries(parsed.sourceAliases ?? {})
25432
+ );
24655
25433
  this.fetchedRange = Object.fromEntries(
24656
25434
  Object.entries(parsed.fetchedRange || {}).map(([id, range]) => [
24657
25435
  id,
@@ -24674,7 +25452,8 @@ var CacheCab = class {
24674
25452
  JSON.stringify({
24675
25453
  fetchedRange: serializableRange,
24676
25454
  cached: this.cachedReportIds,
24677
- cannotBeCached: this.uncacheableReportIDs
25455
+ cannotBeCached: this.uncacheableReportIDs,
25456
+ sourceAliases: Object.fromEntries(this.sourceAliases)
24678
25457
  })
24679
25458
  );
24680
25459
  }
@@ -24687,6 +25466,9 @@ var MemoryStorage = class {
24687
25466
  async setItem(key, value) {
24688
25467
  this.store.set(key, value);
24689
25468
  }
25469
+ async removeItem(key) {
25470
+ this.store.delete(key);
25471
+ }
24690
25472
  };
24691
25473
  var DB_NAME = "quill.cachecab";
24692
25474
  var STORE_NAME = "kv";
@@ -24709,6 +25491,10 @@ var IdbStorage = class {
24709
25491
  const db = await this.dbPromise;
24710
25492
  await db.put(STORE_NAME, value, key);
24711
25493
  }
25494
+ async removeItem(key) {
25495
+ const db = await this.dbPromise;
25496
+ await db.delete(STORE_NAME, key);
25497
+ }
24712
25498
  };
24713
25499
  function removeQuotes(str) {
24714
25500
  return str.replace(/^['"]|['"]$/g, "");
@@ -24732,7 +25518,7 @@ function hashString(str) {
24732
25518
  }
24733
25519
  function filterNewRows(existingRows, newRows) {
24734
25520
  const seen = /* @__PURE__ */ new Set();
24735
- const getKey = (row) => stableStringify(row);
25521
+ const getKey = (row) => stableStringify3(row);
24736
25522
  existingRows.forEach((row) => seen.add(getKey(row)));
24737
25523
  return newRows.filter((row) => {
24738
25524
  const k = getKey(row);
@@ -24741,13 +25527,13 @@ function filterNewRows(existingRows, newRows) {
24741
25527
  return true;
24742
25528
  });
24743
25529
  }
24744
- function stableStringify(obj) {
25530
+ function stableStringify3(obj) {
24745
25531
  if (obj === null || typeof obj !== "object") return JSON.stringify(obj);
24746
25532
  if (Array.isArray(obj)) {
24747
- return `[${obj.map(stableStringify).join(",")}]`;
25533
+ return `[${obj.map(stableStringify3).join(",")}]`;
24748
25534
  }
24749
25535
  const keys = Object.keys(obj).sort();
24750
- return `{${keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",")}}`;
25536
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableStringify3(obj[k])).join(",")}}`;
24751
25537
  }
24752
25538
  function canonicalizeForKey(value) {
24753
25539
  if (value instanceof Date) {
@@ -26903,6 +27689,7 @@ import jsPDF from "jspdf";
26903
27689
 
26904
27690
  // src/hooks/useDashboard.ts
26905
27691
  import { useContext, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
27692
+ import { useQueryClient } from "@tanstack/react-query";
26906
27693
  init_dateRangePickerUtils();
26907
27694
  init_Filter();
26908
27695
  init_filterProcessing();
@@ -26946,10 +27733,11 @@ function isDashboardPivotTable(report) {
26946
27733
  return String(report?.chartType ?? "").toLowerCase() === "table" && Boolean(report?.pivot);
26947
27734
  }
26948
27735
  function getDashboardReportProcessing(report, serverPagination) {
26949
- return isDashboardPivotTable(report) ? {} : serverPagination;
27736
+ return report.pivot ? {} : serverPagination;
26950
27737
  }
26951
27738
 
26952
27739
  // src/hooks/useDashboard.ts
27740
+ var DEFAULT_DASHBOARD_REPORT_STALE_TIME_MS = 5 * 60 * 1e3;
26953
27741
  var useDashboardInternal = (dashboardName, customFilters) => {
26954
27742
  const [dashboard] = useContext(DashboardContext);
26955
27743
  const {
@@ -27644,12 +28432,15 @@ var useDashboard = (dashboardName, config) => {
27644
28432
  const fetchedInitialReports = useRef2(false);
27645
28433
  const backfilledDashboards = useRef2(false);
27646
28434
  const [client] = useContext(ClientContext);
28435
+ const queryClient = useQueryClient();
28436
+ const reportStaleTimeMs = config?.reportStaleTimeMs ?? DEFAULT_DASHBOARD_REPORT_STALE_TIME_MS;
27647
28437
  const { tenants, flags } = useContext(TenantContext);
27648
28438
  const { getToken } = useContext(FetchContext);
27649
28439
  const { customReportFilters, customFiltersLoaded, setCustomFiltersLoaded } = useContext(ReportFiltersContext);
27650
28440
  const { eventTracking } = useContext(EventTrackingContext);
27651
28441
  const { getCacheCab } = useContext(CacheCabContext);
27652
28442
  const reportRequestIds = useRef2({});
28443
+ const reportFetchAbortController = useRef2(null);
27653
28444
  const lastDashboardName = useRef2(null);
27654
28445
  const pendingNameChangeReload = useRef2(false);
27655
28446
  const [loadedDashes, setLoadedDashes] = useState2([]);
@@ -27659,6 +28450,12 @@ var useDashboard = (dashboardName, config) => {
27659
28450
  () => getCacheCab(config?.cacheType),
27660
28451
  [getCacheCab, config?.cacheType]
27661
28452
  );
28453
+ useEffect2(() => {
28454
+ return () => {
28455
+ reportFetchAbortController.current?.abort();
28456
+ reportFetchAbortController.current = null;
28457
+ };
28458
+ }, [dashboardName]);
27662
28459
  useEffect2(() => {
27663
28460
  const nameChanged = dashboardName !== lastDashboardName.current;
27664
28461
  if (nameChanged) {
@@ -27846,8 +28643,52 @@ var useDashboard = (dashboardName, config) => {
27846
28643
  if (!cacheEnabled) return;
27847
28644
  fetchReports([], dashboardFilters ?? [], config?.pageSize, true);
27848
28645
  };
28646
+ const primeReportSource = async (reportId) => {
28647
+ const reportInfo = allReportsById[reportId];
28648
+ if (!reportInfo || !client) {
28649
+ throw new Error(`Could not prime CacheCab for report ${reportId}`);
28650
+ }
28651
+ const dashboardDateFilter = dashboardFilters?.find(
28652
+ (filter) => filter.filterType === "date_range" /* Date */ && filter.startDate && filter.endDate
28653
+ );
28654
+ const dateRange = config?.cacheDateRange ?? (dashboardDateFilter?.startDate && dashboardDateFilter.endDate ? {
28655
+ startDate: new Date(dashboardDateFilter.startDate),
28656
+ endDate: new Date(dashboardDateFilter.endDate)
28657
+ } : void 0);
28658
+ if (!dateRange) {
28659
+ throw new Error(
28660
+ `CacheCab source for report ${reportId} requires a date range`
28661
+ );
28662
+ }
28663
+ await getCacheCab("memory").get(
28664
+ reportId,
28665
+ dashboardFilters ?? [],
28666
+ customReportFilters[reportId] ?? [],
28667
+ null,
28668
+ client,
28669
+ tenants,
28670
+ flags,
28671
+ void 0,
28672
+ getToken,
28673
+ eventTracking,
28674
+ false,
28675
+ {
28676
+ referencedTables: reportInfo.referencedTables ?? [],
28677
+ dateField: reportInfo.dateField,
28678
+ clientId: client.id ?? client.clientId ?? "",
28679
+ databaseType: client.databaseType,
28680
+ tenants,
28681
+ flags,
28682
+ dateRange
28683
+ }
28684
+ );
28685
+ };
27849
28686
  const fetchReports = async (customFilters, dashboardFilters2, pageSize, forceCacheToRefresh = false) => {
27850
28687
  if (!client || !sections) return;
28688
+ reportFetchAbortController.current?.abort();
28689
+ const abortController = new AbortController();
28690
+ reportFetchAbortController.current = abortController;
28691
+ const abortSignal = abortController.signal;
27851
28692
  const allReports = Object.values(sections).flat();
27852
28693
  const fetchStartTime = Date.now();
27853
28694
  let totalCached = 0;
@@ -27916,6 +28757,12 @@ var useDashboard = (dashboardName, config) => {
27916
28757
  eventTracking,
27917
28758
  forceCacheToRefresh
27918
28759
  );
28760
+ const cachedReport = {
28761
+ ...reportInfo,
28762
+ ...report2,
28763
+ id: reportId,
28764
+ pivot: reportInfo.pivot ?? report2.pivot
28765
+ };
27919
28766
  if (reportRequestIds.current[reportId] !== requestId) {
27920
28767
  return null;
27921
28768
  }
@@ -27924,7 +28771,7 @@ var useDashboard = (dashboardName, config) => {
27924
28771
  type: "UPDATE_REPORT",
27925
28772
  id: reportId,
27926
28773
  data: {
27927
- ...report2,
28774
+ ...cachedReport,
27928
28775
  pagination,
27929
28776
  triggerReload: false
27930
28777
  }
@@ -27935,7 +28782,7 @@ var useDashboard = (dashboardName, config) => {
27935
28782
  data: false
27936
28783
  });
27937
28784
  totalCached += 1;
27938
- return report2;
28785
+ return cachedReport;
27939
28786
  }
27940
28787
  }
27941
28788
  if (cacheEnabled && !cacheCab.isCacheable(reportId)) {
@@ -27958,6 +28805,7 @@ var useDashboard = (dashboardName, config) => {
27958
28805
  filters: allFilters,
27959
28806
  getToken,
27960
28807
  eventTracking,
28808
+ abortSignal,
27961
28809
  usePivotTask,
27962
28810
  overwriteCache: forceCacheToRefresh
27963
28811
  });
@@ -27997,6 +28845,7 @@ var useDashboard = (dashboardName, config) => {
27997
28845
  tenants,
27998
28846
  filters: allFilters,
27999
28847
  getToken,
28848
+ abortSignal,
28000
28849
  additionalProcessing,
28001
28850
  overwriteCache: forceCacheToRefresh
28002
28851
  }).then(({ rows, rowCount, columns, fields }) => {
@@ -28049,7 +28898,32 @@ var useDashboard = (dashboardName, config) => {
28049
28898
  return null;
28050
28899
  }
28051
28900
  }
28052
- const { report, error } = await fetchReport({
28901
+ const requestFilters = allFilters.map(
28902
+ (filter) => Object.fromEntries(
28903
+ Object.entries(filter).filter(([key]) => key !== "options")
28904
+ )
28905
+ );
28906
+ const queryKey = [
28907
+ "quill",
28908
+ "dashboard-report",
28909
+ reportId,
28910
+ {
28911
+ dashboardName,
28912
+ task: usePivotTask ? "pivot-template" : "report",
28913
+ client: {
28914
+ id: client.id ?? client.clientId,
28915
+ databaseType: client.databaseType,
28916
+ queryEndpoint: client.queryEndpoint,
28917
+ queryHeaders: client.queryHeaders,
28918
+ withCredentials: client.withCredentials
28919
+ },
28920
+ tenants,
28921
+ flags,
28922
+ filters: requestFilters,
28923
+ additionalProcessing: reportAdditionalProcessing
28924
+ }
28925
+ ];
28926
+ const fetchDashboardReport = () => fetchReport({
28053
28927
  reportId,
28054
28928
  client,
28055
28929
  tenants,
@@ -28058,10 +28932,23 @@ var useDashboard = (dashboardName, config) => {
28058
28932
  filters: allFilters,
28059
28933
  getToken,
28060
28934
  eventTracking,
28935
+ abortSignal,
28061
28936
  usePivotTask,
28062
- overwriteCache: forceCacheToRefresh
28063
- // usePivotTask: false,
28937
+ overwriteCache: forceCacheToRefresh,
28938
+ shareRequest: !forceCacheToRefresh
28064
28939
  });
28940
+ const result = forceCacheToRefresh ? await fetchDashboardReport() : await queryClient.fetchQuery({
28941
+ queryKey,
28942
+ queryFn: fetchDashboardReport,
28943
+ staleTime: reportStaleTimeMs,
28944
+ gcTime: Math.max(reportStaleTimeMs * 2, 10 * 60 * 1e3)
28945
+ });
28946
+ if (result.error) {
28947
+ queryClient.removeQueries({ queryKey, exact: true });
28948
+ } else if (forceCacheToRefresh) {
28949
+ queryClient.setQueryData(queryKey, result);
28950
+ }
28951
+ const { report, error } = result;
28065
28952
  if (reportRequestIds.current[reportId] !== requestId) {
28066
28953
  return null;
28067
28954
  }
@@ -28093,6 +28980,7 @@ var useDashboard = (dashboardName, config) => {
28093
28980
  tenants,
28094
28981
  filters: allFilters,
28095
28982
  getToken,
28983
+ abortSignal,
28096
28984
  additionalProcessing,
28097
28985
  overwriteCache: forceCacheToRefresh
28098
28986
  }).then(({ rows, rowCount, columns, fields }) => {
@@ -28126,7 +29014,11 @@ var useDashboard = (dashboardName, config) => {
28126
29014
  }
28127
29015
  return report;
28128
29016
  })
28129
- );
29017
+ ).catch((error) => {
29018
+ if (!(error instanceof Error) || error.name !== "AbortError") {
29019
+ throw error;
29020
+ }
29021
+ });
28130
29022
  if (!loadedDashes.includes(dashboardName) || !cacheEnabled || forceCacheToRefresh) {
28131
29023
  setLoadedDashes(
28132
29024
  (prev) => prev.includes(dashboardName) ? prev : [...prev, dashboardName]
@@ -28147,6 +29039,7 @@ var useDashboard = (dashboardName, config) => {
28147
29039
  applyFilters,
28148
29040
  lastUpdated,
28149
29041
  forceCacheRefresh,
29042
+ primeReportSource,
28150
29043
  reload
28151
29044
  };
28152
29045
  };
@@ -36840,164 +37733,6 @@ function DashboardFilter2({
36840
37733
  // src/Chart.tsx
36841
37734
  init_paginationProcessing();
36842
37735
 
36843
- // src/utils/cloudCacheValidation.ts
36844
- var LIMIT_CLAUSE_REGEX = /^limit\b\s+(?:all|\d+|\$\d+|:[a-zA-Z_][a-zA-Z0-9_]*|\?)/i;
36845
- var SQL_CONTENT_TO_IGNORE_REGEX = /'(?:''|[^'])*'|"(?:[^"]|"")*"|`[^`]*`|\[[^\]]*\]|--[^\n]*|\/\*[\s\S]*?\*\//g;
36846
- function isWordChar(char) {
36847
- if (!char) {
36848
- return false;
36849
- }
36850
- return /[A-Za-z0-9_]/.test(char);
36851
- }
36852
- function getFinalSqlStatement(query) {
36853
- const statements = query.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
36854
- return statements[statements.length - 1] ?? "";
36855
- }
36856
- function hasTopLevelLimitClause(statement) {
36857
- let depth = 0;
36858
- for (let i = 0; i < statement.length; i += 1) {
36859
- const char = statement[i];
36860
- if (char === "(") {
36861
- depth += 1;
36862
- continue;
36863
- }
36864
- if (char === ")") {
36865
- depth = Math.max(0, depth - 1);
36866
- continue;
36867
- }
36868
- if (depth !== 0) {
36869
- continue;
36870
- }
36871
- const previousChar = i > 0 ? statement[i - 1] : void 0;
36872
- if (isWordChar(previousChar)) {
36873
- continue;
36874
- }
36875
- if (!LIMIT_CLAUSE_REGEX.test(statement.slice(i))) {
36876
- continue;
36877
- }
36878
- return true;
36879
- }
36880
- return false;
36881
- }
36882
- function unwrapIdentifier(identifier) {
36883
- if (!identifier) {
36884
- return "";
36885
- }
36886
- const trimmed = identifier.trim();
36887
- if (trimmed.length < 2) {
36888
- return trimmed;
36889
- }
36890
- const startsAndEndsWithDoubleQuote = trimmed.startsWith('"') && trimmed.endsWith('"');
36891
- const startsAndEndsWithSingleQuote = trimmed.startsWith("'") && trimmed.endsWith("'");
36892
- const startsAndEndsWithBackticks = trimmed.startsWith("`") && trimmed.endsWith("`");
36893
- const startsAndEndsWithBrackets = trimmed.startsWith("[") && trimmed.endsWith("]");
36894
- if (startsAndEndsWithDoubleQuote || startsAndEndsWithSingleQuote || startsAndEndsWithBackticks || startsAndEndsWithBrackets) {
36895
- return trimmed.slice(1, -1);
36896
- }
36897
- return trimmed;
36898
- }
36899
- function normalizeIdentifier(identifier) {
36900
- return unwrapIdentifier(identifier).toLowerCase();
36901
- }
36902
- function hasRowFieldWithIdentifier(row, normalizedField) {
36903
- if (!row || !normalizedField) {
36904
- return false;
36905
- }
36906
- return Object.keys(row).some(
36907
- (fieldName) => normalizeIdentifier(fieldName) === normalizedField
36908
- );
36909
- }
36910
- function reportReferencesField(report, field, table) {
36911
- const normalizedField = normalizeIdentifier(field);
36912
- if (!normalizedField) {
36913
- return false;
36914
- }
36915
- const referencedColumns = report?.referencedColumns ?? {};
36916
- let entries = Object.entries(referencedColumns);
36917
- if (entries.length === 0) {
36918
- return false;
36919
- }
36920
- const normalizedTable = normalizeIdentifier(table);
36921
- if (normalizedTable) {
36922
- entries = entries.filter(
36923
- ([tableName]) => normalizeIdentifier(tableName) === normalizedTable
36924
- );
36925
- }
36926
- const referencedFields = entries.flatMap(([, fields]) => fields ?? []);
36927
- if (referencedFields.length === 0) {
36928
- return false;
36929
- }
36930
- return referencedFields.some((referencedField) => {
36931
- const normalizedReferencedField = normalizeIdentifier(referencedField);
36932
- return normalizedReferencedField === normalizedField || normalizedReferencedField === "*";
36933
- });
36934
- }
36935
- function isDateFieldMissingInReport(report) {
36936
- if (!report?.dateField?.field || !report.dateField.table) {
36937
- return false;
36938
- }
36939
- const dateField = report.dateField;
36940
- if (!reportReferencesField(report, dateField.field, dateField.table)) {
36941
- return true;
36942
- }
36943
- const firstRow = report.rows?.[0];
36944
- if (!firstRow) {
36945
- return false;
36946
- }
36947
- const normalizedDateField = normalizeIdentifier(dateField.field);
36948
- const dateFieldExistsInFirstRow = hasRowFieldWithIdentifier(
36949
- firstRow,
36950
- normalizedDateField
36951
- );
36952
- return !dateFieldExistsInFirstRow;
36953
- }
36954
- function hasLimitClause(query) {
36955
- if (!query) {
36956
- return false;
36957
- }
36958
- const sanitizedQuery = query.replace(SQL_CONTENT_TO_IGNORE_REGEX, " ");
36959
- const finalStatement = getFinalSqlStatement(sanitizedQuery);
36960
- if (!finalStatement) {
36961
- return false;
36962
- }
36963
- return hasTopLevelLimitClause(finalStatement);
36964
- }
36965
- function reportUsesLimitClause(report) {
36966
- const queriesToInspect = report?.itemQuery && report.itemQuery.length > 0 ? report.itemQuery : report?.queryString ? [report.queryString] : [];
36967
- return queriesToInspect.some(hasLimitClause);
36968
- }
36969
- function getMissingDashboardFilterFields({
36970
- rows = [],
36971
- dashboardFilters = []
36972
- }) {
36973
- const dateIndex = dashboardFilters.findIndex(
36974
- (filter) => filter.filterType === "date_range"
36975
- );
36976
- const requiredFields = dashboardFilters.flatMap((filter, index) => {
36977
- if (index === dateIndex || !filter.field) {
36978
- return [];
36979
- }
36980
- const normalizedField = normalizeIdentifier(filter.field);
36981
- if (!normalizedField) {
36982
- return [];
36983
- }
36984
- return [
36985
- {
36986
- displayField: unwrapIdentifier(filter.field),
36987
- normalizedField
36988
- }
36989
- ];
36990
- });
36991
- const dedupedRequiredFields = requiredFields.filter(
36992
- (field, index, fields) => fields.findIndex(
36993
- (candidate) => candidate.normalizedField === field.normalizedField
36994
- ) === index
36995
- );
36996
- return dedupedRequiredFields.filter(
36997
- ({ normalizedField }) => rows.some((row) => !hasRowFieldWithIdentifier(row, normalizedField))
36998
- ).map(({ displayField }) => displayField);
36999
- }
37000
-
37001
37736
  // src/components/Dashboard/MetricComponent.tsx
37002
37737
  init_dateRangePickerUtils();
37003
37738
  import { useContext as useContext12 } from "react";
@@ -43707,7 +44442,7 @@ function StaticChart(props) {
43707
44442
  pageLoading,
43708
44443
  nextPage,
43709
44444
  prevPage,
43710
- sortRows: sortRows2
44445
+ sortRows: sortRows3
43711
44446
  } = useDashboardReportInternal(reportId);
43712
44447
  const baseStyle = report?.chartType && CHART_TYPE_STYLES[report.chartType] ? CHART_TYPE_STYLES[report.chartType] : DEFAULT_STYLE;
43713
44448
  const mergedStyle = className ? { ...containerStyle ?? {} } : { ...baseStyle, ...containerStyle ?? {} };
@@ -43727,7 +44462,7 @@ function StaticChart(props) {
43727
44462
  }
43728
44463
  };
43729
44464
  const onSortChange = (sort) => {
43730
- sortRows2({
44465
+ sortRows3({
43731
44466
  field: sort.field,
43732
44467
  direction: sort.direction
43733
44468
  });
@@ -45154,7 +45889,7 @@ var PivotModal = ({
45154
45889
  setPivotRowField,
45155
45890
  pivotColumnField,
45156
45891
  setPivotColumnField,
45157
- pivotAggregations,
45892
+ pivotAggregations: pivotAggregations2,
45158
45893
  setPivotAggregations,
45159
45894
  pivotSort,
45160
45895
  setPivotSort,
@@ -45271,18 +46006,18 @@ var PivotModal = ({
45271
46006
  columnField: pivotColumnField,
45272
46007
  columnFieldType: columnTypes[pivotColumnField ?? ""],
45273
46008
  dateBucket: pivotDateBucket,
45274
- aggregations: pivotAggregations?.map((p) => ({
46009
+ aggregations: pivotAggregations2?.map((p) => ({
45275
46010
  valueField: p.valueField,
45276
46011
  valueFieldType: columnTypes[p.valueField ?? ""],
45277
46012
  valueField2: p.valueField2,
45278
46013
  valueField2Type: columnTypes[p.valueField2 ?? ""],
45279
46014
  aggregationType: p.aggregationType
45280
46015
  })),
45281
- valueField: pivotAggregations?.[0]?.valueField,
45282
- valueFieldType: columnTypes[pivotAggregations?.[0]?.valueField ?? ""],
45283
- valueField2: pivotAggregations?.[0]?.valueField2,
45284
- valueField2Type: columnTypes[pivotAggregations?.[0]?.valueField2 ?? ""],
45285
- aggregationType: pivotAggregations?.[0]?.aggregationType
46016
+ valueField: pivotAggregations2?.[0]?.valueField,
46017
+ valueFieldType: columnTypes[pivotAggregations2?.[0]?.valueField ?? ""],
46018
+ valueField2: pivotAggregations2?.[0]?.valueField2,
46019
+ valueField2Type: columnTypes[pivotAggregations2?.[0]?.valueField2 ?? ""],
46020
+ aggregationType: pivotAggregations2?.[0]?.aggregationType
45286
46021
  });
45287
46022
  const getResolvedDateBucket = (pivot) => resolvePivotDateBucket({
45288
46023
  pivot,
@@ -45398,7 +46133,7 @@ var PivotModal = ({
45398
46133
  }, [showUpdatePivot, isOpen]);
45399
46134
  useEffect21(() => {
45400
46135
  const fetchPivotData = async () => {
45401
- if (pivotRowField && data && columns && pivotAggregations?.every((p) => p?.valueField && p?.aggregationType)) {
46136
+ if (pivotRowField && data && columns && pivotAggregations2?.every((p) => p?.valueField && p?.aggregationType)) {
45402
46137
  const pivot = buildCurrentPivot();
45403
46138
  try {
45404
46139
  const { rows, columns: columns2 } = await generatePivotTable({
@@ -45621,17 +46356,17 @@ var PivotModal = ({
45621
46356
  };
45622
46357
  const onCommitPivot = () => {
45623
46358
  const errors2 = [];
45624
- if ((pivotAggregations?.length ?? 0) === 0) {
46359
+ if ((pivotAggregations2?.length ?? 0) === 0) {
45625
46360
  errors2.push("You must have at least one aggregation");
45626
46361
  }
45627
- if (pivotAggregations.some(
46362
+ if (pivotAggregations2.some(
45628
46363
  (p) => !p.valueField && p.aggregationType !== "count" && p.aggregationType !== "percentage"
45629
46364
  )) {
45630
46365
  errors2.push(
45631
46366
  "Value field cannot be empty when aggregation is not 'count' or 'percentage'"
45632
46367
  );
45633
46368
  }
45634
- if (pivotAggregations.some((p) => !p.aggregationType)) {
46369
+ if (pivotAggregations2.some((p) => !p.aggregationType)) {
45635
46370
  errors2.push("Aggregation cannot be empty");
45636
46371
  }
45637
46372
  if (pivotRowField && !columnsToShow[pivotRowField]) {
@@ -45643,7 +46378,7 @@ var PivotModal = ({
45643
46378
  if (showLimitInput && limitInput && !Number.isInteger(Number(limitInput))) {
45644
46379
  errors2.push("Limit must be an integer");
45645
46380
  }
45646
- if (errors2.length === 0 && pivotAggregations?.every(
46381
+ if (errors2.length === 0 && pivotAggregations2?.every(
45647
46382
  (p) => p.aggregationType && (p.valueField || p.aggregationType === "count" || p.aggregationType === "percentage")
45648
46383
  )) {
45649
46384
  const sort = showSortInput && !!sortFieldInput && !!sortDirectionInput;
@@ -45987,7 +46722,7 @@ var PivotModal = ({
45987
46722
  setPivotColumnField(void 0);
45988
46723
  setPivotDateBucket(void 0);
45989
46724
  setPivotAggregations(
45990
- pivotAggregations.map((agg) => {
46725
+ pivotAggregations2.map((agg) => {
45991
46726
  return {
45992
46727
  ...agg,
45993
46728
  aggregationType: agg.aggregationType === "percentage" ? void 0 : agg.aggregationType
@@ -46007,9 +46742,9 @@ var PivotModal = ({
46007
46742
  if (!value && agg.valueField === agg.valueField2) {
46008
46743
  agg.valueField2 = void 0;
46009
46744
  setPivotAggregations([
46010
- ...pivotAggregations.slice(0, index),
46011
- { ...pivotAggregations[index], valueField2: void 0 },
46012
- ...pivotAggregations.slice(index + 1)
46745
+ ...pivotAggregations2.slice(0, index),
46746
+ { ...pivotAggregations2[index], valueField2: void 0 },
46747
+ ...pivotAggregations2.slice(index + 1)
46013
46748
  ]);
46014
46749
  }
46015
46750
  });
@@ -46430,7 +47165,7 @@ var PivotModal = ({
46430
47165
  /* @__PURE__ */ jsx66("div", { style: { width: 200 }, children: /* @__PURE__ */ jsx66(SubheaderComponent, { label: "Aggregation Type" }) }),
46431
47166
  /* @__PURE__ */ jsx66("div", { style: { width: 200 }, children: /* @__PURE__ */ jsx66(SubheaderComponent, { label: "Value Field" }) })
46432
47167
  ] }),
46433
- pivotAggregations?.map((agg, index) => /* @__PURE__ */ jsxs47(PivotRowContainer, { children: [
47168
+ pivotAggregations2?.map((agg, index) => /* @__PURE__ */ jsxs47(PivotRowContainer, { children: [
46434
47169
  /* @__PURE__ */ jsx66(
46435
47170
  SelectComponent,
46436
47171
  {
@@ -46438,12 +47173,12 @@ var PivotModal = ({
46438
47173
  value: agg.aggregationType ?? "",
46439
47174
  onChange: (e) => {
46440
47175
  const newAgg = [
46441
- ...pivotAggregations.slice(0, index),
47176
+ ...pivotAggregations2.slice(0, index),
46442
47177
  {
46443
47178
  ...agg,
46444
47179
  aggregationType: e.target.value === "" ? void 0 : e.target.value
46445
47180
  },
46446
- ...pivotAggregations.slice(index + 1)
47181
+ ...pivotAggregations2.slice(index + 1)
46447
47182
  ];
46448
47183
  pivotFieldChange("aggregations", newAgg);
46449
47184
  setPivotAggregations(newAgg);
@@ -46470,12 +47205,12 @@ var PivotModal = ({
46470
47205
  value: agg.valueField ?? "",
46471
47206
  onChange: (e) => {
46472
47207
  const newAgg = [
46473
- ...pivotAggregations.slice(0, index),
47208
+ ...pivotAggregations2.slice(0, index),
46474
47209
  {
46475
47210
  ...agg,
46476
47211
  valueField: e.target.value === "" ? void 0 : e.target.value
46477
47212
  },
46478
- ...pivotAggregations.slice(index + 1)
47213
+ ...pivotAggregations2.slice(index + 1)
46479
47214
  ];
46480
47215
  pivotFieldChange("aggregations", newAgg);
46481
47216
  setPivotAggregations(newAgg);
@@ -46503,8 +47238,8 @@ var PivotModal = ({
46503
47238
  {
46504
47239
  onClick: () => {
46505
47240
  setPivotAggregations([
46506
- ...pivotAggregations.slice(0, index),
46507
- ...pivotAggregations.slice(index + 1)
47241
+ ...pivotAggregations2.slice(0, index),
47242
+ ...pivotAggregations2.slice(index + 1)
46508
47243
  ]);
46509
47244
  }
46510
47245
  }
@@ -46531,7 +47266,7 @@ var PivotModal = ({
46531
47266
  label: "Add Aggregation",
46532
47267
  onClick: () => {
46533
47268
  setPivotAggregations([
46534
- ...pivotAggregations,
47269
+ ...pivotAggregations2,
46535
47270
  {
46536
47271
  aggregationType: void 0,
46537
47272
  valueField: void 0,
@@ -46765,7 +47500,7 @@ var PivotModal = ({
46765
47500
  label: "Save",
46766
47501
  disabled: showSortInput && (!sortFieldInput || !(samplePivotTable?.columns ?? []).some(
46767
47502
  (c) => c.field === sortFieldInput
46768
- ) || !sortDirectionInput) || !(pivotAggregations?.length > 0) || pivotAggregations.some(
47503
+ ) || !sortDirectionInput) || !(pivotAggregations2?.length > 0) || pivotAggregations2.some(
46769
47504
  (agg) => !agg.aggregationType || !agg.valueField && agg.aggregationType !== "count" && (agg.aggregationType !== "percentage" || !pivotRowField || pivotColumnField)
46770
47505
  )
46771
47506
  }
@@ -48747,7 +49482,7 @@ function ChartBuilder({
48747
49482
  const [pivotColumnField, setPivotColumnField] = useState31(
48748
49483
  report?.pivot?.columnField
48749
49484
  );
48750
- const [pivotAggregations, setPivotAggregations] = useState31(
49485
+ const [pivotAggregations2, setPivotAggregations] = useState31(
48751
49486
  report?.pivot?.aggregations ?? [
48752
49487
  {
48753
49488
  valueField: report?.pivot?.valueField,
@@ -50558,7 +51293,7 @@ function ChartBuilder({
50558
51293
  setPivotRowField,
50559
51294
  pivotColumnField,
50560
51295
  setPivotColumnField,
50561
- pivotAggregations,
51296
+ pivotAggregations: pivotAggregations2,
50562
51297
  pivotSort,
50563
51298
  setPivotSort,
50564
51299
  pivotLimit,
@@ -54203,7 +54938,7 @@ var useReportBuilderInternal = ({
54203
54938
  const [pivotColumnField, setPivotColumnField] = useState34(
54204
54939
  void 0
54205
54940
  );
54206
- const [pivotAggregations, setPivotAggregations] = useState34([]);
54941
+ const [pivotAggregations2, setPivotAggregations] = useState34([]);
54207
54942
  const [pivotDateBucket, setPivotDateBucket] = useState34(void 0);
54208
54943
  const [pivotLimit, setPivotLimit] = useState34(void 0);
54209
54944
  const [pivotSort, setPivotSort] = useState34(void 0);
@@ -54531,7 +55266,7 @@ var useReportBuilderInternal = ({
54531
55266
  if (changeField === "" || changeField === void 0) {
54532
55267
  setPivotColumnField(void 0);
54533
55268
  setPivotAggregations(
54534
- pivotAggregations.map((agg) => ({
55269
+ pivotAggregations2.map((agg) => ({
54535
55270
  ...agg,
54536
55271
  aggregationType: agg.aggregationType === "percentage" ? void 0 : agg.aggregationType
54537
55272
  }))
@@ -55447,7 +56182,7 @@ var useReportBuilderInternal = ({
55447
56182
  pivotData,
55448
56183
  pivotRowField,
55449
56184
  pivotColumnField,
55450
- pivotAggregations,
56185
+ pivotAggregations: pivotAggregations2,
55451
56186
  pivotLimit,
55452
56187
  pivotSort,
55453
56188
  pivotDateBucket,
@@ -56588,7 +57323,7 @@ function PivotForm({
56588
57323
  setPivotRowField,
56589
57324
  pivotColumnField,
56590
57325
  setPivotColumnField,
56591
- pivotAggregations,
57326
+ pivotAggregations: pivotAggregations2,
56592
57327
  setPivotAggregations,
56593
57328
  pivotLimit,
56594
57329
  setPivotLimit,
@@ -56697,7 +57432,7 @@ function PivotForm({
56697
57432
  gap: 24
56698
57433
  },
56699
57434
  children: [
56700
- pivotAggregations.map((pivotAggregation, index) => /* @__PURE__ */ jsxs56(
57435
+ pivotAggregations2.map((pivotAggregation, index) => /* @__PURE__ */ jsxs56(
56701
57436
  "div",
56702
57437
  {
56703
57438
  style: {
@@ -56724,12 +57459,12 @@ function PivotForm({
56724
57459
  value: pivotAggregation.aggregationType,
56725
57460
  onChange: (e) => {
56726
57461
  setPivotAggregations([
56727
- ...pivotAggregations.slice(0, index),
57462
+ ...pivotAggregations2.slice(0, index),
56728
57463
  {
56729
- ...pivotAggregations[index],
57464
+ ...pivotAggregations2[index],
56730
57465
  aggregationType: e.target.value === "" ? void 0 : e.target.value
56731
57466
  },
56732
- ...pivotAggregations.slice(index + 1)
57467
+ ...pivotAggregations2.slice(index + 1)
56733
57468
  ]);
56734
57469
  },
56735
57470
  options: [
@@ -56755,12 +57490,12 @@ function PivotForm({
56755
57490
  value: pivotAggregation.valueField,
56756
57491
  onChange: (e) => {
56757
57492
  setPivotAggregations([
56758
- ...pivotAggregations.slice(0, index),
57493
+ ...pivotAggregations2.slice(0, index),
56759
57494
  {
56760
- ...pivotAggregations[index],
57495
+ ...pivotAggregations2[index],
56761
57496
  valueField: e.target.value === "" ? void 0 : e.target.value
56762
57497
  },
56763
- ...pivotAggregations.slice(index + 1)
57498
+ ...pivotAggregations2.slice(index + 1)
56764
57499
  ]);
56765
57500
  },
56766
57501
  isLoading: uniqueValuesIsLoading,
@@ -56783,8 +57518,8 @@ function PivotForm({
56783
57518
  {
56784
57519
  onClick: () => {
56785
57520
  setPivotAggregations([
56786
- ...pivotAggregations.slice(0, index),
56787
- ...pivotAggregations.slice(index + 1)
57521
+ ...pivotAggregations2.slice(0, index),
57522
+ ...pivotAggregations2.slice(index + 1)
56788
57523
  ]);
56789
57524
  }
56790
57525
  }
@@ -56798,7 +57533,7 @@ function PivotForm({
56798
57533
  {
56799
57534
  onClick: () => {
56800
57535
  setPivotAggregations([
56801
- ...pivotAggregations,
57536
+ ...pivotAggregations2,
56802
57537
  {
56803
57538
  valueField: void 0,
56804
57539
  aggregationType: void 0
@@ -58844,7 +59579,7 @@ function ChartEditor({
58844
59579
  }
58845
59580
 
58846
59581
  // src/Chat.tsx
58847
- import { useContext as useContext37, useEffect as useEffect32, useRef as useRef25, useState as useState42 } from "react";
59582
+ import { useContext as useContext37, useEffect as useEffect33, useRef as useRef25, useState as useState43 } from "react";
58848
59583
 
58849
59584
  // src/ChatChartCard.tsx
58850
59585
  import { useMemo as useMemo34 } from "react";
@@ -58855,14 +59590,19 @@ init_ReportBuilder();
58855
59590
  import {
58856
59591
  useCallback as useCallback5,
58857
59592
  useContext as useContext36,
58858
- useEffect as useEffect31,
59593
+ useEffect as useEffect32,
58859
59594
  useLayoutEffect as useLayoutEffect4,
58860
59595
  useMemo as useMemo33,
58861
59596
  useReducer as useReducer2,
58862
59597
  useRef as useRef24,
58863
- useState as useState41
59598
+ useState as useState42
58864
59599
  } from "react";
58865
- import { keepPreviousData, useMutation, useQuery } from "@tanstack/react-query";
59600
+ import {
59601
+ keepPreviousData,
59602
+ useMutation,
59603
+ useQuery,
59604
+ useQueryClient as useQueryClient2
59605
+ } from "@tanstack/react-query";
58866
59606
  init_util();
58867
59607
  init_columnType();
58868
59608
  init_columnProcessing();
@@ -60046,6 +60786,20 @@ function useFormReducer(state, action) {
60046
60786
  }
60047
60787
  }
60048
60788
 
60789
+ // src/hooks/useForm.refreshDebounce.ts
60790
+ import { useEffect as useEffect31, useState as useState41 } from "react";
60791
+ var REPORT_REFRESH_DEBOUNCE_MS = 200;
60792
+ function useDebouncedRefreshInput(value, delayMs = REPORT_REFRESH_DEBOUNCE_MS) {
60793
+ const [debouncedValue, setDebouncedValue] = useState41(value);
60794
+ useEffect31(() => {
60795
+ const timeout = setTimeout(() => {
60796
+ setDebouncedValue(value);
60797
+ }, delayMs);
60798
+ return () => clearTimeout(timeout);
60799
+ }, [delayMs, value]);
60800
+ return debouncedValue;
60801
+ }
60802
+
60049
60803
  // src/utils/pivotDateBuckets.ts
60050
60804
  import {
60051
60805
  eachDayOfInterval as eachDayOfInterval3,
@@ -60077,10 +60831,252 @@ function buildPivotDateBucketStarts(range, bucket) {
60077
60831
  );
60078
60832
  }
60079
60833
 
60834
+ // src/utils/cacheCabReportResolver.ts
60835
+ init_inMemoryPivotEngine();
60836
+ var availableRowKeys = (report) => {
60837
+ const keys = /* @__PURE__ */ new Set();
60838
+ for (const row of report.rows?.slice(0, 10) ?? []) {
60839
+ Object.keys(row).forEach((key) => keys.add(key));
60840
+ }
60841
+ for (const column of report.columnInternal ?? []) {
60842
+ if (column.field) keys.add(column.field);
60843
+ }
60844
+ for (const column of report.reportBuilderState?.columns ?? []) {
60845
+ if (column.field) keys.add(column.field);
60846
+ if (column.alias) keys.add(column.alias);
60847
+ }
60848
+ return keys;
60849
+ };
60850
+ var createFieldResolver = (report) => {
60851
+ const keys = availableRowKeys(report);
60852
+ return (field, table) => {
60853
+ const matchingColumns = (report.reportBuilderState?.columns ?? []).filter(
60854
+ (column) => column.field === field && (!table || !column.table || column.table === table)
60855
+ );
60856
+ const candidates = [
60857
+ ...matchingColumns.map((column) => column.alias).filter(Boolean),
60858
+ field,
60859
+ table ? `${table}.${field}` : void 0,
60860
+ table ? `${table}__${field}` : void 0,
60861
+ table ? `${table}_${field}` : void 0
60862
+ ].filter((candidate) => Boolean(candidate));
60863
+ return candidates.find((candidate) => keys.has(candidate));
60864
+ };
60865
+ };
60866
+ var pivotAggregations = (pivot) => pivot.aggregations ?? (pivot.aggregationType ? [
60867
+ {
60868
+ aggregationType: pivot.aggregationType,
60869
+ valueField: pivot.valueField,
60870
+ valueField2: pivot.valueField2,
60871
+ valueFieldType: pivot.valueFieldType,
60872
+ valueField2Type: pivot.valueField2Type
60873
+ }
60874
+ ] : []);
60875
+ var resolvePivot = (pivot, resolveField) => {
60876
+ const rowField = pivot.rowField ? resolveField(pivot.rowField, pivot.rowFieldTable) : void 0;
60877
+ const columnField = pivot.columnField ? resolveField(pivot.columnField, pivot.columnFieldTable) : void 0;
60878
+ if (pivot.rowField && !rowField || pivot.columnField && !columnField) {
60879
+ return null;
60880
+ }
60881
+ const aggregations = pivotAggregations(pivot).map((aggregation) => ({
60882
+ ...aggregation,
60883
+ valueField: aggregation.valueField ? resolveField(aggregation.valueField, aggregation.valueFieldTable) : void 0,
60884
+ valueField2: aggregation.valueField2 ? resolveField(aggregation.valueField2, aggregation.valueFieldTable) : void 0
60885
+ }));
60886
+ if (aggregations.some(
60887
+ (aggregation, index) => pivotAggregations(pivot)[index]?.valueField && !aggregation.valueField || pivotAggregations(pivot)[index]?.valueField2 && !aggregation.valueField2
60888
+ )) {
60889
+ return null;
60890
+ }
60891
+ return {
60892
+ ...pivot,
60893
+ rowField,
60894
+ columnField,
60895
+ aggregations,
60896
+ aggregationType: void 0,
60897
+ valueField: void 0,
60898
+ valueField2: void 0,
60899
+ valueFieldType: void 0,
60900
+ valueField2Type: void 0
60901
+ };
60902
+ };
60903
+ var sortRows2 = (rows, state, resolveField) => {
60904
+ if (!state.sort.length) return rows;
60905
+ return [...rows].sort((left, right) => {
60906
+ for (const sort of state.sort) {
60907
+ const key = resolveField(sort.field);
60908
+ if (!key) continue;
60909
+ const a = left[key];
60910
+ const b = right[key];
60911
+ if (a === b) continue;
60912
+ const comparison = a == null ? -1 : b == null ? 1 : a < b ? -1 : 1;
60913
+ return sort.direction.toLowerCase() === "desc" ? -comparison : comparison;
60914
+ }
60915
+ return 0;
60916
+ });
60917
+ };
60918
+ var buildUniqueValues = (report, resolveField) => {
60919
+ const result = {};
60920
+ const stringColumns = (report.columnInternal ?? []).filter(
60921
+ (column) => column.jsType === "string" || /char|text|string|uuid/i.test(column.fieldType ?? "")
60922
+ );
60923
+ for (const column of stringColumns) {
60924
+ const table = column.table ?? "";
60925
+ const key = resolveField(column.field, column.table);
60926
+ if (!key) continue;
60927
+ const values = /* @__PURE__ */ new Set();
60928
+ for (const row of report.rows ?? []) {
60929
+ const value = row[key];
60930
+ if (value !== null && value !== void 0) values.add(String(value));
60931
+ }
60932
+ result[table] ??= {};
60933
+ result[table][column.field] = [...values].sort();
60934
+ }
60935
+ return result;
60936
+ };
60937
+ function resolveReportFromCacheCab({
60938
+ snapshot,
60939
+ reportBuilderState,
60940
+ filters,
60941
+ pivot = reportBuilderState?.pivot ?? null,
60942
+ operation = "table",
60943
+ rowLimit
60944
+ }) {
60945
+ if (!snapshot) return { resolved: false, reason: "cache_miss" };
60946
+ if (!snapshot.complete) {
60947
+ return {
60948
+ resolved: false,
60949
+ reason: "incomplete_cache",
60950
+ details: { snapshotReason: snapshot.reason }
60951
+ };
60952
+ }
60953
+ const state = reportBuilderState ?? snapshot.report.reportBuilderState;
60954
+ if (!state) return { resolved: false, reason: "missing_builder_state" };
60955
+ const resolveField = createFieldResolver(snapshot.report);
60956
+ const missingFields = [];
60957
+ state.columns.forEach((column) => {
60958
+ if (!resolveField(column.field, column.table)) {
60959
+ missingFields.push({
60960
+ role: "column",
60961
+ field: column.field,
60962
+ table: column.table
60963
+ });
60964
+ }
60965
+ });
60966
+ filters.forEach((filter) => {
60967
+ if (filter.field && !resolveField(filter.field, filter.table)) {
60968
+ missingFields.push({
60969
+ role: "filter",
60970
+ field: filter.field,
60971
+ table: filter.table
60972
+ });
60973
+ }
60974
+ });
60975
+ if (pivot?.rowField && !resolveField(pivot.rowField, pivot.rowFieldTable)) {
60976
+ missingFields.push({
60977
+ role: "pivot-row",
60978
+ field: pivot.rowField,
60979
+ table: pivot.rowFieldTable
60980
+ });
60981
+ }
60982
+ if (pivot?.columnField && !resolveField(pivot.columnField, pivot.columnFieldTable)) {
60983
+ missingFields.push({
60984
+ role: "pivot-column",
60985
+ field: pivot.columnField,
60986
+ table: pivot.columnFieldTable
60987
+ });
60988
+ }
60989
+ if (pivot) {
60990
+ pivotAggregations(pivot).forEach((aggregation) => {
60991
+ [
60992
+ [aggregation.valueField, aggregation.valueFieldTable],
60993
+ [aggregation.valueField2, aggregation.valueFieldTable]
60994
+ ].forEach(([field, table]) => {
60995
+ if (field && !resolveField(field, table)) {
60996
+ missingFields.push({
60997
+ role: "aggregation",
60998
+ field,
60999
+ table
61000
+ });
61001
+ }
61002
+ });
61003
+ });
61004
+ }
61005
+ const resolvedPivot = pivot ? resolvePivot(pivot, resolveField) : null;
61006
+ if (missingFields.length > 0 || pivot && !resolvedPivot) {
61007
+ return {
61008
+ resolved: false,
61009
+ reason: "missing_field",
61010
+ details: { missingFields }
61011
+ };
61012
+ }
61013
+ const filteredRows = applyFiltersInMemory(
61014
+ snapshot.report.rows ?? [],
61015
+ filters,
61016
+ {
61017
+ columns: snapshot.report.columnInternal,
61018
+ fieldKeyResolver: resolveField
61019
+ }
61020
+ );
61021
+ const sortedRows = operation === "unique-values" ? [] : sortRows2(filteredRows, state, resolveField);
61022
+ const effectiveRowLimit = Math.min(
61023
+ state.limit?.value ?? Number.MAX_SAFE_INTEGER,
61024
+ rowLimit ?? Number.MAX_SAFE_INTEGER
61025
+ );
61026
+ const limitedRows = sortedRows.length > effectiveRowLimit ? sortedRows.slice(0, effectiveRowLimit) : sortedRows;
61027
+ const pivotRows = resolvedPivot ? applyPivotInMemory(filteredRows, resolvedPivot, filters) : void 0;
61028
+ const pivotColumns = resolvedPivot ? generatePivotColumnsInMemory({
61029
+ pivot: resolvedPivot,
61030
+ pivotRows: pivotRows ?? [],
61031
+ sourceColumns: snapshot.report.columnInternal
61032
+ }) : void 0;
61033
+ const requestedRowField = pivot?.rowField;
61034
+ const resolvedRowField = resolvedPivot?.rowField;
61035
+ const normalizedPivotRows = requestedRowField && resolvedRowField && requestedRowField !== resolvedRowField ? pivotRows?.map((row) => ({
61036
+ ...row,
61037
+ [requestedRowField]: row[resolvedRowField]
61038
+ })) : pivotRows;
61039
+ const normalizedPivotColumns = pivotColumns?.map(
61040
+ (column) => requestedRowField && resolvedRowField && column.field === resolvedRowField ? { ...column, field: requestedRowField } : column
61041
+ );
61042
+ return {
61043
+ resolved: true,
61044
+ report: {
61045
+ ...snapshot.report,
61046
+ rows: limitedRows,
61047
+ rowCount: filteredRows.length,
61048
+ reportBuilderState: { ...state, pivot },
61049
+ pivot,
61050
+ pivotRows: normalizedPivotRows,
61051
+ pivotColumns: normalizedPivotColumns,
61052
+ pivotRowCount: normalizedPivotRows?.length,
61053
+ pivotResultRowField: requestedRowField,
61054
+ pivotResultSourceRowField: requestedRowField,
61055
+ ...operation === "unique-values" ? {
61056
+ uniqueStringsByTable: buildUniqueValues(
61057
+ snapshot.report,
61058
+ resolveField
61059
+ )
61060
+ } : {},
61061
+ filtersApplied: filters
61062
+ }
61063
+ };
61064
+ }
61065
+
60080
61066
  // src/hooks/useForm.queries.ts
60081
61067
  var QUERY_KEY_UNDEFINED = "__undefined__";
60082
61068
  var QUERY_KEY_FUNCTION = "__function__";
60083
61069
  var QUERY_KEY_BIGINT = "__bigint__";
61070
+ function createFilterUniqueValuesReportBuilderState(source, columns) {
61071
+ return {
61072
+ tables: source.tables,
61073
+ columns,
61074
+ filterStack: [],
61075
+ pivot: null,
61076
+ sort: [],
61077
+ limit: null
61078
+ };
61079
+ }
60084
61080
  function stableSerializeForQueryKey(value) {
60085
61081
  try {
60086
61082
  return JSON.stringify(value, (_key, nestedValue) => {
@@ -60141,6 +61137,7 @@ function createUseFormPivotTableDataRefreshQueryKey(input) {
60141
61137
  "pivot-table-data-refresh",
60142
61138
  input.reportId,
60143
61139
  input.reportBuilderStateHash,
61140
+ input.pageSize,
60144
61141
  input.tenantHash,
60145
61142
  input.clientHash
60146
61143
  ];
@@ -60167,13 +61164,36 @@ function createUseFormReportNameQueryKey(input) {
60167
61164
  ];
60168
61165
  }
60169
61166
  function createUseFormQueryFn(loader) {
60170
- return async () => loader();
61167
+ return ({ signal }) => loader(signal);
60171
61168
  }
60172
61169
 
60173
61170
  // src/hooks/useForm.pagination.ts
60174
61171
  var DEFAULT_PAGE_INDEX = 0;
60175
61172
  var DEFAULT_PAGE_SIZE = 10;
60176
- var DEFAULT_USE_REPORT_ROWS_PER_REQUEST = DEFAULT_PAGE_SIZE * 10;
61173
+ var DEFAULT_USE_REPORT_ROWS_PER_REQUEST = DEFAULT_PAGE_SIZE;
61174
+ var isBareIdentifier = (value) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
61175
+ function getStablePaginationSort({
61176
+ columns,
61177
+ sort
61178
+ }) {
61179
+ const outputColumns = (columns ?? []).map((column) => {
61180
+ const field2 = String(column.field ?? "").trim();
61181
+ const alias = String(column.alias ?? "").trim();
61182
+ const outputField = isBareIdentifier(alias) ? alias : isBareIdentifier(field2) ? field2 : "";
61183
+ return { field: field2, alias, outputField };
61184
+ }).filter((column) => column.outputField);
61185
+ const requestedSort = sort?.[0];
61186
+ const requestedField = String(requestedSort?.field ?? "").trim();
61187
+ const matchedColumn = requestedField ? outputColumns.find(
61188
+ (column) => column.field === requestedField || column.alias === requestedField
61189
+ ) : void 0;
61190
+ const field = matchedColumn?.outputField ?? outputColumns[0]?.outputField;
61191
+ if (!field) return void 0;
61192
+ return {
61193
+ field,
61194
+ direction: String(requestedSort?.direction ?? "ASC").toUpperCase() === "DESC" ? "DESC" : "ASC"
61195
+ };
61196
+ }
60177
61197
  function getDefaultPaginationState() {
60178
61198
  return { pageIndex: DEFAULT_PAGE_INDEX, pageSize: DEFAULT_PAGE_SIZE };
60179
61199
  }
@@ -60323,11 +61343,11 @@ var isMultiValueOperator = (operator) => {
60323
61343
  };
60324
61344
  var collectSelectedStringMultiselectValuesByField = (group) => {
60325
61345
  const out = /* @__PURE__ */ new Map();
60326
- const visit = (entries) => {
60327
- if (!entries) {
61346
+ const visit = (entries2) => {
61347
+ if (!entries2) {
60328
61348
  return;
60329
61349
  }
60330
- for (const entry of entries) {
61350
+ for (const entry of entries2) {
60331
61351
  if (!entry || typeof entry === "string") {
60332
61352
  continue;
60333
61353
  }
@@ -60525,11 +61545,11 @@ var cloneQueryBuilderRuleGroup = (group) => JSON.parse(JSON.stringify(group));
60525
61545
  var canonicalizeMultiselectStringRulesToOptionValues = (group, optionsByField, configByName) => {
60526
61546
  const next = cloneQueryBuilderRuleGroup(group);
60527
61547
  let changed = false;
60528
- const visit = (entries) => {
60529
- if (!entries) {
61548
+ const visit = (entries2) => {
61549
+ if (!entries2) {
60530
61550
  return;
60531
61551
  }
60532
- for (const entry of entries) {
61552
+ for (const entry of entries2) {
60533
61553
  if (!entry || typeof entry === "string") {
60534
61554
  continue;
60535
61555
  }
@@ -62894,15 +63914,17 @@ async function loadViaInMemoryEngines({
62894
63914
  pivot,
62895
63915
  reportBuilderState,
62896
63916
  allowReportTaskBootstrap = false,
62897
- draftSessionId
63917
+ draftSessionId,
63918
+ shareInitialRequest = false,
63919
+ abortSignal
62898
63920
  }) {
62899
63921
  const requestedTask = allowReportTaskBootstrap ? "report" : "item";
62900
- const additionalProcessing = allowReportTaskBootstrap ? {
63922
+ const additionalProcessing = {
62901
63923
  page: {
62902
63924
  ...DEFAULT_PAGINATION,
62903
63925
  rowsPerRequest: DEFAULT_USE_REPORT_ROWS_PER_REQUEST
62904
63926
  }
62905
- } : {};
63927
+ };
62906
63928
  const { report, error } = await fetchReport({
62907
63929
  reportId,
62908
63930
  client,
@@ -62916,7 +63938,9 @@ async function loadViaInMemoryEngines({
62916
63938
  usePivotTask: false,
62917
63939
  pivot,
62918
63940
  reportBuilderState,
62919
- draftSessionId
63941
+ draftSessionId,
63942
+ shareRequest: shareInitialRequest,
63943
+ abortSignal
62920
63944
  });
62921
63945
  if (error || !report) {
62922
63946
  return { report: null, error };
@@ -62982,7 +64006,9 @@ async function loadViaPivotTemplate({
62982
64006
  reportBuilderState,
62983
64007
  baseReport,
62984
64008
  schema,
62985
- draftSessionId
64009
+ draftSessionId,
64010
+ shareInitialRequest = false,
64011
+ abortSignal
62986
64012
  }) {
62987
64013
  const pivotForTemplate = enrichPivotRowFieldTypeForTemplate(
62988
64014
  pivot,
@@ -63002,6 +64028,8 @@ async function loadViaPivotTemplate({
63002
64028
  reportBuilderState,
63003
64029
  baseReport,
63004
64030
  draftSessionId,
64031
+ shareRequest: shareInitialRequest,
64032
+ abortSignal,
63005
64033
  responseSelection: baseReport ? {
63006
64034
  includeRows: false,
63007
64035
  includeColumns: false,
@@ -63020,7 +64048,8 @@ async function fetchReportBuilderStateByReportId({
63020
64048
  client,
63021
64049
  getToken,
63022
64050
  draftSessionId,
63023
- tenants
64051
+ tenants,
64052
+ abortSignal
63024
64053
  }) {
63025
64054
  try {
63026
64055
  const { data, error, status } = await quillFetch({
@@ -63031,7 +64060,8 @@ async function fetchReportBuilderStateByReportId({
63031
64060
  tenants,
63032
64061
  ...draftSessionId ? { draftSessionId: String(draftSessionId).trim() } : {}
63033
64062
  },
63034
- getToken
64063
+ getToken,
64064
+ abortSignal
63035
64065
  });
63036
64066
  if (error || status === "error") {
63037
64067
  return null;
@@ -63043,6 +64073,9 @@ async function fetchReportBuilderStateByReportId({
63043
64073
  const parsedState = state;
63044
64074
  return parsedState;
63045
64075
  } catch (error) {
64076
+ if (error instanceof Error && error.name === "AbortError") {
64077
+ throw error;
64078
+ }
63046
64079
  return null;
63047
64080
  }
63048
64081
  }
@@ -63104,7 +64137,8 @@ async function fetchUniqueValuesFromTables({
63104
64137
  customFields,
63105
64138
  tenants,
63106
64139
  dashboardName,
63107
- getToken
64140
+ getToken,
64141
+ abortSignal
63108
64142
  }) {
63109
64143
  const normalizedTables = Array.from(
63110
64144
  new Set(
@@ -63121,6 +64155,9 @@ async function fetchUniqueValuesFromTables({
63121
64155
  }
63122
64156
  const fetchResults = await Promise.all(
63123
64157
  normalizedTables.map(async (tableName) => {
64158
+ if (abortSignal?.aborted) {
64159
+ throw new DOMException("Aborted", "AbortError");
64160
+ }
63124
64161
  const tableColumns = stringColumnsByTable.filter((column) => {
63125
64162
  return String(column.table ?? "").trim() === tableName;
63126
64163
  });
@@ -63153,7 +64190,8 @@ async function fetchUniqueValuesFromTables({
63153
64190
  client,
63154
64191
  task: "report-builder-unique-values",
63155
64192
  metadata: requestMetadata,
63156
- getToken
64193
+ getToken,
64194
+ abortSignal
63157
64195
  });
63158
64196
  const responseData = fetchResult?.data;
63159
64197
  const responseRows = Array.isArray(responseData?.rows) && responseData.rows || Array.isArray(fetchResult?.queries?.queryResults?.[0]?.rows) && fetchResult.queries.queryResults[0].rows || [];
@@ -63216,7 +64254,9 @@ async function loadPivotTemplateInParallelWithReportTask({
63216
64254
  customFields,
63217
64255
  dashboardName,
63218
64256
  pivotRefreshOnly = false,
63219
- draftSessionId
64257
+ draftSessionId,
64258
+ shareInitialRequest = false,
64259
+ abortSignal
63220
64260
  }) {
63221
64261
  if (pivotRefreshOnly) {
63222
64262
  const pivotOnlyResult = await loadViaPivotTemplate({
@@ -63230,7 +64270,9 @@ async function loadPivotTemplateInParallelWithReportTask({
63230
64270
  reportBuilderState,
63231
64271
  baseReport,
63232
64272
  schema,
63233
- draftSessionId
64273
+ draftSessionId,
64274
+ shareInitialRequest,
64275
+ abortSignal
63234
64276
  });
63235
64277
  return pivotOnlyResult;
63236
64278
  }
@@ -63245,7 +64287,9 @@ async function loadPivotTemplateInParallelWithReportTask({
63245
64287
  pivot,
63246
64288
  reportBuilderState,
63247
64289
  allowReportTaskBootstrap: true,
63248
- draftSessionId
64290
+ draftSessionId,
64291
+ shareInitialRequest,
64292
+ abortSignal
63249
64293
  }),
63250
64294
  loadViaPivotTemplate({
63251
64295
  reportId,
@@ -63258,14 +64302,17 @@ async function loadPivotTemplateInParallelWithReportTask({
63258
64302
  reportBuilderState,
63259
64303
  baseReport,
63260
64304
  schema,
63261
- draftSessionId
64305
+ draftSessionId,
64306
+ shareInitialRequest,
64307
+ abortSignal
63262
64308
  }),
63263
64309
  fetchReportBuilderStateByReportId({
63264
64310
  reportId,
63265
64311
  client,
63266
64312
  getToken,
63267
64313
  draftSessionId,
63268
- tenants
64314
+ tenants,
64315
+ abortSignal
63269
64316
  })
63270
64317
  ]);
63271
64318
  const withFetchedReportBuilderState = (result) => applyFetchedReportBuilderState(result, fetchedReportBuilderState);
@@ -63300,7 +64347,8 @@ async function loadPivotTemplateInParallelWithReportTask({
63300
64347
  customFields,
63301
64348
  dashboardName: String(dashboardName ?? "").trim(),
63302
64349
  report: baseReport,
63303
- draftSessionId
64350
+ draftSessionId,
64351
+ abortSignal
63304
64352
  });
63305
64353
  if (reportBuilderStateFallbackResult.report && !reportBuilderStateFallbackResult.error) {
63306
64354
  return withFetchedReportBuilderState(reportBuilderStateFallbackResult);
@@ -63330,7 +64378,8 @@ async function loadViaReportBuilderState({
63330
64378
  pagination,
63331
64379
  paginationSort,
63332
64380
  rowsOnly,
63333
- rowCountOnly
64381
+ rowCountOnly,
64382
+ abortSignal
63334
64383
  }) {
63335
64384
  const baseReport = {
63336
64385
  ...EMPTY_INTERNAL_REPORT,
@@ -63369,7 +64418,8 @@ async function loadViaReportBuilderState({
63369
64418
  dashboardName,
63370
64419
  getToken,
63371
64420
  eventTracking: null,
63372
- draftSessionId
64421
+ draftSessionId,
64422
+ abortSignal
63373
64423
  });
63374
64424
  if (!reportBuilderInfo) {
63375
64425
  return {
@@ -63450,7 +64500,9 @@ async function loadReportForUseForm({
63450
64500
  dashboardName,
63451
64501
  useInMemoryEngines,
63452
64502
  draftSessionId,
63453
- rowsOnly = false
64503
+ shareInitialRequest = false,
64504
+ rowsOnly = false,
64505
+ abortSignal
63454
64506
  }) {
63455
64507
  const effectivePivot = pivot ?? initialReportBuilderState?.pivot ?? void 0;
63456
64508
  const hasPivot = Boolean(effectivePivot);
@@ -63473,7 +64525,9 @@ async function loadReportForUseForm({
63473
64525
  customFields,
63474
64526
  dashboardName,
63475
64527
  pivotRefreshOnly: includeReportBuilderStateInPivotTask,
63476
- draftSessionId
64528
+ draftSessionId,
64529
+ shareInitialRequest,
64530
+ abortSignal
63477
64531
  });
63478
64532
  const pivotRows = pivotResult.report?.pivotRows;
63479
64533
  const rowField = pivotResult.report?.pivotResultRowField ?? effectivePivot?.rowField;
@@ -63496,7 +64550,8 @@ async function loadReportForUseForm({
63496
64550
  customFields,
63497
64551
  dashboardName,
63498
64552
  report: baseReport ?? void 0,
63499
- draftSessionId
64553
+ draftSessionId,
64554
+ abortSignal
63500
64555
  });
63501
64556
  const rbPivotRows = rbResult.report?.pivotRows;
63502
64557
  const rbEmpty = !Array.isArray(rbPivotRows) || rbPivotRows.length === 0;
@@ -63521,7 +64576,9 @@ async function loadReportForUseForm({
63521
64576
  schema,
63522
64577
  customFields,
63523
64578
  dashboardName,
63524
- draftSessionId
64579
+ draftSessionId,
64580
+ shareInitialRequest,
64581
+ abortSignal
63525
64582
  });
63526
64583
  const normalizedBootstrapResult = bootstrapResult.report?.pivot == null ? stripPivotFromResult(bootstrapResult) : bootstrapResult;
63527
64584
  return normalizedBootstrapResult;
@@ -63535,7 +64592,9 @@ async function loadReportForUseForm({
63535
64592
  tenants,
63536
64593
  flags,
63537
64594
  allowReportTaskBootstrap: shouldUseReportTaskForReload,
63538
- draftSessionId
64595
+ draftSessionId,
64596
+ shareInitialRequest,
64597
+ abortSignal
63539
64598
  });
63540
64599
  }
63541
64600
  return loadViaReportBuilderState({
@@ -63549,7 +64608,8 @@ async function loadReportForUseForm({
63549
64608
  dashboardName,
63550
64609
  report: baseReport,
63551
64610
  draftSessionId,
63552
- rowsOnly
64611
+ rowsOnly,
64612
+ abortSignal
63553
64613
  });
63554
64614
  }
63555
64615
  function generateDraftSessionId() {
@@ -63557,10 +64617,10 @@ function generateDraftSessionId() {
63557
64617
  }
63558
64618
  function useReport(reportIdArg, options = {}) {
63559
64619
  const propReportId = String(reportIdArg ?? "").trim();
63560
- const [createdReportBootstrap, setCreatedReportBootstrap] = useState41(null);
64620
+ const [createdReportBootstrap, setCreatedReportBootstrap] = useState42(null);
63561
64621
  const effectiveReportId = propReportId || createdReportBootstrap?.reportId || "";
63562
64622
  const { eventTracking } = useContext36(EventTrackingContext);
63563
- const [draftSessionId, setDraftSessionId] = useState41(generateDraftSessionId);
64623
+ const [draftSessionId, setDraftSessionId] = useState42(generateDraftSessionId);
63564
64624
  const useInMemoryEngines = options.useInMemoryEngines ?? false;
63565
64625
  const dateBucketMode = options.dateBucketMode ?? "dynamic";
63566
64626
  const restrictFieldOptionsToSelectedDatasources = options.restrictFieldOptionsToSelectedDatasources ?? true;
@@ -63574,16 +64634,10 @@ function useReport(reportIdArg, options = {}) {
63574
64634
  ...getDefaultPaginationState(),
63575
64635
  ...options.initialState?.pagination ?? {}
63576
64636
  });
63577
- const [internalPagination, setInternalPagination] = useState41(
64637
+ const [internalPagination, setInternalPagination] = useState42(
63578
64638
  initialPaginationRef.current
63579
64639
  );
63580
- const [paginationInteracted, setPaginationInteracted] = useState41(
63581
- () => Boolean(
63582
- options.state?.pagination || options.initialState?.pagination || options.onPaginationChange
63583
- )
63584
- );
63585
64640
  const pagination = controlledPagination ?? internalPagination;
63586
- const paginationActive = paginationInteracted || Boolean(controlledPagination);
63587
64641
  const paginationRef = useRef24(pagination);
63588
64642
  paginationRef.current = pagination;
63589
64643
  const onPaginationChangeRef = useRef24(options.onPaginationChange);
@@ -63594,10 +64648,7 @@ function useReport(reportIdArg, options = {}) {
63594
64648
  autoResetPageIndexRef.current = options.autoResetPageIndex ?? true;
63595
64649
  const paginationPageCountRef = useRef24(-1);
63596
64650
  const applyPaginationUpdate = useCallback5(
63597
- (updater, { activate = true } = {}) => {
63598
- if (activate) {
63599
- setPaginationInteracted(true);
63600
- }
64651
+ (updater) => {
63601
64652
  onPaginationChangeRef.current?.(updater);
63602
64653
  if (!controlledPaginationRef.current) {
63603
64654
  setInternalPagination((old) => functionalUpdate(updater, old));
@@ -63676,13 +64727,13 @@ function useReport(reportIdArg, options = {}) {
63676
64727
  dateBucketSetViaSetReportRef.current = false;
63677
64728
  aggregationStateSetViaSetReportRef.current = false;
63678
64729
  }
63679
- const [chartPivotHydrationEpoch, setChartPivotHydrationEpoch] = useState41(0);
64730
+ const [chartPivotHydrationEpoch, setChartPivotHydrationEpoch] = useState42(0);
63680
64731
  const [
63681
64732
  expandReportBuilderColumnsForFlatTable,
63682
64733
  setExpandReportBuilderColumnsForFlatTable
63683
- ] = useState41(false);
64734
+ ] = useState42(false);
63684
64735
  const prevPivotStateForColumnExpansionRef = useRef24(null);
63685
- const [sourceReport, setSourceReport] = useState41(
64736
+ const [sourceReport, setSourceReport] = useState42(
63686
64737
  null
63687
64738
  );
63688
64739
  const prevSourceReportIdForPivotHydrationRef = useRef24(null);
@@ -63697,18 +64748,22 @@ function useReport(reportIdArg, options = {}) {
63697
64748
  lastPivotHydrateCompletedSourceIdRef.current = null;
63698
64749
  }
63699
64750
  }
63700
- const [preserveSchemaWideOptions, setPreserveSchemaWideOptions] = useState41(false);
63701
- const [initialSchemaScopedTableNames, setInitialSchemaScopedTableNames] = useState41(null);
63702
- const [tableColumnsEditedSignature, setTableColumnsEditedSignature] = useState41(null);
63703
- useEffect31(() => {
64751
+ const [preserveSchemaWideOptions, setPreserveSchemaWideOptions] = useState42(false);
64752
+ const [initialSchemaScopedTableNames, setInitialSchemaScopedTableNames] = useState42(null);
64753
+ const [tableColumnsEditedSignature, setTableColumnsEditedSignature] = useState42(null);
64754
+ useEffect32(() => {
63704
64755
  setTableColumnsEditedSignature(null);
63705
64756
  }, [effectiveReportId]);
63706
- const [chartAxisEdits, setChartAxisEdits] = useState41({});
63707
- const [chartVisibilityOverrides, setChartVisibilityOverrides] = useState41({});
64757
+ const [chartAxisEdits, setChartAxisEdits] = useState42({});
64758
+ const [chartVisibilityOverrides, setChartVisibilityOverrides] = useState42({});
63708
64759
  const [client] = useContext36(ClientContext);
64760
+ const { loadDashboard } = useContext36(DashboardConfigContext);
64761
+ const queryClient = useQueryClient2();
63709
64762
  const [schemaData] = useContext36(SchemaDataContext);
63710
64763
  const { tenants, flags } = useContext36(TenantContext);
63711
64764
  const { getToken } = useContext36(FetchContext);
64765
+ const { getCacheCab } = useContext36(CacheCabContext);
64766
+ const cacheCab = useMemo33(() => getCacheCab(), [getCacheCab]);
63712
64767
  const clientDefaultDashboardName = String(
63713
64768
  client?.defaultDashboard?.name ?? ""
63714
64769
  ).trim();
@@ -63824,16 +64879,17 @@ function useReport(reportIdArg, options = {}) {
63824
64879
  filterStackRef.current = filterStack;
63825
64880
  const resolvedGroupRowsBy = decodePivotGroupOptionValue(groupRowsBy);
63826
64881
  const resolvedGroupColumnsBy = decodePivotGroupOptionValue(groupColumnsBy);
63827
- useEffect31(() => {
64882
+ useEffect32(() => {
63828
64883
  if (propReportId) {
63829
64884
  setCreatedReportBootstrap(null);
63830
64885
  }
63831
64886
  }, [propReportId]);
63832
- useEffect31(() => {
64887
+ useEffect32(() => {
63833
64888
  setChartAxisEdits({});
63834
64889
  setChartVisibilityOverrides({});
63835
64890
  }, [effectiveReportId]);
63836
64891
  const bootstrapReportTaskUsedForReportIdRef = useRef24(null);
64892
+ const sharedInitialRequestUsedForReportIdRef = useRef24(null);
63837
64893
  const schemaScopeInitializedForReportIdRef = useRef24(null);
63838
64894
  const initializeSchemaScopeForReport = (report) => {
63839
64895
  if (!report) return;
@@ -63855,6 +64911,55 @@ function useReport(reportIdArg, options = {}) {
63855
64911
  () => customFiltersFromFilterStack(filterStack),
63856
64912
  [filterStack]
63857
64913
  );
64914
+ const forceIncompleteCacheCabForParity = process.env.NODE_ENV === "test" && process.env.QUILL_FORCE_INCOMPLETE_CACHECAB_FOR_PARITY === "true";
64915
+ const resolveCachedReport = useCallback5(
64916
+ async ({
64917
+ reportBuilderState,
64918
+ filters,
64919
+ pivot,
64920
+ operation
64921
+ }) => {
64922
+ const startedAt = performance.now();
64923
+ const snapshot = await cacheCab.getReportSnapshot(
64924
+ effectiveReportId,
64925
+ tenants
64926
+ );
64927
+ const state = reportBuilderState ?? snapshot?.report.reportBuilderState;
64928
+ const resolution = resolveReportFromCacheCab({
64929
+ snapshot: snapshot && forceIncompleteCacheCabForParity ? { ...snapshot, complete: true, reason: "complete" } : snapshot,
64930
+ reportBuilderState: state,
64931
+ filters: filters ?? customFiltersFromFilterStack(state?.filterStack ?? []),
64932
+ pivot,
64933
+ operation,
64934
+ rowLimit: operation === "unique-values" ? void 0 : Math.max(
64935
+ DEFAULT_USE_REPORT_ROWS_PER_REQUEST,
64936
+ pagination.pageSize
64937
+ )
64938
+ });
64939
+ const diagnostics = globalThis.__QUILL_CACHECAB_DIAGNOSTICS__;
64940
+ if (Array.isArray(diagnostics)) {
64941
+ diagnostics.push({
64942
+ reportId: effectiveReportId,
64943
+ operation,
64944
+ outcome: resolution.resolved ? "hit" : "fallback",
64945
+ reason: resolution.resolved ? "complete" : resolution.reason,
64946
+ details: resolution.resolved ? void 0 : resolution.details,
64947
+ snapshotReason: snapshot?.reason ?? "cache_miss",
64948
+ forcedIncompleteSnapshot: Boolean(snapshot) && !snapshot?.complete && forceIncompleteCacheCabForParity,
64949
+ rowCount: snapshot?.rowCount ?? 0,
64950
+ durationMs: performance.now() - startedAt
64951
+ });
64952
+ }
64953
+ return resolution.resolved ? resolution.report : null;
64954
+ },
64955
+ [
64956
+ cacheCab,
64957
+ effectiveReportId,
64958
+ forceIncompleteCacheCabForParity,
64959
+ pagination.pageSize,
64960
+ tenants
64961
+ ]
64962
+ );
63858
64963
  const reloadKey = useMemo33(() => 0, []);
63859
64964
  const schemaForReportBuilderState = useMemo33(() => {
63860
64965
  if (schemaData.schemaWithCustomFields?.length) {
@@ -63928,7 +65033,7 @@ function useReport(reportIdArg, options = {}) {
63928
65033
  restrictFieldOptionsToSelectedDatasources,
63929
65034
  selectedBaseTableNamesForFieldOptions
63930
65035
  ]);
63931
- useEffect31(() => {
65036
+ useEffect32(() => {
63932
65037
  if (reportOverride) return;
63933
65038
  if (propReportId) return;
63934
65039
  if (createdReportBootstrap) return;
@@ -64278,15 +65383,11 @@ function useReport(reportIdArg, options = {}) {
64278
65383
  return null;
64279
65384
  }
64280
65385
  return {
64281
- reportBuilderState: {
64282
- ...sourceReportBuilderState,
64283
- columns: columnsForUniqueValues,
64284
- // Distinct string options for the filter query builder must not use the
64285
- // live filterStack: on surfaces that call setFilters on every query-builder
64286
- // change, in-progress rules refetch unique values and the engine often
64287
- // returns empty columns (no rows / invalid stack), clearing all multiselects.
64288
- filterStack: []
64289
- },
65386
+ // Distinct string options must not depend on presentation or live filters.
65387
+ reportBuilderState: createFilterUniqueValuesReportBuilderState(
65388
+ sourceReportBuilderState,
65389
+ columnsForUniqueValues
65390
+ ),
64290
65391
  stringColumns: stringColumns2,
64291
65392
  stringColumnsByTable: columnsForUniqueValues,
64292
65393
  dashboardName
@@ -64390,11 +65491,36 @@ function useReport(reportIdArg, options = {}) {
64390
65491
  databaseType: client?.databaseType
64391
65492
  })
64392
65493
  ],
64393
- queryFn: createUseFormQueryFn(async () => {
65494
+ queryFn: createUseFormQueryFn(async (signal) => {
64394
65495
  try {
64395
65496
  if (!client || !filterUniqueValuesRequest) {
64396
65497
  return { uniqueValuesByColumn: {} };
64397
65498
  }
65499
+ const cachedReport = await resolveCachedReport({
65500
+ reportBuilderState: filterUniqueValuesRequest.reportBuilderState,
65501
+ filters: [],
65502
+ pivot: null,
65503
+ operation: "unique-values"
65504
+ });
65505
+ if (cachedReport?.uniqueStringsByTable) {
65506
+ const localValues = {};
65507
+ let hasEveryColumn = true;
65508
+ for (const column of filterUniqueValuesRequest.stringColumnsByTable) {
65509
+ const table2 = String(column.table ?? "").trim();
65510
+ const field = String(column.field ?? "").trim();
65511
+ const values = cachedReport.uniqueStringsByTable[table2]?.[field] ?? cachedReport.uniqueStringsByTable[""]?.[field];
65512
+ if (!values) {
65513
+ hasEveryColumn = false;
65514
+ break;
65515
+ }
65516
+ localValues[field] = values;
65517
+ const alias = String(column.alias ?? "").trim();
65518
+ if (alias) localValues[alias] = values;
65519
+ }
65520
+ if (hasEveryColumn) {
65521
+ return { uniqueValuesByColumn: localValues };
65522
+ }
65523
+ }
64398
65524
  const tablesForUniqueValues = filterUniqueValuesRequest.reportBuilderState?.tables?.map(
64399
65525
  (table2) => String(table2?.name ?? "").trim()
64400
65526
  ) ?? filterUniqueValuesRequest.stringColumnsByTable.map((column) => String(column.table ?? "").trim()).filter(Boolean);
@@ -64407,7 +65533,8 @@ function useReport(reportIdArg, options = {}) {
64407
65533
  customFields: schemaData.customFields ?? void 0,
64408
65534
  tenants,
64409
65535
  dashboardName: filterUniqueValuesRequest.dashboardName,
64410
- getToken
65536
+ getToken,
65537
+ abortSignal: signal
64411
65538
  });
64412
65539
  const aliasToField = {};
64413
65540
  for (const column of filterUniqueValuesRequest.stringColumnsByTable) {
@@ -64439,12 +65566,18 @@ function useReport(reportIdArg, options = {}) {
64439
65566
  }
64440
65567
  });
64441
65568
  return { uniqueValuesByColumn: normalizedUniqueValuesByColumn };
64442
- } catch {
65569
+ } catch (error) {
65570
+ if (signal.aborted || error instanceof Error && error.name === "AbortError") {
65571
+ throw error;
65572
+ }
64443
65573
  return { uniqueValuesByColumn: {} };
64444
65574
  }
64445
65575
  }),
64446
65576
  enabled: filterUniqueValuesEnabled,
64447
- retry: false
65577
+ retry: false,
65578
+ staleTime: 5 * 6e4,
65579
+ gcTime: 15 * 6e4,
65580
+ placeholderData: keepPreviousData
64448
65581
  });
64449
65582
  const backendUniqueValuesByFieldName = useMemo33(() => {
64450
65583
  const valuesByField = /* @__PURE__ */ new Map();
@@ -64824,14 +65957,15 @@ function useReport(reportIdArg, options = {}) {
64824
65957
  () => stableSerializeForQueryKey(reloadKey),
64825
65958
  [reloadKey]
64826
65959
  );
64827
- useEffect31(() => {
65960
+ useEffect32(() => {
64828
65961
  if (!reportOverride) return;
64829
65962
  initializeSchemaScopeForReport(reportOverride);
64830
65963
  setSourceReport(reportOverride);
64831
65964
  }, [reportOverride, propReportId]);
64832
- useEffect31(() => {
65965
+ useEffect32(() => {
64833
65966
  schemaScopeInitializedForReportIdRef.current = null;
64834
65967
  bootstrapReportTaskUsedForReportIdRef.current = null;
65968
+ sharedInitialRequestUsedForReportIdRef.current = null;
64835
65969
  setPreserveSchemaWideOptions(false);
64836
65970
  setInitialSchemaScopedTableNames(null);
64837
65971
  if (!reportOverride) {
@@ -64885,7 +66019,33 @@ function useReport(reportIdArg, options = {}) {
64885
66019
  flagHash,
64886
66020
  clientHash
64887
66021
  }),
64888
- queryFn: createUseFormQueryFn(async () => {
66022
+ queryFn: createUseFormQueryFn(async (signal) => {
66023
+ let cachedReportBuilderState = initialReportBuilderStateForLoad ?? void 0;
66024
+ if (!cachedReportBuilderState) {
66025
+ const snapshot = await cacheCab.getReportSnapshot(
66026
+ effectiveReportId,
66027
+ tenants
66028
+ );
66029
+ if (snapshot?.complete || snapshot && forceIncompleteCacheCabForParity) {
66030
+ cachedReportBuilderState = await fetchReportBuilderStateByReportId({
66031
+ reportId: effectiveReportId,
66032
+ client,
66033
+ getToken,
66034
+ draftSessionId: draftSessionId || void 0,
66035
+ tenants,
66036
+ abortSignal: signal
66037
+ }) ?? void 0;
66038
+ }
66039
+ }
66040
+ const cachedReport = await resolveCachedReport({
66041
+ reportBuilderState: cachedReportBuilderState,
66042
+ filters: cachedReportBuilderState ? customFiltersFromFilterStack(cachedReportBuilderState.filterStack) : void 0,
66043
+ pivot: cachedReportBuilderState?.pivot,
66044
+ operation: "initial"
66045
+ });
66046
+ if (cachedReport) {
66047
+ return { report: cachedReport };
66048
+ }
64889
66049
  const allowReportTaskBootstrap = !initialReportBuilderStateForLoad && bootstrapReportTaskUsedForReportIdRef.current !== effectiveReportId;
64890
66050
  const loadTargetId = String(effectiveReportId ?? "").trim();
64891
66051
  const sourceReportIdentity = String(
@@ -64893,9 +66053,7 @@ function useReport(reportIdArg, options = {}) {
64893
66053
  ).trim();
64894
66054
  const formFiltersBelongToLoadTarget = Boolean(loadTargetId) && sourceReportIdentity === loadTargetId;
64895
66055
  const internalFiltersForLoad = useInMemoryEngines || !formFiltersBelongToLoadTarget ? [] : internalFilters;
64896
- if (allowReportTaskBootstrap) {
64897
- bootstrapReportTaskUsedForReportIdRef.current = effectiveReportId;
64898
- }
66056
+ const shareInitialRequest = sharedInitialRequestUsedForReportIdRef.current !== effectiveReportId;
64899
66057
  const loadResult = await loadReportForUseForm({
64900
66058
  reportId: effectiveReportId,
64901
66059
  initialReportBuilderState: initialReportBuilderStateForLoad,
@@ -64911,8 +66069,18 @@ function useReport(reportIdArg, options = {}) {
64911
66069
  dashboardName: resolvedSourceDashboardName,
64912
66070
  useInMemoryEngines,
64913
66071
  draftSessionId: draftSessionId || void 0,
64914
- rowsOnly: isCreatedReportBootstrapLoad
66072
+ shareInitialRequest,
66073
+ rowsOnly: isCreatedReportBootstrapLoad,
66074
+ abortSignal: signal
64915
66075
  });
66076
+ if (loadResult.report && !loadResult.error) {
66077
+ if (allowReportTaskBootstrap) {
66078
+ bootstrapReportTaskUsedForReportIdRef.current = effectiveReportId;
66079
+ }
66080
+ if (shareInitialRequest) {
66081
+ sharedInitialRequestUsedForReportIdRef.current = effectiveReportId;
66082
+ }
66083
+ }
64916
66084
  return loadResult;
64917
66085
  }),
64918
66086
  enabled: Boolean(effectiveReportId) && Boolean(client) && !reportOverride,
@@ -64924,7 +66092,7 @@ function useReport(reportIdArg, options = {}) {
64924
66092
  reportId: reportNameQueryReportId,
64925
66093
  clientHash
64926
66094
  }),
64927
- queryFn: createUseFormQueryFn(async () => {
66095
+ queryFn: createUseFormQueryFn(async (signal) => {
64928
66096
  if (!client) {
64929
66097
  return { name: null };
64930
66098
  }
@@ -64932,7 +66100,8 @@ function useReport(reportIdArg, options = {}) {
64932
66100
  reportId: reportNameQueryReportId,
64933
66101
  client,
64934
66102
  getToken,
64935
- tenants
66103
+ tenants,
66104
+ abortSignal: signal
64936
66105
  });
64937
66106
  }),
64938
66107
  enabled: Boolean(reportNameQueryReportId) && Boolean(client),
@@ -64945,13 +66114,13 @@ function useReport(reportIdArg, options = {}) {
64945
66114
  const reportTitleFromTask = String(reportNameQuery.data?.name ?? "").trim();
64946
66115
  const resolvedReportName = reportNameQueryReportId && sourceReportIdForTitle === reportNameQueryReportId && sourceReportNameForTitle ? sourceReportNameForTitle : reportTitleFromTask || sourceReportNameForTitle || "";
64947
66116
  const displayReportName = formReportName !== void 0 ? formReportName : resolvedReportName;
64948
- useEffect31(() => {
66117
+ useEffect32(() => {
64949
66118
  if (reportOverride) return;
64950
66119
  const report = initialLoadQuery.data?.report ?? null;
64951
66120
  initializeSchemaScopeForReport(report);
64952
66121
  setSourceReport(report);
64953
66122
  }, [initialLoadQuery.data, reportOverride, effectiveReportId]);
64954
- useEffect31(() => {
66123
+ useEffect32(() => {
64955
66124
  if (!sourceReport) {
64956
66125
  return;
64957
66126
  }
@@ -65177,7 +66346,7 @@ function useReport(reportIdArg, options = {}) {
65177
66346
  chartPivotHydrationEpoch
65178
66347
  ]);
65179
66348
  const hasDatePivotRow = Boolean(String(pivotState?.rowField ?? "").trim()) && isDateType(String(pivotState?.rowFieldType ?? ""));
65180
- useEffect31(() => {
66349
+ useEffect32(() => {
65181
66350
  const previous = prevPivotStateForColumnExpansionRef.current;
65182
66351
  if (previous && !pivotState) {
65183
66352
  setExpandReportBuilderColumnsForFlatTable(true);
@@ -65442,7 +66611,7 @@ function useReport(reportIdArg, options = {}) {
65442
66611
  queryColumns,
65443
66612
  sourceReport
65444
66613
  ]);
65445
- useEffect31(() => {
66614
+ useEffect32(() => {
65446
66615
  if (!Array.isArray(filterStack) || filterStack.length === 0) {
65447
66616
  return;
65448
66617
  }
@@ -65546,7 +66715,7 @@ function useReport(reportIdArg, options = {}) {
65546
66715
  [effectiveReportBuilderState]
65547
66716
  );
65548
66717
  const prevPaginationResetStateHashRef = useRef24(null);
65549
- useEffect31(() => {
66718
+ useEffect32(() => {
65550
66719
  const prevHash = prevPaginationResetStateHashRef.current;
65551
66720
  prevPaginationResetStateHashRef.current = effectiveReportBuilderStateHash;
65552
66721
  if (prevHash === null || prevHash === effectiveReportBuilderStateHash) {
@@ -65558,9 +66727,7 @@ function useReport(reportIdArg, options = {}) {
65558
66727
  if (paginationRef.current.pageIndex === 0) {
65559
66728
  return;
65560
66729
  }
65561
- applyPaginationUpdate((old) => ({ ...old, pageIndex: 0 }), {
65562
- activate: false
65563
- });
66730
+ applyPaginationUpdate((old) => ({ ...old, pageIndex: 0 }));
65564
66731
  }, [applyPaginationUpdate, effectiveReportBuilderStateHash]);
65565
66732
  const pivotTableDataReportBuilderState = useMemo33(() => {
65566
66733
  if (!effectiveReportBuilderState?.pivot) {
@@ -65622,22 +66789,60 @@ function useReport(reportIdArg, options = {}) {
65622
66789
  filterStack
65623
66790
  ]);
65624
66791
  const shouldForceTableRefreshFromFilters = !useInMemoryEngines && !pivotState;
65625
- const tableRefreshQueryEnabled = !reportOverride && tableRefreshVersion > 0 && Boolean(sourceReport) && Boolean(client) && Boolean(effectiveReportBuilderState) && !pendingPivotRefresh && sourceReportMatchesEffectiveReportId && (refreshDecision.shouldRefresh || shouldForceTableRefreshFromFilters && !shouldSkipRedundantFlatTableReportBuilderQuery);
66792
+ const immediateTableRefreshInput = useMemo33(
66793
+ () => ({
66794
+ version: tableRefreshVersion,
66795
+ reportBuilderStateHash: effectiveReportBuilderStateHash,
66796
+ reportBuilderState: effectiveReportBuilderState,
66797
+ shouldRefresh: refreshDecision.shouldRefresh || shouldForceTableRefreshFromFilters && !shouldSkipRedundantFlatTableReportBuilderQuery
66798
+ }),
66799
+ [
66800
+ effectiveReportBuilderState,
66801
+ effectiveReportBuilderStateHash,
66802
+ refreshDecision.shouldRefresh,
66803
+ shouldForceTableRefreshFromFilters,
66804
+ shouldSkipRedundantFlatTableReportBuilderQuery,
66805
+ tableRefreshVersion
66806
+ ]
66807
+ );
66808
+ const debouncedTableRefreshInput = useDebouncedRefreshInput(
66809
+ immediateTableRefreshInput
66810
+ );
66811
+ const disableTableRefreshDebounceForDiagnostics = process.env.NODE_ENV === "test" && process.env.QUILL_DISABLE_REPORT_REFRESH_DEBOUNCE === "true";
66812
+ const tableRefreshInput = disableTableRefreshDebounceForDiagnostics ? immediateTableRefreshInput : debouncedTableRefreshInput;
66813
+ const tableRefreshInputIsCurrent = tableRefreshInput.version === tableRefreshVersion && tableRefreshInput.reportBuilderStateHash === effectiveReportBuilderStateHash;
66814
+ const completedTableRefreshRef = useRef24(null);
66815
+ if (completedTableRefreshRef.current?.reportId !== effectiveReportId) {
66816
+ completedTableRefreshRef.current = null;
66817
+ }
66818
+ const tableRefreshInputAlreadyCompleted = !disableTableRefreshDebounceForDiagnostics && completedTableRefreshRef.current?.reportId === effectiveReportId && completedTableRefreshRef.current.version >= tableRefreshInput.version;
66819
+ const tableRefreshQueryEnabled = !reportOverride && tableRefreshInputIsCurrent && !tableRefreshInputAlreadyCompleted && tableRefreshInput.version > 0 && Boolean(sourceReport) && Boolean(client) && Boolean(tableRefreshInput.reportBuilderState) && !pendingPivotRefresh && sourceReportMatchesEffectiveReportId && tableRefreshInput.shouldRefresh;
65626
66820
  const tableRefreshQuery = useQuery({
65627
66821
  queryKey: createUseFormTableRefreshQueryKey({
65628
66822
  reportId: effectiveReportId,
65629
- tableRefreshVersion,
65630
- reportBuilderStateHash: effectiveReportBuilderStateHash,
66823
+ tableRefreshVersion: tableRefreshInput.version,
66824
+ reportBuilderStateHash: tableRefreshInput.reportBuilderStateHash,
65631
66825
  tenantHash,
65632
66826
  clientHash
65633
66827
  }),
65634
- queryFn: createUseFormQueryFn(async () => {
65635
- if (!effectiveReportBuilderState || !sourceReport || !client) {
66828
+ queryFn: createUseFormQueryFn(async (signal) => {
66829
+ if (!tableRefreshInput.reportBuilderState || !sourceReport || !client) {
65636
66830
  return { report: null, error: "Missing table refresh prerequisites" };
65637
66831
  }
66832
+ const cachedReport = await resolveCachedReport({
66833
+ reportBuilderState: tableRefreshInput.reportBuilderState,
66834
+ filters: customFiltersFromFilterStack(
66835
+ tableRefreshInput.reportBuilderState.filterStack
66836
+ ),
66837
+ pivot: null,
66838
+ operation: "table"
66839
+ });
66840
+ if (cachedReport) {
66841
+ return { report: cachedReport };
66842
+ }
65638
66843
  return loadViaReportBuilderState({
65639
66844
  reportId: effectiveReportId,
65640
- reportBuilderState: effectiveReportBuilderState,
66845
+ reportBuilderState: tableRefreshInput.reportBuilderState,
65641
66846
  client,
65642
66847
  getToken,
65643
66848
  tenants,
@@ -65645,7 +66850,8 @@ function useReport(reportIdArg, options = {}) {
65645
66850
  customFields: schemaData.customFields,
65646
66851
  dashboardName: resolvedSourceDashboardName,
65647
66852
  report: sourceReport,
65648
- draftSessionId: draftSessionId || void 0
66853
+ draftSessionId: draftSessionId || void 0,
66854
+ abortSignal: signal
65649
66855
  });
65650
66856
  }),
65651
66857
  enabled: tableRefreshQueryEnabled,
@@ -65660,7 +66866,7 @@ function useReport(reportIdArg, options = {}) {
65660
66866
  tenantHash,
65661
66867
  clientHash
65662
66868
  ],
65663
- queryFn: createUseFormQueryFn(async () => {
66869
+ queryFn: createUseFormQueryFn(async (signal) => {
65664
66870
  if (!createdBootstrapForInitialLoad || !initialReportBuilderStateForLoad || !client) {
65665
66871
  return { report: null, error: "Missing bootstrap count prerequisites" };
65666
66872
  }
@@ -65675,19 +66881,29 @@ function useReport(reportIdArg, options = {}) {
65675
66881
  dashboardName: resolvedSourceDashboardName,
65676
66882
  report: createdBootstrapForInitialLoad.report,
65677
66883
  draftSessionId: draftSessionId || void 0,
65678
- rowCountOnly: true
66884
+ rowCountOnly: true,
66885
+ abortSignal: signal
65679
66886
  });
65680
66887
  }),
65681
66888
  enabled: isCreatedReportBootstrapLoad && Boolean(client) && !reportOverride,
65682
66889
  retry: false
65683
66890
  });
65684
- useEffect31(() => {
66891
+ useEffect32(() => {
65685
66892
  if (!tableRefreshQueryEnabled) return;
65686
66893
  const report = tableRefreshQuery.data?.report;
65687
66894
  if (tableRefreshQuery.data?.error || !report) return;
66895
+ completedTableRefreshRef.current = {
66896
+ reportId: effectiveReportId,
66897
+ version: tableRefreshInput.version
66898
+ };
65688
66899
  setSourceReport(report);
65689
- }, [effectiveReportId, tableRefreshQuery.data, tableRefreshQueryEnabled]);
65690
- useEffect31(() => {
66900
+ }, [
66901
+ effectiveReportId,
66902
+ tableRefreshInput.version,
66903
+ tableRefreshQuery.data,
66904
+ tableRefreshQueryEnabled
66905
+ ]);
66906
+ useEffect32(() => {
65691
66907
  const countReport = createdBootstrapRowCountQuery.data?.report;
65692
66908
  if (createdBootstrapRowCountQuery.data?.error || !countReport) return;
65693
66909
  setSourceReport((currentReport) => {
@@ -65707,7 +66923,7 @@ function useReport(reportIdArg, options = {}) {
65707
66923
  return getChartTypeOptions2({ pivot: pivotState });
65708
66924
  }, [pivotState]);
65709
66925
  const resolvedChartType = chartTypes.find((option) => option.value === chartType)?.value ?? chartTypes.find((option) => option.value === sourceReport?.chartType)?.value ?? chartTypes.find((option) => option.value === DEFAULT_CHART_TYPE)?.value ?? chartTypes[0]?.value ?? DEFAULT_CHART_TYPE;
65710
- useEffect31(() => {
66926
+ useEffect32(() => {
65711
66927
  if (!sourceReport) {
65712
66928
  return;
65713
66929
  }
@@ -66202,7 +67418,7 @@ function useReport(reportIdArg, options = {}) {
66202
67418
  );
66203
67419
  return hasSamePivot && sourceFilterStackHash === filterStackHash;
66204
67420
  }, [filterStackHash, nextPivot, sourceReport]);
66205
- useEffect31(() => {
67421
+ useEffect32(() => {
66206
67422
  if (!pendingPivotRefresh) return;
66207
67423
  if (!nextPivot) {
66208
67424
  if (!sourceReport) {
@@ -66261,10 +67477,19 @@ function useReport(reportIdArg, options = {}) {
66261
67477
  flagHash,
66262
67478
  clientHash
66263
67479
  }),
66264
- queryFn: createUseFormQueryFn(async () => {
67480
+ queryFn: createUseFormQueryFn(async (signal) => {
66265
67481
  if (!effectiveReportBuilderState || !nextPivot || !sourceReport || !client) {
66266
67482
  return { report: null, error: "Missing pivot refresh prerequisites" };
66267
67483
  }
67484
+ const cachedReport = await resolveCachedReport({
67485
+ reportBuilderState: effectiveReportBuilderState,
67486
+ filters: customFilters,
67487
+ pivot: nextPivot,
67488
+ operation: "pivot"
67489
+ });
67490
+ if (cachedReport) {
67491
+ return { report: cachedReport };
67492
+ }
66268
67493
  const pivotRefreshBaseReport = {
66269
67494
  ...sourceReport,
66270
67495
  reportBuilderState: effectiveReportBuilderState
@@ -66284,7 +67509,8 @@ function useReport(reportIdArg, options = {}) {
66284
67509
  customFields: schemaData.customFields,
66285
67510
  dashboardName: resolvedSourceDashboardName,
66286
67511
  useInMemoryEngines,
66287
- draftSessionId: draftSessionId || void 0
67512
+ draftSessionId: draftSessionId || void 0,
67513
+ abortSignal: signal
66288
67514
  });
66289
67515
  return pivotRefreshResult;
66290
67516
  }),
@@ -66295,16 +67521,28 @@ function useReport(reportIdArg, options = {}) {
66295
67521
  queryKey: createUseFormPivotTableDataRefreshQueryKey({
66296
67522
  reportId: effectiveReportId,
66297
67523
  reportBuilderStateHash: pivotTableDataReportBuilderStateHash,
67524
+ pageSize: pagination.pageSize,
66298
67525
  tenantHash,
66299
67526
  clientHash
66300
67527
  }),
66301
- queryFn: createUseFormQueryFn(async () => {
67528
+ queryFn: createUseFormQueryFn(async (signal) => {
66302
67529
  if (!pivotTableDataReportBuilderState || !sourceReport || !client) {
66303
67530
  return {
66304
67531
  report: null,
66305
67532
  error: "Missing pivot table-data refresh prerequisites"
66306
67533
  };
66307
67534
  }
67535
+ const cachedReport = await resolveCachedReport({
67536
+ reportBuilderState: pivotTableDataReportBuilderState,
67537
+ filters: customFiltersFromFilterStack(
67538
+ pivotTableDataReportBuilderState.filterStack
67539
+ ),
67540
+ pivot: null,
67541
+ operation: "pivot-table"
67542
+ });
67543
+ if (cachedReport) {
67544
+ return { report: cachedReport };
67545
+ }
66308
67546
  const tableDataResult = await loadViaReportBuilderState({
66309
67547
  reportId: effectiveReportId,
66310
67548
  reportBuilderState: pivotTableDataReportBuilderState,
@@ -66315,7 +67553,12 @@ function useReport(reportIdArg, options = {}) {
66315
67553
  customFields: schemaData.customFields,
66316
67554
  dashboardName: resolvedSourceDashboardName,
66317
67555
  report: sourceReport,
66318
- draftSessionId: draftSessionId || void 0
67556
+ draftSessionId: draftSessionId || void 0,
67557
+ pagination: {
67558
+ pageIndex: 0,
67559
+ pageSize: pagination.pageSize
67560
+ },
67561
+ abortSignal: signal
66319
67562
  });
66320
67563
  return tableDataResult;
66321
67564
  }),
@@ -66324,28 +67567,20 @@ function useReport(reportIdArg, options = {}) {
66324
67567
  });
66325
67568
  const tablePageReportBuilderState = pivotTableDataReportBuilderState ?? effectiveReportBuilderState;
66326
67569
  const tablePageReportBuilderStateHash = pivotTableDataReportBuilderState ? pivotTableDataReportBuilderStateHash : effectiveReportBuilderStateHash;
66327
- const tablePaginationSort = useMemo33(() => {
66328
- const sortEntry = tablePageReportBuilderState?.sort?.[0];
66329
- const field = String(sortEntry?.field ?? "").trim();
66330
- if (!field || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) {
66331
- return void 0;
66332
- }
66333
- const isOutputColumn = (tablePageReportBuilderState?.columns ?? []).some(
66334
- (column) => String(column?.field ?? "").trim() === field
66335
- );
66336
- if (!isOutputColumn) {
66337
- return void 0;
66338
- }
66339
- const direction = String(sortEntry?.direction ?? "ASC").toUpperCase() === "DESC" ? "DESC" : "ASC";
66340
- return { field, direction };
66341
- }, [tablePageReportBuilderState]);
67570
+ const tablePaginationSort = useMemo33(
67571
+ () => getStablePaginationSort({
67572
+ columns: tablePageReportBuilderState?.columns,
67573
+ sort: tablePageReportBuilderState?.sort
67574
+ }),
67575
+ [tablePageReportBuilderState]
67576
+ );
66342
67577
  const baseWindowRowsLength = Array.isArray(sourceReport?.rows) ? sourceReport.rows.length : 0;
66343
67578
  const sourceReportRowCount = typeof sourceReport?.rowCount === "number" ? sourceReport.rowCount : void 0;
66344
67579
  const trustedSourceRowCount = sourceReportRowCount !== void 0 && (sourceReportRowCount > 0 || baseWindowRowsLength === 0) && sourceReportRowCount >= baseWindowRowsLength ? sourceReportRowCount : void 0;
66345
67580
  const paginationRangeStart = pagination.pageIndex * pagination.pageSize;
66346
67581
  const paginationRangeEnd = paginationRangeStart + pagination.pageSize;
66347
67582
  const pageWithinBaseWindow = paginationRangeEnd <= baseWindowRowsLength || trustedSourceRowCount !== void 0 && trustedSourceRowCount <= baseWindowRowsLength;
66348
- const tablePageQueryEnabled = paginationActive && !pageWithinBaseWindow && !reportOverride && !useInMemoryEngines && Boolean(client) && Boolean(sourceReport) && Boolean(tablePageReportBuilderState) && sourceReportMatchesEffectiveReportId && !pendingPivotRefresh;
67583
+ const tablePageQueryEnabled = !pageWithinBaseWindow && !reportOverride && !useInMemoryEngines && Boolean(client) && Boolean(sourceReport) && Boolean(tablePageReportBuilderState) && sourceReportMatchesEffectiveReportId && !pendingPivotRefresh;
66349
67584
  const tablePageQuery = useQuery({
66350
67585
  queryKey: createUseFormTablePageQueryKey({
66351
67586
  reportId: effectiveReportId,
@@ -66356,7 +67591,7 @@ function useReport(reportIdArg, options = {}) {
66356
67591
  tenantHash,
66357
67592
  clientHash
66358
67593
  }),
66359
- queryFn: createUseFormQueryFn(async () => {
67594
+ queryFn: createUseFormQueryFn(async (signal) => {
66360
67595
  if (!tablePageReportBuilderState || !sourceReport || !client) {
66361
67596
  return { report: null, error: "Missing table page prerequisites" };
66362
67597
  }
@@ -66376,7 +67611,8 @@ function useReport(reportIdArg, options = {}) {
66376
67611
  pageSize: pagination.pageSize
66377
67612
  },
66378
67613
  paginationSort: tablePaginationSort,
66379
- rowsOnly: true
67614
+ rowsOnly: true,
67615
+ abortSignal: signal
66380
67616
  });
66381
67617
  }),
66382
67618
  enabled: tablePageQueryEnabled,
@@ -66410,7 +67646,7 @@ function useReport(reportIdArg, options = {}) {
66410
67646
  pagination.pageSize
66411
67647
  );
66412
67648
  paginationPageCountRef.current = paginationPageCount;
66413
- useEffect31(() => {
67649
+ useEffect32(() => {
66414
67650
  if (!pivotTableDataRefreshQueryEnabled) return;
66415
67651
  if (pivotTableDataRefreshQuery.status !== "success" && pivotTableDataRefreshQuery.status !== "error") {
66416
67652
  return;
@@ -66451,7 +67687,7 @@ function useReport(reportIdArg, options = {}) {
66451
67687
  pivotTableDataRefreshQueryEnabled,
66452
67688
  effectiveReportId
66453
67689
  ]);
66454
- useEffect31(() => {
67690
+ useEffect32(() => {
66455
67691
  if (!pivotRefreshQueryEnabled) return;
66456
67692
  if (pivotRefreshQuery.status !== "success" && pivotRefreshQuery.status !== "error") {
66457
67693
  return;
@@ -66841,18 +68077,18 @@ function useReport(reportIdArg, options = {}) {
66841
68077
  resolvedYAxisFieldsForDisplay
66842
68078
  );
66843
68079
  if (String(baseChart.chartType ?? "").toLowerCase() === "table" && baseChart.pivot) {
66844
- const pivotAggregations = getPivotAggregations(baseChart);
68080
+ const pivotAggregations2 = getPivotAggregations(baseChart);
66845
68081
  const hasCustomMeasureLabel = resolvedYAxisFields.some((axis) => {
66846
68082
  const aggregation = findAggregationForFieldStrict(
66847
68083
  axis.field,
66848
- pivotAggregations
68084
+ pivotAggregations2
66849
68085
  );
66850
68086
  if (!aggregation) return false;
66851
68087
  const label = String(axis.label ?? "").trim();
66852
68088
  const defaultLabel = buildAggregationLabel(aggregation, baseChart);
66853
68089
  return Boolean(label) && label !== defaultLabel;
66854
68090
  });
66855
- if (pivotAggregations.length > 1 || hasCustomMeasureLabel) {
68091
+ if (pivotAggregations2.length > 1 || hasCustomMeasureLabel) {
66856
68092
  columns2 = mergePivotTableDisplayColumnLabelsFromYAxis({
66857
68093
  columns: columns2 ?? baseChart.columns,
66858
68094
  yAxisFields: resolvedYAxisFieldsForDisplay,
@@ -67204,7 +68440,7 @@ function useReport(reportIdArg, options = {}) {
67204
68440
  );
67205
68441
  const table = useMemo33(() => {
67206
68442
  const baseWindowRows = Array.isArray(sourceReport?.rows) ? sourceReport.rows : [];
67207
- const rawRows = !paginationActive ? baseWindowRows : tablePageRows ?? baseWindowRows.slice(paginationRangeStart, paginationRangeEnd);
68443
+ const rawRows = tablePageRows ?? baseWindowRows.slice(paginationRangeStart, paginationRangeEnd);
67208
68444
  const { columns: mergedFromReport } = mergeDisplayAndSourceForTableFormats({
67209
68445
  effectiveReportBuilderTableNames,
67210
68446
  scopedSchemaColumns,
@@ -67313,7 +68549,6 @@ function useReport(reportIdArg, options = {}) {
67313
68549
  sourceReport,
67314
68550
  tableColumnsEditedSignature,
67315
68551
  pagination,
67316
- paginationActive,
67317
68552
  paginationPageCount,
67318
68553
  paginationRangeEnd,
67319
68554
  paginationRangeStart,
@@ -68399,15 +69634,54 @@ function useReport(reportIdArg, options = {}) {
68399
69634
  });
68400
69635
  }
68401
69636
  } catch (error) {
69637
+ const requestedFieldNames = (resolved?.rules ?? []).map((rule) => String(rule?.field ?? "").trim()).filter(Boolean);
69638
+ const relevantFieldConfigs = Object.fromEntries(
69639
+ Object.entries(queryBuilderFieldConfigByName ?? {}).filter(
69640
+ ([key, config]) => {
69641
+ const configuredField = String(config?.field ?? "").trim();
69642
+ return requestedFieldNames.some((requestedField) => {
69643
+ const bareRequestedField = requestedField.split(".").pop() ?? "";
69644
+ return key === requestedField || configuredField === requestedField || configuredField === bareRequestedField;
69645
+ });
69646
+ }
69647
+ )
69648
+ );
68402
69649
  console.error("[useForm] setFilters swallowed error", {
68403
69650
  error: error instanceof Error ? error.message : String(error),
68404
69651
  stack: error instanceof Error ? error.stack : void 0,
69652
+ reportId: effectiveReportId,
68405
69653
  requestedRules: (resolved?.rules ?? []).map((rule) => ({
68406
69654
  table: rule?.table,
68407
69655
  field: rule?.field,
68408
69656
  operator: rule?.operator,
68409
69657
  value: rule?.value
68410
69658
  })),
69659
+ normalizedRules: (nextFiltersNormalizedForConfig?.rules ?? []).map((rule) => ({
69660
+ table: rule?.table,
69661
+ field: rule?.field,
69662
+ operator: rule?.operator,
69663
+ value: rule?.value
69664
+ })),
69665
+ relevantFieldConfigs,
69666
+ pivot: nextPivot,
69667
+ reportBuilderTables: effectiveReportBuilderState?.tables,
69668
+ reportBuilderColumns: effectiveReportBuilderColumns.filter(
69669
+ (column) => requestedFieldNames.some((requestedField) => {
69670
+ const bareRequestedField = requestedField.split(".").pop() ?? "";
69671
+ return String(column.field ?? "").trim() === bareRequestedField;
69672
+ })
69673
+ ).map((column) => ({
69674
+ table: column.table,
69675
+ field: column.field,
69676
+ alias: column.alias
69677
+ })),
69678
+ schemaTablesWithRequestedFields: schemaForReportBuilderState.filter(
69679
+ (table2) => table2.columns?.some(
69680
+ (column) => requestedFieldNames.some(
69681
+ (requestedField) => String(column.field ?? "").trim() === (requestedField.split(".").pop() ?? "")
69682
+ )
69683
+ )
69684
+ ).map((table2) => table2.name),
68411
69685
  fieldConfigKeys: Object.keys(queryBuilderFieldConfigByName ?? {})
68412
69686
  });
68413
69687
  }
@@ -68457,8 +69731,20 @@ function useReport(reportIdArg, options = {}) {
68457
69731
  tenants,
68458
69732
  draftSessionId: draftSessionId || void 0
68459
69733
  });
69734
+ const savedReportId = String(
69735
+ resp?.id ?? resp?._id ?? effectiveReportId ?? ""
69736
+ ).trim();
69737
+ if (savedReportId && resp && resp.name !== "error" && !resp.error) {
69738
+ await cacheCab.evictReport(savedReportId, tenants);
69739
+ await queryClient.invalidateQueries({
69740
+ queryKey: ["quill", "dashboard-report", savedReportId],
69741
+ refetchType: "none"
69742
+ });
69743
+ await loadDashboard(dashboardNameForNewReport, true);
69744
+ }
68460
69745
  return resp;
68461
69746
  }, [
69747
+ cacheCab,
68462
69748
  client,
68463
69749
  dashboardNameForNewReport,
68464
69750
  draftSessionId,
@@ -68467,6 +69753,8 @@ function useReport(reportIdArg, options = {}) {
68467
69753
  eventTracking,
68468
69754
  formReportName,
68469
69755
  getToken,
69756
+ loadDashboard,
69757
+ queryClient,
68470
69758
  resolvedXAxisField,
68471
69759
  resolvedXAxisFormat,
68472
69760
  resolvedXAxisLabel,
@@ -68850,7 +70138,7 @@ function ToolCallBlock({ toolCall }) {
68850
70138
  );
68851
70139
  }
68852
70140
  function ToolCallResult({ content }) {
68853
- const [expanded, setExpanded] = useState42(false);
70141
+ const [expanded, setExpanded] = useState43(false);
68854
70142
  if (!content) return null;
68855
70143
  const isLong = content.length > 300;
68856
70144
  const displayed = isLong && !expanded ? content.slice(0, 300) + "..." : content;
@@ -68905,15 +70193,15 @@ function Chat({
68905
70193
  const [client] = useContext37(ClientContext);
68906
70194
  const { getToken } = useContext37(FetchContext);
68907
70195
  const { tenants } = useContext37(TenantContext);
68908
- const [messages, setMessages] = useState42([]);
68909
- const [input, setInput] = useState42("");
68910
- const [inputError, setInputError] = useState42("");
68911
- const [isLoading, setIsLoading] = useState42(false);
68912
- const [model, setModel] = useState42("gemini-3-flash-preview");
70196
+ const [messages, setMessages] = useState43([]);
70197
+ const [input, setInput] = useState43("");
70198
+ const [inputError, setInputError] = useState43("");
70199
+ const [isLoading, setIsLoading] = useState43(false);
70200
+ const [model, setModel] = useState43("gemini-3-flash-preview");
68913
70201
  const containerRef = useRef25(null);
68914
70202
  const textareaRef = useRef25(null);
68915
70203
  const abortControllerRef = useRef25(null);
68916
- useEffect32(() => {
70204
+ useEffect33(() => {
68917
70205
  if (!containerRef.current) {
68918
70206
  return;
68919
70207
  }
@@ -69347,7 +70635,7 @@ function Chat({
69347
70635
  }
69348
70636
 
69349
70637
  // src/hooks/useReportFilterDraft.ts
69350
- import { useCallback as useCallback6, useEffect as useEffect33, useMemo as useMemo35, useRef as useRef26, useState as useState43 } from "react";
70638
+ import { useCallback as useCallback6, useEffect as useEffect34, useMemo as useMemo35, useRef as useRef26, useState as useState44 } from "react";
69351
70639
  var normalizeOperatorKey = (operator) => String(operator ?? "").trim().toLowerCase().replace(/[_\s]+/g, "");
69352
70640
  var defaultFilterRuleValueForOperator = (operator) => {
69353
70641
  const key = normalizeOperatorKey(operator);
@@ -69409,11 +70697,11 @@ function useReportFilterDraft(args) {
69409
70697
  () => fieldCatalogSignature(effectiveFields),
69410
70698
  [effectiveFields]
69411
70699
  );
69412
- const [resetEpoch, setResetEpoch] = useState43(0);
69413
- const [draftQuery, setDraftQuery] = useState43(committed);
69414
- const [hasUnappliedFilterChanges, setHasUnappliedFilterChanges] = useState43(false);
70700
+ const [resetEpoch, setResetEpoch] = useState44(0);
70701
+ const [draftQuery, setDraftQuery] = useState44(committed);
70702
+ const [hasUnappliedFilterChanges, setHasUnappliedFilterChanges] = useState44(false);
69415
70703
  const draftResetKey = `${reportId}|${committedSignature}|${resetEpoch}`;
69416
- useEffect33(() => {
70704
+ useEffect34(() => {
69417
70705
  setDraftQuery(committedRef.current);
69418
70706
  setHasUnappliedFilterChanges(false);
69419
70707
  }, [draftResetKey]);
@@ -69505,7 +70793,7 @@ import {
69505
70793
  useContext as useContext38,
69506
70794
  useLayoutEffect as useLayoutEffect5,
69507
70795
  useRef as useRef27,
69508
- useState as useState44
70796
+ useState as useState45
69509
70797
  } from "react";
69510
70798
  import { jsx as jsx89, jsxs as jsxs66 } from "react/jsx-runtime";
69511
70799
  var CHART_MIN_PX = 220;
@@ -69607,14 +70895,14 @@ function ReportDetail({
69607
70895
  const useDetailTableFromUseForm = isTableChart && !isPivotTableChartConfig(chart);
69608
70896
  const showBottomRawTable = !isTableChart;
69609
70897
  const chartSlotRef = useRef27(null);
69610
- const [chartHeightPx, setChartHeightPx] = useState44(360);
70898
+ const [chartHeightPx, setChartHeightPx] = useState45(360);
69611
70899
  useLayoutEffect5(() => {
69612
70900
  const el = chartSlotRef.current;
69613
70901
  if (!el) {
69614
70902
  return;
69615
70903
  }
69616
- const ro = new ResizeObserver((entries) => {
69617
- const entry = entries[0];
70904
+ const ro = new ResizeObserver((entries2) => {
70905
+ const entry = entries2[0];
69618
70906
  if (!entry) return;
69619
70907
  const h = Math.floor(entry.contentRect.height);
69620
70908
  if (h >= CHART_MIN_PX) {
@@ -69733,7 +71021,7 @@ function ReportDetail({
69733
71021
  init_valueFormatter();
69734
71022
 
69735
71023
  // src/hooks/useTenants.ts
69736
- import { useContext as useContext39, useEffect as useEffect34 } from "react";
71024
+ import { useContext as useContext39, useEffect as useEffect35 } from "react";
69737
71025
  var useTenants = (dashboardName) => {
69738
71026
  const {
69739
71027
  tenants,
@@ -69747,12 +71035,12 @@ var useTenants = (dashboardName) => {
69747
71035
  getMappedTenantsForDashboard,
69748
71036
  getViewerTenantsByOwner
69749
71037
  } = useContext39(TenantContext);
69750
- useEffect34(() => {
71038
+ useEffect35(() => {
69751
71039
  if (dashboardName) {
69752
71040
  fetchViewerTenantsForDashboard(dashboardName);
69753
71041
  }
69754
71042
  }, [dashboardName, fetchViewerTenantsForDashboard]);
69755
- useEffect34(() => {
71043
+ useEffect35(() => {
69756
71044
  if (dashboardName) {
69757
71045
  fetchMappedTenantsForDashboard(dashboardName);
69758
71046
  }
@@ -69771,7 +71059,7 @@ var useTenants = (dashboardName) => {
69771
71059
  };
69772
71060
 
69773
71061
  // src/hooks/useQuill.ts
69774
- import { useContext as useContext40, useEffect as useEffect35, useMemo as useMemo36, useState as useState45 } from "react";
71062
+ import { useContext as useContext40, useEffect as useEffect36, useMemo as useMemo36, useState as useState46 } from "react";
69775
71063
  init_paginationProcessing();
69776
71064
  init_tableProcessing();
69777
71065
  init_dataProcessing();
@@ -69794,9 +71082,9 @@ var useQuill = (reportId, pagination) => {
69794
71082
  const [client, isClientLoading] = useContext40(ClientContext);
69795
71083
  const { tenants } = useContext40(TenantContext);
69796
71084
  const { eventTracking } = useContext40(EventTrackingContext);
69797
- const [loading, setLoading] = useState45(true);
69798
- const [error, setError] = useState45(void 0);
69799
- const [previousPage, setPreviousPage] = useState45(0);
71085
+ const [loading, setLoading] = useState46(true);
71086
+ const [error, setError] = useState46(void 0);
71087
+ const [previousPage, setPreviousPage] = useState46(0);
69800
71088
  const processedReport = useMemo36(() => {
69801
71089
  return reportId && allReportsById[reportId] ? convertInternalReportToReport(
69802
71090
  mergeComparisonRange(allReportsById[reportId]),
@@ -69805,7 +71093,7 @@ var useQuill = (reportId, pagination) => {
69805
71093
  "useQuill"
69806
71094
  ) : void 0;
69807
71095
  }, [reportId, reportId && allReportsById[reportId], specificReportFilters]);
69808
- const [additionalProcessing, setAdditionProcessing] = useState45(
71096
+ const [additionalProcessing, setAdditionProcessing] = useState46(
69809
71097
  pagination ? {
69810
71098
  page: pagination
69811
71099
  } : void 0
@@ -69954,7 +71242,7 @@ var useQuill = (reportId, pagination) => {
69954
71242
  setLoading(false);
69955
71243
  }
69956
71244
  };
69957
- useEffect35(() => {
71245
+ useEffect36(() => {
69958
71246
  if (isClientLoading) return;
69959
71247
  if (reportId && specificReportFilters) {
69960
71248
  fetchReportHelper(reportId, {
@@ -70021,7 +71309,7 @@ var useMemoizedRows = (reportId) => {
70021
71309
  };
70022
71310
 
70023
71311
  // src/hooks/useAskQuill.tsx
70024
- import { useContext as useContext41, useEffect as useEffect36, useState as useState46 } from "react";
71312
+ import { useContext as useContext41, useEffect as useEffect37, useState as useState47 } from "react";
70025
71313
  init_astProcessing();
70026
71314
  init_astFilterProcessing();
70027
71315
  init_pivotProcessing();
@@ -70054,8 +71342,8 @@ var useAskQuill = (dashboardName) => {
70054
71342
  const { tenants } = useContext41(TenantContext);
70055
71343
  const { getToken } = useContext41(FetchContext);
70056
71344
  const { eventTracking } = useContext41(EventTrackingContext);
70057
- const [astInfo, setAstInfo] = useState46(void 0);
70058
- const [data, setData] = useState46({
71345
+ const [astInfo, setAstInfo] = useState47(void 0);
71346
+ const [data, setData] = useState47({
70059
71347
  rows: [],
70060
71348
  columns: [],
70061
71349
  pivot: null,
@@ -70065,9 +71353,9 @@ var useAskQuill = (dashboardName) => {
70065
71353
  pivotColumnFields: [],
70066
71354
  pivotValueFields: []
70067
71355
  });
70068
- const [loading, setLoading] = useState46(false);
70069
- const [error, setError] = useState46(void 0);
70070
- const [ask, setAsk] = useState46(
71356
+ const [loading, setLoading] = useState47(false);
71357
+ const [error, setError] = useState47(void 0);
71358
+ const [ask, setAsk] = useState47(
70071
71359
  async () => void 0
70072
71360
  );
70073
71361
  const askHelper = async (query) => {
@@ -70267,7 +71555,7 @@ var useAskQuill = (dashboardName) => {
70267
71555
  });
70268
71556
  setLoading(false);
70269
71557
  };
70270
- useEffect36(() => {
71558
+ useEffect37(() => {
70271
71559
  setAsk(() => askHelper);
70272
71560
  }, [schemaData.schema]);
70273
71561
  return {
@@ -70281,13 +71569,13 @@ var useAskQuill = (dashboardName) => {
70281
71569
  };
70282
71570
 
70283
71571
  // src/hooks/useVirtualTables.tsx
70284
- import { useContext as useContext42, useState as useState47 } from "react";
71572
+ import { useContext as useContext42, useState as useState48 } from "react";
70285
71573
  var useVirtualTables = () => {
70286
71574
  const [schemaData, setSchemaData] = useContext42(SchemaDataContext);
70287
71575
  const { tenants } = useContext42(TenantContext);
70288
71576
  const { getToken, quillFetchWithToken } = useContext42(FetchContext);
70289
71577
  const { eventTracking } = useContext42(EventTrackingContext);
70290
- const [loadingTables, setLoadingTables] = useState47({});
71578
+ const [loadingTables, setLoadingTables] = useState48({});
70291
71579
  const handleReload = async (client, caller) => {
70292
71580
  setSchemaData({ ...schemaData, isSchemaLoading: true });
70293
71581
  setLoadingTables(
@@ -70457,14 +71745,14 @@ var useChangelogRefresh = () => {
70457
71745
  const [, setSchemaData] = useContext43(SchemaDataContext);
70458
71746
  const { allReportsById } = useAllReports();
70459
71747
  const { reloadSome } = useVirtualTables();
70460
- const refreshChangelogEntries = async (entries, client) => {
71748
+ const refreshChangelogEntries = async (entries2, client) => {
70461
71749
  const dashboardsToReload = /* @__PURE__ */ new Set();
70462
71750
  let reloadAllDashboards = false;
70463
71751
  const schemaIds = [];
70464
71752
  const schemaIdsToRemove = /* @__PURE__ */ new Set();
70465
71753
  const dashboardsToRemove = /* @__PURE__ */ new Set();
70466
71754
  const reportsToRemove = /* @__PURE__ */ new Map();
70467
- for (const entry of entries) {
71755
+ for (const entry of entries2) {
70468
71756
  if (!entry.entityId || !entry.entityType) continue;
70469
71757
  const changeType = getChangeType(entry);
70470
71758
  switch (entry.entityType) {