ag-grid-url-sync 0.1.1 → 0.2.2

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.
Files changed (43) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +435 -52
  3. package/dist/{ag-grid-url-sync.js → core/ag-grid-url-sync.js} +4 -2
  4. package/dist/core/ag-grid-url-sync.js.map +1 -0
  5. package/dist/core/grid-integration.d.ts +14 -0
  6. package/dist/core/grid-integration.js +156 -0
  7. package/dist/core/grid-integration.js.map +1 -0
  8. package/dist/core/index.d.ts +6 -0
  9. package/dist/core/index.js +11 -0
  10. package/dist/core/index.js.map +1 -0
  11. package/dist/core/types.d.ts +107 -0
  12. package/dist/core/types.js +117 -0
  13. package/dist/core/types.js.map +1 -0
  14. package/dist/core/url-generator.d.ts +9 -0
  15. package/dist/core/url-generator.js +88 -0
  16. package/dist/core/url-generator.js.map +1 -0
  17. package/dist/core/url-parser.d.ts +9 -0
  18. package/dist/core/url-parser.js +164 -0
  19. package/dist/core/url-parser.js.map +1 -0
  20. package/dist/core/validation.d.ts +47 -0
  21. package/dist/core/validation.js +87 -0
  22. package/dist/core/validation.js.map +1 -0
  23. package/dist/index.d.ts +6 -3
  24. package/dist/index.js +11 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/react/index.d.ts +3 -0
  27. package/dist/react/index.js +2 -0
  28. package/dist/react/index.js.map +1 -0
  29. package/dist/react/types.d.ts +64 -0
  30. package/dist/react/types.js +2 -0
  31. package/dist/react/types.js.map +1 -0
  32. package/dist/react/use-ag-grid-url-sync.d.ts +10 -0
  33. package/dist/react/use-ag-grid-url-sync.js +215 -0
  34. package/dist/react/use-ag-grid-url-sync.js.map +1 -0
  35. package/package.json +97 -8
  36. package/dist/ag-grid-url-sync.js.map +0 -1
  37. package/dist/types.d.ts +0 -69
  38. package/dist/types.js +0 -31
  39. package/dist/types.js.map +0 -1
  40. package/dist/utils.d.ts +0 -36
  41. package/dist/utils.js +0 -188
  42. package/dist/utils.js.map +0 -1
  43. /package/dist/{ag-grid-url-sync.d.ts → core/ag-grid-url-sync.d.ts} +0 -0
