@pythia-software/query-table-react 0.9.3 → 0.9.5

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
@@ -312,9 +312,9 @@ import {
312
312
  createGroupingModel,
313
313
  prepareGrouping,
314
314
  withoutGrouping as withoutGrouping2,
315
- sourceExecutionSchema,
316
- toQueryRequestV3,
317
- validateQueryResultV3,
315
+ sourceExecutionSchema as sourceExecutionSchema2,
316
+ toQueryRequestV3 as toQueryRequestV32,
317
+ validateQueryResultV3 as validateQueryResultV32,
318
318
  metricPopulationError as metricPopulationError2
319
319
  } from "@pythia-software/query-table-core";
320
320
  import { ITEM_DETAILS_COLUMN, itemPageFields } from "@pythia-software/query-table-core";
@@ -688,7 +688,8 @@ function validateComputedResponse(request, result, schema) {
688
688
  );
689
689
  const executed = /* @__PURE__ */ new Set([
690
690
  ...request.select.filter(isComputedField),
691
- ...request.orderBy.map((term) => term.field).filter(isComputedField)
691
+ ...request.orderBy.map((term) => term.field).filter(isComputedField),
692
+ ...request.where.flatMap((term) => ("any" in term ? term.any : [term]).map((predicate) => predicate.field)).filter(isComputedField)
692
693
  ]);
