@furystack/rest 5.0.9 → 5.0.10
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/esm/deserialize-query-string.d.ts +7 -1
- package/esm/deserialize-query-string.d.ts.map +1 -1
- package/esm/deserialize-query-string.js +11 -40
- package/esm/deserialize-query-string.js.map +1 -1
- package/esm/serialize-to-query-string.d.ts +12 -2
- package/esm/serialize-to-query-string.d.ts.map +1 -1
- package/esm/serialize-to-query-string.js +14 -15
- package/esm/serialize-to-query-string.js.map +1 -1
- package/package.json +1 -1
- package/src/deserialize-query-string.spec.ts +23 -15
- package/src/deserialize-query-string.ts +11 -46
- package/src/serialize-to-query-string.spec.ts +10 -6
- package/src/serialize-to-query-string.ts +20 -16
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Decoding steps: See the encoding steps in reverse order
|
|
4
|
+
* @param value The value to decode
|
|
5
|
+
* @returns The decoded value
|
|
6
|
+
*/
|
|
7
|
+
export declare const decode: <T>(value: string) => T;
|
|
2
8
|
export declare const deserializeQueryString: (fullQueryString: string) => any;
|
|
3
9
|
//# sourceMappingURL=deserialize-query-string.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deserialize-query-string.d.ts","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"deserialize-query-string.d.ts","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,MAAM,aAAc,MAAM,MAAiF,CAAA;AAExH,eAAO,MAAM,sBAAsB,oBAAqB,MAAM,QAM7D,CAAA"}
|
|
@@ -1,43 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
try {
|
|
11
|
-
return decodeURIComponent(queryParam.toString());
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return queryParam;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Decoding steps: See the encoding steps in reverse order
|
|
4
|
+
* @param value The value to decode
|
|
5
|
+
* @returns The decoded value
|
|
6
|
+
*/
|
|
7
|
+
export const decode = (value) => JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(value)))));
|
|
19
8
|
export const deserializeQueryString = (fullQueryString) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const entries = queryString
|
|
25
|
-
.split('&')
|
|
26
|
-
.map((value) => value.split('='))
|
|
27
|
-
.filter(([key, value]) => key?.length && value?.length); // filter out empty keys
|
|
28
|
-
const dedupedValues = entries
|
|
29
|
-
.reduce((prev, current) => {
|
|
30
|
-
const currentKey = current[0];
|
|
31
|
-
const currentValue = tryDecodeQueryParam(current[1]);
|
|
32
|
-
const existing = prev.find(([key]) => key === currentKey);
|
|
33
|
-
if (existing) {
|
|
34
|
-
existing[1] instanceof Array ? existing[1].push(currentValue) : (existing[1] = currentValue);
|
|
35
|
-
return [...prev];
|
|
36
|
-
}
|
|
37
|
-
const newValue = [currentKey, currentKey.includes('[]') ? [currentValue] : currentValue];
|
|
38
|
-
return [...prev, newValue];
|
|
39
|
-
}, [])
|
|
40
|
-
.map(([key, value]) => [key.replace('[]', ''), value]);
|
|
41
|
-
return Object.fromEntries(dedupedValues);
|
|
9
|
+
const params = [...new URLSearchParams(fullQueryString).entries()]
|
|
10
|
+
.filter(([key, value]) => key && value)
|
|
11
|
+
.map(([key, value]) => [key, decode(value)]);
|
|
12
|
+
return Object.fromEntries(params);
|
|
42
13
|
};
|
|
43
14
|
//# sourceMappingURL=deserialize-query-string.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deserialize-query-string.js","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"deserialize-query-string.js","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAI,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAM,CAAA;AAExH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,eAAuB,EAAE,EAAE;IAChE,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,eAAe,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/D,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAE9C,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;AACnC,CAAC,CAAA"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Serialize steps:
|
|
3
|
+
* 1. Stringify the value (even primitives, ensure type safety), e.g.: { foo: 'bar😉' } => '{"foo":"bar😉"}'
|
|
4
|
+
* 2. Encode as an URI Component, e.g.: ''{"foo":"bar😉"}'' => '%7B%22foo%22%3A%22bar%F0%9F%98%89%22%7D'
|
|
5
|
+
* 3. Unescape the URI Component, e.g.: '%7B%22foo%22%3A%22bar%F0%9F%98%89%22%7D' => '{"foo":"barð\x9F\x98\x89"}' - This and the first encodeURIComponent is needed because btoa only supports ASCII characters. We also don't want to encode the whole JSON string to keep a reasonable string length
|
|
6
|
+
* 4. Encode the string as base64, e.g.: '{"foo":"barð\x9F\x98\x89"}' => 'eyJmb28iOiJiYXLwn5iJIn0='
|
|
7
|
+
* 5. Encode as an URL Param: 'eyJmb28iOiJiYXLwn5iJIn0=' => 'eyJmb28iOiJiYXLwn5iJIn0%3D'
|
|
8
|
+
* @param value The value to encode
|
|
9
|
+
* @returns The encoded value that can be used as an URL search parameter
|
|
10
|
+
*/
|
|
11
|
+
export declare const serializeValue: (value: any) => string;
|
|
12
|
+
export declare const serializeToQueryString: <T extends object>(queryObject: T) => string;
|
|
3
13
|
//# sourceMappingURL=serialize-to-query-string.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize-to-query-string.d.ts","sourceRoot":"","sources":["../src/serialize-to-query-string.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"serialize-to-query-string.d.ts","sourceRoot":"","sources":["../src/serialize-to-query-string.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,UAAW,GAAG,WACsC,CAAA;AAE/E,eAAO,MAAM,sBAAsB,wCAAuC,MAQzE,CAAA"}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export const serializeToQueryString = (
|
|
13
|
-
return Object.entries(
|
|
1
|
+
/**
|
|
2
|
+
* Serialize steps:
|
|
3
|
+
* 1. Stringify the value (even primitives, ensure type safety), e.g.: { foo: 'bar😉' } => '{"foo":"bar😉"}'
|
|
4
|
+
* 2. Encode as an URI Component, e.g.: ''{"foo":"bar😉"}'' => '%7B%22foo%22%3A%22bar%F0%9F%98%89%22%7D'
|
|
5
|
+
* 3. Unescape the URI Component, e.g.: '%7B%22foo%22%3A%22bar%F0%9F%98%89%22%7D' => '{"foo":"barð\x9F\x98\x89"}' - This and the first encodeURIComponent is needed because btoa only supports ASCII characters. We also don't want to encode the whole JSON string to keep a reasonable string length
|
|
6
|
+
* 4. Encode the string as base64, e.g.: '{"foo":"barð\x9F\x98\x89"}' => 'eyJmb28iOiJiYXLwn5iJIn0='
|
|
7
|
+
* 5. Encode as an URL Param: 'eyJmb28iOiJiYXLwn5iJIn0=' => 'eyJmb28iOiJiYXLwn5iJIn0%3D'
|
|
8
|
+
* @param value The value to encode
|
|
9
|
+
* @returns The encoded value that can be used as an URL search parameter
|
|
10
|
+
*/
|
|
11
|
+
export const serializeValue = (value) => encodeURIComponent(btoa(unescape(encodeURIComponent(JSON.stringify(value)))));
|
|
12
|
+
export const serializeToQueryString = (queryObject) => {
|
|
13
|
+
return new URLSearchParams(Object.fromEntries(Object.entries(queryObject)
|
|
14
14
|
.filter(([, value]) => value !== undefined)
|
|
15
|
-
.map(serializeValue)
|
|
16
|
-
.join('&');
|
|
15
|
+
.map(([key, value]) => [key, serializeValue(value)]))).toString();
|
|
17
16
|
};
|
|
18
17
|
//# sourceMappingURL=serialize-to-query-string.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize-to-query-string.js","sourceRoot":"","sources":["../src/serialize-to-query-string.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"serialize-to-query-string.js","sourceRoot":"","sources":["../src/serialize-to-query-string.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAU,EAAE,EAAE,CAC3C,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAmB,WAAc,EAAU,EAAE;IACjF,OAAO,IAAI,eAAe,CACxB,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CACvD,CACF,CAAC,QAAQ,EAAE,CAAA;AACd,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,42 +1,50 @@
|
|
|
1
1
|
import { deserializeQueryString } from './deserialize-query-string'
|
|
2
|
-
import { serializeToQueryString } from './serialize-to-query-string'
|
|
2
|
+
import { serializeToQueryString, serializeValue } from './serialize-to-query-string'
|
|
3
3
|
import { describe, it, expect } from 'vitest'
|
|
4
4
|
|
|
5
5
|
describe('deserializeQueryString', () => {
|
|
6
|
-
it('Should
|
|
6
|
+
it('Should deserialize a null value', () => {
|
|
7
7
|
expect(deserializeQueryString(null as any)).toEqual({})
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
it('Should
|
|
10
|
+
it('Should deserialize an undefined value', () => {
|
|
11
11
|
expect(deserializeQueryString(undefined as any)).toEqual({})
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
it('Should
|
|
14
|
+
it('Should deserialize an empty string value', () => {
|
|
15
15
|
expect(deserializeQueryString('')).toEqual({})
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
-
it('Should
|
|
18
|
+
it('Should deserialize a string value with no keys / values', () => {
|
|
19
19
|
expect(deserializeQueryString('?')).toEqual({})
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
it('Should
|
|
22
|
+
it('Should deserialize a string with given value but empty key', () => {
|
|
23
23
|
expect(deserializeQueryString('?=alma')).toEqual({})
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
it('Should
|
|
26
|
+
it('Should deserialize a string with given key but empty value', () => {
|
|
27
27
|
expect(deserializeQueryString('?alma=')).toEqual({})
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
it('Should
|
|
31
|
-
expect(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
it('Should deserialize a list of primitive values', () => {
|
|
31
|
+
expect(
|
|
32
|
+
deserializeQueryString(`?foo=${serializeValue('value')}&bar=${serializeValue(2)}&baz=${serializeValue(false)}`),
|
|
33
|
+
).toEqual({
|
|
34
|
+
foo: 'value',
|
|
35
|
+
bar: 2,
|
|
36
|
+
baz: false,
|
|
37
|
+
})
|
|
36
38
|
})
|
|
37
39
|
|
|
38
40
|
it('Should override a value if not specified as an array', () => {
|
|
39
|
-
expect(
|
|
41
|
+
expect(
|
|
42
|
+
deserializeQueryString(
|
|
43
|
+
`?foo=${serializeValue('value')}&foo=${serializeValue(2)}&foo=${serializeValue(false)}&foo=${serializeValue(
|
|
44
|
+
'bar',
|
|
45
|
+
)}`,
|
|
46
|
+
),
|
|
47
|
+
).toEqual({ foo: 'bar' })
|
|
40
48
|
})
|
|
41
49
|
|
|
42
50
|
it('Should serialize and deserialize an object with primitives', () => {
|
|
@@ -60,6 +68,6 @@ describe('deserializeQueryString', () => {
|
|
|
60
68
|
})
|
|
61
69
|
|
|
62
70
|
it('Should deserialize escaped values', () => {
|
|
63
|
-
expect(deserializeQueryString('
|
|
71
|
+
expect(deserializeQueryString(`?alma=${serializeValue('asd/*-@?')}`)).toEqual({ alma: 'asd/*-@?' })
|
|
64
72
|
})
|
|
65
73
|
})
|
|
@@ -1,50 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
return decodeURIComponent(queryParam.toString())
|
|
10
|
-
} catch {
|
|
11
|
-
return queryParam
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Decoding steps: See the encoding steps in reverse order
|
|
4
|
+
* @param value The value to decode
|
|
5
|
+
* @returns The decoded value
|
|
6
|
+
*/
|
|
7
|
+
export const decode = <T>(value: string) => JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(value))))) as T
|
|
16
8
|
|
|
17
9
|
export const deserializeQueryString = (fullQueryString: string) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return {}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const entries = queryString
|
|
25
|
-
.split('&')
|
|
26
|
-
.map((value) => value.split('='))
|
|
27
|
-
.filter(([key, value]) => key?.length && value?.length) // filter out empty keys
|
|
28
|
-
|
|
29
|
-
const dedupedValues = entries
|
|
30
|
-
.reduce(
|
|
31
|
-
(prev, current) => {
|
|
32
|
-
const currentKey = current[0]
|
|
33
|
-
const currentValue = tryDecodeQueryParam(current[1])
|
|
34
|
-
const existing = prev.find(([key]) => key === currentKey)
|
|
35
|
-
if (existing) {
|
|
36
|
-
existing[1] instanceof Array ? existing[1].push(currentValue) : (existing[1] = currentValue)
|
|
37
|
-
return [...prev]
|
|
38
|
-
}
|
|
39
|
-
const newValue = [currentKey, currentKey.includes('[]') ? [currentValue] : currentValue] as [
|
|
40
|
-
string,
|
|
41
|
-
string | string[],
|
|
42
|
-
]
|
|
43
|
-
return [...prev, newValue]
|
|
44
|
-
},
|
|
45
|
-
[] as Array<[string, string | string[]]>,
|
|
46
|
-
)
|
|
47
|
-
.map(([key, value]) => [key.replace('[]', ''), value])
|
|
10
|
+
const params = [...new URLSearchParams(fullQueryString).entries()]
|
|
11
|
+
.filter(([key, value]) => key && value)
|
|
12
|
+
.map(([key, value]) => [key, decode(value)])
|
|
48
13
|
|
|
49
|
-
return Object.fromEntries(
|
|
14
|
+
return Object.fromEntries(params)
|
|
50
15
|
}
|
|
@@ -3,25 +3,29 @@ import { describe, it, expect } from 'vitest'
|
|
|
3
3
|
|
|
4
4
|
describe('serializeToQueryString', () => {
|
|
5
5
|
it('Should serialize primitive values', () => {
|
|
6
|
-
expect(serializeToQueryString({ a: 1, b: false, c: 'foo', d: 0, e: null })).
|
|
6
|
+
expect(serializeToQueryString({ a: 1, b: false, c: 'foo', d: 0, e: null })).toMatchInlineSnapshot(
|
|
7
|
+
'"a=MQ%253D%253D&b=ZmFsc2U%253D&c=ImZvbyI%253D&d=MA%253D%253D&e=bnVsbA%253D%253D"',
|
|
8
|
+
)
|
|
7
9
|
})
|
|
8
10
|
|
|
9
11
|
it('Should exclude explicit undefined', () => {
|
|
10
|
-
expect(serializeToQueryString({ a: 1, b: false, c: 'foo', d: undefined })).
|
|
12
|
+
expect(serializeToQueryString({ a: 1, b: false, c: 'foo', d: undefined })).toMatchInlineSnapshot(
|
|
13
|
+
'"a=MQ%253D%253D&b=ZmFsc2U%253D&c=ImZvbyI%253D"',
|
|
14
|
+
)
|
|
11
15
|
})
|
|
12
16
|
|
|
13
17
|
it('Should serialize primitive arrays', () => {
|
|
14
|
-
expect(serializeToQueryString({ array: [1, 2, 3, 4] })).
|
|
18
|
+
expect(serializeToQueryString({ array: [1, 2, 3, 4] })).toMatchInlineSnapshot('"array=WzEsMiwzLDRd"')
|
|
15
19
|
})
|
|
16
20
|
|
|
17
21
|
it('Should serialize objects', () => {
|
|
18
|
-
expect(serializeToQueryString({ foo: { a: 1, b: 'value' } })).
|
|
19
|
-
|
|
22
|
+
expect(serializeToQueryString({ foo: { a: 1, b: 'value' } })).toMatchInlineSnapshot(
|
|
23
|
+
'"foo=eyJhIjoxLCJiIjoidmFsdWUifQ%253D%253D"',
|
|
20
24
|
)
|
|
21
25
|
})
|
|
22
26
|
|
|
23
27
|
it('Should serialize an array that contains a non-primitive entry', () => {
|
|
24
28
|
const array = [1, 2, 3, { foo: 1 }]
|
|
25
|
-
expect(serializeToQueryString({ array })).
|
|
29
|
+
expect(serializeToQueryString({ array })).toMatchInlineSnapshot('"array=WzEsMiwzLHsiZm9vIjoxfV0%253D"')
|
|
26
30
|
})
|
|
27
31
|
})
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Serialize steps:
|
|
3
|
+
* 1. Stringify the value (even primitives, ensure type safety), e.g.: { foo: 'bar😉' } => '{"foo":"bar😉"}'
|
|
4
|
+
* 2. Encode as an URI Component, e.g.: ''{"foo":"bar😉"}'' => '%7B%22foo%22%3A%22bar%F0%9F%98%89%22%7D'
|
|
5
|
+
* 3. Unescape the URI Component, e.g.: '%7B%22foo%22%3A%22bar%F0%9F%98%89%22%7D' => '{"foo":"barð\x9F\x98\x89"}' - This and the first encodeURIComponent is needed because btoa only supports ASCII characters. We also don't want to encode the whole JSON string to keep a reasonable string length
|
|
6
|
+
* 4. Encode the string as base64, e.g.: '{"foo":"barð\x9F\x98\x89"}' => 'eyJmb28iOiJiYXLwn5iJIn0='
|
|
7
|
+
* 5. Encode as an URL Param: 'eyJmb28iOiJiYXLwn5iJIn0=' => 'eyJmb28iOiJiYXLwn5iJIn0%3D'
|
|
8
|
+
* @param value The value to encode
|
|
9
|
+
* @returns The encoded value that can be used as an URL search parameter
|
|
10
|
+
*/
|
|
11
|
+
export const serializeValue = (value: any) =>
|
|
12
|
+
encodeURIComponent(btoa(unescape(encodeURIComponent(JSON.stringify(value)))))
|
|
12
13
|
|
|
13
|
-
export const serializeToQueryString = <T extends object>(
|
|
14
|
-
return
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
export const serializeToQueryString = <T extends object>(queryObject: T): string => {
|
|
15
|
+
return new URLSearchParams(
|
|
16
|
+
Object.fromEntries(
|
|
17
|
+
Object.entries(queryObject)
|
|
18
|
+
.filter(([, value]) => value !== undefined)
|
|
19
|
+
.map(([key, value]) => [key, serializeValue(value)]),
|
|
20
|
+
),
|
|
21
|
+
).toString()
|
|
18
22
|
}
|