@atscript/ui-table 0.1.140 → 0.1.142

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.d.cts CHANGED
@@ -31,6 +31,14 @@ declare function isSimpleEq(condition: FilterCondition): boolean;
31
31
  declare function conditionLabel(type: FilterConditionType): string;
32
32
  /** Count of fields that have at least one filled condition. */
33
33
  declare function filledFilterCount(filters: FieldFilters): number;
34
+ /**
35
+ * `filters` without the fields that have no filled condition — the shape
36
+ * table state holds (a field with nothing filled has no entry). Returns
37
+ * `filters` itself when every field has one; never mutates it.
38
+ *
39
+ * @since 0.1.142
40
+ */
41
+ declare function compactFieldFilters(filters: FieldFilters): FieldFilters;
34
42
  /** Summarize a field's conditions into a human-readable token label. */
35
43
  declare function filterTokenLabel(path: string, conditions: FilterCondition[], columnLabel?: string): string;
36
44
  //#endregion
@@ -175,6 +183,18 @@ interface UnsupportedFilter {
175
183
  /** Field paths the sub-expression references, in order of appearance. */
176
184
  fields: string[];
177
185
  }
186
+ /**
187
+ * One AND-ed piece of a Uniquery filter left out because it names a field
188
+ * outside `knownFields` — alone or next to known ones.
189
+ *
190
+ * @since 0.1.141
191
+ */
192
+ interface UnknownFilter {
193
+ /** The left-out sub-expression, as it appeared in the input. */
194
+ expr: FilterExpr;
195
+ /** The paths it names that are outside `knownFields`. */
196
+ fields: string[];
197
+ }
178
198
  /**
179
199
  * Field paths a Uniquery filter expression references, deduped, in order of
180
200
  * appearance (logical operators are walked, operator keys skipped).
@@ -185,9 +205,10 @@ declare function filterExprFields(expr: unknown): string[];
185
205
  /** Options for {@link decomposeUniqueryFilter}. @since 0.1.140 */
186
206
  interface DecomposeUniqueryFilterOptions {
187
207
  /**
188
- * Field paths the table knows. Pieces on fields outside it are ignored
189
- * silently; a piece mixing known and unknown fields is reported, never
190
- * carried. Omit to accept every field.
208
+ * Field paths the table knows. A piece that names a field outside it —
209
+ * alone or mixed with known ones — is left out whole and listed in
210
+ * `unknown`, never carried or reported as `unsupported`. Omit to accept
211
+ * every field.
191
212
  */
192
213
  knownFields?: Iterable<string>;
193
214
  /**
@@ -205,14 +226,22 @@ interface DecomposedUniqueryFilter {
205
226
  residual: FilterExpr[];
206
227
  /** Pieces left out and lost — the result is broader by exactly these. */
207
228
  unsupported: UnsupportedFilter[];
229
+ /**
230
+ * Pieces left out because they name a field outside `knownFields` (hidden
231
+ * from the caller, or not in the schema). Each is a whole AND-ed piece, so
232
+ * the result is broader by exactly these too. `[]` without `knownFields`.
233
+ * Since 0.1.141 — before, pieces on unknown fields only were dropped
234
+ * silently and mixed known/unknown pieces went to `unsupported`.
235
+ */
236
+ unknown: UnknownFilter[];
208
237
  }
209
238
  /**
210
239
  * Split a Uniquery `FilterExpr` into what the table's field-filter model
211
240
  * holds exactly (`filters`), what it cannot hold but carries as residual
212
- * conditions (`residual`, with `carry`), and what it leaves out
213
- * (`unsupported`). Nothing is approximated: `filters AND residual` selects
214
- * `expr` minus the `unsupported` pieces and pieces on fields outside
215
- * `knownFields`.
241
+ * conditions (`residual`, with `carry`), what it leaves out (`unsupported`)
242
+ * and what names a field outside `knownFields` (`unknown`). Nothing is
243
+ * approximated: `filters AND residual` selects `expr` minus the
244
+ * `unsupported` and `unknown` pieces.
216
245
  *
217
246
  * Never throws, never warns — the caller decides how to report.
218
247
  *
@@ -253,9 +282,10 @@ declare function normalizeResidualFilters(exprs: readonly FilterExpr[]): FilterE
253
282
  * to a dev-mode `console.warn` when no handler is given. To keep those
254
283
  * pieces instead, use {@link decomposeUniqueryFilter} with `carry`.
255
284
  *
256
- * Conditions on fields outside `knownFields` (when provided) are ignored
257
- * silently: they are not this table's (a host page flag, a stale column). A
258
- * piece that mixes known and unknown fields is reported.
285
+ * Pieces that name a field outside `knownFields` (when provided) are ignored
286
+ * silently: they are not this table's (a host page flag, a hidden or stale
287
+ * column). Since 0.1.141 that includes a piece mixing known and unknown
288
+ * fields — {@link decomposeUniqueryFilter} lists them as `unknown`.
259
289
  *
260
290
  * Returns `{}` for an empty/missing expression. Never throws.
261
291
  */
