ag-grid-url-sync 0.4.1 → 0.5.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
@@ -196,12 +196,11 @@ import type {
196
196
  ```typescript
197
197
  import { parseUrlFilters } from 'ag-grid-url-sync/url-parser'
198
198
  import { DEFAULT_CONFIG } from 'ag-grid-url-sync/validation'
199
- import type { FilterState } from 'ag-grid-url-sync/types'
199
+ import type { FilterState, ParseConfig } from 'ag-grid-url-sync/types'
200
200
 
201
201
  function getFiltersFromUrl(url: string): FilterState {
202
- const config = {
203
- gridApi: null, // Not needed for parsing
204
- ...DEFAULT_CONFIG,
202
+ const config: ParseConfig = {
203
+ ...DEFAULT_CONFIG, // gridApi is not needed for parsing
205
204
  onParseError: error => console.error('Parse error:', error)
206
205
  }
207
206
 
@@ -209,18 +208,32 @@ function getFiltersFromUrl(url: string): FilterState {
209
208
  }
210
209
  ```
211
210
 
211
+ `parseUrlFilters` reads the input with these rules, in this order:
212
+
213
+ | # | Input | Result | Example |
214
+ | --- | --------------------------- | ------------------------ | ----------------------------------------- |
215
+ | 1 | A host with no scheme | Throws `InvalidURLError` | `example.com?…`, `localhost:3000?…` |
216
+ | 2 | Starts with a scheme | Absolute URL | `https://example.com/users?f_name_eq=bob` |
217
+ | 3 | Starts with `/`, `?` or `#` | Relative URL | `/users?f_name_eq=bob`, `?f_name_eq=bob` |
218
+ | 4 | Anything else | Raw query string | `f_name_eq=bob` |
219
+
220
+ - A relative path must start with `/`. `users?f_name_eq=bob` matches rule 4, so it gives no filters.
221
+ - A bare `localhost?…` with no port cannot be told apart from a raw query, so it matches rule 4.
222
+ - In a raw query string, a `?` or `#` is part of a param name or value.
223
+
224
+ `InvalidURLError` is thrown for rule 1, and for any input that the `URL` constructor rejects under rules 2 and 3 (for example `http://` or `//[`). A param that is not valid is skipped and sent to `onParseError`.
225
+
212
226
  #### Example: Custom URL Generator
213
227
 
214
228
  ```typescript
215
- import { generateUrl, serializeFilters } from 'ag-grid-url-sync/url-generator'
216
- import type { FilterState } from 'ag-grid-url-sync/types'
229
+ import { generateUrl } from 'ag-grid-url-sync/url-generator'
230
+ import { DEFAULT_CONFIG } from 'ag-grid-url-sync/validation'
231
+ import type { FilterState, ParseConfig } from 'ag-grid-url-sync/types'
217
232
 
218
233
  function createShareableUrl(filters: FilterState, baseUrl: string): string {
219
- const config = {
220
- gridApi: null, // Not needed for generation
221
- paramPrefix: 'f_',
222
- maxValueLength: 200,
223
- onParseError: () => {}
234
+ const config: ParseConfig = {
235
+ ...DEFAULT_CONFIG, // gridApi is not needed for generation
236
+ maxValueLength: 500
224
237
  }
225
238
 
226
239
  return generateUrl(baseUrl, filters, config)
@@ -300,6 +313,10 @@ interface AGGridUrlSyncConfig {
300
313
  }
301
314
  ```
302
315
 
316
+ When the library reads a URL, it reads only the `groupedParam` name and decodes it with the configured `format`. It does not guess the format. This works in both modes, so a grouped link opens in an app in individual mode when the name and format match. If the param is present and not empty, the prefixed params are not read.
317
+
318
+ > **Note:** A URL written with a different `groupedParam` or `format` does not decode. If you change either option, links that were shared before the change stop working.
319
+
303
320
  ### Format Options
304
321
 
305
322
  #### QueryString Format (Default)
@@ -420,7 +437,7 @@ import {
420
437
 
421
438
  ### Error Handling for Grouped Serialization
422
439
 
423
- You can provide a custom error handler to gracefully handle malformed grouped parameters or serialization errors. The library will throw an `InvalidSerializationError` for invalid formats:
440
+ You can provide a custom error handler to gracefully handle malformed grouped parameters or serialization errors. In grouped mode, `onParseError` receives an `InvalidSerializationError` for invalid formats:
424
441
 
425
442
  ```typescript
426
443
  import { InvalidSerializationError } from 'ag-grid-url-sync/types'
@@ -440,6 +457,18 @@ const urlSync = createUrlSync(gridApi, {
440
457
  })
441
458
  ```
442
459
 
460
+ In every grouped format (`querystring`, `json`, `base64`), a text value over `maxValueLength` (default 200) is dropped and reported through `onParseError`, both when the URL is generated and when it is parsed.
461
+
462
+ In grouped mode, an error that comes from the grouped URL value is an `InvalidSerializationError`. This includes an error for one bad parameter inside a grouped querystring value, such as a value over `maxValueLength`. The original error is in `error.cause`. In individual mode, the same failure arrives as the original error, with no wrapper. To handle a specific error in both modes, check the error and its cause:
463
+
464
+ ```typescript
465
+ import { InvalidFilterError } from 'ag-grid-url-sync/types'
466
+
467
+ const isFilterError = (error: Error) =>
468
+ error instanceof InvalidFilterError ||
469
+ error.cause instanceof InvalidFilterError
470
+ ```
471
+
443
472
  ### Exports
444
473
 
445
474
  The package supports granular subpath exports for advanced usage and optimal bundle size:
@@ -594,7 +623,7 @@ interface UseAGGridUrlSyncOptions {
594
623
  | `clearFilters` | `() => void` | Clear all text filters |
595
624
  | `isReady` | `boolean` | Whether grid API is available and hook is ready |
596
625
  | `currentUrl` | `string` | Current URL with filters applied |
597
- | `hasFilters` | `boolean` | Whether grid has any active text filters |
626
+ | `hasFilters` | `boolean` | Whether the grid has supported filters |
598
627
  | `parseUrlFilters` | `(url: string) => FilterState` | Parse filters from URL without applying |
599
628
  | `applyFilters` | `(filters: FilterState) => void` | Apply filter state object to grid |
600
629
  | `getFiltersAsFormat` | `(format: 'querystring' | 'json' | 'base64') => string` | Serialize filters to any supported format (for sharing/export) |
@@ -889,6 +918,15 @@ const url = urlSync.generateUrl('https://app.com/page')
889
918
  // https://app.com/page?f_name_contains=john
890
919
  ```
891
920
 
921
+ ##### `getUrlState(baseUrl?: string): { url: string; hasFilters: boolean }`
922
+
923
+ Generates the same URL as `generateUrl`, and tells if the grid has filters that the library supports. It reads the grid one time for both values. `hasFilters` comes from the filter state, not from the URL, because the URL also keeps the page's own params.
924
+
925
+ ```typescript
926
+ const { url, hasFilters } = urlSync.getUrlState('https://app.com/page?tab=1')
927
+ // url: https://app.com/page?tab=1 (no filters), hasFilters: false
928
+ ```
929
+
892
930
  ##### `getQueryParams(): string`
893
931
 
894
932
  Gets the current filter state as URL query parameters.
@@ -17,6 +17,19 @@ export declare class AGGridUrlSync {
17
17
  * @returns URL string with filter parameters
18
18
  */
19
19
  generateUrl(baseUrl?: string): string;
20
+ /**
21
+ * Generates a URL with the current filter state, and tells if the grid has
22
+ * filters that the library supports.
23
+ *
24
+ * Reads the grid filter model one time, so an error in it is reported one
25
+ * time. `hasFilters` comes from the filter state, not from the URL: the URL
26
+ * also keeps the page's own params (#215).
27
+ * @param baseUrl - Optional base URL (defaults to current URL)
28
+ */
29
+ getUrlState(baseUrl?: string): {
30
+ url: string;
31
+ hasFilters: boolean;
32
+ };
20
33
  /**
21
34
  * Gets the current filter state as URL query parameters
22
35
  * @returns Query parameter string
@@ -6,16 +6,23 @@ import { serializeGrouped } from './serialization/grouped.js';
6
6
  export class AGGridUrlSync {
7
7
  config;
8
8
  constructor(gridApi, config = {}) {
9
+ const definedConfig = Object.fromEntries(Object.entries(config).filter(([, value]) => value !== undefined));
9
10
  this.config = {
10
11
  gridApi,
11
12
  ...DEFAULT_CONFIG,
12
- ...config
13
+ ...definedConfig
13
14
  };
14
15
  }
15
16
  generateUrl(baseUrl) {
17
+ return this.getUrlState(baseUrl).url;
18
+ }
19
+ getUrlState(baseUrl) {
16
20
  const filterState = getFilterModel(this.config);
17
21
  const url = baseUrl ?? window.location.href;
18
- return generateUrl(url, filterState, this.config);
22
+ return {
23
+ url: generateUrl(url, filterState, this.config),
24
+ hasFilters: Object.keys(filterState).length > 0
25
+ };
19
26
  }
20
27
  getQueryParams() {
21
28
  const url = this.generateUrl();
@@ -1 +1 @@
1
- {"version":3,"file":"ag-grid-url-sync.js","sourceRoot":"","sources":["../../src/core/ag-grid-url-sync.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAK7D,MAAM,OAAO,aAAa;IAChB,MAAM,CAAgB;IAO9B,YAAY,OAAgB,EAAE,MAAM,GAAwB,EAAE;QAC5D,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO;YACP,GAAG,cAAc;YACjB,GAAG,MAAM;SACV,CAAA;IACH,CAAC;IAOD,WAAW,CAAC,OAAgB;QAC1B,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/C,MAAM,GAAG,GAAG,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAC3C,OAAO,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC;IAMD,cAAc;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,MAAM,CAAA;IACtB,CAAC;IAMD,YAAY,CAAC,GAAY;QACvB,MAAM,UAAU,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAC9C,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5D,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC;IAMD,YAAY,CAAC,WAAwB;QACnC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5C,CAAC;IAKD,YAAY;QACV,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACvB,CAAC;IAKD,OAAO;IAEP,CAAC;IAMD,oBAAoB;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAA;IAClC,CAAC;IAMD,gBAAgB;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;IAC3B,CAAC;IAKD,kBAAkB,CAAC,MAA2B;QAC5C,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAG/C,MAAM,UAAU,GAAG;YACjB,GAAG,IAAI,CAAC,MAAM;YACd,MAAM;YACN,aAAa,EAAE,SAAkB;SAClC,CAAA;QAGD,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAExD,OAAO,MAAM,CAAC,KAAK,CAAA;IACrB,CAAC;CACF;AAQD,MAAM,UAAU,aAAa,CAC3B,OAAgB,EAChB,MAA4B;IAE5B,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAC3C,CAAC"}
1
+ {"version":3,"file":"ag-grid-url-sync.js","sourceRoot":"","sources":["../../src/core/ag-grid-url-sync.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAK7D,MAAM,OAAO,aAAa;IAChB,MAAM,CAAgB;IAO9B,YAAY,OAAgB,EAAE,MAAM,GAAwB,EAAE;QAI5D,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CACtC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAC3C,CAAA;QAExB,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO;YACP,GAAG,cAAc;YACjB,GAAG,aAAa;SACjB,CAAA;IACH,CAAC;IAOD,WAAW,CAAC,OAAgB;QAC1B,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,CAAA;IACtC,CAAC;IAWD,WAAW,CAAC,OAAgB;QAC1B,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC/C,MAAM,GAAG,GAAG,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAC3C,OAAO;YACL,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;YAC/C,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC;SAChD,CAAA;IACH,CAAC;IAMD,cAAc;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAC9B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAC,MAAM,CAAA;IACtB,CAAC;IAMD,YAAY,CAAC,GAAY;QACvB,MAAM,UAAU,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAC9C,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5D,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC;IAMD,YAAY,CAAC,WAAwB;QACnC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5C,CAAC;IAKD,YAAY;QACV,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACvB,CAAC;IAKD,OAAO;IAEP,CAAC;IAMD,oBAAoB;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAA;IAClC,CAAC;IAMD,gBAAgB;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;IAC3B,CAAC;IAKD,kBAAkB,CAAC,MAA2B;QAC5C,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAG/C,MAAM,UAAU,GAAG;YACjB,GAAG,IAAI,CAAC,MAAM;YACd,MAAM;YACN,aAAa,EAAE,SAAkB;SAClC,CAAA;QAGD,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAExD,OAAO,MAAM,CAAC,KAAK,CAAA;IACrB,CAAC;CACF;AAQD,MAAM,UAAU,aAAa,CAC3B,OAAgB,EAChB,MAA4B;IAE5B,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAC3C,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { FilterState, ColumnFilter, ParseConfig } from './types.js';
2
+ /**
3
+ * Converts a filter state object into URL search parameters.
4
+ * This function does not require a GridApi instance: leave `config.gridApi` out
5
+ * (or set it to `null`) when you only need to serialize filters without a grid.
6
+ * A text value over `config.maxValueLength` is reported through
7
+ * `config.onParseError` and skipped. The other filters are kept.
8
+ */
9
+ export declare function serializeFilters(filterState: FilterState, config: ParseConfig): URLSearchParams;
10
+ /**
11
+ * Parses a URL parameter into a column filter
12
+ */
13
+ export declare function parseFilterParam(param: string, value: string, expectedPrefix?: string): ColumnFilter;
14
+ /**
15
+ * Parses prefixed filter parameters into a filter state object.
16
+ * Parameters without the configured prefix are skipped. A parameter that fails
17
+ * to parse is reported through `config.onParseError` and skipped.
18
+ */
19
+ export declare function parseIndividualParams(searchParams: URLSearchParams, config: ParseConfig): FilterState;
@@ -0,0 +1,260 @@
1
+ import { InvalidFilterError, OPERATION_MAP, INTERNAL_TO_URL_OPERATION_MAP } from './types.js';
2
+ import { validateFilterValue, validateAndParseNumber, validateNumberRange, validateAndParseDate, validateAndParseDateRange, validateDateFilter, DEFAULT_CONFIG } from './validation.js';
3
+ const FILTER_TYPES = {
4
+ TEXT: 'text',
5
+ NUMBER: 'number',
6
+ DATE: 'date'
7
+ };
8
+ function serializeColumnFilter(columnName, filter, paramPrefix) {
9
+ const urlOperation = INTERNAL_TO_URL_OPERATION_MAP[filter.type];
10
+ if (!urlOperation)
11
+ return null;
12
+ const paramName = `${paramPrefix}${columnName}_${urlOperation}`;
13
+ if (filter.type === 'dateRange' || filter.type === 'inRange') {
14
+ return 'filterTo' in filter && filter.filterTo !== undefined
15
+ ? [paramName, `${filter.filter},${filter.filterTo}`]
16
+ : null;
17
+ }
18
+ if (filter.type === 'blank' || filter.type === 'notBlank') {
19
+ return [paramName, 'true'];
20
+ }
21
+ if (filter.filterType === FILTER_TYPES.NUMBER ||
22
+ filter.filterType === FILTER_TYPES.DATE) {
23
+ const value = String(filter.filter);
24
+ return value ? [paramName, value] : null;
25
+ }
26
+ if (filter.filterType === FILTER_TYPES.TEXT && filter.filter) {
27
+ return [paramName, filter.filter];
28
+ }
29
+ return null;
30
+ }
31
+ export function serializeFilters(filterState, config) {
32
+ const params = new URLSearchParams();
33
+ for (const [column, filter] of Object.entries(filterState)) {
34
+ if (filter.filterType !== FILTER_TYPES.TEXT &&
35
+ filter.filterType !== FILTER_TYPES.NUMBER &&
36
+ filter.filterType !== FILTER_TYPES.DATE)
37
+ continue;
38
+ let checkedFilter = filter;
39
+ if (filter.filterType === FILTER_TYPES.TEXT) {
40
+ try {
41
+ checkedFilter = {
42
+ ...filter,
43
+ filter: validateFilterValue(filter.filter, config, filter.type)
44
+ };
45
+ }
46
+ catch (error) {
47
+ config.onParseError?.(error);
48
+ continue;
49
+ }
50
+ }
51
+ const param = serializeColumnFilter(column, checkedFilter, config.paramPrefix);
52
+ if (param) {
53
+ params.append(...param);
54
+ }
55
+ }
56
+ return params;
57
+ }
58
+ function isNumberOperation(operation) {
59
+ return [
60
+ 'lessThan',
61
+ 'lessThanOrEqual',
62
+ 'greaterThan',
63
+ 'greaterThanOrEqual',
64
+ 'inRange'
65
+ ].includes(operation);
66
+ }
67
+ function isDateOperation(operation) {
68
+ return [
69
+ 'dateBefore',
70
+ 'dateBeforeOrEqual',
71
+ 'dateAfter',
72
+ 'dateAfterOrEqual',
73
+ 'dateRange'
74
+ ].includes(operation);
75
+ }
76
+ function isSharedOperation(operation) {
77
+ return ['eq', 'notEqual', 'blank', 'notBlank'].includes(operation);
78
+ }
79
+ function tryParseAsDate(value) {
80
+ try {
81
+ validateAndParseDate(value);
82
+ return 'date';
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ function tryParseNumber(value) {
89
+ try {
90
+ return validateAndParseNumber(value);
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ function detectFilterTypeForSharedOperation(operation, value) {
97
+ if (operation === 'blank' || operation === 'notBlank') {
98
+ return 'text';
99
+ }
100
+ if (operation === 'eq' || operation === 'notEqual') {
101
+ if (tryParseAsDate(value) === 'date') {
102
+ return 'date';
103
+ }
104
+ if (tryParseNumber(value) !== null) {
105
+ return 'number';
106
+ }
107
+ return 'text';
108
+ }
109
+ return 'text';
110
+ }
111
+ function extractColumnAndOperation(param, expectedPrefix) {
112
+ const ensureTrailingUnderscore = (prefix) => prefix.endsWith('_') ? prefix : prefix + '_';
113
+ const sanitizedPrefix = expectedPrefix
114
+ ? ensureTrailingUnderscore(expectedPrefix)
115
+ : ensureTrailingUnderscore(DEFAULT_CONFIG.paramPrefix);
116
+ const prefixToCheck = sanitizedPrefix.slice(0, -1);
117
+ if (!param.startsWith(prefixToCheck + '_')) {
118
+ throw new InvalidFilterError(`Invalid filter prefix in parameter: ${param}`);
119
+ }
120
+ const withoutPrefix = param.substring(prefixToCheck.length + 1);
121
+ const lastUnderscoreIndex = withoutPrefix.lastIndexOf('_');
122
+ if (lastUnderscoreIndex === -1) {
123
+ throw new InvalidFilterError(`Invalid filter parameter format: ${param}`);
124
+ }
125
+ const column = withoutPrefix.substring(0, lastUnderscoreIndex);
126
+ const operation = withoutPrefix.substring(lastUnderscoreIndex + 1);
127
+ if (!column.trim()) {
128
+ throw new InvalidFilterError(`Empty column name in parameter: ${param}`);
129
+ }
130
+ return { column, operation };
131
+ }
132
+ export function parseFilterParam(param, value, expectedPrefix) {
133
+ const { operation } = extractColumnAndOperation(param, expectedPrefix);
134
+ return parseFilterOperation(operation, value);
135
+ }
136
+ function parseFilterOperation(operation, value) {
137
+ const filterOp = OPERATION_MAP[operation];
138
+ if (!filterOp) {
139
+ throw new InvalidFilterError(`Unsupported filter operation: ${operation}`);
140
+ }
141
+ if (filterOp === 'dateRange') {
142
+ try {
143
+ const [startDate, endDate] = validateAndParseDateRange(value);
144
+ return {
145
+ filterType: 'date',
146
+ type: filterOp,
147
+ filter: startDate,
148
+ filterTo: endDate
149
+ };
150
+ }
151
+ catch (error) {
152
+ throw new InvalidFilterError(error instanceof Error ? error.message : 'Invalid date range');
153
+ }
154
+ }
155
+ if (filterOp === 'inRange') {
156
+ const rangeParts = value.split(',');
157
+ if (rangeParts.length !== 2) {
158
+ throw new InvalidFilterError('Range operation requires exactly two values separated by comma');
159
+ }
160
+ const min = validateAndParseNumber(rangeParts[0] || '');
161
+ const max = validateAndParseNumber(rangeParts[1] || '');
162
+ const rangeValidation = validateNumberRange(min, max);
163
+ if (!rangeValidation.valid) {
164
+ throw new InvalidFilterError(rangeValidation.error || 'Invalid range values');
165
+ }
166
+ return {
167
+ filterType: 'number',
168
+ type: filterOp,
169
+ filter: min,
170
+ filterTo: max
171
+ };
172
+ }
173
+ if (isDateOperation(filterOp)) {
174
+ try {
175
+ const dateValue = validateAndParseDate(value);
176
+ const validation = validateDateFilter(dateValue, filterOp);
177
+ if (!validation.valid) {
178
+ throw new InvalidFilterError(validation.error || 'Invalid date filter');
179
+ }
180
+ return {
181
+ filterType: 'date',
182
+ type: filterOp,
183
+ filter: dateValue
184
+ };
185
+ }
186
+ catch (error) {
187
+ throw new InvalidFilterError(error instanceof Error ? error.message : 'Invalid date filter');
188
+ }
189
+ }
190
+ if (isNumberOperation(filterOp)) {
191
+ const numValue = validateAndParseNumber(value);
192
+ return {
193
+ filterType: 'number',
194
+ type: filterOp,
195
+ filter: numValue
196
+ };
197
+ }
198
+ if (isSharedOperation(filterOp)) {
199
+ const detectedType = detectFilterTypeForSharedOperation(filterOp, value);
200
+ if (detectedType === 'date') {
201
+ try {
202
+ const dateValue = validateAndParseDate(value);
203
+ return {
204
+ filterType: 'date',
205
+ type: filterOp,
206
+ filter: dateValue
207
+ };
208
+ }
209
+ catch {
210
+ }
211
+ }
212
+ if (detectedType === 'number') {
213
+ const numValue = tryParseNumber(value);
214
+ if (numValue !== null) {
215
+ return {
216
+ filterType: 'number',
217
+ type: filterOp,
218
+ filter: numValue
219
+ };
220
+ }
221
+ }
222
+ return {
223
+ filterType: 'text',
224
+ type: filterOp,
225
+ filter: filterOp === 'blank' || filterOp === 'notBlank' ? '' : value || ''
226
+ };
227
+ }
228
+ return {
229
+ filterType: 'text',
230
+ type: filterOp,
231
+ filter: value || ''
232
+ };
233
+ }
234
+ export function parseIndividualParams(searchParams, config) {
235
+ const filterState = {};
236
+ for (const [param, value] of searchParams.entries()) {
237
+ if (!param.startsWith(config.paramPrefix))
238
+ continue;
239
+ try {
240
+ const { column: columnName, operation } = extractColumnAndOperation(param, config.paramPrefix);
241
+ const filterParam = parseFilterOperation(operation, value);
242
+ if (filterParam.filterType === 'text') {
243
+ const validatedValue = validateFilterValue(value, config, filterParam.type);
244
+ filterState[columnName] = {
245
+ ...filterParam,
246
+ filter: validatedValue
247
+ };
248
+ }
249
+ else {
250
+ filterState[columnName] = filterParam;
251
+ }
252
+ }
253
+ catch (error) {
254
+ config.onParseError?.(error);
255
+ continue;
256
+ }
257
+ }
258
+ return filterState;
259
+ }
260
+ //# sourceMappingURL=individual-params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"individual-params.js","sourceRoot":"","sources":["../../src/core/individual-params.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,12 @@
1
- import type { FilterState, SerializationFormat, FormatSerializer } from '../types.js';
1
+ import type { FilterState, SerializationFormat, FormatSerializer, ParseConfig } from '../types.js';
2
2
  /**
3
3
  * QueryString format serializer
4
- * Reuses existing URL generation logic but groups into single parameter
4
+ * Writes the individual filter parameters as one query string value
5
5
  */
6
6
  export declare class QueryStringSerializer implements FormatSerializer {
7
7
  readonly format: SerializationFormat;
8
- serialize(filterState: FilterState): string;
9
- deserialize(value: string): FilterState;
8
+ serialize(filterState: FilterState, config?: ParseConfig): string;
9
+ deserialize(value: string, config?: ParseConfig): FilterState;
10
10
  }
11
11
  /**
12
12
  * JSON format serializer
@@ -14,8 +14,8 @@ export declare class QueryStringSerializer implements FormatSerializer {
14
14
  */
15
15
  export declare class JsonSerializer implements FormatSerializer {
16
16
  readonly format: SerializationFormat;
17
- serialize(filterState: FilterState): string;
18
- deserialize(value: string): FilterState;
17
+ serialize(filterState: FilterState, config?: ParseConfig): string;
18
+ deserialize(value: string, config?: ParseConfig): FilterState;
19
19
  }
20
20
  /**
21
21
  * Base64 format serializer
@@ -23,7 +23,6 @@ export declare class JsonSerializer implements FormatSerializer {
23
23
  */
24
24
  export declare class Base64Serializer implements FormatSerializer {
25
25
  readonly format: SerializationFormat;
26
- private jsonSerializer;
27
- serialize(filterState: FilterState): string;
28
- deserialize(value: string): FilterState;
26
+ serialize(filterState: FilterState, config?: ParseConfig): string;
27
+ deserialize(value: string, config?: ParseConfig): FilterState;
29
28
  }
@@ -1,68 +1,85 @@
1
- import { DEFAULT_CONFIG } from '../index.js';
2
- import { generateUrl } from '../url-generator.js';
3
- import { parseUrlFilters } from '../url-parser.js';
4
- import { isValidColumnFilter } from '../validation.js';
1
+ import { InvalidSerializationError } from '../types.js';
2
+ import { parseIndividualParams, serializeFilters } from '../individual-params.js';
3
+ import { DEFAULT_CONFIG, isValidColumnFilter, validateFilterValue } from '../validation.js';
4
+ function dropOverLengthTextValues(filterState, config, format) {
5
+ const cfg = config ?? DEFAULT_CONFIG;
6
+ const result = {};
7
+ for (const [column, filter] of Object.entries(filterState)) {
8
+ if (filter.filterType === 'text') {
9
+ try {
10
+ validateFilterValue(filter.filter, cfg, filter.type);
11
+ }
12
+ catch (error) {
13
+ cfg.onParseError?.(new InvalidSerializationError(format, error));
14
+ continue;
15
+ }
16
+ }
17
+ result[column] = filter;
18
+ }
19
+ return result;
20
+ }
21
+ function parseAndValidateJson(value) {
22
+ try {
23
+ const parsed = JSON.parse(value);
24
+ if (typeof parsed !== 'object' || parsed === null) {
25
+ throw new Error('Invalid JSON: must be an object');
26
+ }
27
+ for (const [columnId, filter] of Object.entries(parsed)) {
28
+ if (!isValidColumnFilter(filter)) {
29
+ throw new Error(`Invalid JSON: filter for column ${columnId} is not a valid filter object`);
30
+ }
31
+ }
32
+ return parsed;
33
+ }
34
+ catch (error) {
35
+ throw new Error(`JSON parsing failed: ${error.message}`);
36
+ }
37
+ }
5
38
  export class QueryStringSerializer {
6
39
  format = 'querystring';
7
- serialize(filterState) {
8
- const tempConfig = {
9
- gridApi: null,
10
- ...DEFAULT_CONFIG
11
- };
12
- const tempUrl = generateUrl('http://example.com', filterState, tempConfig);
13
- const urlObj = new URL(tempUrl);
14
- return urlObj.search.slice(1);
40
+ serialize(filterState, config) {
41
+ return serializeFilters(filterState, innerConfig(config)).toString();
15
42
  }
16
- deserialize(value) {
17
- const tempUrl = `http://example.com?${value}`;
18
- const tempConfig = {
19
- gridApi: null,
20
- ...DEFAULT_CONFIG
21
- };
22
- return parseUrlFilters(tempUrl, tempConfig);
43
+ deserialize(value, config) {
44
+ return parseIndividualParams(new URLSearchParams(value), innerConfig(config));
23
45
  }
24
46
  }
47
+ function innerConfig(config) {
48
+ if (!config)
49
+ return DEFAULT_CONFIG;
50
+ const onParseError = config.onParseError ?? DEFAULT_CONFIG.onParseError;
51
+ return {
52
+ ...DEFAULT_CONFIG,
53
+ maxValueLength: config.maxValueLength ?? DEFAULT_CONFIG.maxValueLength,
54
+ onParseError: error => onParseError(new InvalidSerializationError('querystring', error))
55
+ };
56
+ }
25
57
  export class JsonSerializer {
26
58
  format = 'json';
27
- serialize(filterState) {
28
- return JSON.stringify(filterState);
59
+ serialize(filterState, config) {
60
+ return JSON.stringify(dropOverLengthTextValues(filterState, config, 'json'));
29
61
  }
30
- deserialize(value) {
31
- try {
32
- const parsed = JSON.parse(value);
33
- if (typeof parsed !== 'object' || parsed === null) {
34
- throw new Error('Invalid JSON: must be an object');
35
- }
36
- for (const [columnId, filter] of Object.entries(parsed)) {
37
- if (typeof columnId !== 'string') {
38
- throw new Error('Invalid JSON: column IDs must be strings');
39
- }
40
- if (!isValidColumnFilter(filter)) {
41
- throw new Error(`Invalid JSON: filter for column ${columnId} is not a valid filter object`);
42
- }
43
- }
44
- return parsed;
45
- }
46
- catch (error) {
47
- throw new Error(`JSON parsing failed: ${error.message}`);
48
- }
62
+ deserialize(value, config) {
63
+ return dropOverLengthTextValues(parseAndValidateJson(value), config, 'json');
49
64
  }
50
65
  }
51
66
  export class Base64Serializer {
52
67
  format = 'base64';
53
- jsonSerializer = new JsonSerializer();
54
- serialize(filterState) {
55
- const jsonString = this.jsonSerializer.serialize(filterState);
56
- return btoa(jsonString);
68
+ serialize(filterState, config) {
69
+ const filtered = dropOverLengthTextValues(filterState, config, 'base64');
70
+ return btoa(escapeNonAscii(JSON.stringify(filtered)));
57
71
  }
58
- deserialize(value) {
72
+ deserialize(value, config) {
59
73
  try {
60
74
  const decoded = atob(value);
61
- return this.jsonSerializer.deserialize(decoded);
75
+ return dropOverLengthTextValues(parseAndValidateJson(decoded), config, 'base64');
62
76
  }
63
77
  catch (error) {
64
78
  throw new Error(`Base64 decoding failed: ${error.message}`);
65
79
  }
66
80
  }
67
81
  }
82
+ function escapeNonAscii(json) {
83
+ return json.replace(/[\u0080-\uffff]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`);
84
+ }
68
85
  //# sourceMappingURL=formats.js.map