ag-grid-url-sync 0.4.1 → 0.5.1
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 +143 -50
- package/dist/core/ag-grid-url-sync.d.ts +19 -5
- package/dist/core/ag-grid-url-sync.js +28 -17
- package/dist/core/ag-grid-url-sync.js.map +1 -1
- package/dist/core/grid-integration.d.ts +27 -1
- package/dist/core/grid-integration.js +161 -129
- package/dist/core/grid-integration.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/individual-params.d.ts +26 -0
- package/dist/core/individual-params.js +236 -0
- package/dist/core/individual-params.js.map +1 -0
- package/dist/core/issue-columns.d.ts +2 -0
- package/dist/core/issue-columns.js +11 -0
- package/dist/core/issue-columns.js.map +1 -0
- package/dist/core/issues.d.ts +34 -0
- package/dist/core/issues.js +58 -0
- package/dist/core/issues.js.map +1 -0
- package/dist/core/operators.d.ts +189 -0
- package/dist/core/operators.js +60 -0
- package/dist/core/operators.js.map +1 -0
- package/dist/core/options.d.ts +21 -0
- package/dist/core/options.js +21 -0
- package/dist/core/options.js.map +1 -0
- package/dist/core/schemas.d.ts +51 -0
- package/dist/core/schemas.js +59 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/serialization/formats.d.ts +8 -9
- package/dist/core/serialization/formats.js +70 -47
- package/dist/core/serialization/formats.js.map +1 -1
- package/dist/core/serialization/grouped.d.ts +13 -11
- package/dist/core/serialization/grouped.js +24 -17
- package/dist/core/serialization/grouped.js.map +1 -1
- package/dist/core/to-error.d.ts +5 -0
- package/dist/core/to-error.js +4 -0
- package/dist/core/to-error.js.map +1 -0
- package/dist/core/types.d.ts +172 -33
- package/dist/core/types.js +35 -108
- package/dist/core/types.js.map +1 -1
- package/dist/core/url-generator.d.ts +10 -7
- package/dist/core/url-generator.js +29 -121
- package/dist/core/url-generator.js.map +1 -1
- package/dist/core/url-parser.d.ts +19 -7
- package/dist/core/url-parser.js +31 -239
- package/dist/core/url-parser.js.map +1 -1
- package/dist/core/validation.d.ts +21 -14
- package/dist/core/validation.js +58 -54
- package/dist/core/validation.js.map +1 -1
- package/dist/core/view-storage.js +21 -19
- package/dist/core/view-storage.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/react/types.d.ts +36 -11
- package/dist/react/use-ag-grid-url-sync.js +151 -97
- package/dist/react/use-ag-grid-url-sync.js.map +1 -1
- package/package.json +35 -16
|
@@ -1,132 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { INTERNAL_TO_URL_OPERATION_MAP } from './types.js';
|
|
1
|
+
import { serializeFilters, resolveParamPrefix } from './individual-params.js';
|
|
3
2
|
import { serializeGrouped } from './serialization/grouped.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function serializeColumnFilter(columnName, filter, paramPrefix) {
|
|
10
|
-
const urlOperation = INTERNAL_TO_URL_OPERATION_MAP[filter.type];
|
|
11
|
-
if (!urlOperation)
|
|
12
|
-
return '';
|
|
13
|
-
const paramName = `${paramPrefix}${columnName}_${urlOperation}`;
|
|
14
|
-
if (filter.type === 'dateRange' && filter.filterType === FILTER_TYPES.DATE) {
|
|
15
|
-
if ('filterTo' in filter && filter.filterTo !== undefined) {
|
|
16
|
-
return `${paramName}=${filter.filter},${filter.filterTo}`;
|
|
17
|
-
}
|
|
18
|
-
return '';
|
|
19
|
-
}
|
|
20
|
-
if (filter.type === 'inRange' && filter.filterType === FILTER_TYPES.NUMBER) {
|
|
21
|
-
if ('filterTo' in filter && filter.filterTo !== undefined) {
|
|
22
|
-
return `${paramName}=${filter.filter},${filter.filterTo}`;
|
|
23
|
-
}
|
|
24
|
-
return '';
|
|
25
|
-
}
|
|
26
|
-
if (filter.type === 'blank' || filter.type === 'notBlank') {
|
|
27
|
-
return `${paramName}=true`;
|
|
28
|
-
}
|
|
29
|
-
if (filter.filterType === FILTER_TYPES.DATE) {
|
|
30
|
-
return `${paramName}=${filter.filter}`;
|
|
31
|
-
}
|
|
32
|
-
if (filter.filterType === FILTER_TYPES.NUMBER) {
|
|
33
|
-
return `${paramName}=${filter.filter}`;
|
|
34
|
-
}
|
|
35
|
-
if (filter.filterType === FILTER_TYPES.TEXT && filter.filter) {
|
|
36
|
-
return `${paramName}=${encodeURIComponent(filter.filter.toString())}`;
|
|
37
|
-
}
|
|
38
|
-
return '';
|
|
39
|
-
}
|
|
40
|
-
function appendSerializedParam(serialized, params, isRangeOperation = false) {
|
|
41
|
-
const [paramName, paramValue] = serialized.split('=');
|
|
42
|
-
if (paramName && paramValue) {
|
|
43
|
-
if (isRangeOperation) {
|
|
44
|
-
params.append(paramName, paramValue);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
params.append(paramName, decodeURIComponent(paramValue));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
3
|
+
import { resolveOptions } from './options.js';
|
|
4
|
+
export { serializeFilters };
|
|
5
|
+
function isFilterParam(key, config) {
|
|
6
|
+
return (key === config.groupedParam ||
|
|
7
|
+
key.startsWith(resolveParamPrefix(config.paramPrefix)));
|
|
50
8
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
for (const [column, filter] of Object.entries(filterState)) {
|
|
54
|
-
if (filter.filterType !== FILTER_TYPES.TEXT &&
|
|
55
|
-
filter.filterType !== FILTER_TYPES.NUMBER &&
|
|
56
|
-
filter.filterType !== FILTER_TYPES.DATE)
|
|
57
|
-
continue;
|
|
58
|
-
if (filter.filterType === FILTER_TYPES.TEXT) {
|
|
59
|
-
const validatedValue = validateFilterValue(filter.filter, config, filter.type);
|
|
60
|
-
const updatedFilter = { ...filter, filter: validatedValue };
|
|
61
|
-
const serialized = serializeColumnFilter(column, updatedFilter, config.paramPrefix);
|
|
62
|
-
if (serialized) {
|
|
63
|
-
appendSerializedParam(serialized, params, false);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
const serialized = serializeColumnFilter(column, filter, config.paramPrefix);
|
|
68
|
-
if (serialized) {
|
|
69
|
-
const isRangeOp = filter.type === 'inRange' || filter.type === 'dateRange';
|
|
70
|
-
appendSerializedParam(serialized, params, isRangeOp);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return params;
|
|
9
|
+
function getHostParams(url, config) {
|
|
10
|
+
return [...url.searchParams].filter(([key]) => !isFilterParam(key, config));
|
|
75
11
|
}
|
|
76
|
-
function
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
params.set(key, value);
|
|
12
|
+
function toUrlString(url, params) {
|
|
13
|
+
const queryString = params.toString();
|
|
14
|
+
if (queryString) {
|
|
15
|
+
url.search = queryString;
|
|
16
|
+
return url.toString();
|
|
84
17
|
}
|
|
85
|
-
|
|
18
|
+
url.search = '';
|
|
19
|
+
const result = url.toString();
|
|
20
|
+
return result === `${url.origin}/` ? result.slice(0, -1) : result;
|
|
86
21
|
}
|
|
87
|
-
export function generateUrl(baseUrl, filterState, config) {
|
|
22
|
+
export function generateUrl(baseUrl, filterState, config = {}) {
|
|
23
|
+
const options = resolveOptions(config);
|
|
88
24
|
const url = new URL(baseUrl);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
filterParams.append(key, value);
|
|
96
|
-
}
|
|
97
|
-
const queryString = filterParams.toString();
|
|
98
|
-
if (!queryString) {
|
|
99
|
-
let result = url.toString().replace(/\?$/, '');
|
|
100
|
-
if (result.endsWith('/') && result === url.origin + '/') {
|
|
101
|
-
result = result.slice(0, -1);
|
|
25
|
+
const hostParams = getHostParams(url, options);
|
|
26
|
+
if (options.serialization === 'grouped') {
|
|
27
|
+
const params = new URLSearchParams(hostParams);
|
|
28
|
+
const groupedResult = serializeGrouped(filterState, options);
|
|
29
|
+
if (groupedResult.value) {
|
|
30
|
+
params.append(groupedResult.paramName, groupedResult.value);
|
|
102
31
|
}
|
|
103
|
-
return
|
|
32
|
+
return toUrlString(url, params);
|
|
104
33
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
function generateGroupedUrl(baseUrl, filterState, config) {
|
|
109
|
-
const url = new URL(baseUrl);
|
|
110
|
-
const groupedResult = serializeGrouped(filterState, config);
|
|
111
|
-
const preservedParams = getPreservedParams(url, config, config.groupedParam);
|
|
112
|
-
url.search = '';
|
|
113
|
-
for (const [key, value] of preservedParams.entries()) {
|
|
114
|
-
url.searchParams.set(key, value);
|
|
115
|
-
}
|
|
116
|
-
if (groupedResult.value) {
|
|
117
|
-
url.searchParams.set(groupedResult.paramName, groupedResult.value);
|
|
118
|
-
}
|
|
119
|
-
if (!groupedResult.value && url.searchParams.has(config.groupedParam)) {
|
|
120
|
-
url.searchParams.delete(config.groupedParam);
|
|
121
|
-
}
|
|
122
|
-
const queryString = url.searchParams.toString();
|
|
123
|
-
if (!queryString) {
|
|
124
|
-
let result = url.toString().replace(/\?$/, '');
|
|
125
|
-
if (result.endsWith('/') && result === url.origin + '/') {
|
|
126
|
-
result = result.slice(0, -1);
|
|
127
|
-
}
|
|
128
|
-
return result;
|
|
34
|
+
const params = serializeFilters(filterState, options);
|
|
35
|
+
for (const [key, value] of hostParams) {
|
|
36
|
+
params.append(key, value);
|
|
129
37
|
}
|
|
130
|
-
return url
|
|
38
|
+
return toUrlString(url, params);
|
|
131
39
|
}
|
|
132
40
|
//# sourceMappingURL=url-generator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-generator.js","sourceRoot":"","sources":["../../src/core/url-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"url-generator.js","sourceRoot":"","sources":["../../src/core/url-generator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAQ3B,SAAS,aAAa,CAAC,GAAW,EAAE,MAAuB;IACzD,OAAO,CACL,GAAG,KAAK,MAAM,CAAC,YAAY;QAC3B,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CACvD,CAAA;AACH,CAAC;AAMD,SAAS,aAAa,CAAC,GAAQ,EAAE,MAAuB;IACtD,OAAO,CAAC,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;AAC7E,CAAC;AAMD,SAAS,WAAW,CAAC,GAAQ,EAAE,MAAuB;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;IAErC,IAAI,WAAW,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,GAAG,WAAW,CAAA;QAExB,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;IACvB,CAAC;IAED,GAAG,CAAC,MAAM,GAAG,EAAE,CAAA;IACf,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAA;IAE7B,OAAO,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;AACnE,CAAC;AAUD,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,WAAwB,EACxB,MAAM,GAAwB,EAAE;IAEhC,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;IACtC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;IAC5B,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAE9C,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QAExC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,UAAU,CAAC,CAAA;QAC9C,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QAE5D,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;YACxB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;QAC7D,CAAC;QAED,OAAO,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACjC,CAAC;IAGD,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAErD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,UAAU,EAAE,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC3B,CAAC;IAED,OAAO,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AACjC,CAAC"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AGGridUrlSyncConfig, FilterState } from './types.js';
|
|
2
|
+
import { parseFilterParam } from './individual-params.js';
|
|
3
|
+
export { parseFilterParam };
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
5
|
+
* Converts URL search params into a filter state object.
|
|
6
|
+
* This function does not need a grid. An option that the config leaves out
|
|
7
|
+
* gets its default.
|
|
8
|
+
*
|
|
9
|
+
* Accepts an absolute URL, a relative path, a query string or a raw query
|
|
10
|
+
* string (see {@link toUrl}). A param that is not valid is skipped and reported
|
|
11
|
+
* through `onParseError`.
|
|
12
|
+
*
|
|
13
|
+
* If the URL has a non-empty `config.groupedParam`, it is decoded with
|
|
14
|
+
* `config.format` and the prefixed params are not read. This works in both
|
|
15
|
+
* serialization modes, so a grouped link opens in an app in individual mode
|
|
16
|
+
* when the param name and format match.
|
|
17
|
+
*
|
|
18
|
+
* @throws InvalidURLError if the input is an absolute URL that is not valid,
|
|
19
|
+
* or a host with no scheme
|
|
4
20
|
*/
|
|
5
|
-
export declare function
|
|
6
|
-
/**
|
|
7
|
-
* Converts URL search params into a filter state object
|
|
8
|
-
*/
|
|
9
|
-
export declare function parseUrlFilters(url: string, config: InternalConfig): FilterState;
|
|
21
|
+
export declare function parseUrlFilters(url: string, config?: AGGridUrlSyncConfig): FilterState;
|
package/dist/core/url-parser.js
CHANGED
|
@@ -1,249 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
function isDateOperation(operation) {
|
|
14
|
-
return [
|
|
15
|
-
'dateBefore',
|
|
16
|
-
'dateBeforeOrEqual',
|
|
17
|
-
'dateAfter',
|
|
18
|
-
'dateAfterOrEqual',
|
|
19
|
-
'dateRange'
|
|
20
|
-
].includes(operation);
|
|
21
|
-
}
|
|
22
|
-
function isSharedOperation(operation) {
|
|
23
|
-
return ['eq', 'notEqual', 'blank', 'notBlank'].includes(operation);
|
|
24
|
-
}
|
|
25
|
-
function tryParseAsDate(value) {
|
|
26
|
-
try {
|
|
27
|
-
validateAndParseDate(value);
|
|
28
|
-
return 'date';
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return null;
|
|
1
|
+
import { InvalidURLError } from './types.js';
|
|
2
|
+
import { parseFilterParam, parseIndividualParams } from './individual-params.js';
|
|
3
|
+
import { deserializeGrouped } from './serialization/grouped.js';
|
|
4
|
+
import { resolveOptions } from './options.js';
|
|
5
|
+
export { parseFilterParam };
|
|
6
|
+
const PARSE_BASE = 'http://localhost';
|
|
7
|
+
const HAS_SCHEME = /^[a-z][a-z\d+.-]*:/i;
|
|
8
|
+
const SCHEMELESS_HOST = /^[\p{L}\p{N}-]+(?:(?:\.[\p{L}\p{N}-]+)+(?::\d+)?|:\d+)(?=[/?#]|$)/u;
|
|
9
|
+
function toUrl(input) {
|
|
10
|
+
if (SCHEMELESS_HOST.test(input)) {
|
|
11
|
+
throw new InvalidURLError(`Failed to parse URL: "${input}" has a host but no scheme. ` +
|
|
12
|
+
'Add a scheme (https://...) or start a relative path with "/".');
|
|
32
13
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return 'text';
|
|
37
|
-
}
|
|
38
|
-
if (operation === 'eq' || operation === 'notEqual') {
|
|
39
|
-
if (tryParseAsDate(value) === 'date') {
|
|
40
|
-
return 'date';
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
const numValue = parseFloat(value);
|
|
44
|
-
if (!isNaN(numValue) && Number.isFinite(numValue)) {
|
|
45
|
-
const validation = validateNumberFilter(numValue);
|
|
46
|
-
if (validation.valid) {
|
|
47
|
-
return 'number';
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
}
|
|
53
|
-
return 'text';
|
|
54
|
-
}
|
|
55
|
-
return 'text';
|
|
56
|
-
}
|
|
57
|
-
function extractColumnAndOperation(param, expectedPrefix) {
|
|
58
|
-
const ensureTrailingUnderscore = (prefix) => prefix.endsWith('_') ? prefix : prefix + '_';
|
|
59
|
-
const sanitizedPrefix = expectedPrefix
|
|
60
|
-
? ensureTrailingUnderscore(expectedPrefix)
|
|
61
|
-
: ensureTrailingUnderscore(DEFAULT_CONFIG.paramPrefix);
|
|
62
|
-
const prefixToCheck = sanitizedPrefix.slice(0, -1);
|
|
63
|
-
if (!param.startsWith(prefixToCheck + '_')) {
|
|
64
|
-
throw new InvalidFilterError(`Invalid filter prefix in parameter: ${param}`);
|
|
65
|
-
}
|
|
66
|
-
const withoutPrefix = param.substring(prefixToCheck.length + 1);
|
|
67
|
-
const lastUnderscoreIndex = withoutPrefix.lastIndexOf('_');
|
|
68
|
-
if (lastUnderscoreIndex === -1) {
|
|
69
|
-
throw new InvalidFilterError(`Invalid filter parameter format: ${param}`);
|
|
70
|
-
}
|
|
71
|
-
const column = withoutPrefix.substring(0, lastUnderscoreIndex);
|
|
72
|
-
const operation = withoutPrefix.substring(lastUnderscoreIndex + 1);
|
|
73
|
-
if (!column.trim()) {
|
|
74
|
-
throw new InvalidFilterError(`Empty column name in parameter: ${param}`);
|
|
75
|
-
}
|
|
76
|
-
return { column, operation };
|
|
77
|
-
}
|
|
78
|
-
export function parseFilterParam(param, value, expectedPrefix) {
|
|
79
|
-
const { operation } = extractColumnAndOperation(param, expectedPrefix);
|
|
80
|
-
const filterOp = OPERATION_MAP[operation];
|
|
81
|
-
if (!filterOp) {
|
|
82
|
-
throw new InvalidFilterError(`Unsupported filter operation: ${operation}`);
|
|
83
|
-
}
|
|
84
|
-
if (filterOp === 'dateRange') {
|
|
85
|
-
try {
|
|
86
|
-
const [startDate, endDate] = validateAndParseDateRange(value);
|
|
87
|
-
return {
|
|
88
|
-
filterType: 'date',
|
|
89
|
-
type: filterOp,
|
|
90
|
-
filter: startDate,
|
|
91
|
-
filterTo: endDate
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
throw new InvalidFilterError(error instanceof Error ? error.message : 'Invalid date range');
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (filterOp === 'inRange') {
|
|
99
|
-
const rangeParts = value.split(',');
|
|
100
|
-
if (rangeParts.length !== 2) {
|
|
101
|
-
throw new InvalidFilterError('Range operation requires exactly two values separated by comma');
|
|
102
|
-
}
|
|
103
|
-
const min = validateAndParseNumber(rangeParts[0] || '');
|
|
104
|
-
const max = validateAndParseNumber(rangeParts[1] || '');
|
|
105
|
-
const rangeValidation = validateNumberRange(min, max);
|
|
106
|
-
if (!rangeValidation.valid) {
|
|
107
|
-
throw new InvalidFilterError(rangeValidation.error || 'Invalid range values');
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
filterType: 'number',
|
|
111
|
-
type: filterOp,
|
|
112
|
-
filter: min,
|
|
113
|
-
filterTo: max
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
if (isDateOperation(filterOp)) {
|
|
117
|
-
try {
|
|
118
|
-
const dateValue = validateAndParseDate(value);
|
|
119
|
-
const validation = validateDateFilter(dateValue, filterOp);
|
|
120
|
-
if (!validation.valid) {
|
|
121
|
-
throw new InvalidFilterError(validation.error || 'Invalid date filter');
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
filterType: 'date',
|
|
125
|
-
type: filterOp,
|
|
126
|
-
filter: dateValue
|
|
127
|
-
};
|
|
14
|
+
try {
|
|
15
|
+
if (HAS_SCHEME.test(input)) {
|
|
16
|
+
return new URL(input);
|
|
128
17
|
}
|
|
129
|
-
|
|
130
|
-
|
|
18
|
+
if (/^[/?#]/.test(input)) {
|
|
19
|
+
return new URL(input, PARSE_BASE);
|
|
131
20
|
}
|
|
21
|
+
const url = new URL(PARSE_BASE);
|
|
22
|
+
url.search = input;
|
|
23
|
+
return url;
|
|
132
24
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
filterType: 'number',
|
|
137
|
-
type: filterOp,
|
|
138
|
-
filter: numValue
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (isSharedOperation(filterOp)) {
|
|
142
|
-
const detectedType = detectFilterTypeForSharedOperation(filterOp, value);
|
|
143
|
-
if (detectedType === 'date') {
|
|
144
|
-
try {
|
|
145
|
-
const dateValue = validateAndParseDate(value);
|
|
146
|
-
return {
|
|
147
|
-
filterType: 'date',
|
|
148
|
-
type: filterOp,
|
|
149
|
-
filter: dateValue
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (detectedType === 'number') {
|
|
156
|
-
try {
|
|
157
|
-
const numValue = parseFloat(value);
|
|
158
|
-
if (!isNaN(numValue) && Number.isFinite(numValue)) {
|
|
159
|
-
const validation = validateNumberFilter(numValue);
|
|
160
|
-
if (validation.valid) {
|
|
161
|
-
return {
|
|
162
|
-
filterType: 'number',
|
|
163
|
-
type: filterOp,
|
|
164
|
-
filter: numValue
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
filterType: 'text',
|
|
174
|
-
type: filterOp,
|
|
175
|
-
filter: filterOp === 'blank' || filterOp === 'notBlank' ? '' : value || ''
|
|
176
|
-
};
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw new InvalidURLError(`Failed to parse URL: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
|
|
177
27
|
}
|
|
178
|
-
return {
|
|
179
|
-
filterType: 'text',
|
|
180
|
-
type: filterOp,
|
|
181
|
-
filter: value || ''
|
|
182
|
-
};
|
|
183
28
|
}
|
|
184
|
-
export function parseUrlFilters(url, config) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return {};
|
|
188
|
-
}
|
|
189
|
-
if (url === 'not-a-url' ||
|
|
190
|
-
url.startsWith('://') ||
|
|
191
|
-
(!url.includes('://') &&
|
|
192
|
-
!url.startsWith('/') &&
|
|
193
|
-
!url.startsWith('?') &&
|
|
194
|
-
url.includes('.'))) {
|
|
195
|
-
throw new Error('Invalid URL format');
|
|
196
|
-
}
|
|
197
|
-
let urlToProcess = url;
|
|
198
|
-
if (url.startsWith('?')) {
|
|
199
|
-
urlToProcess = `http://example.com${url}`;
|
|
200
|
-
}
|
|
201
|
-
else if (!url.includes('://') && !url.startsWith('/')) {
|
|
202
|
-
urlToProcess = `http://example.com?${url}`;
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
urlToProcess = url;
|
|
206
|
-
}
|
|
207
|
-
const DEFAULT_GROUPED_PARAMS = ['grid_filters', 'filters'];
|
|
208
|
-
const groupedDetection = detectGroupedSerialization(urlToProcess, [config.groupedParam, ...DEFAULT_GROUPED_PARAMS]);
|
|
209
|
-
if (groupedDetection.isGrouped &&
|
|
210
|
-
groupedDetection.value &&
|
|
211
|
-
groupedDetection.format) {
|
|
212
|
-
return deserializeGrouped(groupedDetection.value, groupedDetection.format, config);
|
|
213
|
-
}
|
|
214
|
-
const urlObj = new URL(urlToProcess);
|
|
215
|
-
const filterState = {};
|
|
216
|
-
for (const [param, value] of urlObj.searchParams.entries()) {
|
|
217
|
-
if (!param.startsWith(config.paramPrefix))
|
|
218
|
-
continue;
|
|
219
|
-
try {
|
|
220
|
-
const { column: columnName } = extractColumnAndOperation(param, config.paramPrefix);
|
|
221
|
-
const filterParam = parseFilterParam(param, value, config.paramPrefix);
|
|
222
|
-
if (filterParam.filterType === 'text') {
|
|
223
|
-
const validatedValue = validateFilterValue(value, config, filterParam.type);
|
|
224
|
-
filterState[columnName] = {
|
|
225
|
-
...filterParam,
|
|
226
|
-
filter: validatedValue
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
else if (filterParam.filterType === 'date') {
|
|
230
|
-
filterState[columnName] = filterParam;
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
filterState[columnName] = filterParam;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch (error) {
|
|
237
|
-
if (config.onParseError) {
|
|
238
|
-
config.onParseError(error);
|
|
239
|
-
}
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return filterState;
|
|
29
|
+
export function parseUrlFilters(url, config = {}) {
|
|
30
|
+
if (!url || url.trim() === '') {
|
|
31
|
+
return {};
|
|
244
32
|
}
|
|
245
|
-
|
|
246
|
-
|
|
33
|
+
const urlObj = toUrl(url.trim());
|
|
34
|
+
const options = resolveOptions(config);
|
|
35
|
+
const groupedValue = urlObj.searchParams.get(options.groupedParam);
|
|
36
|
+
if (groupedValue) {
|
|
37
|
+
return deserializeGrouped(groupedValue, options.format, options);
|
|
247
38
|
}
|
|
39
|
+
return parseIndividualParams(urlObj.searchParams, options);
|
|
248
40
|
}
|
|
249
41
|
//# sourceMappingURL=url-parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-parser.js","sourceRoot":"","sources":["../../src/core/url-parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"url-parser.js","sourceRoot":"","sources":["../../src/core/url-parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAM3B,MAAM,UAAU,GAAG,kBAAkB,CAAA;AAGrC,MAAM,UAAU,GAAG,qBAAqB,CAAA;AAcxC,MAAM,eAAe,GACnB,oEAAoE,CAAA;AAqBtE,SAAS,KAAK,CAAC,KAAa;IAC1B,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,eAAe,CACvB,yBAAyB,KAAK,8BAA8B;YAC1D,+DAA+D,CAClE,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;QACvB,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;QACnC,CAAC;QAID,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAA;QAC/B,GAAG,CAAC,MAAM,GAAG,KAAK,CAAA;QAElB,OAAO,GAAG,CAAA;IACZ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,eAAe,CACvB,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAClF,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC3C,CAAA;IACH,CAAC;AACH,CAAC;AAmBD,MAAM,UAAU,eAAe,CAC7B,GAAW,EACX,MAAM,GAAwB,EAAE;IAGhC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;IAChC,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;IAMtC,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAElE,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,kBAAkB,CAAC,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClE,CAAC;IAGD,OAAO,qBAAqB,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;AAC5D,CAAC"}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export declare const DEFAULT_CONFIG: {
|
|
6
|
-
readonly paramPrefix: 'f_';
|
|
7
|
-
readonly maxValueLength: 200;
|
|
8
|
-
readonly serialization: 'individual';
|
|
9
|
-
readonly groupedParam: 'grid_filters';
|
|
10
|
-
readonly format: 'querystring';
|
|
11
|
-
readonly onParseError: () => void;
|
|
12
|
-
};
|
|
1
|
+
import type { AGGridUrlSyncConfig, FilterOperation, DateFilterOperation, ColumnFilter } from './types.js';
|
|
2
|
+
import { InvalidFilterError } from './types.js';
|
|
3
|
+
export { DEFAULT_CONFIG } from './options.js';
|
|
13
4
|
/**
|
|
14
5
|
* Validation result interface
|
|
15
6
|
*/
|
|
@@ -23,12 +14,13 @@ export type ValidationResult = {
|
|
|
23
14
|
* Validates a text filter value against configuration constraints
|
|
24
15
|
*
|
|
25
16
|
* @param value - The filter value to validate
|
|
26
|
-
* @param config -
|
|
17
|
+
* @param config - Options with the validation rules. An option that is left
|
|
18
|
+
* out gets its default.
|
|
27
19
|
* @param operation - Optional filter operation type. Blank operations (blank/notBlank) bypass value validation
|
|
28
20
|
* @returns The validated filter value (empty string for blank operations)
|
|
29
21
|
* @throws InvalidFilterError if value exceeds maximum length
|
|
30
22
|
*/
|
|
31
|
-
export declare function validateFilterValue(value: string, config
|
|
23
|
+
export declare function validateFilterValue(value: string, config?: AGGridUrlSyncConfig, operation?: FilterOperation): string;
|
|
32
24
|
/**
|
|
33
25
|
* Validates a number filter value
|
|
34
26
|
*
|
|
@@ -44,6 +36,21 @@ export declare function validateNumberFilter(value: number): ValidationResult;
|
|
|
44
36
|
* @returns Validation result
|
|
45
37
|
*/
|
|
46
38
|
export declare function validateNumberRange(min: number, max: number): ValidationResult;
|
|
39
|
+
/**
|
|
40
|
+
* Reads the value as a number, or gives the error that says why it is not
|
|
41
|
+
* one. The one rule for every number in a URL or a grid input (#238): the
|
|
42
|
+
* whole value must be a number, it must be finite, and it must not be rounded
|
|
43
|
+
* when it is stored as a double.
|
|
44
|
+
*
|
|
45
|
+
* The form does not matter: "1e21", "1E+21" and "1000000000000000000000" are
|
|
46
|
+
* all 1e21, and "1.50" and "007" are 1.5 and 7.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readNumber(value: string): number | InvalidFilterError;
|
|
49
|
+
/**
|
|
50
|
+
* The number in the value, or null when readNumber gives an error. The type
|
|
51
|
+
* guess for shared operations uses this, so such a value stays text.
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseExactNumber(value: string): number | null;
|
|
47
54
|
/**
|
|
48
55
|
* Validates a parsed number from string input
|
|
49
56
|
*
|