@@ -530,6 +560,113 @@ interface SystemPresetInput {
530
560
  */
531
561
  declare function resolveSystemPresets(input?: SystemPresetInput[]): SystemPreset[];
532
562
  //#endregion
563
+ //#region src/presets/prune-preset-snapshot.d.ts
564
+ /**
565
+ * The field paths a table can use, derived from its column list. A path
566
+ * outside these sets is one the server does not expose to the current caller
567
+ * (hidden by role, removed from the schema) — naming it in a query is rejected.
568
+ *
569
+ * @since 0.1.141
570
+ */
571
+ interface KnownFields {
572
+ /**
573
+ * Every column path, client-owned (display) columns included, in column
574
+ * order. Gates `columnNames`, column widths and sorters (a sorter on a
575
+ * client-owned column is applied in memory).
576
+ */
577
+ columns: ReadonlySet<string>;
578
+ /**
579
+ * Server-backed column paths. Gates displayed filter inputs, field filters
580
+ * and residual filter conditions.
581
+ */
582
+ server: ReadonlySet<string>;
583
+ }
584
+ /**
585
+ * What pruning left out of a preset snapshot or of residual conditions
586
+ * because it names a field outside {@link KnownFields}.
587
+ *
588
+ * @since 0.1.141
589
+ */
590
+ interface DroppedFields {
591
+ /** The unavailable field paths behind the drops, deduped, in order of appearance. */
592
+ fields: string[];
593
+ /** Dropped `columnNames` entries. */
594
+ columns: string[];
595
+ /** Dropped displayed filter inputs. */
596
+ filterFields: string[];
597
+ /** Dropped field filters — whole entries, one per field. */
598
+ filters: FieldFilters;
599
+ /** Dropped filter conditions — each a whole AND-ed conjunct. */
600
+ residual: FilterExpr[];
601
+ /** Dropped sorters. */
602
+ sorters: SortControl[];
603
+ }
604
+ /**
605
+ * Remove every entry of `snapshot` that names a field outside `known`, per
606
+ * aspect:
607
+ *
608
+ * - `columns.columnNames` — unknown names go, the rest keep their order. When
609
+ * none survives, it falls back to every column, never an empty grid.
610
+ * - `columns.columnWidths` — unknown keys go.
611
+ * - `filters` (displayed inputs) and `filterOps` — entries on a field outside
612
+ * `known.server` go. A field's conditions are one AND-ed conjunct, so
613
+ * dropping one only broadens the result.
614
+ * - `sorters` — unknown entries go, the rest keep their priority.
615
+ * - `itemsPerPage` — untouched.
616
+ *
617
+ * `dropped` is `null` when nothing user-visible went (a width-only drop is
618
+ * hygiene). The input is never mutated.
619
+ *
620
+ * @since 0.1.141
621
+ */
622
+ declare function prunePresetSnapshot(snapshot: PresetSnapshot, known: KnownFields): {
623
+ snapshot: PresetSnapshot;
624
+ dropped: DroppedFields | null;
625
+ };
626
+ /**
627
+ * Spell a snapshot the way table state holds it, so it compares equal to a
628
+ * capture of the state it produces:
629
+ *
630
+ * - `columns.columnWidths` — an entry equal to its column's default width, or
631
+ * on a path with no default (not a column), goes; an emptied map is
632
+ * omitted. Capture writes overrides only.
633
+ * - `filterOps` — a field with no filled condition goes
634
+ * (`compactFieldFilters`). Table state never holds one.
635
+ *
636
+ * `defaultWidths` maps each column path to its default width — the `d` of
637
+ * its `ColumnWidthsMap` entry. Other aspects pass through. Returns `snapshot`
638
+ * itself when nothing changes; the input is never mutated.
639
+ *
640
+ * @since 0.1.142
641
+ */
642
+ declare function canonicalPresetSnapshot(snapshot: PresetSnapshot, defaultWidths: Readonly<Record<string, string>>): PresetSnapshot;
643
+ /**
644
+ * Split residual filter conditions, in one pass, into the ones that name
645
+ * only `known` fields and the ones that do not. A condition naming any
646
+ * unknown field is dropped WHOLE — never pruned inside an `$or` (that would
647
+ * narrow the result) or a `$not` (that would invert it). Each condition is an
648
+ * AND-ed conjunct, so dropping one only broadens the result.
649
+ *
650
+ * `dropped` holds the conditions in `residual` and the unknown paths in
651
+ * `fields`; `null` when every condition is kept.
652
+ *
653
+ * @since 0.1.141
654
+ */
655
+ declare function pruneResidualFilters(exprs: FilterExpr[], known: ReadonlySet<string>): {
656
+ kept: FilterExpr[];
657
+ dropped: DroppedFields | null;
658
+ };
659
+ /**
660
+ * Append what a prune dropped back onto a snapshot captured from (pruned)
661
+ * table state, so overwriting a preset with a narrower view does not destroy
662
+ * the parts the saver cannot see. Column names, filter inputs and sorters go
663
+ * after the captured ones; field filters are added back. Only aspects
664
+ * `captured` carries are touched; widths of hidden columns are not kept.
665
+ *
666
+ * @since 0.1.141
667
+ */
668
+ declare function restoreDroppedEntries(captured: PresetSnapshot, dropped: DroppedFields): PresetSnapshot;
669
+ //#endregion
533
670
  //#region src/presets/preset-dirty.d.ts
534
671
  /**
535
672
  * JSON-stringify with object keys sorted alphabetically at every depth so
@@ -888,6 +1025,21 @@ interface UrlQueryStateSnapshot {
888
1025
  * or `sync.residual` is `false`. Since 0.1.140.
889
1026
  */
890
1027
  residual?: FilterExpr[];
1028
+ /**
1029
+ * Filter pieces left out because they name a field outside `knownFields`
1030
+ * and `localFields` — a column hidden from this caller, or gone from the
1031
+ * schema — with those paths in `fields`. Each is a whole AND-ed piece (a
1032
+ * mixed known/unknown one included), so the result is broader by exactly
1033
+ * these. A bare `field=value` piece is not listed: without the table
1034
+ * definition it reads the same as a flag of the host page. Omitted when
1035
+ * none. Since 0.1.141 — before, a mixed piece was listed in `unsupported`.
1036
+ */
1037
+ unknown?: UnknownFilter[];
1038
+ /**
1039
+ * `$sort` entries left out because their field is outside `knownFields`
1040
+ * and `localFields`. Omitted when none. Since 0.1.141.
1041
+ */
1042
+ unknownSorters?: SortControl[];
891
1043
  /**
892
1044
  * Raw record offset from `$skip` (omitted when no `$skip` in URL). The
893
1045
  * decoder does NOT compute a page index — that requires `itemsPerPage`,
@@ -1013,20 +1165,29 @@ declare function stateToUrlQueryString(state: UrlQueryStateLike, defaults: UrlQu
1013
1165
  declare function urlQueryConsumesKey(key: string): boolean;
1014
1166
  interface UrlQueryParseOptions {
1015
1167
  /**
1016
- * Field paths the table knows about. Conditions on fields outside this set
1017
- * are silently dropped. Omit to accept any field (useful when the table
1018
- * definition isn't loaded yet).
1168
+ * Field paths the table knows about. Conditions and sorters on fields
1169
+ * outside this set are dropped and listed in `unknown` / `unknownSorters`.
1170
+ * Omit to accept any field (useful when the table definition isn't loaded
1171
+ * yet).
1019
1172
  */
1020
1173
  knownFields?: Iterable<string>;
1174
+ /**
1175
+ * Client-owned column paths. The URL never restores them (no server query
1176
+ * can use them), but they are not unknown either: a sorter on one is
1177
+ * ignored, and a filter piece correlating one with a known field is
1178
+ * reported as `unsupported` (`"cross-field"`). Since 0.1.141.
1179
+ */
1180
+ localFields?: Iterable<string>;
1021
1181
  /** Per-aspect sync gates — must match the encoder's config to keep the round-trip symmetric. */
1022
1182
  sync?: UrlQuerySync;
1023
1183
  }
