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 +51 -13
- package/dist/core/ag-grid-url-sync.d.ts +13 -0
- package/dist/core/ag-grid-url-sync.js +9 -2
- package/dist/core/ag-grid-url-sync.js.map +1 -1
- package/dist/core/individual-params.d.ts +19 -0
- package/dist/core/individual-params.js +260 -0
- package/dist/core/individual-params.js.map +1 -0
- package/dist/core/serialization/formats.d.ts +8 -9
- package/dist/core/serialization/formats.js +63 -46
- package/dist/core/serialization/formats.js.map +1 -1
- package/dist/core/serialization/grouped.d.ts +10 -5
- package/dist/core/serialization/grouped.js +4 -4
- package/dist/core/serialization/grouped.js.map +1 -1
- package/dist/core/types.d.ts +38 -5
- package/dist/core/types.js.map +1 -1
- package/dist/core/url-generator.d.ts +10 -7
- package/dist/core/url-generator.js +25 -119
- package/dist/core/url-generator.js.map +1 -1
- package/dist/core/url-parser.d.ts +19 -7
- package/dist/core/url-parser.js +28 -238
- package/dist/core/url-parser.js.map +1 -1
- package/dist/core/validation.d.ts +2 -2
- package/dist/core/validation.js.map +1 -1
- package/dist/react/types.d.ts +7 -1
- package/dist/react/use-ag-grid-url-sync.js +5 -12
- package/dist/react/use-ag-grid-url-sync.js.map +1 -1
- package/package.json +14 -2
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
|
-
|
|
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
|
|
216
|
-
import
|
|
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
|
-
|
|
221
|
-
|
|
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.
|
|
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
|
|
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
|
-
...
|
|
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
|
|
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;
|
|
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
|
-
*
|
|
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
|
-
|
|
27
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|