@@ -0,0 +1,156 @@
1
+ import { AG_GRID_OPERATION_NAMES, REVERSE_AG_GRID_OPERATION_NAMES, TEXT_FILTER_OPERATIONS, NUMBER_FILTER_OPERATIONS } from './types.js';
2
+ /**
3
+ * Type predicate to check if an operation is a valid text filter operation
4
+ */
5
+ function isTextFilterOperation(operation) {
6
+ return TEXT_FILTER_OPERATIONS.includes(operation);
7
+ }
8
+ /**
9
+ * Type predicate to check if an operation is a valid number filter operation
10
+ */
11
+ function isNumberFilterOperation(operation) {
12
+ return NUMBER_FILTER_OPERATIONS.includes(operation);
13
+ }
14
+ /**
15
+ * Detects the expected filter type for a column based on AG Grid configuration
16
+ */
17
+ export function detectColumnFilterType(gridApi, columnId) {
18
+ try {
19
+ const column = gridApi.getColumn(columnId);
20
+ if (!column)
21
+ return null;
22
+ const colDef = column.getColDef();
23
+ // 1. Explicit filter configuration takes priority
24
+ if (colDef.filter === 'agNumberColumnFilter')
25
+ return 'number';
26
+ if (colDef.filter === 'agTextColumnFilter')
27
+ return 'text';
28
+ // 2. Cell data type configuration
29
+ if (colDef.cellDataType === 'number')
30
+ return 'number';
31
+ if (colDef.cellDataType === 'text')
32
+ return 'text';
33
+ // 3. Default to text for backward compatibility
34
+ return 'text';
35
+ }
36
+ catch {
37
+ return 'text'; // Safe fallback
38
+ }
39
+ }
40
+ /**
41
+ * Gets the current filter model from AG Grid
42
+ */
43
+ export function getFilterModel(config) {
44
+ try {
45
+ const model = config.gridApi.getFilterModel();
46
+ const filterState = {};
47
+ if (!model || typeof model !== 'object') {
48
+ return filterState;
49
+ }
50
+ for (const [column, filter] of Object.entries(model)) {
51
+ if (!filter || typeof filter !== 'object')
52
+ continue;
53
+ const { filterType, type, filter: value, filterTo } = filter;
54
+ // Handle both text and number filters
55
+ if (filterType === 'text') {
56
+ // Map AG Grid operations back to our internal operations using centralized mapping
57
+ const internalOperation = REVERSE_AG_GRID_OPERATION_NAMES[type];
58
+ // Use type predicate to validate and narrow the type
59
+ if (internalOperation && isTextFilterOperation(internalOperation)) {
60
+ filterState[column] = {
61
+ filterType: 'text',
62
+ type: internalOperation, // Now properly typed as TextFilterOperation
63
+ filter: value || ''
64
+ };
65
+ }
66
+ }
67
+ else if (filterType === 'number') {
68
+ // Map AG Grid number operations back to our internal operations
69
+ const internalOperation = REVERSE_AG_GRID_OPERATION_NAMES[type];
70
+ // Use type predicate to validate and narrow the type
71
+ if (internalOperation && isNumberFilterOperation(internalOperation)) {
72
+ const numberFilter = {
73
+ filterType: 'number',
74
+ type: internalOperation, // Now properly typed as NumberFilterOperation
75
+ filter: typeof value === 'number' ? value : 0
76
+ };
77
+ // Handle range operations
78
+ if (internalOperation === 'inRange' && typeof filterTo === 'number') {
79
+ ;
80
+ numberFilter.filterTo = filterTo;
81
+ }
82
+ filterState[column] = numberFilter;
83
+ }
84
+ }
85
+ }
86
+ return filterState;
87
+ }
88
+ catch (error) {
89
+ if (config.onParseError) {
90
+ config.onParseError(error);
91
+ }
92
+ return {};
93
+ }
94
+ }
95
+ /**
96
+ * Converts internal filter to AG Grid filter format
97
+ */
98
+ function convertToAGGridFilter(filter) {
99
+ if (filter.filterType === 'number') {
100
+ const agGridOperation = AG_GRID_OPERATION_NAMES[filter.type];
101
+ if (filter.type === 'inRange') {
102
+ const baseFilter = {
103
+ filterType: 'number',
104
+ type: agGridOperation,
105
+ filter: filter.filter
106
+ };
107
+ // Only include filterTo if it's defined
108
+ return filter.filterTo !== undefined
109
+ ? { ...baseFilter, filterTo: filter.filterTo }
110
+ : baseFilter;
111
+ }
112
+ return {
113
+ filterType: 'number',
114
+ type: agGridOperation,
115
+ filter: filter.filter
116
+ };
117
+ }
118
+ // Text filter handling
119
+ const agGridOperation = AG_GRID_OPERATION_NAMES[filter.type];
120
+ return {
121
+ filterType: 'text',
122
+ type: agGridOperation,
123
+ filter: filter.filter
124
+ };
125
+ }
126
+ /**
127
+ * Applies a filter state to AG Grid
128
+ */
129
+ export function applyFilterModel(filterState, config) {
130
+ try {
131
+ const model = {};
132
+ for (const [column, filter] of Object.entries(filterState)) {
133
+ if (!filter || typeof filter !== 'object')
134
+ continue;
135
+ // Detect expected filter type for this column
136
+ const expectedType = detectColumnFilterType(config.gridApi, column);
137
+ // Validate filter type compatibility
138
+ if (filter.filterType === 'number' && expectedType !== 'number') {
139
+ console.warn(`Column '${column}' expects a text filter but received a number filter. This may indicate a configuration mismatch in the column definition or filter application.`);
140
+ continue;
141
+ }
142
+ // Convert to AG Grid filter model
143
+ const agGridFilter = convertToAGGridFilter(filter);
144
+ if (agGridFilter) {
145
+ model[column] = agGridFilter;
146
+ }
147
+ }
148
+ config.gridApi.setFilterModel(model);
149
+ }
150
+ catch (error) {
151
+ if (config.onParseError) {
152
+ config.onParseError(error);
153
+ }
154
+ }
155
+ }
156
+ //# sourceMappingURL=grid-integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid-integration.js","sourceRoot":"","sources":["../../src/core/grid-integration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { AGGridUrlSync, createUrlSync } from './ag-grid-url-sync.js';
2
+ export * from './types.js';
3
+ export { validateFilterValue, DEFAULT_CONFIG } from './validation.js';
4
+ export { parseUrlFilters, parseFilterParam } from './url-parser.js';
5
+ export { serializeFilters, generateUrl } from './url-generator.js';
6
+ export { getFilterModel, applyFilterModel } from './grid-integration.js';
@@ -0,0 +1,11 @@
1
+ export { AGGridUrlSync, createUrlSync } from './ag-grid-url-sync.js';
2
+ export * from './types.js';
3
+ // Validation utilities
4
+ export { validateFilterValue, DEFAULT_CONFIG } from './validation.js';
5
+ // URL parsing utilities
6
+ export { parseUrlFilters, parseFilterParam } from './url-parser.js';
7
+ // URL generation utilities
8
+ export { serializeFilters, generateUrl } from './url-generator.js';
9
+ // AG Grid integration utilities
10
+ export { getFilterModel, applyFilterModel } from './grid-integration.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAEpE,cAAc,YAAY,CAAA;AAE1B,uBAAuB;AACvB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErE,wBAAwB;AACxB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEnE,2BAA2B;AAC3B,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAElE,gCAAgC;AAChC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,107 @@
1
+ import type { GridApi as AGGridApi, TextFilterModel, NumberFilterModel } from 'ag-grid-community';
2
+ /**
3
+ * Configuration options for AG Grid URL Sync
4
+ */
5
+ export interface AGGridUrlSyncConfig {
6
+ /**
7
+ * Prefix for URL parameters. Default: 'f_'
8
+ */
9
+ paramPrefix?: string;
10
+ /**
11
+ * Maximum length for filter values. Default: 200
12
+ */
13
+ maxValueLength?: number;
14
+ /**
15
+ * Optional error handler for parsing errors
16
+ */
17
+ onParseError?: (error: Error) => void;
18
+ }
19
+ /**
20
+ * Supported filter operations for both text and number filters
21
+ */
22
+ export type FilterOperation = 'contains' | 'eq' | 'notContains' | 'notEqual' | 'startsWith' | 'endsWith' | 'blank' | 'notBlank' | 'lessThan' | 'lessThanOrEqual' | 'greaterThan' | 'greaterThanOrEqual' | 'inRange';
23
+ /**
24
+ * Valid text filter operations as const array
25
+ */
26
+ export declare const TEXT_FILTER_OPERATIONS: readonly ["contains", "eq", "notContains", "notEqual", "startsWith", "endsWith", "blank", "notBlank"];
27
+ /**
28
+ * Valid number filter operations as const array
29
+ */
30
+ export declare const NUMBER_FILTER_OPERATIONS: readonly ["eq", "notEqual", "lessThan", "lessThanOrEqual", "greaterThan", "greaterThanOrEqual", "inRange", "blank", "notBlank"];
31
+ /**
32
+ * Text-specific filter operations
33
+ */
34
+ export type TextFilterOperation = (typeof TEXT_FILTER_OPERATIONS)[number];
35
+ /**
36
+ * Number-specific filter operations
37
+ */
38
+ export type NumberFilterOperation = (typeof NUMBER_FILTER_OPERATIONS)[number];
39
+ /**
40
+ * Base interface for column filters
41
+ */
42
+ interface BaseColumnFilter {
43
+ type: FilterOperation;
44
+ }
45
+ /**
46
+ * Text filter configuration
47
+ */
48
+ export interface TextColumnFilter extends BaseColumnFilter {
49
+ filterType: 'text';
50
+ type: TextFilterOperation;
51
+ filter: string;
52
+ }
53
+ /**
54
+ * Number filter configuration
55
+ */
56
+ export interface NumberColumnFilter extends BaseColumnFilter {
57
+ filterType: 'number';
58
+ type: NumberFilterOperation;
59
+ filter: number;
60
+ filterTo?: number;
61
+ }
62
+ /**
63
+ * Filter state for a single column (union of text and number filters)
64
+ */
65
+ export type ColumnFilter = TextColumnFilter | NumberColumnFilter;
66
+ /**
67
+ * Complete filter state mapping
68
+ */
69
+ export interface FilterState {
70
+ [columnId: string]: ColumnFilter;
71
+ }
72
+ /**
73
+ * Internal configuration with defaults
74
+ */
75
+ export interface InternalConfig extends Required<AGGridUrlSyncConfig> {
76
+ gridApi: AGGridApi;
77
+ }
78
+ /**
79
+ * Error types for URL sync operations
80
+ */
81
+ export declare class URLSyncError extends Error {
82
+ constructor(message: string);
83
+ }
84
+ export declare class InvalidFilterError extends URLSyncError {
85
+ constructor(message: string);
86
+ }
87
+ export declare class InvalidURLError extends URLSyncError {
88
+ constructor(message: string);
89
+ }
90
+ export type ParsedFilterParam = {
91
+ columnName: string;
92
+ operation: FilterOperation;
93
+ filterType: 'text' | 'number' | 'auto';
94
+ value: string | number;
95
+ filterTo?: number;
96
+ action: 'apply' | 'remove';
97
+ };
98
+ export type GridApi = AGGridApi;
99
+ /**
100
+ * Union type for AG Grid filters using official AG Grid types
101
+ */
102
+ export type AGGridFilter = TextFilterModel | NumberFilterModel;
103
+ export declare const OPERATION_MAP: Record<string, FilterOperation>;
104
+ export declare const INTERNAL_TO_URL_OPERATION_MAP: Record<FilterOperation, string>;
105
+ export declare const AG_GRID_OPERATION_NAMES: Record<FilterOperation, string>;
106
+ export declare const REVERSE_AG_GRID_OPERATION_NAMES: Record<string, FilterOperation>;
107
+ export {};
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Valid text filter operations as const array
3
+ */
4
+ export const TEXT_FILTER_OPERATIONS = [
5
+ 'contains',
6
+ 'eq',
7
+ 'notContains',
8
+ 'notEqual',
9
+ 'startsWith',
10
+ 'endsWith',
11
+ 'blank',
12
+ 'notBlank'
13
+ ];
14
+ /**
15
+ * Valid number filter operations as const array
16
+ */
17
+ export const NUMBER_FILTER_OPERATIONS = [
18
+ 'eq',
19
+ 'notEqual',
20
+ 'lessThan',
21
+ 'lessThanOrEqual',
22
+ 'greaterThan',
23
+ 'greaterThanOrEqual',
24
+ 'inRange',
25
+ 'blank',
26
+ 'notBlank'
27
+ ];
28
+ /**
29
+ * Error types for URL sync operations
30
+ */
31
+ export class URLSyncError extends Error {
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = 'URLSyncError';
35
+ }
36
+ }
37
+ export class InvalidFilterError extends URLSyncError {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = 'InvalidFilterError';
41
+ }
42
+ }
43
+ export class InvalidURLError extends URLSyncError {
44
+ constructor(message) {
45
+ super(message);
46
+ this.name = 'InvalidURLError';
47
+ }
48
+ }
49
+ /**
50
+ * Operation Mapping Strategy:
51
+ *
52
+ * We use three different naming conventions for better user experience:
53
+ *
54
+ * 1. URL Parameters: Short, clean names for URLs (e.g., 'eq', 'neq', 'lt', 'gt')
55
+ * 2. Internal Types: Consistent naming for our FilterOperation type
56
+ * 3. AG Grid Operations: Exact names expected by AG Grid API (e.g., 'equals', 'notEqual')
57
+ *
58
+ * This approach keeps URLs readable while maintaining compatibility with AG Grid.
59
+ */
60
+ /**
61
+ * Define special URL shorthand mappings (internal -> URL)
62
+ * Operations not listed here use their internal name as URL param
63
+ */
64
+ const URL_SHORTHAND_MAPPINGS = {
65
+ notEqual: 'neq',
66
+ lessThan: 'lt',
67
+ lessThanOrEqual: 'lte',
68
+ greaterThan: 'gt',
69
+ greaterThanOrEqual: 'gte',
70
+ inRange: 'range'
71
+ };
72
+ /**
73
+ * Define special AG Grid name mappings (internal -> AG Grid)
74
+ * Operations not listed here use their internal name for AG Grid
75
+ */
76
+ const AG_GRID_NAME_MAPPINGS = {
77
+ eq: 'equals'
78
+ };
79
+ /**
80
+ * Helper function to create operation mappings dynamically
81
+ */
82
+ function createOperationMaps() {
83
+ const allOperations = [...TEXT_FILTER_OPERATIONS, ...NUMBER_FILTER_OPERATIONS];
84
+ // Remove duplicates (like 'eq', 'blank', etc. that appear in both arrays)
85
+ const uniqueOperations = [...new Set(allOperations)];
86
+ // Generate URL mappings
87
+ const operationMap = {};
88
+ const internalToUrlMap = {};
89
+ uniqueOperations.forEach(operation => {
90
+ const urlName = URL_SHORTHAND_MAPPINGS[operation] || operation;
91
+ operationMap[urlName] = operation;
92
+ internalToUrlMap[operation] = urlName;
93
+ });
94
+ // Generate AG Grid mappings
95
+ const agGridMap = {};
96
+ const reverseAgGridMap = {};
97
+ uniqueOperations.forEach(operation => {
98
+ const agGridName = AG_GRID_NAME_MAPPINGS[operation] ||
99
+ operation;
100
+ agGridMap[operation] = agGridName;
101
+ reverseAgGridMap[agGridName] = operation;
102
+ });
103
+ return {
104
+ operationMap,
105
+ internalToUrlMap,
106
+ agGridMap,
107
+ reverseAgGridMap
108
+ };
109
+ }
110
+ // Generate all mappings dynamically
111
+ const { operationMap, internalToUrlMap, agGridMap, reverseAgGridMap } = createOperationMaps();
112
+ // Export the dynamically generated maps
113
+ export const OPERATION_MAP = operationMap;
114
+ export const INTERNAL_TO_URL_OPERATION_MAP = internalToUrlMap;
115
+ export const AG_GRID_OPERATION_NAMES = agGridMap;
116
+ export const REVERSE_AG_GRID_OPERATION_NAMES = reverseAgGridMap;
117
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { FilterState, InternalConfig } from './types.js';
2
+ /**
3
+ * Converts a filter state object into URL search parameters
4
+ */
5
+ export declare function serializeFilters(filterState: FilterState, config: InternalConfig): URLSearchParams;
6
+ /**
7
+ * Generates a URL with the current filter state
8
+ */
9
+ export declare function generateUrl(baseUrl: string, filterState: FilterState, config: InternalConfig): string;
@@ -0,0 +1,88 @@
1
+ import { validateFilterValue } from './validation.js';
2
+ import { INTERNAL_TO_URL_OPERATION_MAP } from './types.js';
3
+ /**
4
+ * Serializes a single column filter to URL parameter
5
+ */
6
+ function serializeColumnFilter(columnName, filter, paramPrefix) {
7
+ const urlOperation = INTERNAL_TO_URL_OPERATION_MAP[filter.type];
8
+ if (!urlOperation)
9
+ return '';
10
+ const paramName = `${paramPrefix}${columnName}_${urlOperation}`;
11
+ // Handle range operations
12
+ if (filter.type === 'inRange' && filter.filterType === 'number') {
13
+ // Type guard: ensure this is a NumberColumnFilter with inRange operation
14
+ if ('filterTo' in filter && filter.filterTo !== undefined) {
15
+ // Don't encode the comma for range values - handle this directly
16
+ return `${paramName}=${filter.filter},${filter.filterTo}`;
17
+ }
18
+ return '';
19
+ }
20
+ // Handle blank operations
21
+ if (filter.type === 'blank' || filter.type === 'notBlank') {
22
+ return `${paramName}=true`;
23
+ }
24
+ // Handle number operations
25
+ if (filter.filterType === 'number') {
26
+ return `${paramName}=${filter.filter}`;
27
+ }
28
+ // Handle text operations (existing logic)
29
+ if (filter.filterType === 'text' && filter.filter) {
30
+ // Note: Validation should be done at the serializeFilters level, not here
31
+ return `${paramName}=${encodeURIComponent(filter.filter.toString())}`;
32
+ }
33
+ return '';
34
+ }
35
+ /**
36
+ * Helper function to append a serialized parameter to URLSearchParams
37
+ */
38
+ function appendSerializedParam(serialized, params) {
39
+ const [paramName, paramValue] = serialized.split('=');
40
+ if (paramName && paramValue) {
41
+ // Decode the parameter value to handle commas correctly
42
+ params.append(paramName, decodeURIComponent(paramValue));
43
+ }
44
+ }
45
+ /**
46
+ * Converts a filter state object into URL search parameters
47
+ */
48
+ export function serializeFilters(filterState, config) {
49
+ const params = new URLSearchParams();
50
+ for (const [column, filter] of Object.entries(filterState)) {
51
+ // Support both text and number filters
52
+ if (filter.filterType !== 'text' && filter.filterType !== 'number')
53
+ continue;
54
+ // Apply validation for text filters before serialization
55
+ if (filter.filterType === 'text') {
56
+ const validatedValue = validateFilterValue(filter.filter, config, filter.type);
57
+ const updatedFilter = { ...filter, filter: validatedValue };
58
+ const serialized = serializeColumnFilter(column, updatedFilter, config.paramPrefix);
59
+ if (serialized) {
60
+ appendSerializedParam(serialized, params);
61
+ }
62
+ }
63
+ else {
64
+ // Number filters don't need additional validation as they're already validated
65
+ const serialized = serializeColumnFilter(column, filter, config.paramPrefix);
66
+ if (serialized) {
67
+ appendSerializedParam(serialized, params);
68
+ }
69
+ }
70
+ }
71
+ return params;
72
+ }
73
+ /**
74
+ * Generates a URL with the current filter state
75
+ */
76
+ export function generateUrl(baseUrl, filterState, config) {
77
+ const url = new URL(baseUrl);
78
+ const filterParams = serializeFilters(filterState, config);
79
+ // Preserve existing non-filter parameters
80
+ for (const [key, value] of url.searchParams.entries()) {
81
+ if (!key.startsWith(config.paramPrefix)) {
82
+ filterParams.append(key, value);
83
+ }
84
+ }
85
+ url.search = filterParams.toString();
86
+ return url.toString();
87
+ }
88
+ //# sourceMappingURL=url-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-generator.js","sourceRoot":"","sources":["../../src/core/url-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { FilterState, ColumnFilter, InternalConfig } from './types.js';
2
+ /**
3
+ * Parses a URL parameter into a column filter
4
+ */
5
+ export declare function parseFilterParam(param: string, value: string, expectedPrefix?: string): ColumnFilter;
6
+ /**
7
+ * Converts URL search params into a filter state object
8
+ */
9
+ export declare function parseUrlFilters(url: string, config: InternalConfig): FilterState;