1024
1184
  /**
1025
1185
  * Parse a URL query string back into the table state subset.
1026
1186
  *
1027
- * Robust by design — schema drift and copy-paste errors must not break the
1028
- * recipient's view:
1029
- * - unknown fields (not in `knownFields`) → silently dropped
1187
+ * Robust by design — schema drift, fields hidden from the recipient and
1188
+ * copy-paste errors must not break the recipient's view:
1189
+ * - unknown fields (not in `knownFields`) → dropped, listed in `unknown` /
1190
+ * `unknownSorters` (the parser does not warn — the caller decides)
1030
1191
  * - filter pieces field filters cannot express (cross-field OR, unknown
1031
1192
  * operator, …) → left out of `filters` and listed in `unsupported`, never
1032
1193
  * approximated (the parser does not warn — the caller decides). Unless
@@ -1561,4 +1722,4 @@ declare function sortRowsLocally<T extends Record<string, unknown>>(rows: T[], s
1561
1722
  //#region src/utils/dev.d.ts
1562
1723
  declare const DEV: boolean;
1563
1724
  //#endregion
1564
- export { APP_CONF_PREFIX, type AppConfData, AppPrefsClient, type AppPrefsClientConfig, type AppPrefsLoadResult, type AsPresetEntryData, type AsPresetEntryRow, type AsPresetsErrorCode, type AspectGate, type AspectMask, type BuildTableQueryOptions, type ColumnFilterType, type ColumnReorderPosition, type ColumnWidthEntry, type ColumnWidthsMap, type ConfigTab, type CsvOptions, DEFAULT_EXPORT_PAGE_SIZE, DEFAULT_ROW_HEIGHT_PX, DEV, DRAFT_PERSISTED_ASPECTS, type DateShortcut, type DecomposeUniqueryFilterOptions, type DecomposedUniqueryFilter, type DisplayColumnDef, type DraftPersistedAspect, ExportAbortError, type ExportPage, type ExportPageFetcher, type ExportScalar, type FetchPlan, type FetchPlanMode, type FieldFilters, type FilterCondition, type FilterConditionType, type FilterableColumn, MAX_DEFAULT_COLUMN_WIDTH_PX, type MergeResult, NULL_OPS, PRESET_ASPECTS, type PageAlignedBlock, type PlanFetchArgs, type PresetAspect, type PresetCapabilities, type PresetColumnWidthEntry, type PresetData, type PresetDraft, type PresetFilterOpEntry, type PresetLimitReachedBody, type PresetSnapshot, type PresetSnapshotWire, type PresetSorterEntry, PresetsClient, type PresetsClientConfig, PresetsHttpError, type PresetsListResult, type PresetsSaveAsOptions, type PresetsSaveResult, type QueryOptions, RESERVED_ID_PREFIXES, STANDARD_PRESET_ID, SYSTEM_PRESET_PREFIX, type SelectionMode, type SystemPreset, type SystemPresetInput, type TableStateData, type TableStateMethods, URL_SNAPSHOT_KEY, USER_CONF_PREFIX, type UnsupportedFilter, type UnsupportedFilterReason, type UrlQueryDefaults, type UrlQueryParseOptions, type UrlQueryStateLike, type UrlQueryStateSnapshot, type UrlQuerySync, type UserConfData, appConfId, arraysEqual, blockStartFor, buildTableQuery, cellAsString, clampTopIndex, collectExportRows, columnDefaultCondition, columnFilterConditions, columnFilterType, computeDefaultColumnWidth, conditionLabel, conditionsForType, csvCell, dateShortcuts, debounce, decomposeUniqueryFilter, defaultCondition, derivePresetAspects, deserializeDraft, draftMatchesPreset, escapeRegex, filledFilterCount, filterExprFields, filterExprKey, filterTokenLabel, filtersToUniqueryFilter, formatFilterCondition, formatFilterExpr, fromWireSnapshot, gateOwns, hasSecondValue, isAuthError, isColumnFilterable, isDirtyAgainst, isEmptyDraft, isFilled, isSimpleEq, isSystemPresetId, isUnavailableError, mergeDisplayColumns, mergeFilters, mergeSorters, normaliseSystemPresetId, normalizeResidualFilters, pageAlignedBlocksFor, parseColumnFilterInput, parseFilterInput, planFetch, reconcileColumnWidthDefaults, reorderColumnNames, residualGateOwns, resolveAspectGate, resolveExportValue, resolveSystemPresets, rowsToPks, sameColumnSet, serializeDraft, setsEqual, sortRowsLocally, sortersEqual, stableStringify, stateToUrlQueryString, toCsv, toWireSnapshot, togglePk, trimSelection, unescapeRegex, uniqueryFilterToFieldFilters, urlQueryConsumesKey, urlQueryStringToState, userConfId, walkBackwardAbsorb, walkForwardAbsorb, withStableOrder };
1725
+ export { APP_CONF_PREFIX, type AppConfData, AppPrefsClient, type AppPrefsClientConfig, type AppPrefsLoadResult, type AsPresetEntryData, type AsPresetEntryRow, type AsPresetsErrorCode, type AspectGate, type AspectMask, type BuildTableQueryOptions, type ColumnFilterType, type ColumnReorderPosition, type ColumnWidthEntry, type ColumnWidthsMap, type ConfigTab, type CsvOptions, DEFAULT_EXPORT_PAGE_SIZE, DEFAULT_ROW_HEIGHT_PX, DEV, DRAFT_PERSISTED_ASPECTS, type DateShortcut, type DecomposeUniqueryFilterOptions, type DecomposedUniqueryFilter, type DisplayColumnDef, type DraftPersistedAspect, type DroppedFields, ExportAbortError, type ExportPage, type ExportPageFetcher, type ExportScalar, type FetchPlan, type FetchPlanMode, type FieldFilters, type FilterCondition, type FilterConditionType, type FilterableColumn, type KnownFields, MAX_DEFAULT_COLUMN_WIDTH_PX, type MergeResult, NULL_OPS, PRESET_ASPECTS, type PageAlignedBlock, type PlanFetchArgs, type PresetAspect, type PresetCapabilities, type PresetColumnWidthEntry, type PresetData, type PresetDraft, type PresetFilterOpEntry, type PresetLimitReachedBody, type PresetSnapshot, type PresetSnapshotWire, type PresetSorterEntry, PresetsClient, type PresetsClientConfig, PresetsHttpError, type PresetsListResult, type PresetsSaveAsOptions, type PresetsSaveResult, type QueryOptions, RESERVED_ID_PREFIXES, STANDARD_PRESET_ID, SYSTEM_PRESET_PREFIX, type SelectionMode, type SystemPreset, type SystemPresetInput, type TableStateData, type TableStateMethods, URL_SNAPSHOT_KEY, USER_CONF_PREFIX, type UnknownFilter, type UnsupportedFilter, type UnsupportedFilterReason, type UrlQueryDefaults, type UrlQueryParseOptions, type UrlQueryStateLike, type UrlQueryStateSnapshot, type UrlQuerySync, type UserConfData, appConfId, arraysEqual, blockStartFor, buildTableQuery, canonicalPresetSnapshot, cellAsString, clampTopIndex, collectExportRows, columnDefaultCondition, columnFilterConditions, columnFilterType, compactFieldFilters, computeDefaultColumnWidth, conditionLabel, conditionsForType, csvCell, dateShortcuts, debounce, decomposeUniqueryFilter, defaultCondition, derivePresetAspects, deserializeDraft, draftMatchesPreset, escapeRegex, filledFilterCount, filterExprFields, filterExprKey, filterTokenLabel, filtersToUniqueryFilter, formatFilterCondition, formatFilterExpr, fromWireSnapshot, gateOwns, hasSecondValue, isAuthError, isColumnFilterable, isDirtyAgainst, isEmptyDraft, isFilled, isSimpleEq, isSystemPresetId, isUnavailableError, mergeDisplayColumns, mergeFilters, mergeSorters, normaliseSystemPresetId, normalizeResidualFilters, pageAlignedBlocksFor, parseColumnFilterInput, parseFilterInput, planFetch, prunePresetSnapshot, pruneResidualFilters, reconcileColumnWidthDefaults, reorderColumnNames, residualGateOwns, resolveAspectGate, resolveExportValue, resolveSystemPresets, restoreDroppedEntries, rowsToPks, sameColumnSet, serializeDraft, setsEqual, sortRowsLocally, sortersEqual, stableStringify, stateToUrlQueryString, toCsv, toWireSnapshot, togglePk, trimSelection, unescapeRegex, uniqueryFilterToFieldFilters, urlQueryConsumesKey, urlQueryStringToState, userConfId, walkBackwardAbsorb, walkForwardAbsorb, withStableOrder };
package/dist/index.d.mts CHANGED
@@ -31,6 +31,14 @@ declare function isSimpleEq(condition: FilterCondition): boolean;
31
31
  declare function conditionLabel(type: FilterConditionType): string;
32
32
  /** Count of fields that have at least one filled condition. */
