@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 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":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,OAAO,MAAM,KAAgF,CAAC,CAAA;AAExH,eAAO,MAAM,sBAAsB,GAAI,iBAAiB,MAAM,KAKvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAC5D,CAAA"}
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) => JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(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,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,CAAU,CAAC,CAAA;IAEvD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAA4B,CAAA;AAC9D,CAAC,CAAA"}
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":"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"}
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.39",
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.1",
42
- "@furystack/inject": "^12.0.30"
41
+ "@furystack/core": "^15.2.3",
42
+ "@furystack/inject": "^12.0.31"
43
43
  },
44
44
  "devDependencies": {
45
- "@types/node": "^25.3.0",
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) => JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(value))))) as T
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()]