693
694
  for (const [name, revision] of Object.entries(request.expectedRevisions)) {
694
695
  const id = name.replace(/^@computed\//, "");
@@ -721,6 +722,53 @@ function validateComputedResponse(request, result, schema) {
721
722
  }
722
723
  }
723
724
 
725
+ // src/previewRows.ts
726
+ import {
727
+ sourceExecutionSchema,
728
+ toQueryRequestV3,
729
+ toServerQuery,
730
+ toServerQueryV2,
731
+ validateQueryResultV3
732
+ } from "@pythia-software/query-table-core";
733
+ async function fetchPreviewRows(transport, query, schema, execution, definitions, now, signal, baseSchema = schema) {
734
+ if (signal.aborted) throw new DOMException("Aborted", "AbortError");
735
+ const sourceSchema = sourceExecutionSchema(baseSchema, execution, definitions);
736
+ let result;
737
+ const computedFilter = query.where.some((term) => ("any" in term ? term.any : [term]).some((predicate) => predicate.field.startsWith("@computed/")));
738
+ if (transport.fetchRowsV2 && execution && !query.grouping && !computedFilter) {
739
+ const request = toServerQueryV2(query, sourceSchema, execution);
740
+ if (request.diagnostics.length)
741
+ throw Error(request.diagnostics.map((issue) => issue.message).join(" "));
742
+ for (const definition of definitions) {
743
+ if (executionRevision(execution, definition.id) !== definition.revision && (request.select.includes(`@computed/${definition.id}`) || request.orderBy.some((term) => term.field === `@computed/${definition.id}`) || request.where.some((term) => ("any" in term ? term.any : [term]).some((predicate) => predicate.field === `@computed/${definition.id}`))))
744
+ throw Error("Computed definitions changed. Refresh the server execution handshake.");
745
+ }
746
+ const response = await transport.fetchRowsV2(request, signal);
747
+ validateComputedResponse(request, response, sourceSchema);
748
+ result = response;
749
+ } else if (transport.fetchQueryV3 && transport.queryCapabilities?.version === 3) {
750
+ const request = toQueryRequestV3(
751
+ { ...query, aggregations: [] },
752
+ sourceSchema,
753
+ transport.queryCapabilities,
754
+ execution,
755
+ { now }
756
+ );
757
+ if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
758
+ const response = await transport.fetchQueryV3(request, signal);
759
+ validateQueryResultV3(response, request, query.grouping ? schema : sourceSchema);
760
+ result = { rows: response.rows, total: response.total };
761
+ } else if (transport.fetchRows && !query.grouping && !(computedFilter && transport.fetchRowsV2 && execution)) {
762
+ result = await transport.fetchRows(toServerQuery(query, schema), signal);
763
+ } else {
764
+ throw Error("Row execution requires fetchRows, negotiated fetchRowsV2, or negotiated fetchQueryV3.");
765
+ }
766
+ if (signal.aborted) throw new DOMException("Aborted", "AbortError");
767
+ if (!Number.isSafeInteger(result.total) || result.total < 0 || !Array.isArray(result.rows) || result.rows.length > query.limit)
768
+ throw Error("Invalid preview row response.");
769
+ return result;
770
+ }
771
+
724
772
  // src/useComputedColumns.ts
725
773
  import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
726
774
  import {
@@ -732,7 +780,6 @@ import {
732
780
  isComputedField as isComputedField2,
733
781
  memoryComputedColumnStore,
734
782
  readFieldValue as readFieldValue2,
735
- toServerQuery,
736
783
  validateComputedColumn,
737
784
  isSelectable,
738
785
  hasRegex
@@ -1101,59 +1148,80 @@ function useComputedColumns(options) {
1101
1148
  },
1102
1149
  [compile, store, schema.name]
1103
1150
  );
1151
+ const previewRequest = useRef4(null);
1104
1152
  const preview = useCallback4(
1105
1153
  async (source, count, signal, editingId, includeInputs = true) => {
1106
- if (!Number.isInteger(count) || count < 1 || count > 1e4)
1107
- throw new Error("Choose between 1 and 10,000 rows.");
1108
- const plan = compile(source, editingId);
1109
- let batch = [], total = 0;
1110
- if (transport) {
1111
- const request = toServerQuery(
1112
- {
1154
+ previewRequest.current?.abort();
1155
+ const controller = new AbortController();
1156
+ previewRequest.current = controller;
1157
+ const abort2 = () => controller.abort();
1158
+ signal?.addEventListener("abort", abort2, { once: true });
1159
+ if (signal?.aborted) controller.abort();
1160
+ try {
1161
+ if (!Number.isInteger(count) || count < 1 || count > 1e4)
1162
+ throw new Error("Choose between 1 and 10,000 rows.");
1163
+ const plan = compile(source, editingId);
1164
+ let batch = [], total = 0;
1165
+ if (transport) {
1166
+ const request = {
1113
1167
  ...query,
1168
+ ...options.previewQuery?.grouping ? { grouping: options.previewQuery.grouping } : {},
1114
1169
  select: [
1115
1170
  .../* @__PURE__ */ new Set([schema.idField, ...backendDependencies(plan)])
1116
1171
  ].map((field) => ({ field })),
1117
1172
  limit: Math.min(count, 500),
1118
1173
  offset: 0
1119
- },
1120
- schema
1121
- );
1122
- while (batch.length < count) {
1123
- if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
1124
- const res = await transport.fetchRows(
1125
- {
1126
- ...request,
1127
- offset: batch.length,
1128
- limit: Math.min(500, count - batch.length)
1129
- },
1130
- signal
1174
+ };
1175
+ const now = options.now ?? Date.now();
1176
+ const computedFilter = request.where.some((term) => ("any" in term ? term.any : [term]).some((predicate) => isComputedField2(predicate.field)));
1177
+ const v3 = !(transport.fetchRowsV2 && options.execution && !request.grouping && !computedFilter) && transport.fetchQueryV3 && transport.queryCapabilities?.version === 3;
1178
+ const pageSize = Math.min(500, v3 ? transport.queryCapabilities.maxRows : 500);
1179
+ if (!Number.isInteger(pageSize) || pageSize < 1) throw Error("Invalid negotiated preview row limit.");
1180
+ while (batch.length < count) {
1181
+ const res = await fetchPreviewRows(
1182
+ transport,
1183
+ {
1184
+ ...request,
1185
+ offset: batch.length,
1186
+ limit: Math.min(pageSize, count - batch.length)
1187
+ },
1188
+ schema,
1189
+ options.execution,
1190
+ definitions,
1191
+ now,
1192
+ controller.signal,
1193
+ options.sourceSchema
1194
+ );
1195
+ total = res.total;
1196
+ batch.push(...res.rows.slice(0, count - batch.length));
1197
+ if (!res.rows.length || batch.length >= total) break;
1198
+ }
1199
+ } else {
1200
+ const result = applyQuery(
1201
+ clientRows ?? [],
1202
+ { ...query, limit: count, offset: 0 },
1203
+ schema
1131
1204
  );
1132
- total = res.total;
1133
- batch.push(...res.rows.slice(0, count - batch.length));
1134
- if (!res.rows.length || batch.length >= total) break;
1205
+ batch = result.rows;
1206
+ total = result.total;
1135
1207
  }
1136
- } else {
1137
- const result = applyQuery(
1138
- clientRows ?? [],
1139
- { ...query, limit: count, offset: 0 },
1140
- schema
1208
+ const inputs = inputsFor(plan, batch), results = await evaluateFormulaRows(
1209
+ plan.ast,
1210
+ inputs,
1211
+ controller.signal,
1212
+ workerFactory
1213
+ );
1214
+ if (controller.signal.aborted) throw new DOMException("Aborted", "AbortError");
1215
+ return groupPreview(
1216
+ includeInputs ? plan.dependencies : [],
1217
+ inputs,
1218
+ results,
1219
+ total
1141
1220
  );
1142
- batch = result.rows;
1143
- total = result.total;
1221
+ } finally {
1222
+ signal?.removeEventListener("abort", abort2);
1223
+ if (previewRequest.current === controller) previewRequest.current = null;
1144
1224
  }
1145
- const inputs = inputsFor(plan, batch), results = await evaluateFormulaRows(
1146
- plan.ast,
1147
- inputs,
1148
- signal,
1149
- workerFactory
1150
- );
1151
- return groupPreview(
1152
- includeInputs ? plan.dependencies : [],
1153
- inputs,
1154
- results,
1155
- total
1156
- );
1157
1225
  },
1158
1226
  [
1159
1227
  compile,
@@ -1163,9 +1231,15 @@ function useComputedColumns(options) {
1163
1231
  backendDependencies,
1164
1232
  clientRows,
1165
1233
  inputsFor,
1166
- workerFactory
1234
+ workerFactory,
1235
+ options.execution,
1236
+ definitions,
1237
+ options.sourceSchema,
1238
+ options.previewQuery?.grouping,
1239
+ options.now
1167
1240
  ]
1168
1241
  );
1242
+ useEffect4(() => () => previewRequest.current?.abort(), [preview]);
1169
1243
  return {
1170
1244
  displaySchema,
1171
1245
  api: {
@@ -1198,7 +1272,7 @@ import {
1198
1272
  metricDependencies,
1199
1273
  isComputedField as isComputedField4,
1200
1274
  computedFieldName as computedFieldName2,
1201
- toServerQueryV2
1275
+ toServerQueryV2 as toServerQueryV22
1202
1276
  } from "@pythia-software/query-table-core";
1203
1277
 
1204
1278
  // src/useRequestActivity.ts
@@ -2318,7 +2392,7 @@ function useQueryTable(opts) {
2318
2392
  () => opts.canonicalizeQuery?.(storedQuery) ?? storedQuery,
2319
2393
  [storedQuery, opts.canonicalizeQuery]
2320
2394
  );
2321
- const sourceCatalogue = useMemo9(() => sourceExecutionSchema(sourceSchema, opts.computedExecution), [sourceSchema, opts.computedExecution]);
2395
+ const sourceCatalogue = useMemo9(() => sourceExecutionSchema2(sourceSchema, opts.computedExecution), [sourceSchema, opts.computedExecution]);
2322
2396
  const sourceRows = providedRows;
2323
2397
  const groupingModel = useMemo9(() => createGroupingModel(sourceCatalogue), [sourceCatalogue]);
2324
2398
  const remoteGrouping = !!transport?.fetchQueryV3 && transport.queryCapabilities?.version === 3 && transport.queryCapabilities.groupedRows;
@@ -2493,6 +2567,9 @@ function useQueryTable(opts) {
2493
2567
  const { api: computed, displaySchema } = useComputedColumns({
2494
2568
  schema,
2495
2569
  query: executionQuery,
2570
+ sourceSchema,
2571
+ previewQuery: query,
2572
+ ...groupingNow !== void 0 ? { now: groupingNow } : {},
2496
2573
  rows,
2497
2574
  serverComputed,
2498
2575
  ...serverExecution ? { serverExecution } : {},
@@ -2508,7 +2585,7 @@ function useQueryTable(opts) {
2508
2585
  (name) => isComputedField4(name) ? computed.compile(`[${name.replace(/]/g, "]] ".trim())}]`) : void 0,
2509
2586
  [computed.compile]
2510
2587
  );
2511
- const metricCompileSchema = useMemo9(() => opts.metricExecution ? sourceExecutionSchema(displaySchema, opts.metricExecution, computed.definitions) : displaySchema, [displaySchema, opts.metricExecution, computed.definitions]);
2588
+ const metricCompileSchema = useMemo9(() => opts.metricExecution ? sourceExecutionSchema2(displaySchema, opts.metricExecution, computed.definitions) : displaySchema, [displaySchema, opts.metricExecution, computed.definitions]);
2512
2589
  const metricCompile = useCallback12(
2513
2590
  (clause) => {
2514
2591
  const diagnostics = validateMetric2(
@@ -2538,7 +2615,7 @@ function useQueryTable(opts) {
2538
2615
  ...query.select.length ? query.select : defaults.select,
2539
2616
  ...pageFields.filter((name) => displaySchema.fields.some((field) => field.name === name)).map((field) => ({ field }))
2540
2617
  ] } : executionQuery;
2541
- const v2Request = execution && transport?.fetchRowsV2 ? toServerQueryV2(requestQuery, displaySchema, execution) : void 0;
2618
+ const v2Request = execution && transport?.fetchRowsV2 ? toServerQueryV22(requestQuery, displaySchema, execution) : void 0;
2542
2619
  const request = v2Request ?? toServerQuery2(requestQuery, displaySchema);
2543
2620
  if (v2Request)
2544
2621
  for (const definition of computed.definitions) {
@@ -2622,8 +2699,8 @@ function useQueryTable(opts) {
2622
2699
  try {
2623
2700
  validateExecution(query);
2624
2701
  if (transport) {
2625
- if (transport.fetchQueryV3 && transport.queryCapabilities?.version === 3 && (query.grouping || query.select.some((column) => isComputedField4(column.field)) || query.orderBy.some((term) => isComputedField4(term.field)) || query.where.some((term) => ("any" in term ? term.any : [term]).some((predicate) => isComputedField4(predicate.field))))) {
2626
- const request = toQueryRequestV3(
2702
+ if (transport.fetchQueryV3 && transport.queryCapabilities?.version === 3 && (query.grouping || query.select.some((column) => isComputedField4(column.field)) || query.orderBy.some((term) => isComputedField4(term.field)) || query.where.some((term) => ("any" in term ? term.any : [term]).some((predicate) => isComputedField4(predicate.field))) || !transport.fetchRows && !(transport.fetchRowsV2 && opts.computedExecution))) {
2703
+ const request = toQueryRequestV32(
2627
2704
  { ...query, aggregations: [], select: [
2628
2705
  ...query.select.filter((column) => isComputedField4(column.field)),
2629
2706
  ...serverRowQuery.select.map((field) => ({ field }))
@@ -2635,7 +2712,7 @@ function useQueryTable(opts) {
2635
2712
  );
2636
2713
  if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
2637
2714
  const result = await transport.fetchQueryV3(request, ac.signal);
2638
- validateQueryResultV3(result, request, query.grouping ? schema : sourceCatalogue);
2715
+ validateQueryResultV32(result, request, query.grouping ? schema : sourceCatalogue);
2639
2716
  if (!cancelled) {
2640
2717
  setCommittedFields(request.result.select);
2641
2718
  setServerExecution(result.execution);
@@ -2651,6 +2728,8 @@ function useQueryTable(opts) {
2651
2728
  return;
2652
2729
  }
2653
2730
  const useV2 = "version" in serverRowQuery && transport.fetchRowsV2;
2731
+ if (!useV2 && !transport.fetchRows)
2732
+ throw Error("Row execution requires fetchRows, negotiated fetchRowsV2, or negotiated fetchQueryV3.");
2654
2733
  if ("diagnostics" in serverRowQuery && Array.isArray(serverRowQuery.diagnostics) && serverRowQuery.diagnostics.length)
2655
2734
  throw Error(
2656
2735
  serverRowQuery.diagnostics.map((d) => d.message).join(" ")
@@ -2787,7 +2866,7 @@ function useQueryTable(opts) {
2787
2866
  const metricExecution = opts.metricExecution ?? (opts.computedExecution ? pageReady && serverExecution ? serverExecution : opts.computedExecution : void 0);
2788
2867
  const v3MetricExecution = opts.metricExecution ?? opts.computedExecution;
2789
2868
  const metricSourceCatalogue = useMemo9(
2790
- () => sourceExecutionSchema(sourceSchema, v3MetricExecution),
2869
+ () => sourceExecutionSchema2(sourceSchema, v3MetricExecution),
2791
2870
  [sourceSchema, v3MetricExecution]
2792
2871
  );
2793
2872
  const executeV3Metrics = useCallback12(async (metricQuery, signal) => {
@@ -2802,7 +2881,7 @@ function useQueryTable(opts) {
2802
2881
  let metrics = [];
2803
2882
  if (runnable.length) {
2804
2883
  const scoped = { ...metricQuery, aggregations: runnable };
2805
- const request = toQueryRequestV3(
2884
+ const request = toQueryRequestV32(
2806
2885
  query.grouping ? { ...scoped, grouping: query.grouping } : scoped,
2807
2886
  sourceSchema,
2808
2887
  capabilities,
@@ -2811,7 +2890,7 @@ function useQueryTable(opts) {
2811
2890
  );
2812
2891
  if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
2813
2892
  const result = await metricTransport.fetchQueryV3(request, signal);
2814
- validateQueryResultV3(result, request, query.grouping ? schema : metricSourceCatalogue);
2893
+ validateQueryResultV32(result, request, query.grouping ? schema : metricSourceCatalogue);
2815
2894
  metrics = result.metrics;
2816
2895
  }
2817
2896
  if (!refused.size) return { metrics };
@@ -2826,7 +2905,7 @@ function useQueryTable(opts) {
2826
2905
  const previewGrouping = useCallback12(async (draft, signal) => {
2827
2906
  if (!active || !storageReady || restoration.loading || restoration.error) throw Error("Wait for query restoration before previewing groups.");
2828
2907
  if (!transport?.fetchQueryV3 || !transport.queryCapabilities?.groupedRows || !draft.grouping) throw Error("The server group preview is unavailable.");
2829
- const request = toQueryRequestV3(
2908
+ const request = toQueryRequestV32(
2830
2909
  { ...draft, aggregations: [] },
2831
2910
  sourceSchema,
2832
2911
  transport.queryCapabilities,
@@ -2835,7 +2914,7 @@ function useQueryTable(opts) {
2835
2914
  );
2836
2915
  if (request.diagnostics.length) throw Error(request.diagnostics.join(" "));
2837
2916
  const result = await transport.fetchQueryV3(request, signal);
2838
- validateQueryResultV3(result, request, groupingModel.resultSchema(draft.grouping.keys, draft.grouping.summaries));
2917
+ validateQueryResultV32(result, request, groupingModel.resultSchema(draft.grouping.keys, draft.grouping.summaries));
2839
2918
  return { rows: result.rows, total: result.total, sourceTotal: result.sourceTotal };
2840
2919
  }, [transport, sourceSchema, opts.computedExecution, groupingModel, groupingNow, opts.evaluationNow, active, storageReady, restoration.loading, restoration.error]);
2841
2920
  const aggState = useAggregations(