33
33
  declare function filledFilterCount(filters: FieldFilters): number;
34
+ /**
35
+ * `filters` without the fields that have no filled condition — the shape
36
+ * table state holds (a field with nothing filled has no entry). Returns
37
+ * `filters` itself when every field has one; never mutates it.
38
+ *
39
+ * @since 0.1.142
40
+ */
41
+ declare function compactFieldFilters(filters: FieldFilters): FieldFilters;
34
42
  /** Summarize a field's conditions into a human-readable token label. */
35
43
  declare function filterTokenLabel(path: string, conditions: FilterCondition[], columnLabel?: string): string;
36
44
  //#endregion
@@ -175,6 +183,18 @@ interface UnsupportedFilter {
175
183
  /** Field paths the sub-expression references, in order of appearance. */
176
184
  fields: string[];
177
185
  }
186
+ /**
187
+ * One AND-ed piece of a Uniquery filter left out because it names a field
188
+ * outside `knownFields` — alone or next to known ones.
189
+ *
190
+ * @since 0.1.141
191
+ */
192
+ interface UnknownFilter {
193
+ /** The left-out sub-expression, as it appeared in the input. */
194
+ expr: FilterExpr;
195
+ /** The paths it names that are outside `knownFields`. */
196
+ fields: string[];
197
+ }
178
198
  /**
179
199
  * Field paths a Uniquery filter expression references, deduped, in order of
180
200
  * appearance (logical operators are walked, operator keys skipped).
@@ -185,9 +205,10 @@ declare function filterExprFields(expr: unknown): string[];
185
205
  /** Options for {@link decomposeUniqueryFilter}. @since 0.1.140 */
