@furystack/rest 8.0.39 → 8.0.41
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/CHANGELOG.md +22 -0
- package/esm/deserialize-query-string.d.ts.map +1 -1
- package/esm/deserialize-query-string.js +9 -1
- package/esm/deserialize-query-string.js.map +1 -1
- package/esm/deserialize-query-string.spec.js +45 -1
- package/esm/deserialize-query-string.spec.js.map +1 -1
- package/package.json +4 -4
- package/src/deserialize-query-string.spec.ts +45 -1
- package/src/deserialize-query-string.ts +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [8.0.41] - 2026-03-03
|
|
4
|
+
|
|
5
|
+
### ⬆️ Dependencies
|
|
6
|
+
|
|
7
|
+
- Updated `@furystack/utils` with EventHub listener error handling
|
|
8
|
+
|
|
9
|
+
### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- `decode()` now throws a `RequestError` with status 400 when query parameter values contain invalid base64, invalid percent-encoding, or invalid JSON, instead of letting raw errors propagate
|
|
12
|
+
|
|
13
|
+
### 🧪 Tests
|
|
14
|
+
|
|
15
|
+
- Added unit tests for `decode()` error handling covering invalid base64, invalid percent-encoding, and invalid JSON inputs
|
|
16
|
+
- Added test for `deserializeQueryString()` rejecting malformed query parameter values
|
|
17
|
+
|
|
18
|
+
## [8.0.40] - 2026-02-26
|
|
19
|
+
|
|
20
|
+
### ⬆️ Dependencies
|
|
21
|
+
|
|
22
|
+
- Updated internal `@furystack/*` dependencies
|
|
23
|
+
- Bumped `@types/node` from ^25.3.0 to ^25.3.1
|
|
24
|
+
|
|
3
25
|
## [8.0.39] - 2026-02-26
|
|
4
26
|
|
|
5
27
|
### ⬆️ Dependencies
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deserialize-query-string.d.ts","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deserialize-query-string.d.ts","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,OAAO,MAAM,KAAG,CAMzC,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,iBAAiB,MAAM,KAKvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAC5D,CAAA"}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { RequestError } from './request-error.js';
|
|
1
2
|
/**
|
|
2
3
|
* Decodes a URL-safe base64 encoded JSON string back to its original value
|
|
3
4
|
* Decoding steps: See the encoding steps in reverse order
|
|
4
5
|
* @param value The value to decode
|
|
5
6
|
* @returns The decoded value
|
|
6
7
|
*/
|
|
7
|
-
export const decode = (value) =>
|
|
8
|
+
export const decode = (value) => {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(value)))));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new RequestError('Failed to decode query parameter value', 400);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
8
16
|
export const deserializeQueryString = (fullQueryString) => {
|
|
9
17
|
const params = [...new URLSearchParams(fullQueryString).entries()]
|
|
10
18
|
.filter(([key, value]) => key && value)
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"deserialize-query-string.js","sourceRoot":"","sources":["../src/deserialize-query-string.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAI,KAAa,EAAK,EAAE;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAM,CAAA;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAA;IACvE,CAAC;AACH,CAAC,CAAA;AAED,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,CAAU,CAAC,CAAA;IAEvD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAA4B,CAAA;AAC9D,CAAC,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { deserializeQueryString } from './deserialize-query-string.js';
|
|
3
|
+
import { decode, deserializeQueryString } from './deserialize-query-string.js';
|
|
4
|
+
import { RequestError } from './request-error.js';
|
|
4
5
|
import { serializeToQueryString, serializeValue } from './serialize-to-query-string.js';
|
|
5
6
|
describe('deserializeQueryString', () => {
|
|
6
7
|
it('Should deserialize a null value', () => {
|
|
@@ -52,5 +53,48 @@ describe('deserializeQueryString', () => {
|
|
|
52
53
|
it('Should deserialize escaped values', () => {
|
|
53
54
|
expect(deserializeQueryString(`?alma=${serializeValue('asd/*-@?')}`)).toEqual({ alma: 'asd/*-@?' });
|
|
54
55
|
});
|
|
56
|
+
describe('decode error handling', () => {
|
|
57
|
+
it('Should throw a RequestError with 400 for invalid base64 input', () => {
|
|
58
|
+
expect(() => decode('not-valid-base64!!!')).toThrowError(RequestError);
|
|
59
|
+
try {
|
|
60
|
+
decode('not-valid-base64!!!');
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
expect(e).toBeInstanceOf(RequestError);
|
|
64
|
+
expect(e.responseCode).toBe(400);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
it('Should throw a RequestError with 400 for invalid percent-encoding', () => {
|
|
68
|
+
expect(() => decode('%ZZ')).toThrowError(RequestError);
|
|
69
|
+
try {
|
|
70
|
+
decode('%ZZ');
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
expect(e).toBeInstanceOf(RequestError);
|
|
74
|
+
expect(e.responseCode).toBe(400);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
it('Should throw a RequestError with 400 for invalid JSON after decoding', () => {
|
|
78
|
+
const invalidJsonBase64 = btoa('not-json');
|
|
79
|
+
expect(() => decode(invalidJsonBase64)).toThrowError(RequestError);
|
|
80
|
+
try {
|
|
81
|
+
decode(invalidJsonBase64);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
expect(e).toBeInstanceOf(RequestError);
|
|
85
|
+
expect(e.responseCode).toBe(400);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
it('Should throw a RequestError with 400 for malformed query parameter values', () => {
|
|
90
|
+
expect(() => deserializeQueryString('?key=not-valid-base64!!!')).toThrowError(RequestError);
|
|
91
|
+
try {
|
|
92
|
+
deserializeQueryString('?key=not-valid-base64!!!');
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
expect(e).toBeInstanceOf(RequestError);
|
|
96
|
+
expect(e.responseCode).toBe(400);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
55
99
|
});
|
|
56
100
|
//# sourceMappingURL=deserialize-query-string.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deserialize-query-string.spec.js","sourceRoot":"","sources":["../src/deserialize-query-string.spec.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;
|
|
1
|
+
{"version":3,"file":"deserialize-query-string.spec.js","sourceRoot":"","sources":["../src/deserialize-query-string.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furystack/rest",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.41",
|
|
4
4
|
"description": "Generic REST package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/furystack/furystack",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@furystack/core": "^15.2.
|
|
42
|
-
"@furystack/inject": "^12.0.
|
|
41
|
+
"@furystack/core": "^15.2.3",
|
|
42
|
+
"@furystack/inject": "^12.0.31"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@types/node": "^25.3.
|
|
45
|
+
"@types/node": "^25.3.1",
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
47
|
"vitest": "^4.0.18"
|
|
48
48
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
2
|
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import { deserializeQueryString } from './deserialize-query-string.js'
|
|
3
|
+
import { decode, deserializeQueryString } from './deserialize-query-string.js'
|
|
4
|
+
import { RequestError } from './request-error.js'
|
|
4
5
|
import { serializeToQueryString, serializeValue } from './serialize-to-query-string.js'
|
|
5
6
|
|
|
6
7
|
describe('deserializeQueryString', () => {
|
|
@@ -73,4 +74,47 @@ describe('deserializeQueryString', () => {
|
|
|
73
74
|
it('Should deserialize escaped values', () => {
|
|
74
75
|
expect(deserializeQueryString(`?alma=${serializeValue('asd/*-@?')}`)).toEqual({ alma: 'asd/*-@?' })
|
|
75
76
|
})
|
|
77
|
+
|
|
78
|
+
describe('decode error handling', () => {
|
|
79
|
+
it('Should throw a RequestError with 400 for invalid base64 input', () => {
|
|
80
|
+
expect(() => decode('not-valid-base64!!!')).toThrowError(RequestError)
|
|
81
|
+
try {
|
|
82
|
+
decode('not-valid-base64!!!')
|
|
83
|
+
} catch (e) {
|
|
84
|
+
expect(e).toBeInstanceOf(RequestError)
|
|
85
|
+
expect((e as RequestError).responseCode).toBe(400)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('Should throw a RequestError with 400 for invalid percent-encoding', () => {
|
|
90
|
+
expect(() => decode('%ZZ')).toThrowError(RequestError)
|
|
91
|
+
try {
|
|
92
|
+
decode('%ZZ')
|
|
93
|
+
} catch (e) {
|
|
94
|
+
expect(e).toBeInstanceOf(RequestError)
|
|
95
|
+
expect((e as RequestError).responseCode).toBe(400)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('Should throw a RequestError with 400 for invalid JSON after decoding', () => {
|
|
100
|
+
const invalidJsonBase64 = btoa('not-json')
|
|
101
|
+
expect(() => decode(invalidJsonBase64)).toThrowError(RequestError)
|
|
102
|
+
try {
|
|
103
|
+
decode(invalidJsonBase64)
|
|
104
|
+
} catch (e) {
|
|
105
|
+
expect(e).toBeInstanceOf(RequestError)
|
|
106
|
+
expect((e as RequestError).responseCode).toBe(400)
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('Should throw a RequestError with 400 for malformed query parameter values', () => {
|
|
112
|
+
expect(() => deserializeQueryString('?key=not-valid-base64!!!')).toThrowError(RequestError)
|
|
113
|
+
try {
|
|
114
|
+
deserializeQueryString('?key=not-valid-base64!!!')
|
|
115
|
+
} catch (e) {
|
|
116
|
+
expect(e).toBeInstanceOf(RequestError)
|
|
117
|
+
expect((e as RequestError).responseCode).toBe(400)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
76
120
|
})
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { RequestError } from './request-error.js'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Decodes a URL-safe base64 encoded JSON string back to its original value
|
|
3
5
|
* Decoding steps: See the encoding steps in reverse order
|
|
4
6
|
* @param value The value to decode
|
|
5
7
|
* @returns The decoded value
|
|
6
8
|
*/
|
|
7
|
-
export const decode = <T>(value: string) =>
|
|
9
|
+
export const decode = <T>(value: string): T => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(value))))) as T
|
|
12
|
+
} catch {
|
|
13
|
+
throw new RequestError('Failed to decode query parameter value', 400)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
8
16
|
|
|
9
17
|
export const deserializeQueryString = (fullQueryString: string) => {
|
|
10
18
|
const params = [...new URLSearchParams(fullQueryString).entries()]
|