nodester 0.6.74 → 0.6.76

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.
@@ -2,7 +2,7 @@
2
2
  * nodester
3
3
  * MIT Licensed
4
4
  */
5
-
5
+
6
6
  'use strict';
7
7
 
8
8
  const ENCODES = require('./encodes');
@@ -12,49 +12,60 @@ module.exports = {
12
12
  decodeQueryString: _decodeQueryString
13
13
  }
14
14
 
15
- function _decodeQueryString(queryString='') {
15
+ function _decodeQueryString(queryString = '') {
16
16
  let decoded = '';
17
+ let i = 0;
17
18
 
18
- // Token is a String, accumulated char-by-char.
19
- let token = '';
20
-
21
- let isEcodedChar = false;
22
- let encodedCharsCount = 0;
23
-
24
- for (let i=0; i < queryString.length; i++) {
19
+ while (i < queryString.length) {
25
20
  const char = queryString[i];
26
21
 
27
22
  if (char === '%') {
28
- isEcodedChar = true;
29
- decoded += token;
30
- token = '';
31
- }
32
-
33
- // Continue accumulating token.
34
- token += char;
35
-
36
- if (isEcodedChar) {
37
- encodedCharsCount++;
38
-
39
- if (encodedCharsCount === 3) {
40
- const decodedChar = ENCODES[token];
41
-
42
- if (!decodedChar) {
43
- const err = new Error(`Uknown token '${ token }' at index ${ i }.`);
44
- throw err;
23
+ // Collect all consecutive %XX tokens for multi-byte UTF-8 sequences
24
+ const percentTokens = [];
25
+ let j = i;
26
+
27
+ // Collect consecutive %XX patterns:
28
+ while (j < queryString.length && queryString[j] === '%' && j + 2 < queryString.length) {
29
+ const token = queryString.substring(j, j + 3);
30
+ percentTokens.push(token);
31
+ j += 3;
32
+ }
33
+
34
+ // Try single-byte dictionary lookup first (optimization for ASCII):
35
+ if (percentTokens.length === 1) {
36
+ const decodedChar = ENCODES[percentTokens[0]];
37
+ if (decodedChar) {
38
+ decoded += decodedChar;
39
+ i = j;
40
+ continue;
45
41
  }
42
+ }
43
+
44
+ // Multi-byte sequence or unknown single byte - decode as UTF-8:
45
+ try {
46
+ const hexBytes = percentTokens.map(token => {
47
+ // Remove '%'.
48
+ const hex = token.substring(1);
49
+ return parseInt(hex, 16);
50
+ });
46
51
 
47
- decoded += decodedChar;
48
- // Reset:
49
- token = '';
50
- isEcodedChar = false;
51
- encodedCharsCount = 0;
52
+ const buffer = Buffer.from(hexBytes);
53
+ const decodedString = buffer.toString('utf8');
54
+ decoded += decodedString;
55
+ i = j;
52
56
  }
57
+ catch (error) {
58
+ const err = new Error(`Failed to decode UTF-8 sequence at index ${i}: ${percentTokens.join('')}`);
59
+ err.originalError = error;
60
+ throw err;
61
+ }
62
+ }
63
+ else {
64
+ // Regular character, just append.
65
+ decoded += char;
66
+ i++;
53
67
  }
54
68
  }
55
69
 
56
- // Last chunk.
57
- decoded += token;
58
-
59
70
  return decoded;
60
71
  }
@@ -2,7 +2,7 @@
2
2
  * nodester
3
3
  * MIT Licensed
4
4
  */
5
-
5
+
6
6
  'use strict';
7
7
 
8
8
  const {
@@ -41,7 +41,20 @@ function Params(
41
41
 
42
42
  // If String:
43
43
  if (typeof defaultValuesList[key] === 'string') {
44
- result[key] = `${ sourceObj[key] }`;
44
+ result[key] = `${sourceObj[key]}`;
45
+ continue;
46
+ }
47
+
48
+
49
+ // If Array:
50
+ if (Array.isArray(defaultValuesList[key])) {
51
+ if (Array.isArray(sourceObj[key])) {
52
+ result[key] = sourceObj[key];
53
+ }
54
+ else {
55
+ // Type mismatch!
56
+ result[key] = defaultValuesList[key];
57
+ }
45
58
  continue;
46
59
  }
47
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodester",
3
- "version": "0.6.74",
3
+ "version": "0.6.76",
4
4
  "description": "A versatile REST framework for Node.js",
5
5
  "directories": {
6
6
  "docs": "docs",
@@ -130,4 +130,4 @@
130
130
  "app",
131
131
  "api"
132
132
  ]
133
- }
133
+ }
@@ -0,0 +1,48 @@
1
+ // Test utils.
2
+ const {
3
+ describe,
4
+ it,
5
+ expect,
6
+ test
7
+ } = require('@jest/globals');
8
+
9
+ const { decodeQueryString } = require('../lib/middlewares/ql/sequelize/decoder');
10
+
11
+ describe('decoding', () => {
12
+ describe('utf8mb4', () => {
13
+ const queries = {
14
+ cyrillic: encodeURIComponent(`name=like(книга)`),
15
+ diacritics: encodeURIComponent(`salad=like(šopska)`),
16
+ chinese: encodeURIComponent(`name=like(中)`),
17
+ emoji: encodeURIComponent(`emoji=like(🌸👍)`),
18
+ };
19
+
20
+ test('cyrillic', async () => {
21
+ const decoded = decodeQueryString(queries.cyrillic);
22
+ const expected = 'name=like(книга)';
23
+
24
+ expect(decoded).toBe(expected);
25
+ });
26
+
27
+ test('diacritics', async () => {
28
+ const decoded = decodeQueryString(queries.diacritics);
29
+ const expected = 'salad=like(šopska)';
30
+
31
+ expect(decoded).toBe(expected);
32
+ });
33
+
34
+ test('chinese', async () => {
35
+ const decoded = decodeQueryString(queries.chinese);
36
+ const expected = 'name=like(中)';
37
+
38
+ expect(decoded).toBe(expected);
39
+ });
40
+
41
+ test('emoji', async () => {
42
+ const decoded = decodeQueryString(queries.emoji);
43
+ const expected = 'emoji=like(🌸👍)';
44
+
45
+ expect(decoded).toBe(expected);
46
+ });
47
+ });
48
+ });