186
206
  interface DecomposeUniqueryFilterOptions {
187
207
  /**
188
- * Field paths the table knows. Pieces on fields outside it are ignored
189
- * silently; a piece mixing known and unknown fields is reported, never
190
- * carried. Omit to accept every field.
208
+ * Field paths the table knows. A piece that names a field outside it —
209
+ * alone or mixed with known ones — is left out whole and listed in
210
+ * `unknown`, never carried or reported as `unsupported`. Omit to accept
211
+ * every field.
191
212
  */
192
213
  knownFields?: Iterable<string>;
193
214
  /**
@@ -205,14 +226,22 @@ interface DecomposedUniqueryFilter {
205
226
  residual: FilterExpr[];
206
227
  /** Pieces left out and lost — the result is broader by exactly these. */
207
228
  unsupported: UnsupportedFilter[];
229
+ /**
230
+ * Pieces left out because they name a field outside `knownFields` (hidden
231
+ * from the caller, or not in the schema). Each is a whole AND-ed piece, so
232
+ * the result is broader by exactly these too. `[]` without `knownFields`.
233
+ * Since 0.1.141 — before, pieces on unknown fields only were dropped
234
+ * silently and mixed known/unknown pieces went to `unsupported`.
235
+ */
236
+ unknown: UnknownFilter[];
208
237
  }
209
238
  /**
210
239
  * Split a Uniquery `FilterExpr` into what the table's field-filter model
211
240
  * holds exactly (`filters`), what it cannot hold but carries as residual
212
- * conditions (`residual`, with `carry`), and what it leaves out
213
- * (`unsupported`). Nothing is approximated: `filters AND residual` selects
214
- * `expr` minus the `unsupported` pieces and pieces on fields outside
215
- * `knownFields`.
241
+ * conditions (`residual`, with `carry`), what it leaves out (`unsupported`)
242
+ * and what names a field outside `knownFields` (`unknown`). Nothing is
243
+ * approximated: `filters AND residual` selects `expr` minus the
244
+ * `unsupported` and `unknown` pieces.
216
245
  *
217
246
  * Never throws, never warns — the caller decides how to report.
218
247
  *
@@ -253,9 +282,10 @@ declare function normalizeResidualFilters(exprs: readonly FilterExpr[]): FilterE
253
282
  * to a dev-mode `console.warn` when no handler is given. To keep those
254
283
  * pieces instead, use {@link decomposeUniqueryFilter} with `carry`.
255
284
  *
256
- * Conditions on fields outside `knownFields` (when provided) are ignored
257
- * silently: they are not this table's (a host page flag, a stale column). A
258
- * piece that mixes known and unknown fields is reported.
285
+ * Pieces that name a field outside `knownFields` (when provided) are ignored
286
+ * silently: they are not this table's (a host page flag, a hidden or stale
287
+ * column). Since 0.1.141 that includes a piece mixing known and unknown
288
+ * fields — {@link decomposeUniqueryFilter} lists them as `unknown`.
259
289
  *
260
290
  * Returns `{}` for an empty/missing expression. Never throws.
261
291
  */
