@st-gr/sail-proxy 0.9.3 → 0.9.4
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/bundled/gateway/node_modules/.package-lock.json +17 -17
- package/bundled/gateway/node_modules/form-data/CHANGELOG.md +29 -2
- package/bundled/gateway/node_modules/form-data/README.md +4 -4
- package/bundled/gateway/node_modules/form-data/lib/form_data.js +14 -2
- package/bundled/gateway/node_modules/form-data/package.json +7 -7
- package/bundled/gateway/node_modules/qs/CHANGELOG.md +16 -0
- package/bundled/gateway/node_modules/qs/README.md +1 -1
- package/bundled/gateway/node_modules/qs/dist/qs.js +15 -15
- package/bundled/gateway/node_modules/qs/eslint.config.mjs +1 -0
- package/bundled/gateway/node_modules/qs/lib/parse.js +54 -24
- package/bundled/gateway/node_modules/qs/lib/stringify.js +11 -4
- package/bundled/gateway/node_modules/qs/package.json +3 -3
- package/bundled/gateway/node_modules/qs/test/parse.js +135 -0
- package/bundled/gateway/node_modules/qs/test/stringify.js +138 -0
- package/bundled/gateway/node_modules/qs/test/utils.js +31 -3
- package/bundled/gateway/node_modules/semver/classes/range.js +17 -4
- package/bundled/gateway/node_modules/semver/package.json +2 -2
- package/bundled/gateway/node_modules/side-channel/CHANGELOG.md +10 -0
- package/bundled/gateway/node_modules/side-channel/README.md +1 -1
- package/bundled/gateway/node_modules/side-channel/index.js +5 -2
- package/bundled/gateway/node_modules/side-channel/package.json +10 -10
- package/bundled/gateway/node_modules/side-channel/test/index.js +16 -0
- package/bundled/gateway/package-lock.json +22 -22
- package/bundled/gateway/package.json +13 -5
- package/bundled/gateway/services/gateway/src/controllers/anthropicController.js +1 -1
- package/bundled/gateway/services/gateway/src/controllers/awsBedrockController.js +5 -1
- package/bundled/gateway/services/gateway/src/index.js +14 -0
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/detectors/index.js +23 -1
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/detectors/regexDetectors.js +149 -10
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/entityToggles.d.ts +24 -0
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/entityToggles.js +54 -0
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/index.d.ts +2 -1
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/index.js +441 -92
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacementMap.d.ts +24 -0
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacementMap.js +93 -4
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacer.d.ts +22 -0
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacer.js +77 -0
- package/bundled/gateway/services/gateway/src/plugins/pseudonymization/streamBuffer.js +26 -8
- package/bundled/gateway/services/gateway/src/services/awsBedrockService.js +118 -46
- package/bundled/gateway/services/gateway/src/services/betaFlagQuarantine.d.ts +18 -0
- package/bundled/gateway/services/gateway/src/services/betaFlagQuarantine.js +111 -0
- package/bundled/gateway/services/gateway/src/services/configService.d.ts +8 -0
- package/bundled/gateway/services/gateway/src/services/configService.js +61 -53
- package/bundled/gateway/services/gateway/src/utils/bedrockStreamParser.d.ts +9 -0
- package/bundled/gateway/services/gateway/src/utils/bedrockStreamParser.js +69 -3
- package/bundled/gateway/services/gateway/src/utils/betaFeatureFilter.d.ts +31 -0
- package/bundled/gateway/services/gateway/src/utils/betaFeatureFilter.js +58 -0
- package/bundled/gateway/services/gateway/src/utils/payloadLogger.d.ts +6 -0
- package/bundled/gateway/services/gateway/src/utils/payloadLogger.js +11 -7
- package/bundled/gateway/services/gateway/src/utils/pluginCacheSelector.d.ts +15 -0
- package/bundled/gateway/services/gateway/src/utils/pluginCacheSelector.js +24 -0
- package/bundled/gateway/services/gateway/src/utils/sseWriter.js +4 -2
- package/bundled/gateway/services/gateway/src/utils/upstreamErrorBody.d.ts +10 -0
- package/bundled/gateway/services/gateway/src/utils/upstreamErrorBody.js +71 -0
- package/bundled/ollama/node_modules/.package-lock.json +14 -14
- package/bundled/ollama/node_modules/form-data/CHANGELOG.md +29 -2
- package/bundled/ollama/node_modules/form-data/README.md +4 -4
- package/bundled/ollama/node_modules/form-data/lib/form_data.js +14 -2
- package/bundled/ollama/node_modules/form-data/package.json +7 -7
- package/bundled/ollama/node_modules/qs/CHANGELOG.md +16 -0
- package/bundled/ollama/node_modules/qs/README.md +1 -1
- package/bundled/ollama/node_modules/qs/dist/qs.js +15 -15
- package/bundled/ollama/node_modules/qs/eslint.config.mjs +1 -0
- package/bundled/ollama/node_modules/qs/lib/parse.js +54 -24
- package/bundled/ollama/node_modules/qs/lib/stringify.js +11 -4
- package/bundled/ollama/node_modules/qs/package.json +3 -3
- package/bundled/ollama/node_modules/qs/test/parse.js +135 -0
- package/bundled/ollama/node_modules/qs/test/stringify.js +138 -0
- package/bundled/ollama/node_modules/qs/test/utils.js +31 -3
- package/bundled/ollama/node_modules/side-channel/CHANGELOG.md +10 -0
- package/bundled/ollama/node_modules/side-channel/README.md +1 -1
- package/bundled/ollama/node_modules/side-channel/index.js +5 -2
- package/bundled/ollama/node_modules/side-channel/package.json +10 -10
- package/bundled/ollama/node_modules/side-channel/test/index.js +16 -0
- package/bundled/ollama/package-lock.json +15 -15
- package/bundled/ollama/package.json +13 -5
- package/dist/templates/api_config.template.json +266 -18
- package/node_modules/@sap-llm-gateway/service-key-parser/package.json +1 -1
- package/package.json +2 -2
|
@@ -118,7 +118,7 @@ var stringify = function stringify(
|
|
|
118
118
|
|
|
119
119
|
if (obj === null) {
|
|
120
120
|
if (strictNullHandling) {
|
|
121
|
-
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
|
|
121
|
+
return formatter(encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
obj = '';
|
|
@@ -142,7 +142,9 @@ var stringify = function stringify(
|
|
|
142
142
|
if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
|
143
143
|
// we need to join elements in
|
|
144
144
|
if (encodeValuesOnly && encoder) {
|
|
145
|
-
obj = utils.maybeMap(obj,
|
|
145
|
+
obj = utils.maybeMap(obj, function (v) {
|
|
146
|
+
return v == null ? v : encoder(v);
|
|
147
|
+
});
|
|
146
148
|
}
|
|
147
149
|
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
|
|
148
150
|
} else if (isArray(filter)) {
|
|
@@ -312,6 +314,11 @@ module.exports = function (object, opts) {
|
|
|
312
314
|
var sideChannel = getSideChannel();
|
|
313
315
|
for (var i = 0; i < objKeys.length; ++i) {
|
|
314
316
|
var key = objKeys[i];
|
|
317
|
+
|
|
318
|
+
if (typeof key === 'undefined' || key === null) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
315
322
|
var value = obj[key];
|
|
316
323
|
|
|
317
324
|
if (options.skipNulls && value === null) {
|
|
@@ -345,10 +352,10 @@ module.exports = function (object, opts) {
|
|
|
345
352
|
if (options.charsetSentinel) {
|
|
346
353
|
if (options.charset === 'iso-8859-1') {
|
|
347
354
|
// encodeURIComponent('✓'), the "numeric entity" representation of a checkmark
|
|
348
|
-
prefix += 'utf8=%26%2310003%3B
|
|
355
|
+
prefix += 'utf8=%26%2310003%3B' + options.delimiter;
|
|
349
356
|
} else {
|
|
350
357
|
// encodeURIComponent('✓')
|
|
351
|
-
prefix += 'utf8=%E2%9C%93
|
|
358
|
+
prefix += 'utf8=%E2%9C%93' + options.delimiter;
|
|
352
359
|
}
|
|
353
360
|
}
|
|
354
361
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "qs",
|
|
3
3
|
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
|
4
4
|
"homepage": "https://github.com/ljharb/qs",
|
|
5
|
-
"version": "6.15.
|
|
5
|
+
"version": "6.15.2",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/ljharb/qs.git"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@browserify/envify": "^6.0.0",
|
|
38
38
|
"@browserify/uglifyify": "^6.0.0",
|
|
39
|
-
"@ljharb/eslint-config": "^22.
|
|
39
|
+
"@ljharb/eslint-config": "^22.2.3",
|
|
40
40
|
"browserify": "^16.5.2",
|
|
41
41
|
"bundle-collapser": "^1.4.0",
|
|
42
42
|
"common-shakeify": "~1.0.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"has-property-descriptors": "^1.0.2",
|
|
52
52
|
"has-proto": "^1.2.0",
|
|
53
53
|
"has-symbols": "^1.1.0",
|
|
54
|
-
"iconv-lite": "^0.5.
|
|
54
|
+
"iconv-lite": "^0.5.2",
|
|
55
55
|
"in-publish": "^2.0.1",
|
|
56
56
|
"jackspeak": "=2.1.1",
|
|
57
57
|
"jiti": "^0.0.0",
|
|
@@ -210,6 +210,21 @@ test('parse()', function (t) {
|
|
|
210
210
|
t.test('uses original key when depth = 0', function (st) {
|
|
211
211
|
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: 0 }), { 'a[0]': 'b', 'a[1]': 'c' });
|
|
212
212
|
st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: 0 }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
|
|
213
|
+
st.deepEqual(qs.parse('a.b=c', { depth: 0, allowDots: true }), { 'a[b]': 'c' }, 'normalizes dots before applying depth-0 behavior');
|
|
214
|
+
st.deepEqual(qs.parse('toString=foo', { depth: 0 }), {}, 'respects prototype guard at depth 0');
|
|
215
|
+
st.deepEqual(qs.parse('toString=foo', { depth: 0, allowPrototypes: true }), { toString: 'foo' }, 'allows prototypes at depth 0 when enabled');
|
|
216
|
+
st.end();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
t.test('ignores prototype keys when depth = 0 and allowPrototypes is false', function (st) {
|
|
220
|
+
st.deepEqual(qs.parse('toString=foo', { depth: 0 }), {});
|
|
221
|
+
st.deepEqual(qs.parse('hasOwnProperty=bar', { depth: 0 }), {});
|
|
222
|
+
st.deepEqual(qs.parse('toString=foo&a=b', { depth: 0 }), { a: 'b' });
|
|
223
|
+
st.end();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
t.test('allows prototype keys when depth = 0 and allowPrototypes is true', function (st) {
|
|
227
|
+
st.deepEqual(qs.parse('toString=foo', { depth: 0, allowPrototypes: true }), { toString: 'foo' });
|
|
213
228
|
st.end();
|
|
214
229
|
});
|
|
215
230
|
|
|
@@ -251,6 +266,52 @@ test('parse()', function (t) {
|
|
|
251
266
|
st.end();
|
|
252
267
|
});
|
|
253
268
|
|
|
269
|
+
t.test('parses keys with literal [] inside a bracket group (#493)', function (st) {
|
|
270
|
+
// A bracket pair inside a bracket group should be treated literally as part of the key
|
|
271
|
+
st.deepEqual(
|
|
272
|
+
qs.parse('search[withbracket[]]=foobar'),
|
|
273
|
+
{ search: { 'withbracket[]': 'foobar' } },
|
|
274
|
+
'treats inner [] literally when inside a bracket group'
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// Single-level variant
|
|
278
|
+
st.deepEqual(
|
|
279
|
+
qs.parse('a[b[]]=c'),
|
|
280
|
+
{ a: { 'b[]': 'c' } },
|
|
281
|
+
'keeps "b[]" as a literal key'
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Nested with an array push on the outer level
|
|
285
|
+
st.deepEqual(
|
|
286
|
+
qs.parse('list[][x[]]=y'),
|
|
287
|
+
{ list: [{ 'x[]': 'y' }] },
|
|
288
|
+
'preserves inner [] while still treating outer [] as array push'
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Multiple nested bracket pairs: inner [] remains literal as part of the key
|
|
292
|
+
st.deepEqual(
|
|
293
|
+
qs.parse('a[b[c[]]]=d'),
|
|
294
|
+
{ a: { 'b[c[]]': 'd' } },
|
|
295
|
+
'treats "b[c[]]" as a literal key inside the bracket group'
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
// Depth limits with literal brackets: preserve inner [] while limiting bracket-group parsing
|
|
299
|
+
st.deepEqual(
|
|
300
|
+
qs.parse('a[b[c[]]][d]=e', { depth: 1 }),
|
|
301
|
+
{ a: { 'b[c[]]': { '[d]': 'e' } } },
|
|
302
|
+
'respects depth: 1 and preserves literal inner [] in the parsed key'
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// Unterminated inner bracket group is wrapped as a literal remainder segment
|
|
306
|
+
st.deepEqual(
|
|
307
|
+
qs.parse('a[[]b=c'),
|
|
308
|
+
{ a: { '[[]b': 'c' } },
|
|
309
|
+
'handles unterminated inner bracket groups without throwing'
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
st.end();
|
|
313
|
+
});
|
|
314
|
+
|
|
254
315
|
t.test('allows to specify array indices', function (st) {
|
|
255
316
|
st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
|
|
256
317
|
st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
|
|
@@ -1074,6 +1135,15 @@ test('parse()', function (t) {
|
|
|
1074
1135
|
};
|
|
1075
1136
|
|
|
1076
1137
|
st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
|
|
1138
|
+
|
|
1139
|
+
var noopDecoder = function () { return 'x'; };
|
|
1140
|
+
noopDecoder();
|
|
1141
|
+
st['throws'](
|
|
1142
|
+
function () { decoder('x', noopDecoder, 'utf-8', 'unknown'); },
|
|
1143
|
+
'this should never happen! type: unknown',
|
|
1144
|
+
'decoder throws for unexpected type'
|
|
1145
|
+
);
|
|
1146
|
+
|
|
1077
1147
|
st.end();
|
|
1078
1148
|
});
|
|
1079
1149
|
|
|
@@ -1103,6 +1173,14 @@ test('parse()', function (t) {
|
|
|
1103
1173
|
new RangeError('Parameter limit exceeded. Only 3 parameters allowed.'),
|
|
1104
1174
|
'throws error when parameter limit is exceeded'
|
|
1105
1175
|
);
|
|
1176
|
+
|
|
1177
|
+
sst['throws'](
|
|
1178
|
+
function () {
|
|
1179
|
+
qs.parse('a=1&b=2', { parameterLimit: 1, throwOnLimitExceeded: true });
|
|
1180
|
+
},
|
|
1181
|
+
new RangeError('Parameter limit exceeded. Only 1 parameter allowed.'),
|
|
1182
|
+
'throws error with singular "parameter" when parameterLimit is 1'
|
|
1183
|
+
);
|
|
1106
1184
|
sst.end();
|
|
1107
1185
|
});
|
|
1108
1186
|
|
|
@@ -1124,6 +1202,12 @@ test('parse()', function (t) {
|
|
|
1124
1202
|
sst.end();
|
|
1125
1203
|
});
|
|
1126
1204
|
|
|
1205
|
+
st.test('allows unlimited parameters when parameterLimit is Infinity and throwOnLimitExceeded is true', function (sst) {
|
|
1206
|
+
var result = qs.parse('a=1&b=2&c=3&d=4&e=5&f=6', { parameterLimit: Infinity, throwOnLimitExceeded: true });
|
|
1207
|
+
sst.deepEqual(result, { a: '1', b: '2', c: '3', d: '4', e: '5', f: '6' }, 'parses all parameters without truncation or throwing');
|
|
1208
|
+
sst.end();
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1127
1211
|
st.end();
|
|
1128
1212
|
});
|
|
1129
1213
|
|
|
@@ -1189,6 +1273,14 @@ test('parse()', function (t) {
|
|
|
1189
1273
|
'throws error when a sparse index exceeds arrayLimit'
|
|
1190
1274
|
);
|
|
1191
1275
|
|
|
1276
|
+
sst['throws'](
|
|
1277
|
+
function () {
|
|
1278
|
+
qs.parse('a[2]=b', { arrayLimit: 1, throwOnLimitExceeded: true });
|
|
1279
|
+
},
|
|
1280
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
1281
|
+
'throws error with singular "element" when arrayLimit is 1'
|
|
1282
|
+
);
|
|
1283
|
+
|
|
1192
1284
|
sst.end();
|
|
1193
1285
|
});
|
|
1194
1286
|
|
|
@@ -1206,6 +1298,17 @@ test('parse()', function (t) {
|
|
|
1206
1298
|
sst.end();
|
|
1207
1299
|
});
|
|
1208
1300
|
|
|
1301
|
+
st.test('throws when duplicate bracket keys exceed arrayLimit with throwOnLimitExceeded', function (sst) {
|
|
1302
|
+
sst['throws'](
|
|
1303
|
+
function () {
|
|
1304
|
+
qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5, throwOnLimitExceeded: true });
|
|
1305
|
+
},
|
|
1306
|
+
new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
|
|
1307
|
+
'throws error when duplicate bracket notation exceeds array limit'
|
|
1308
|
+
);
|
|
1309
|
+
sst.end();
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1209
1312
|
st.end();
|
|
1210
1313
|
});
|
|
1211
1314
|
|
|
@@ -1462,6 +1565,14 @@ test('comma + arrayLimit', function (t) {
|
|
|
1462
1565
|
new RangeError('Array limit exceeded. Only 3 elements allowed in an array.'),
|
|
1463
1566
|
'throws error when comma-split exceeds array limit'
|
|
1464
1567
|
);
|
|
1568
|
+
|
|
1569
|
+
st['throws'](
|
|
1570
|
+
function () {
|
|
1571
|
+
qs.parse('a=1,2,3', { comma: true, arrayLimit: 1, throwOnLimitExceeded: true });
|
|
1572
|
+
},
|
|
1573
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
1574
|
+
'throws error with singular "element" when arrayLimit is 1'
|
|
1575
|
+
);
|
|
1465
1576
|
st.end();
|
|
1466
1577
|
});
|
|
1467
1578
|
|
|
@@ -1564,5 +1675,29 @@ test('mixed array and object notation', function (t) {
|
|
|
1564
1675
|
st.end();
|
|
1565
1676
|
});
|
|
1566
1677
|
|
|
1678
|
+
t.test('uses existing array length for currentArrayLength when parsing object input with bracket keys', function (st) {
|
|
1679
|
+
var input = {};
|
|
1680
|
+
var arr = ['x', 'y'];
|
|
1681
|
+
arr.a = ['z', 'w'];
|
|
1682
|
+
input['a[]'] = arr;
|
|
1683
|
+
st.deepEqual(qs.parse(input), { a: ['x', 'y'] }, 'parses object input with bracket keys using existing array values');
|
|
1684
|
+
st.end();
|
|
1685
|
+
});
|
|
1686
|
+
|
|
1687
|
+
t.test('throws with singular message when object input bracket key exceeds arrayLimit of 1', function (st) {
|
|
1688
|
+
var input = {};
|
|
1689
|
+
var arr = ['x'];
|
|
1690
|
+
arr.a = ['z', 'w'];
|
|
1691
|
+
input['a[]'] = arr;
|
|
1692
|
+
st['throws'](
|
|
1693
|
+
function () {
|
|
1694
|
+
qs.parse(input, { throwOnLimitExceeded: true, arrayLimit: 1 });
|
|
1695
|
+
},
|
|
1696
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
1697
|
+
'throws singular error for object input exceeding arrayLimit 1'
|
|
1698
|
+
);
|
|
1699
|
+
st.end();
|
|
1700
|
+
});
|
|
1701
|
+
|
|
1567
1702
|
t.end();
|
|
1568
1703
|
});
|
|
@@ -651,6 +651,49 @@ test('stringify()', function (t) {
|
|
|
651
651
|
st.end();
|
|
652
652
|
});
|
|
653
653
|
|
|
654
|
+
t.test('does not crash on null/undefined entries in arrayFormat=comma with encodeValuesOnly', function (st) {
|
|
655
|
+
st.doesNotThrow(
|
|
656
|
+
function () { qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
|
657
|
+
'does not pass a raw null array entry to the encoder'
|
|
658
|
+
);
|
|
659
|
+
st.doesNotThrow(
|
|
660
|
+
function () { qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
|
661
|
+
'does not pass a raw undefined array entry to the encoder'
|
|
662
|
+
);
|
|
663
|
+
st.doesNotThrow(
|
|
664
|
+
function () { qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
|
665
|
+
'does not crash on a single-null array'
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
st.equal(
|
|
669
|
+
qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
670
|
+
'a=,b',
|
|
671
|
+
'null entry joins as empty, comma stays unencoded under encodeValuesOnly'
|
|
672
|
+
);
|
|
673
|
+
st.equal(
|
|
674
|
+
qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
675
|
+
'a=,b',
|
|
676
|
+
'undefined entry joins as empty, comma stays unencoded under encodeValuesOnly'
|
|
677
|
+
);
|
|
678
|
+
st.equal(
|
|
679
|
+
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
680
|
+
'a=',
|
|
681
|
+
'single-null array stringifies as empty value'
|
|
682
|
+
);
|
|
683
|
+
st.equal(
|
|
684
|
+
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true, strictNullHandling: true }),
|
|
685
|
+
'a',
|
|
686
|
+
'strictNullHandling drops the equals sign for a single-null array'
|
|
687
|
+
);
|
|
688
|
+
st.equal(
|
|
689
|
+
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true, skipNulls: true }),
|
|
690
|
+
'',
|
|
691
|
+
'skipNulls drops a single-null array entirely'
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
st.end();
|
|
695
|
+
});
|
|
696
|
+
|
|
654
697
|
t.test('stringifies a null object', { skip: !hasProto }, function (st) {
|
|
655
698
|
st.equal(qs.stringify({ __proto__: null, a: 'b' }), 'a=b');
|
|
656
699
|
st.end();
|
|
@@ -825,6 +868,35 @@ test('stringify()', function (t) {
|
|
|
825
868
|
st.end();
|
|
826
869
|
});
|
|
827
870
|
|
|
871
|
+
t.test('skips null/undefined entries in filter=array', function (st) {
|
|
872
|
+
st.doesNotThrow(
|
|
873
|
+
function () { qs.stringify({ a: 'b', undefined: 'x' }, { filter: ['a', undefined] }); },
|
|
874
|
+
'does not pass a raw undefined filter entry to the encoder'
|
|
875
|
+
);
|
|
876
|
+
st.doesNotThrow(
|
|
877
|
+
function () { qs.stringify({ a: 'b', 'null': 'x' }, { filter: ['a', null] }); },
|
|
878
|
+
'does not pass a raw null filter entry to the encoder'
|
|
879
|
+
);
|
|
880
|
+
|
|
881
|
+
st.equal(
|
|
882
|
+
qs.stringify({ a: 'b', undefined: 'x', c: 'd' }, { filter: ['a', undefined, 'c'] }),
|
|
883
|
+
'a=b&c=d',
|
|
884
|
+
'undefined filter entry is skipped, remaining keys are kept'
|
|
885
|
+
);
|
|
886
|
+
st.equal(
|
|
887
|
+
qs.stringify({ a: 'b', 'null': 'x', c: 'd' }, { filter: ['a', null, 'c'] }),
|
|
888
|
+
'a=b&c=d',
|
|
889
|
+
'null filter entry is skipped, remaining keys are kept'
|
|
890
|
+
);
|
|
891
|
+
st.equal(
|
|
892
|
+
qs.stringify({ a: 'b', 'null': 'x' }, { filter: [null] }),
|
|
893
|
+
'',
|
|
894
|
+
'filter array containing only null yields empty string'
|
|
895
|
+
);
|
|
896
|
+
|
|
897
|
+
st.end();
|
|
898
|
+
});
|
|
899
|
+
|
|
828
900
|
t.test('supports custom representations when filter=function', function (st) {
|
|
829
901
|
var calls = 0;
|
|
830
902
|
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
|
|
@@ -1111,6 +1183,28 @@ test('stringify()', function (t) {
|
|
|
1111
1183
|
st.end();
|
|
1112
1184
|
});
|
|
1113
1185
|
|
|
1186
|
+
t.test('strictNullHandling: applies the formatter to the encoded key (RFC1738)', function (st) {
|
|
1187
|
+
st.equal(
|
|
1188
|
+
qs.stringify(
|
|
1189
|
+
{ 'a b': null, 'c d': 'e f' },
|
|
1190
|
+
{ strictNullHandling: false, format: 'RFC1738' }
|
|
1191
|
+
),
|
|
1192
|
+
'a+b=&c+d=e+f',
|
|
1193
|
+
'without: as expected'
|
|
1194
|
+
);
|
|
1195
|
+
|
|
1196
|
+
st.equal(
|
|
1197
|
+
qs.stringify(
|
|
1198
|
+
{ 'a b': null, 'c d': 'e f' },
|
|
1199
|
+
{ strictNullHandling: true, format: 'RFC1738' }
|
|
1200
|
+
),
|
|
1201
|
+
'a+b&c+d=e+f',
|
|
1202
|
+
'with: as expected'
|
|
1203
|
+
);
|
|
1204
|
+
|
|
1205
|
+
st.end();
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1114
1208
|
t.test('throws if an invalid charset is specified', function (st) {
|
|
1115
1209
|
st['throws'](function () {
|
|
1116
1210
|
qs.stringify({ a: 'b' }, { charset: 'foobar' });
|
|
@@ -1146,6 +1240,12 @@ test('stringify()', function (t) {
|
|
|
1146
1240
|
'adds the right sentinel when instructed to and the charset is iso-8859-1'
|
|
1147
1241
|
);
|
|
1148
1242
|
|
|
1243
|
+
st.equal(
|
|
1244
|
+
qs.stringify({ a: 1, b: 2 }, { charsetSentinel: true, delimiter: ';' }),
|
|
1245
|
+
'utf8=%E2%9C%93;a=1;b=2',
|
|
1246
|
+
'uses the configured delimiter after the sentinel'
|
|
1247
|
+
);
|
|
1248
|
+
|
|
1149
1249
|
st.end();
|
|
1150
1250
|
});
|
|
1151
1251
|
|
|
@@ -1188,6 +1288,15 @@ test('stringify()', function (t) {
|
|
|
1188
1288
|
};
|
|
1189
1289
|
|
|
1190
1290
|
st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
|
|
1291
|
+
|
|
1292
|
+
var noopEncoder = function () { return 'x'; };
|
|
1293
|
+
noopEncoder();
|
|
1294
|
+
st['throws'](
|
|
1295
|
+
function () { encoder('x', noopEncoder, 'utf-8', 'unknown'); },
|
|
1296
|
+
'this should never happen! type: unknown',
|
|
1297
|
+
'encoder throws for unexpected type'
|
|
1298
|
+
);
|
|
1299
|
+
|
|
1191
1300
|
st.end();
|
|
1192
1301
|
});
|
|
1193
1302
|
|
|
@@ -1307,4 +1416,33 @@ test('stringifies empty keys', function (t) {
|
|
|
1307
1416
|
|
|
1308
1417
|
st.end();
|
|
1309
1418
|
});
|
|
1419
|
+
|
|
1420
|
+
t.test('round-trips keys containing percent-encoded bracket text', function (st) {
|
|
1421
|
+
var cases = [
|
|
1422
|
+
{ 'a%5Bb': 'c' },
|
|
1423
|
+
{ 'a%5Db': 'c' },
|
|
1424
|
+
{ 'a%255Bb': 'c' },
|
|
1425
|
+
{ 'a%255Db': 'c' },
|
|
1426
|
+
{ a: { 'b%5Bc': 'd' } },
|
|
1427
|
+
{ a: { 'b%255Bc': 'd' } },
|
|
1428
|
+
{ 'a%5B%255Bb': 'c' }
|
|
1429
|
+
];
|
|
1430
|
+
for (var i = 0; i < cases.length; i++) {
|
|
1431
|
+
st.deepEqual(
|
|
1432
|
+
qs.parse(qs.stringify(cases[i])),
|
|
1433
|
+
cases[i],
|
|
1434
|
+
'round-trips ' + JSON.stringify(cases[i])
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
st.end();
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
t.test('parses input containing percent-encoded bracket text without mangling', function (st) {
|
|
1442
|
+
st.deepEqual(qs.parse('a%25255Bb=c'), { 'a%255Bb': 'c' }, 'a%25255Bb decodes to a%255Bb, not a%5Bb');
|
|
1443
|
+
st.deepEqual(qs.parse('a%25255Db=c'), { 'a%255Db': 'c' }, 'a%25255Db decodes to a%255Db, not a%5Db');
|
|
1444
|
+
st.deepEqual(qs.parse('a%5Bb%25255Bc%5D=d'), { a: { 'b%255Bc': 'd' } }, 'nested %25255B decodes to %255B inside segment, not %5B');
|
|
1445
|
+
|
|
1446
|
+
st.end();
|
|
1447
|
+
});
|
|
1310
1448
|
});
|
|
@@ -31,6 +31,7 @@ test('merge()', function (t) {
|
|
|
31
31
|
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
|
|
32
32
|
|
|
33
33
|
var func = function f() {};
|
|
34
|
+
func();
|
|
34
35
|
t.deepEqual(
|
|
35
36
|
utils.merge(func, { foo: 'bar' }),
|
|
36
37
|
[func, { foo: 'bar' }],
|
|
@@ -112,6 +113,15 @@ test('merge()', function (t) {
|
|
|
112
113
|
s2t.end();
|
|
113
114
|
});
|
|
114
115
|
|
|
116
|
+
st.test('merges overflow object into primitive with plainObjects', function (s2t) {
|
|
117
|
+
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
118
|
+
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
|
119
|
+
var merged = utils.merge('c', overflow, { plainObjects: true });
|
|
120
|
+
s2t.ok(utils.isOverflow(merged), 'result is also marked as overflow');
|
|
121
|
+
s2t.deepEqual(merged, { __proto__: null, 0: 'c', 1: 'a', 2: 'b' }, 'creates null-proto object with primitive at 0');
|
|
122
|
+
s2t.end();
|
|
123
|
+
});
|
|
124
|
+
|
|
115
125
|
st.test('merges overflow object with multiple values into primitive', function (s2t) {
|
|
116
126
|
// Create an overflow object via combine: 3 elements (indices 0-2) with limit 0
|
|
117
127
|
var overflow = utils.combine(['b', 'c'], 'd', 0, false);
|
|
@@ -128,6 +138,21 @@ test('merge()', function (t) {
|
|
|
128
138
|
s2t.end();
|
|
129
139
|
});
|
|
130
140
|
|
|
141
|
+
st.test('merges primitive into array that exceeds arrayLimit', function (s2t) {
|
|
142
|
+
var arr = ['a', 'b', 'c'];
|
|
143
|
+
var merged = utils.merge(arr, 'd', { arrayLimit: 1 });
|
|
144
|
+
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
|
145
|
+
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'converts to overflow object with primitive appended');
|
|
146
|
+
s2t.end();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
st.test('merges array into primitive that exceeds arrayLimit', function (s2t) {
|
|
150
|
+
var merged = utils.merge('a', ['b', 'c'], { arrayLimit: 1 });
|
|
151
|
+
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
|
152
|
+
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c' }, 'converts to overflow object');
|
|
153
|
+
s2t.end();
|
|
154
|
+
});
|
|
155
|
+
|
|
131
156
|
st.end();
|
|
132
157
|
});
|
|
133
158
|
|
|
@@ -376,7 +401,9 @@ test('encode', function (t) {
|
|
|
376
401
|
});
|
|
377
402
|
|
|
378
403
|
test('isBuffer()', function (t) {
|
|
379
|
-
|
|
404
|
+
var fn = function () {};
|
|
405
|
+
fn();
|
|
406
|
+
forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], fn, /a/g], function (x) {
|
|
380
407
|
t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
|
|
381
408
|
});
|
|
382
409
|
|
|
@@ -386,8 +413,9 @@ test('isBuffer()', function (t) {
|
|
|
386
413
|
var saferBuffer = SaferBuffer.from('abc');
|
|
387
414
|
t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');
|
|
388
415
|
|
|
389
|
-
var buffer =
|
|
390
|
-
t.
|
|
416
|
+
var buffer = SaferBuffer.from('abc');
|
|
417
|
+
t.notEqual(saferBuffer, buffer, 'different buffer instances');
|
|
418
|
+
t.equal(utils.isBuffer(buffer), true, 'another Buffer instance is a buffer');
|
|
391
419
|
t.end();
|
|
392
420
|
});
|
|
393
421
|
|
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [v1.1.1](https://github.com/ljharb/side-channel/compare/v1.1.0...v1.1.1) - 2026-06-08
|
|
9
|
+
|
|
10
|
+
### Commits
|
|
11
|
+
|
|
12
|
+
- [Fix] `assert`: do not observably access object keys when throwing [`fc17361`](https://github.com/ljharb/side-channel/commit/fc173615136e6a6c3194656e6088afbc0bd6f9b8)
|
|
13
|
+
- [actions] update workflows [`35b18c0`](https://github.com/ljharb/side-channel/commit/35b18c0bced5769d59226cd44a7e4003ad0aee81)
|
|
14
|
+
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/eslint-config`, `@ljharb/tsconfig`, `@types/tape`, `auto-changelog`, `eslint`, `npmignore` [`b456a01`](https://github.com/ljharb/side-channel/commit/b456a01396cfdd028e5d39ea84885fe9178a72cf)
|
|
15
|
+
- [Deps] update `object-inspect`, `side-channel-list` [`accf1a1`](https://github.com/ljharb/side-channel/commit/accf1a1a2767a22b1f5eeb5769a1db4c35a78090)
|
|
16
|
+
- [readme] replace runkit CI badge with shields.io check-runs badge [`7e0c956`](https://github.com/ljharb/side-channel/commit/7e0c956411cc29267197a49a155a3d9bd01bacac)
|
|
17
|
+
|
|
8
18
|
## [v1.1.0](https://github.com/ljharb/side-channel/compare/v1.0.6...v1.1.0) - 2024-12-11
|
|
9
19
|
|
|
10
20
|
### Commits
|
|
@@ -57,5 +57,5 @@ Clone the repo, `npm install`, and run `npm test`
|
|
|
57
57
|
[downloads-url]: https://npm-stat.com/charts.html?package=side-channel
|
|
58
58
|
[codecov-image]: https://codecov.io/gh/ljharb/side-channel/branch/main/graphs/badge.svg
|
|
59
59
|
[codecov-url]: https://app.codecov.io/gh/ljharb/side-channel/
|
|
60
|
-
[actions-image]: https://img.shields.io/
|
|
60
|
+
[actions-image]: https://img.shields.io/github/check-runs/ljharb/side-channel/main
|
|
61
61
|
[actions-url]: https://github.com/ljharb/side-channel/actions
|
|
@@ -18,7 +18,10 @@ module.exports = function getSideChannel() {
|
|
|
18
18
|
var channel = {
|
|
19
19
|
assert: function (key) {
|
|
20
20
|
if (!channel.has(key)) {
|
|
21
|
-
|
|
21
|
+
var keyDesc = key && Object(key) === key
|
|
22
|
+
? 'the given object key'
|
|
23
|
+
: inspect(key);
|
|
24
|
+
throw new $TypeError('Side channel does not contain ' + keyDesc);
|
|
22
25
|
}
|
|
23
26
|
},
|
|
24
27
|
'delete': function (key) {
|
|
@@ -38,6 +41,6 @@ module.exports = function getSideChannel() {
|
|
|
38
41
|
$channelData.set(key, value);
|
|
39
42
|
}
|
|
40
43
|
};
|
|
41
|
-
|
|
44
|
+
|
|
42
45
|
return channel;
|
|
43
46
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "side-channel",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Store information about any JS value in a side channel. Uses WeakMap if available.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -44,23 +44,23 @@
|
|
|
44
44
|
"homepage": "https://github.com/ljharb/side-channel#readme",
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"es-errors": "^1.3.0",
|
|
47
|
-
"object-inspect": "^1.13.
|
|
48
|
-
"side-channel-list": "^1.0.
|
|
47
|
+
"object-inspect": "^1.13.4",
|
|
48
|
+
"side-channel-list": "^1.0.1",
|
|
49
49
|
"side-channel-map": "^1.0.1",
|
|
50
50
|
"side-channel-weakmap": "^1.0.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@arethetypeswrong/cli": "^0.
|
|
54
|
-
"@ljharb/eslint-config": "^
|
|
55
|
-
"@ljharb/tsconfig": "^0.
|
|
53
|
+
"@arethetypeswrong/cli": "^0.18.3",
|
|
54
|
+
"@ljharb/eslint-config": "^22.2.3",
|
|
55
|
+
"@ljharb/tsconfig": "^0.3.2",
|
|
56
56
|
"@types/object-inspect": "^1.13.0",
|
|
57
|
-
"@types/tape": "^5.
|
|
58
|
-
"auto-changelog": "^2.
|
|
57
|
+
"@types/tape": "^5.8.1",
|
|
58
|
+
"auto-changelog": "^2.6.0",
|
|
59
59
|
"eclint": "^2.8.1",
|
|
60
60
|
"encoding": "^0.1.13",
|
|
61
|
-
"eslint": "
|
|
61
|
+
"eslint": "^8.57.1",
|
|
62
62
|
"in-publish": "^2.0.1",
|
|
63
|
-
"npmignore": "^0.3.
|
|
63
|
+
"npmignore": "^0.3.5",
|
|
64
64
|
"nyc": "^10.3.2",
|
|
65
65
|
"safe-publish-latest": "^2.0.0",
|
|
66
66
|
"tape": "^5.9.0",
|
|
@@ -28,6 +28,22 @@ test('getSideChannel', function (t) {
|
|
|
28
28
|
channel.set(o, 'data');
|
|
29
29
|
st.doesNotThrow(function () { channel.assert(o); }, 'existent value noops');
|
|
30
30
|
|
|
31
|
+
var accessed = false;
|
|
32
|
+
var trap = {};
|
|
33
|
+
Object.defineProperty(trap, 'foo', {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
get: function () {
|
|
36
|
+
accessed = true;
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
st['throws'](
|
|
41
|
+
function () { channel.assert(trap); },
|
|
42
|
+
TypeError,
|
|
43
|
+
'missing object key throws'
|
|
44
|
+
);
|
|
45
|
+
st.equal(accessed, false, 'a missing object key is not observably accessed');
|
|
46
|
+
|
|
31
47
|
st.end();
|
|
32
48
|
});
|
|
33
49
|
|