@pythia-software/query-table-react 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,6 +3,20 @@
3
3
  Headless React hooks for query-table: query state, fetching, URL/storage sync,
4
4
  selection, saved queries, aggregations, and column interactions.
5
5
 
6
+ `usePropertyView` controls saved read-only presentations per object type;
7
+ `useObjectReferences` batches cancellable relationship lookups. See
8
+ [object property views](../../docs/object-properties.md).
9
+
10
+ ## Grouped rows
11
+
12
+ `useQueryTable({ schema, clientRows })` enables one row-producing grouping stage
13
+ for complete local datasets. The standard query builder supplies GROUP BY,
14
+ HAVING, aggregate column editing and representative-row ordering. Use
15
+ `api.schema`, `api.visibleFields` and `api.rowId` for the active result shape;
16
+ set `grouping: false` to opt out. Backend transports currently reject grouped
17
+ row queries. See [grouping integration](../../docs/grouping.md) for the query
18
+ shape, source-stage controls, saved state and execution boundaries.
19
+
6
20
  ## Install
7
21
 
8
22
  ```bash
@@ -20,11 +34,30 @@ function Orders({ schema, rows }) {
20
34
  }
21
35
  ```
22
36
 
37
+ Row selection supports editable ranges. `selection.toggle(id)` toggles a row
38
+ and starts a range; `selection.toggle(id, true)` grows or shrinks it from that
39
+ fixed anchor, preserving earlier selections. If the anchor was unchecked,
40
+ the range unchecks rows instead, restoring their previous checks when shrunk.
41
+ `selection.toggle(id, true, true)` adds the span to the current selection and
42
+ commits existing checks before subsequent range adjustments. The existing
43
+ one- and two-argument calls remain supported.
44
+
45
+ Ranges use the current page's complete displayed order, including virtualized
46
+ rows outside the viewport. Changes to that order or page reset the active range
47
+ while retaining selected IDs; refreshing the same ordered IDs retains it.
48
+ Page-wide selection changes, `clear`, `replace`, and `retain` reset the range.
49
+ Shift without a valid anchor behaves like an ordinary toggle.
50
+
23
51
  `table.selection.replace(ids)` atomically makes any iterable of stable row IDs
24
52
  the complete selection, including IDs outside the displayed page.
25
53
  `table.selection.retain(ids)` atomically intersects the current selection with
26
54
  an iterable. Both operations reset the Shift-click range anchor.
27
55
 
56
+ Without `transport.fetchDistinctValues`, `table.filterValues(field, search)`
57
+ uses `clientRows` for autocomplete. For `textarray` fields it suggests individual
58
+ array elements, deduplicated and matched case-insensitively by substring. Local
59
+ suggestions are capped at 50 values, with `hasMore` indicating additional matches.
60
+
28
61
  ## Privacy defaults
29
62
 
30
63
  URL synchronization and durable browser storage are disabled by default. Set
@@ -34,12 +67,34 @@ history, referrers, logs, analytics, bookmarks, and screenshots. Pass
34
67
  is appropriate; sensitive applications should use an access-controlled server
35
68
  `StorageAdapter`.
36
69
 