@@ -530,6 +560,113 @@ interface SystemPresetInput {
530
560
  */
531
561
  declare function resolveSystemPresets(input?: SystemPresetInput[]): SystemPreset[];
532
562
  //#endregion
563
+ //#region src/presets/prune-preset-snapshot.d.ts
564
+ /**
565
+ * The field paths a table can use, derived from its column list. A path
566
+ * outside these sets is one the server does not expose to the current caller
567
+ * (hidden by role, removed from the schema) — naming it in a query is rejected.
568
+ *
569
+ * @since 0.1.141
570
+ */
571
+ interface KnownFields {
572
+ /**
573
+ * Every column path, client-owned (display) columns included, in column
574
+ * order. Gates `columnNames`, column widths and sorters (a sorter on a
575
+ * client-owned column is applied in memory).
576
+ */
577
+ columns: ReadonlySet<string>;
578
+ /**
579
+ * Server-backed column paths. Gates displayed filter inputs, field filters
580
+ * and residual filter conditions.
581
+ */
582
+ server: ReadonlySet<string>;
583
+ }
584
+ /**
585
+ * What pruning left out of a preset snapshot or of residual conditions
586
+ * because it names a field outside {@link KnownFields}.
587
+ *
588
+ * @since 0.1.141
589
+ */
590
+ interface DroppedFields {
591
+ /** The unavailable field paths behind the drops, deduped, in order of appearance. */
592
+ fields: string[];
593
+ /** Dropped `columnNames` entries. */
594
+ columns: string[];
595
+ /** Dropped displayed filter inputs. */
596
+ filterFields: string[];
597
+ /** Dropped field filters — whole entries, one per field. */
598
+ filters: FieldFilters;
599
+ /** Dropped filter conditions — each a whole AND-ed conjunct. */
600
+ residual: FilterExpr[];
601
+ /** Dropped sorters. */
602
+ sorters: SortControl[];
603
+ }
604
+ /**
605
+ * Remove every entry of `snapshot` that names a field outside `known`, per
606
+ * aspect:
607
+ *
608
+ * - `columns.columnNames` — unknown names go, the rest keep their order. When
609
+ * none survives, it falls back to every column, never an empty grid.
610
+ * - `columns.columnWidths` — unknown keys go.
611
+ * - `filters` (displayed inputs) and `filterOps` — entries on a field outside
612
+ * `known.server` go. A field's conditions are one AND-ed conjunct, so
613
+ * dropping one only broadens the result.
614
+ * - `sorters` — unknown entries go, the rest keep their priority.
615
+ * - `itemsPerPage` — untouched.
616
+ *
617
+ * `dropped` is `null` when nothing user-visible went (a width-only drop is
618
+ * hygiene). The input is never mutated.
619
+ *
620
+ * @since 0.1.141
621
+ */
622
+ declare function prunePresetSnapshot(snapshot: PresetSnapshot, known: KnownFields): {
623
+ snapshot: PresetSnapshot;
624
+ dropped: DroppedFields | null;
625
+ };
626
+ /**
627
+ * Spell a snapshot the way table state holds it, so it compares equal to a
628
+ * capture of the state it produces:
629
+ *
630
+ * - `columns.columnWidths` — an entry equal to its column's default width, or
631
+ * on a path with no default (not a column), goes; an emptied map is
632
+ * omitted. Capture writes overrides only.
633
+ * - `filterOps` — a field with no filled condition goes
634
+ * (`compactFieldFilters`). Table state never holds one.
635
+ *
636
+ * `defaultWidths` maps each column path to its default width — the `d` of
637
+ * its `ColumnWidthsMap` entry. Other aspects pass through. Returns `snapshot`
638
+ * itself when nothing changes; the input is never mutated.
639
+ *
640
+ * @since 0.1.142
641
+ */
642
+ declare function canonicalPresetSnapshot(snapshot: PresetSnapshot, defaultWidths: Readonly<Record<string, string>>): PresetSnapshot;
643
+ /**
644
+ * Split residual filter conditions, in one pass, into the ones that name
645
+ * only `known` fields and the ones that do not. A condition naming any
646
+ * unknown field is dropped WHOLE — never pruned inside an `$or` (that would
647
+ * narrow the result) or a `$not` (that would invert it). Each condition is an
648
+ * AND-ed conjunct, so dropping one only broadens the result.
649
+ *
650
+ * `dropped` holds the conditions in `residual` and the unknown paths in
651
+ * `fields`; `null` when every condition is kept.
652
+ *
653
+ * @since 0.1.141
654
+ */
655
+ declare function pruneResidualFilters(exprs: FilterExpr[], known: ReadonlySet<string>): {
656
+ kept: FilterExpr[];
657
+ dropped: DroppedFields | null;
658
+ };
659
+ /**
660
+ * Append what a prune dropped back onto a snapshot captured from (pruned)
661
+ * table state, so overwriting a preset with a narrower view does not destroy
662
+ * the parts the saver cannot see. Column names, filter inputs and sorters go
663
+ * after the captured ones; field filters are added back. Only aspects
664
+ * `captured` carries are touched; widths of hidden columns are not kept.
665
+ *
666
+ * @since 0.1.141
667
+ */
668
+ declare function restoreDroppedEntries(captured: PresetSnapshot, dropped: DroppedFields): PresetSnapshot;
669
+ //#endregion
533
670
  //#region src/presets/preset-dirty.d.ts
534
671
  /**
535
672
  * JSON-stringify with object keys sorted alphabetically at every depth so
@@ -888,6 +1025,21 @@ interface UrlQueryStateSnapshot {
888
1025
  * or `sync.residual` is `false`. Since 0.1.140.
889
1026
  */
890
1027
  residual?: FilterExpr[];
1028
+ /**
1029
+ * Filter pieces left out because they name a field outside `knownFields`
1030
+ * and `localFields` — a column hidden from this caller, or gone from the
1031
+ * schema — with those paths in `fields`. Each is a whole AND-ed piece (a
1032
+ * mixed known/unknown one included), so the result is broader by exactly
1033
+ * these. A bare `field=value` piece is not listed: without the table
1034
+ * definition it reads the same as a flag of the host page. Omitted when
1035
+ * none. Since 0.1.141 — before, a mixed piece was listed in `unsupported`.
1036
+ */
1037
+ unknown?: UnknownFilter[];
1038
+ /**
1039
+ * `$sort` entries left out because their field is outside `knownFields`
1040
+ * and `localFields`. Omitted when none. Since 0.1.141.
1041
+ */
1042
+ unknownSorters?: SortControl[];
891
1043
  /**
892
1044
  * Raw record offset from `$skip` (omitted when no `$skip` in URL). The
893
1045
  * decoder does NOT compute a page index — that requires `itemsPerPage`,
@@ -1013,20 +1165,29 @@ declare function stateToUrlQueryString(state: UrlQueryStateLike, defaults: UrlQu
1013
1165
  declare function urlQueryConsumesKey(key: string): boolean;
1014
1166
  interface UrlQueryParseOptions {
1015
1167
  /**
1016
- * Field paths the table knows about. Conditions on fields outside this set
1017
- * are silently dropped. Omit to accept any field (useful when the table
1018
- * definition isn't loaded yet).
1168
+ * Field paths the table knows about. Conditions and sorters on fields
1169
+ * outside this set are dropped and listed in `unknown` / `unknownSorters`.
1170
+ * Omit to accept any field (useful when the table definition isn't loaded
1171
+ * yet).
1019
1172
  */
1020
1173
  knownFields?: Iterable<string>;
1174
+ /**
1175
+ * Client-owned column paths. The URL never restores them (no server query
1176
+ * can use them), but they are not unknown either: a sorter on one is
1177
+ * ignored, and a filter piece correlating one with a known field is
1178
+ * reported as `unsupported` (`"cross-field"`). Since 0.1.141.
1179
+ */
1180
+ localFields?: Iterable<string>;
1021
1181
  /** Per-aspect sync gates — must match the encoder's config to keep the round-trip symmetric. */
1022
1182
  sync?: UrlQuerySync;
1023
1183
  }
1024
1184
  /**
1025
1185
  * Parse a URL query string back into the table state subset.
1026
1186
  *
1027
- * Robust by design — schema drift and copy-paste errors must not break the
1028
- * recipient's view:
1029
- * - unknown fields (not in `knownFields`) → silently dropped
1187
+ * Robust by design — schema drift, fields hidden from the recipient and
1188
+ * copy-paste errors must not break the recipient's view:
1189
+ * - unknown fields (not in `knownFields`) → dropped, listed in `unknown` /
1190
+ * `unknownSorters` (the parser does not warn — the caller decides)
1030
1191
  * - filter pieces field filters cannot express (cross-field OR, unknown
1031
1192
  * operator, …) → left out of `filters` and listed in `unsupported`, never
1032
1193
  * approximated (the parser does not warn — the caller decides). Unless
@@ -1561,4 +1722,4 @@ declare function sortRowsLocally<T extends Record<string, unknown>>(rows: T[], s
1561
1722
  //#region src/utils/dev.d.ts
1562
1723
  declare const DEV: boolean;
1563
1724
  //#endregion
1564
- export { APP_CONF_PREFIX, type AppConfData, AppPrefsClient, type AppPrefsClientConfig, type AppPrefsLoadResult, type AsPresetEntryData, type AsPresetEntryRow, type AsPresetsErrorCode, type AspectGate, type AspectMask, type BuildTableQueryOptions, type ColumnFilterType, type ColumnReorderPosition, type ColumnWidthEntry, type ColumnWidthsMap, type ConfigTab, type CsvOptions, DEFAULT_EXPORT_PAGE_SIZE, DEFAULT_ROW_HEIGHT_PX, DEV, DRAFT_PERSISTED_ASPECTS, type DateShortcut, type DecomposeUniqueryFilterOptions, type DecomposedUniqueryFilter, type DisplayColumnDef, type DraftPersistedAspect, ExportAbortError, type ExportPage, type ExportPageFetcher, type ExportScalar, type FetchPlan, type FetchPlanMode, type FieldFilters, type FilterCondition, type FilterConditionType, type FilterableColumn, MAX_DEFAULT_COLUMN_WIDTH_PX, type MergeResult, NULL_OPS, PRESET_ASPECTS, type PageAlignedBlock, type PlanFetchArgs, type PresetAspect, type PresetCapabilities, type PresetColumnWidthEntry, type PresetData, type PresetDraft, type PresetFilterOpEntry, type PresetLimitReachedBody, type PresetSnapshot, type PresetSnapshotWire, type PresetSorterEntry, PresetsClient, type PresetsClientConfig, PresetsHttpError, type PresetsListResult, type PresetsSaveAsOptions, type PresetsSaveResult, type QueryOptions, RESERVED_ID_PREFIXES, STANDARD_PRESET_ID, SYSTEM_PRESET_PREFIX, type SelectionMode, type SystemPreset, type SystemPresetInput, type TableStateData, type TableStateMethods, URL_SNAPSHOT_KEY, USER_CONF_PREFIX, type UnsupportedFilter, type UnsupportedFilterReason, type UrlQueryDefaults, type UrlQueryParseOptions, type UrlQueryStateLike, type UrlQueryStateSnapshot, type UrlQuerySync, type UserConfData, appConfId, arraysEqual, blockStartFor, buildTableQuery, cellAsString, clampTopIndex, collectExportRows, columnDefaultCondition, columnFilterConditions, columnFilterType, computeDefaultColumnWidth, conditionLabel, conditionsForType, csvCell, dateShortcuts, debounce, decomposeUniqueryFilter, defaultCondition, derivePresetAspects, deserializeDraft, draftMatchesPreset, escapeRegex, filledFilterCount, filterExprFields, filterExprKey, filterTokenLabel, filtersToUniqueryFilter, formatFilterCondition, formatFilterExpr, fromWireSnapshot, gateOwns, hasSecondValue, isAuthError, isColumnFilterable, isDirtyAgainst, isEmptyDraft, isFilled, isSimpleEq, isSystemPresetId, isUnavailableError, mergeDisplayColumns, mergeFilters, mergeSorters, normaliseSystemPresetId, normalizeResidualFilters, pageAlignedBlocksFor, parseColumnFilterInput, parseFilterInput, planFetch, reconcileColumnWidthDefaults, reorderColumnNames, residualGateOwns, resolveAspectGate, resolveExportValue, resolveSystemPresets, rowsToPks, sameColumnSet, serializeDraft, setsEqual, sortRowsLocally, sortersEqual, stableStringify, stateToUrlQueryString, toCsv, toWireSnapshot, togglePk, trimSelection, unescapeRegex, uniqueryFilterToFieldFilters, urlQueryConsumesKey, urlQueryStringToState, userConfId, walkBackwardAbsorb, walkForwardAbsorb, withStableOrder };
1725
+ export { APP_CONF_PREFIX, type AppConfData, AppPrefsClient, type AppPrefsClientConfig, type AppPrefsLoadResult, type AsPresetEntryData, type AsPresetEntryRow, type AsPresetsErrorCode, type AspectGate, type AspectMask, type BuildTableQueryOptions, type ColumnFilterType, type ColumnReorderPosition, type ColumnWidthEntry, type ColumnWidthsMap, type ConfigTab, type CsvOptions, DEFAULT_EXPORT_PAGE_SIZE, DEFAULT_ROW_HEIGHT_PX, DEV, DRAFT_PERSISTED_ASPECTS, type DateShortcut, type DecomposeUniqueryFilterOptions, type DecomposedUniqueryFilter, type DisplayColumnDef, type DraftPersistedAspect, type DroppedFields, ExportAbortError, type ExportPage, type ExportPageFetcher, type ExportScalar, type FetchPlan, type FetchPlanMode, type FieldFilters, type FilterCondition, type FilterConditionType, type FilterableColumn, type KnownFields, MAX_DEFAULT_COLUMN_WIDTH_PX, type MergeResult, NULL_OPS, PRESET_ASPECTS, type PageAlignedBlock, type PlanFetchArgs, type PresetAspect, type PresetCapabilities, type PresetColumnWidthEntry, type PresetData, type PresetDraft, type PresetFilterOpEntry, type PresetLimitReachedBody, type PresetSnapshot, type PresetSnapshotWire, type PresetSorterEntry, PresetsClient, type PresetsClientConfig, PresetsHttpError, type PresetsListResult, type PresetsSaveAsOptions, type PresetsSaveResult, type QueryOptions, RESERVED_ID_PREFIXES, STANDARD_PRESET_ID, SYSTEM_PRESET_PREFIX, type SelectionMode, type SystemPreset, type SystemPresetInput, type TableStateData, type TableStateMethods, URL_SNAPSHOT_KEY, USER_CONF_PREFIX, type UnknownFilter, type UnsupportedFilter, type UnsupportedFilterReason, type UrlQueryDefaults, type UrlQueryParseOptions, type UrlQueryStateLike, type UrlQueryStateSnapshot, type UrlQuerySync, type UserConfData, appConfId, arraysEqual, blockStartFor, buildTableQuery, canonicalPresetSnapshot, cellAsString, clampTopIndex, collectExportRows, columnDefaultCondition, columnFilterConditions, columnFilterType, compactFieldFilters, computeDefaultColumnWidth, conditionLabel, conditionsForType, csvCell, dateShortcuts, debounce, decomposeUniqueryFilter, defaultCondition, derivePresetAspects, deserializeDraft, draftMatchesPreset, escapeRegex, filledFilterCount, filterExprFields, filterExprKey, filterTokenLabel, filtersToUniqueryFilter, formatFilterCondition, formatFilterExpr, fromWireSnapshot, gateOwns, hasSecondValue, isAuthError, isColumnFilterable, isDirtyAgainst, isEmptyDraft, isFilled, isSimpleEq, isSystemPresetId, isUnavailableError, mergeDisplayColumns, mergeFilters, mergeSorters, normaliseSystemPresetId, normalizeResidualFilters, pageAlignedBlocksFor, parseColumnFilterInput, parseFilterInput, planFetch, prunePresetSnapshot, pruneResidualFilters, reconcileColumnWidthDefaults, reorderColumnNames, residualGateOwns, resolveAspectGate, resolveExportValue, resolveSystemPresets, restoreDroppedEntries, rowsToPks, sameColumnSet, serializeDraft, setsEqual, sortRowsLocally, sortersEqual, stableStringify, stateToUrlQueryString, toCsv, toWireSnapshot, togglePk, trimSelection, unescapeRegex, uniqueryFilterToFieldFilters, urlQueryConsumesKey, urlQueryStringToState, userConfId, walkBackwardAbsorb, walkForwardAbsorb, withStableOrder };