70
+ With `syncUrl: true`, oversized queries use a browser-local FIFO cache and a
71
+ temporary short URL. Configure its size/budget with `links: { cache,
72
+ maxUrlBytes }`; the default complete URL budget is 2,000 bytes. Local links
73
+ can expire through cache eviction and do not transfer with browser bookmark sync.
74
+ Configure `storage.snapshots: httpQuerySnapshotStore("/api/query-snapshots")`
75
+ for permanent Save/Share links. `api.links.create()` also supports a host's explicit
76
+ Bookmark action. Normal edits never write snapshots to the server. Missing
77
+ references fail visibly before rows/metrics execute. See the
78
+ [link integration contract](../../docs/url-state-and-saved-search-design.md).
79
+
80
+ `api.saved.update(id)` persists the current snapshot before replacing the saved
81
+ search through `storage.updateNamed`. The adapter must update atomically and
82
+ retain the saved ID and default selection; failed updates preserve the original.
83
+ Both built-in adapters support updates. For custom adapters, QueryBuilder offers
84
+ Update only when `api.saved.canUpdate` is true. Optional last/default storage read
85
+ failures fall back to table defaults; explicit broken URL links still block
86
+ execution. A single empty `?q=` is treated as an absent query.
87
+
88
+ Item-detail property views hydrate independently, but synchronize into the
89
+ collection query only after URL/saved-query restoration settles. Detail layouts
90
+ encoded in the restored query take precedence over stored property views.
91
+
37
92
  See the [repository README](https://github.com/Pythia-Software/query-table#readme)
38
93
  for transport and schema examples.
39
94
 
40
95
  ## Shared computed column definitions
41
96
 
42
- Pass a stable `computedColumnStore` (for example, `httpComputedColumnStore("/api/computed-columns")` from core) to `useQueryTable`. `api.computed` exposes the catalogue, compile/preview, save with revision checking, and reload operations. Queries store only `@computed/<id>` SELECT references; formula evaluation stays in browser workers. Without a store, definitions are in memory for the mounted hook. See the repository’s `docs/computed-columns.md` for the complete integration contract and PostgreSQL adapter.
97
+ Pass a stable `computedColumnStore` (for example, `httpComputedColumnStore("/api/computed-columns")` from core) to `useQueryTable`. `api.computed` exposes the catalogue, compile/preview, save with revision checking, and reload operations. Queries store only `@computed/<id>` SELECT references. Local evaluation uses browser workers. With `fetchRowsV2` and an authoritative `computedExecution` envelope, computed SELECT and global sorting run on the server and values attach through validated stable-row-ID sidecars. Without a store, definitions are in memory for the mounted hook. See the repository’s `docs/computed-columns.md` for the complete integration contract and PostgreSQL adapter.
43
98
 
44
99
  ### Canonical keys and unavailable filters
45
100
 
@@ -59,3 +114,9 @@ For native set editors, use core's `mapSetFilterValues(query.where, (field, key)
59
114
  canonicalKey)` inside `canonicalizeQuery` to update aliases in both the existing
60
115
  predicates and their editor identity. Leave unrecognized/deleted keys unchanged
61
116
  and reject them through `validateQuery` so users can repair the selected tags.
117
+
118
+ ## Metric execution and drafts
119
+
120
+ `api.aggregations.preview(clauses, signal)` evaluates a draft without modifying the query. `replace(clauses, expected?)` commits the entire dashboard as one undoable change and can reject a stale editor baseline. Presentation-only changes reuse computation. Advanced remote execution uses `fetchMetrics` plus explicit v2 `metricCapabilities`; v1 transports continue to support representable simple metrics. All-matching scope requires the complete client dataset or a server metric service. Shown-row fallback uses committed inputs and reports missing dependencies.
121
+
122
+ See [the metrics integration guide](../../docs/metrics.md) for scopes, cancellation, worker-isolated regex, capability negotiation, computed revisions, and snapshot requirements.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,125 @@
1
- import { FormulaNode, FormulaValue, FormulaResult, ComputedColumn, FieldDef, ComputedColumnDraft, FormulaPlan, ColumnPreview, RowId, SelectColumn, QueryState, FieldSchema, SavedQuery, StorageAdapter, AggOp, AggregationClause, AggregationResult, WhereClause, DistinctValuesResult, FieldStats, OrderByClause, Transport, ComputedColumnStore } from '@pythia-software/query-table-core';
1
+ import { QueryState, GroupedRow, GroupingModel, FieldSchema, GroupingView, SummaryOp, RepresentativeRow, SummaryColumn, SelectColumn, PropertyViewState, SavedPropertyView, PropertyViewStorage, QueryLink, QuerySnapshot, ItemPropertyMode, ItemPageOptions, ItemDetailMode, ItemDetailsState, RelationshipAdapter, RowId, FormulaNode, FormulaValue, FormulaResult, ComputedColumn, FieldDef, ComputedColumnDraft, FormulaPlan, ColumnPreview, SavedQuery, StorageAdapter, AggOp, AggregationClause, AggregationResult, WhereClause, DistinctValuesResult, FieldStats, OrderByClause, Transport, ComputedColumnStore, ComputedExecution, QueryLinkOptions, ObjectReference, ResolvedObjectReference } from '@pythia-software/query-table-core';
2
+
3
+ /** Filter controls for either the result query or grouping's source query. */
4
+ type QueryFilterApi<Row> = Pick<QueryTableApi<Row>, "query" | "setQuery" | "loading" | "addFilter" | "updateFilter" | "removeFilter" | "clearFilters" | "updatePredicate" | "removePredicate" | "negatePredicate" | "reorderFilters" | "mergeFilters" | "filterValues" | "fieldStats">;
5
+
6
+ /** Source-stage controls. Rows/columns/metrics remain on the active result API. */
7
+ type GroupingSourceApi<Row> = QueryFilterApi<Row> & Pick<QueryTableApi<Row>, "itemDetails" | "rowId"> & {
8
+ schema: FieldSchema<Row>;
9
+ };
10
+ interface GroupingApi<Row> {
11
+ inputsReady?: boolean;
12
+ preview?: (query: QueryState, signal?: AbortSignal) => Promise<{
13
+ rows: GroupedRow[];
14
+ sourceTotal: number;
15
+ total: number;
16
+ }>;
17
+ supported: boolean;
18
+ model: GroupingModel<Row>;
19
+ api: QueryTableApi<Row>;
20
+ source: GroupingSourceApi<Row>;
21
+ grouped: QueryTableApi<Row>;
22
+ schema: FieldSchema<Row>;
23
+ view: GroupingView;
24
+ inputs: Row[];
25
+ notice: string;
26
+ setNotice: (notice: string) => void;
27
+ changeKeys: (keys: string[]) => void;
28
+ addSummary: (op: SummaryOp, field?: string) => void;
29
+ changeSummary: (id: string, op: SummaryOp) => void;
30
+ changeRepresentative: (rule: RepresentativeRow | null) => void;
31
+ representativeImpact: () => string;
32
+ applyColumns: (summaries: SummaryColumn[], columns: SelectColumn[]) => void;
33
+ }
34
+
35
+ interface UsePropertyViewOptions<Row> {
36
+ schema: FieldSchema<Row>;
37
+ /** Object type, never object ID. Defaults to schema.name. */
38
+ key?: string;
39
+ storage?: PropertyViewStorage;
40
+ initialView?: PropertyViewState;
41
+ }
42
+ interface PropertyViewApi {
43
+ view: PropertyViewState;
44
+ loading: boolean;
45
+ error: Error | null;
46
+ saved: SavedPropertyView[];
47
+ defaultId: string | null;
48
+ setView(view: PropertyViewState): void;
49
+ reset(): void;
50
+ undo(): void;
51
+ redo(): void;
52
+ canUndo: boolean;
53
+ canRedo: boolean;
54
+ load(id: string): void;
55
+ save(name: string): Promise<SavedPropertyView>;
56
+ remove(id: string): Promise<void>;
57
+ setDefault(id: string | null): Promise<void>;
58
+ }
59
+ /** Owns presentation only. Changing the inspected row never resets this controller. */
60
+ declare function usePropertyView<Row>(options: UsePropertyViewOptions<Row>): PropertyViewApi;
61
+
62
+ interface QueryLinksApi {
63
+ maxUrlBytes: number;
64
+ kind: QueryLink["kind"] | null;
65
+ busy: boolean;
66
+ error: Error | null;
67
+ /** Explicit Share/Bookmark: server snapshot if configured, otherwise a fitting inline link. */
68
+ create(query?: QueryState, options?: {
69
+ baseUrl?: string;
70
+ parameter?: string;
71
+ }): Promise<QueryLink>;
72
+ /** Synchronous local-only URL for navigation, never a server write. */
73
+ local(query: QueryState, baseUrl?: string, parameter?: string): QueryLink;
74
+ /** Save uses the same immutable snapshot and promotion path as Share. */
75
+ snapshot(query: QueryState): Promise<QuerySnapshot | null>;
76
+ }
77
+
78
+ interface ItemDetailRecord<Row> {
79
+ row: Row;
80
+ /** Omit for complete records. Supply for a projection to distinguish missing values from NULL. */
81
+ loadedFields?: readonly string[];
82
+ }
83
+ interface ItemDetailsOptions<Row> {
84
+ /** Detail catalogue; defaults to the table catalogue. Useful for non-column properties. */
85
+ schema?: FieldSchema<Row>;
86
+ /** Legacy collection URL parameter. useQueryTable.queryParameter takes precedence. */
87
+ queryParameter?: string;
88
+ /** Creator-approved modalities; users can enable/disable these in Select columns. Defaults to all. */
89
+ modes?: ItemDetailMode[];
90
+ /** Initially selected buttons. Defaults to Modal (or the first allowed mode). */
91
+ defaultModes?: ItemDetailMode[];
92
+ /** Creator's initial Page link settings; users can override in the column editor. */
93
+ page?: ItemPageOptions;
94
+ storage?: PropertyViewStorage;
95
+ objectType?: string;
96
+ relationships?: RelationshipAdapter;
97
+ /** Defaults to complete clientRows or Transport.fetchRow, then the current projected row. */
98
+ loadRow?: (id: RowId, signal: AbortSignal) => Promise<ItemDetailRecord<Row> | null>;
99
+ }
100
+ interface ItemDetailsApi<Row> {
101
+ links?: QueryLinksApi;
102
+ schema: FieldSchema<Row>;
103
+ /** Collection catalogue used to resolve Page template fields. */
104
+ pageSchema: FieldSchema<Row>;
105
+ queryParameter: string;
106
+ /** Modal controller, retained for standalone cards. */
107
+ properties: PropertyViewApi;
108
+ propertiesByMode: Record<ItemPropertyMode, PropertyViewApi>;
109
+ page: ItemPageOptions;
110
+ availableModes: ItemDetailMode[];
111
+ modes: ItemDetailMode[];
112
+ active: ItemDetailsState["active"] | null;
113
+ record: ItemDetailRecord<Row> | null;
114
+ loading: boolean;
115
+ error: Error | null;
116
+ relationships?: RelationshipAdapter;
117
+ setModes(modes: ItemDetailMode[]): void;
118
+ open(id: RowId, mode: "modal" | "expand"): void;
119
+ close(): void;
120
+ retry(): void;
121
+ loadRow(id: RowId, signal: AbortSignal): Promise<ItemDetailRecord<Row> | null>;
122
+ }
2
123
 
3
124
  type FormulaWorkerFactory = () => Worker;
4
125
  /** Host applications with a restrictive CSP can supply their own worker factory. */
@@ -17,13 +138,28 @@ interface ComputedColumnsApi<Row> {
17
138
  preview: (source: string, count: number, signal?: AbortSignal, editingId?: string, includeInputs?: boolean) => Promise<ColumnPreview>;
18
139
  }
19
140
 
141
+ interface RequestActivityEntry {
142
+ id: number;
143
+ kind: string;
144
+ startedAt: number;
145
+ status: "pending" | "success" | "error" | "aborted";
146
+ durationMs?: number;
147
+ error?: string;
148
+ }
149
+ interface RequestActivityApi {
150
+ /** Newest first; bounded to the latest 100 calls. Query values and results are never retained. */
151
+ entries: readonly RequestActivityEntry[];
152
+ clear: () => void;
153
+ }
154
+
20
155
  interface SelectionApi {
21
156
  selected: Set<RowId>;
22
157
  isSelected: (id: RowId) => boolean;
23
- /** Toggle one row. With `shiftKey`, extend from the last-toggled anchor to `id`
24
- * across the *currently displayed* order (falls back to a plain toggle if the
25
- * anchor row is no longer present). */
26
- toggle: (id: RowId, shiftKey?: boolean) => void;
158
+ /** Toggle one row and start an editable range. With `shiftKey`, resize that
159
+ * range in the current page's order, checking or unchecking according to the
160
+ * anchor's toggle. `additive` + Shift adds a span to the current selection.
161
+ * Missing anchors or changed display order fall back to a plain toggle. */
162
+ toggle: (id: RowId, shiftKey?: boolean, additive?: boolean) => void;
27
163
  /** Select / clear every row currently on the page. */
28
164
  setPage: (ids: RowId[], selected: boolean) => void;
29
165
  /** Atomically replace the complete selection with arbitrary stable row ids. */
@@ -65,12 +201,14 @@ interface ColumnDragApi {
65
201
  overIndex: number | null;
66
202
  /** True while a column drag is in progress. */
67
203
  active: boolean;
68
- /** Begin dragging `field`, initially previewed at `index` (its own slot). */
69
- start: (field: string, index: number) => void;
70
- /** Update the previewed insertion index (no-op if unchanged). */
71
- over: (index: number) => void;
204
+ /** Changes only for confirmed drops, so surfaces can persist auxiliary positions. */
205
+ dropRevision?: number;
206
+ /** Begin dragging `field` at `index`. Supply the surface order for cross-surface mapping. */
207
+ start: (field: string, index: number, order?: string[]) => void;
208
+ /** Update the insertion slot and optional surface order (no-op if unchanged). */
209
+ over: (index: number, order?: string[]) => void;
72
210
  /** End the drag (on drop or cancel). */
73
- end: () => void;
211
+ end: (committed?: boolean) => void;
74
212
  /** The live order to render while dragging: `source` moved to `overIndex`.
75
213
  * Returns `order` unchanged when idle, or when `source` isn't in `order`. */
76
214
  preview: (order: string[]) => string[];
@@ -84,6 +222,10 @@ interface SavedQueriesApi {
84
222
  defaultId: string | null;
85
223
  /** Persist the current query under a name. Rejects on duplicate names. */
86
224
  save: (name: string) => Promise<SavedQuery>;
225
+ /** Whether the storage adapter supports atomic updates. */
226
+ canUpdate: boolean;
227
+ /** Replace a saved query with the current state, retaining its id and default. */
228
+ update: (id: string) => Promise<SavedQuery>;
87
229
  /** Load a saved query into the live controller. */
88
230
  load: (id: string) => void;
89
231
  /** Mark a saved query as the default view. No-op when storage lacks support. */
@@ -94,17 +236,24 @@ interface SavedQueriesApi {
94
236
  }
95
237
  declare function useSavedQueries(key: string, currentQuery: QueryState, applyQueryState: (q: QueryState) => void, storage: StorageAdapter,
96
238
  /** Injected clock so saves are deterministic/testable. */
97
- now: () => number): SavedQueriesApi;
239
+ now: () => number, snapshot?: (query: QueryState) => Promise<QuerySnapshot | null>): SavedQueriesApi;
98
240
 
99
241
  interface UseQueryTableOptions<Row> {
242
+ /** Pause execution and URL synchronization while a mounted host view is hidden. */
243
+ active?: boolean;
244
+ /** Capture evaluation time for grouped executions and remote query requests. */
245
+ evaluationNow?: () => number;
100
246
  /** Normalize external aliases without storing presentation in query state. Must be pure and not throw. */
101
247
  canonicalizeQuery?: (query: QueryState) => QueryState;
102
248
  /** Reject an entire query before rows or metrics execute. Errors retain editable query state. */
103
249
  validateQuery?: (query: QueryState) => void;
250
+ /** Disable row-producing grouping for this instance. Enabled for complete client datasets by default. */
251
+ grouping?: boolean;
104
252
  schema: FieldSchema<Row>;
105
253
  /** Data access. Omit for a purely client-side table driven by `clientRows`. */
106
254
  transport?: Transport<Row>;
107
- /** All rows, for client-side mode (applyQuery runs locally over these). */
255
+ /** Dataset already available in memory. Remote grouping/aggregation belongs
256
+ * on the server; do not download a population to supply this option. */
108
257
  clientRows?: Row[];
109
258
  /** Saved/last-query persistence. Defaults to a non-durable in-memory adapter.
110
259
  * Pass localStorageAdapter() explicitly only when filter values are safe to
@@ -112,6 +261,8 @@ interface UseQueryTableOptions<Row> {
112
261
  storage?: StorageAdapter;
113
262
  /** Shared reusable definitions, independent of saved-query storage. */
114
263
  computedColumnStore?: ComputedColumnStore;
264
+ /** Authoritative host handshake for versioned server SELECT, sorts, and metrics. */
265
+ computedExecution?: ComputedExecution;
115
266
  /** Override the worker factory when CSP disallows blob workers. */
116
267
  formulaWorkerFactory?: FormulaWorkerFactory;
117
268
  /** Seed query, normalized and resource-bounded before use. When omitted the
@@ -121,6 +272,13 @@ interface UseQueryTableOptions<Row> {
121
272
  * base64url is encoding, not encryption: filters will appear in browser
122
273
  * history, referrers, logs, analytics, and screenshots when enabled. */
123
274
  syncUrl?: boolean;
275
+ /** Collection URL parameter, independent of item views. Takes precedence
276
+ * over the legacy itemDetails.queryParameter; otherwise defaults to q. */
277
+ queryParameter?: string;
278
+ /** URL budget and browser-local FIFO cache; remote snapshots come from storage.snapshots. */
279
+ links?: QueryLinkOptions;
280
+ /** Enable schema-driven click popovers, item modals and row expansion. */
281
+ itemDetails?: boolean | ItemDetailsOptions<Row>;
124
282
  /** Debounce (ms) between a query change and the fetch it triggers. Default 200. */
125
283
  debounceMs?: number;
126
284
  /** Injected clock for deterministic saved-query timestamps. Default Date.now. */
@@ -150,12 +308,12 @@ interface AutoRefreshApi {
150
308
  /** A patch for one metric. `field`/`label` accept an explicit `undefined` to
151
309
  * CLEAR them (e.g. switching to an op whose measure no longer applies); an
152
310
  * absent key leaves the existing value untouched. */
153
- interface AggregationPatch {
311
+ type AggregationPatch = {
154
312
  op?: AggOp;
155
- field?: string | undefined;
156
313
  groupBy?: string[];
157
- label?: string | undefined;
158
- }
314
+ } & {
315
+ [K in keyof Omit<AggregationClause, "id" | "op" | "groupBy">]?: AggregationClause[K] | undefined;
316
+ };
159
317
  interface AggregationsApi {
160
318
  /** The metric specs currently in the query. */
161
319
  clauses: AggregationClause[];
@@ -175,8 +333,33 @@ interface AggregationsApi {
175
333
  remove: (id: string) => void;
176
334
  /** Remove all metrics. */
177
335
  clear: () => void;
336
+ /** Evaluate a draft without modifying the query. */
337
+ preview?: (clauses: AggregationClause[], signal?: AbortSignal) => Promise<AggregationResult>;
338
+ /** Commit the complete draft as one undoable change. */
339
+ replace?: (clauses: AggregationClause[], expected?: AggregationClause[]) => void;
340
+ /** Validate a clause, including reusable computed dependencies. */
341
+ compile?: (clause: AggregationClause) => unknown;
342
+ /** Invalidation token for page, refresh, schema, and definition changes. */
343
+ contextKey?: string;
178
344
  }
179
345
  interface QueryTableApi<Row> {
346
+ /** Dependent source fetches can use ready without parsing display errors. */
347
+ restoration: {
348
+ status: "resolving" | "ready" | "error";
349
+ ready: boolean;
350
+ error: Error | null;
351
+ };
352
+ /** Call after a host pushState/router update, with an absolute URL or a location
353
+ * relative to window.location.href. Supersedes prior resolution;
354
+ * abort or an intervening query edit prevents stale publication. */
355
+ restoreLocation: (location: string | URL, signal?: AbortSignal) => Promise<void>;
356
+ /** Row-producing GROUP BY controls and source stage, when supported. */
357
+ grouping?: GroupingApi<Row>;
358
+ /** Current result schema; changes when entering or leaving grouping. */
359
+ schema?: FieldSchema<Row>;
360
+ links: QueryLinksApi;
361
+ /** Built-in item details, enabled by the instance creator. */
362
+ itemDetails?: ItemDetailsApi<Row>;
180
363
  query: QueryState;
181
364
  /** Schema defaults used for reset controls. */
182
365
  defaults: QueryState;
@@ -185,16 +368,21 @@ interface QueryTableApi<Row> {
185
368
  canUndo: boolean;
186
369
  /** Can move forward to a previously undone local query state. */
187
370
  canRedo: boolean;
188
- /** Move backward through local, unsaved query history. */
371
+ /** Move backward through local, unsaved query history. Empty history leaves
372
+ * incoming link restoration and errors unchanged. */
189
373
  undo: () => void;
190
- /** Move forward through local, unsaved query history. */
374
+ /** Move forward through local, unsaved query history. Empty history leaves
375
+ * incoming link restoration and errors unchanged. */
191
376
  redo: () => void;
377
+ requestActivity: RequestActivityApi;
192
378
  rows: Row[];
193
379
  total: number | null;
194
380
  loading: boolean;
195
381
  error: Error | null;
196
382
  refresh: () => void;
197
383
  autoRefresh: AutoRefreshApi;
384
+ /** Refresh one legacy row, or schedule a full V2 page refresh so computed
385
+ * values and page membership remain coherent. Track V2 completion via loading/error. */
198
386
  refreshRow: (id: RowId) => Promise<void>;
199
387
  aggregations: AggregationsApi;
200
388
  /** Append a predicate as its own AND term. */
@@ -240,11 +428,39 @@ interface QueryTableApi<Row> {
240
428
  }
241
429
  declare function useQueryTable<Row>(opts: UseQueryTableOptions<Row>): QueryTableApi<Row>;
242
430
 
431
+ interface MetricExecutionContext<Row> {
432
+ executeV3?: (query: QueryState, signal?: AbortSignal) => Promise<AggregationResult>;
433
+ queryContextKey?: string;
434
+ schema: FieldSchema<Row>;
435
+ /** One clock shared by source grouping, HAVING, rows and local metrics. */
436
+ now?: number;
437
+ /** Group projections use typed semantics even for shorthand metric clauses. */
438
+ strictLocalMetrics?: boolean;
439
+ transport?: Transport<Row> | undefined;
440
+ clientRows?: Row[] | undefined;
441
+ shownRows?: Row[] | undefined;
442
+ rowsReady?: boolean | undefined;
443
+ rowsError?: Error | null | undefined;
444
+ shownFields?: readonly string[] | undefined;
445
+ resolveComputed?: ((name: string) => FormulaPlan | undefined) | undefined;
446
+ workerFactory?: FormulaWorkerFactory | undefined;
447
+ revisions?: Record<string, string> | undefined;
448
+ execution?: ComputedExecution;
449
+ }
450
+
243
451
  interface AggregationsApiState {
244
452
  results: AggregationResult | null;
245
453
  loading: boolean;
246
454
  error: Error | null;
455
+ preview: (clauses: AggregationClause[], signal?: AbortSignal) => Promise<AggregationResult>;
247
456
  }
248
- declare function useAggregations<Row>(query: QueryState, schema: FieldSchema<Row>, transport: Transport<Row> | undefined, clientRows: Row[] | undefined, debounceMs: number, nonce: number, validateQuery?: (query: QueryState) => void): AggregationsApiState;
457
+ /** Fetch state is keyed by computation and scope, never card presentation. */
458
+ declare function useAggregations<Row>(query: QueryState, schema: FieldSchema<Row>, transport: Transport<Row> | undefined, clientRows: Row[] | undefined, debounceMs: number, nonce: number, validateQuery?: (q: QueryState) => void, options?: Omit<MetricExecutionContext<Row>, "schema" | "transport" | "clientRows">, enabled?: boolean): AggregationsApiState;
459
+
460
+ /** One cancellable batch per displayed reference set; rejects unrelated results. */
461
+ declare function useObjectReferences(references: ObjectReference[], adapter?: RelationshipAdapter): {
462
+ values: Map<string, ResolvedObjectReference>;
463
+ error: Error | null;
464
+ };
249
465
 
250
- export { type AggregationPatch, type AggregationsApi, type AggregationsApiState, type AutoRefreshApi, type AutoRefreshConfig, type AutoRefreshStatus, type ColumnDragApi, type ComputedColumnsApi, type FormulaWorkerFactory, type QueryTableApi, type SavedQueriesApi, type SelectApi, type SelectionApi, type UseQueryTableOptions, createFormulaWorker, evaluateFormulaRows, useAggregations, useColumnDrag, useQueryTable, useSavedQueries, useSelect, useSelection };
466
+ export { type AggregationPatch, type AggregationsApi, type AggregationsApiState, type AutoRefreshApi, type AutoRefreshConfig, type AutoRefreshStatus, type ColumnDragApi, type ComputedColumnsApi, type FormulaWorkerFactory, type GroupingApi, type GroupingSourceApi, type ItemDetailRecord, type ItemDetailsApi, type ItemDetailsOptions, type PropertyViewApi, type QueryFilterApi, type QueryLinksApi, type QueryTableApi, type RequestActivityApi, type RequestActivityEntry, type SavedQueriesApi, type SelectApi, type SelectionApi, type UsePropertyViewOptions, type UseQueryTableOptions, createFormulaWorker, evaluateFormulaRows, useAggregations, useColumnDrag, useObjectReferences, usePropertyView, useQueryTable, useSavedQueries, useSelect, useSelection };