@tickernelz/paperclip-pro-server 2026.1014.2 → 2026.1015.0
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/dist/build-info.json +1 -1
- package/dist/services/heartbeat/retries.d.ts.map +1 -1
- package/dist/services/heartbeat/retries.js +33 -20
- package/dist/services/heartbeat/retries.js.map +1 -1
- package/dist/services/heartbeat.d.ts.map +1 -1
- package/dist/services/heartbeat.js +2 -1
- package/dist/services/heartbeat.js.map +1 -1
- package/dist/services/model-fallback.d.ts +40 -24
- package/dist/services/model-fallback.d.ts.map +1 -1
- package/dist/services/model-fallback.js +93 -61
- package/dist/services/model-fallback.js.map +1 -1
- package/dist/services/model-fallback.test.js +42 -56
- package/dist/services/model-fallback.test.js.map +1 -1
- package/node_modules/qs/.github/THREAT_MODEL.md +4 -2
- package/node_modules/qs/CHANGELOG.md +38 -14
- package/node_modules/qs/README.md +31 -3
- package/node_modules/qs/dist/qs.js +20 -20
- package/node_modules/qs/lib/parse.js +94 -4
- package/node_modules/qs/lib/stringify.js +4 -4
- package/node_modules/qs/lib/utils.js +130 -6
- package/node_modules/qs/package.json +4 -4
- package/node_modules/qs/test/parse.js +174 -0
- package/node_modules/qs/test/stringify.js +263 -33
- package/node_modules/qs/test/utils.js +184 -0
- package/package.json +21 -21
- package/ui-dist/assets/{Agents.production-8FUtTpS_.js → Agents.production-BHWznw8y.js} +2 -2
- package/ui-dist/assets/{CompanyActivity.production-pI5bepy4.js → CompanyActivity.production-8aUIBuOH.js} +1 -1
- package/ui-dist/assets/{CompanyExport-CYMUQK5t.js → CompanyExport-wysQLLwi.js} +1 -1
- package/ui-dist/assets/{CompanySkills.production-Cx4u1ng7.js → CompanySkills.production-CeGKxxeo.js} +1 -1
- package/ui-dist/assets/{OrgChart.production-BfX2mFHX.js → OrgChart.production-Bv25raI7.js} +1 -1
- package/ui-dist/assets/{RoutineDetail.production-B4xHviF2.js → RoutineDetail.production-BP0s3T7M.js} +1 -1
- package/ui-dist/assets/{Routines.production-C-WFr4aR.js → Routines.production-BQL74XW8.js} +1 -1
- package/ui-dist/assets/{definition-BTyYsf5f.js → definition-CX59WzhL.js} +1 -1
- package/ui-dist/assets/{index-OBprYZQn.js → index-DvHeLBFO.js} +88 -88
- package/ui-dist/index.html +1 -1
- package/ui-dist/sw.js +1 -1
|
@@ -212,63 +212,94 @@ test('stringify()', function (t) {
|
|
|
212
212
|
st.end();
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
// `stringify` percent-encodes the structural `.` separators that `allowDots` inserts between
|
|
217
|
-
// nested keys, not just the literal dots inside a key, which breaks the stringify -> parse
|
|
218
|
-
// round-trip. These assertions pin that behavior so it cannot change silently: the fix in
|
|
219
|
-
// https://github.com/ljharb/qs/pull/564 changes every output below, so landing that fix MUST
|
|
220
|
-
// update this test (that is the point — it forces the behavior change to be explicit).
|
|
221
|
-
t.test('[characterization] encodeDotInKeys currently over-encodes structural separator dots (pre-#564)', function (st) {
|
|
215
|
+
t.test('does not encode the structural separator dots between nested keys when encodeDotInKeys and allowDots are provided', function (st) {
|
|
222
216
|
st.equal(
|
|
223
|
-
qs.stringify(
|
|
224
|
-
|
|
225
|
-
|
|
217
|
+
qs.stringify(
|
|
218
|
+
{ a: { b: { c: 'd' } } },
|
|
219
|
+
{ allowDots: true, encodeDotInKeys: true }
|
|
220
|
+
),
|
|
221
|
+
'a.b.c=d',
|
|
222
|
+
'no key contains a literal dot, so encodeDotInKeys is a no-op on the separators'
|
|
226
223
|
);
|
|
227
224
|
st.equal(
|
|
228
|
-
qs.stringify(
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
qs.stringify(
|
|
226
|
+
{ a: { b: { c: { d: 'e' } } } },
|
|
227
|
+
{ allowDots: true, encodeDotInKeys: true }
|
|
228
|
+
),
|
|
229
|
+
'a.b.c.d=e',
|
|
230
|
+
'every nesting separator is preserved, not just the last one'
|
|
231
231
|
);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
|
|
233
|
+
st.deepEqual(
|
|
234
|
+
qs.parse(
|
|
235
|
+
qs.stringify({ a: { b: { c: 'd' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
236
|
+
{ allowDots: true, decodeDotInKeys: true }
|
|
237
|
+
),
|
|
238
|
+
{ a: { b: { c: 'd' } } },
|
|
239
|
+
'stringify -> parse round-trips a deeply nested object'
|
|
236
240
|
);
|
|
241
|
+
|
|
242
|
+
st.end();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
t.test('encodeDotInKeys encodes only literal dots inside nested keys, never the structural separators, at any depth', function (st) {
|
|
237
246
|
st.equal(
|
|
238
247
|
qs.stringify({ a: { 'b.c': { d: 'e' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
239
|
-
'a%
|
|
240
|
-
'nested
|
|
248
|
+
'a.b%252Ec.d=e',
|
|
249
|
+
'a nested key with a literal dot and an object value encodes only its own dot'
|
|
250
|
+
);
|
|
251
|
+
st.equal(
|
|
252
|
+
qs.stringify({ 'a.b': { c: { d: 'e' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
253
|
+
'a%252Eb.c.d=e',
|
|
254
|
+
'a top-level dotted key stays encoded while deeper separators stay literal'
|
|
241
255
|
);
|
|
242
256
|
st.equal(
|
|
243
257
|
qs.stringify({ a: { b: { 'c.d': { e: 'f' } } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
244
|
-
'a%
|
|
245
|
-
'deeply nested
|
|
258
|
+
'a.b.c%252Ed.e=f',
|
|
259
|
+
'a literal dot in a deeply nested key is encoded without touching the separators around it'
|
|
246
260
|
);
|
|
247
261
|
|
|
248
262
|
st.equal(
|
|
249
263
|
qs.stringify({ a: { b: [1, 2] } }, { allowDots: true, encodeDotInKeys: true }),
|
|
250
|
-
'a%
|
|
251
|
-
'
|
|
264
|
+
'a.b%5B0%5D=1&a.b%5B1%5D=2',
|
|
265
|
+
'separator dots are preserved for an array value nested under keys'
|
|
252
266
|
);
|
|
253
267
|
st.equal(
|
|
254
268
|
qs.stringify({ a: { 'b.c': [1, 2] } }, { allowDots: true, encodeDotInKeys: true }),
|
|
255
|
-
'a%
|
|
256
|
-
'
|
|
269
|
+
'a.b%252Ec%5B0%5D=1&a.b%252Ec%5B1%5D=2',
|
|
270
|
+
'a nested dotted key with an array value encodes only its own dot'
|
|
257
271
|
);
|
|
258
272
|
|
|
259
|
-
st.
|
|
260
|
-
qs.
|
|
261
|
-
|
|
262
|
-
|
|
273
|
+
st.deepEqual(
|
|
274
|
+
qs.parse(
|
|
275
|
+
qs.stringify({ a: { 'b.c': { d: 'e' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
276
|
+
{ allowDots: true, decodeDotInKeys: true }
|
|
277
|
+
),
|
|
278
|
+
{ a: { 'b.c': { d: 'e' } } },
|
|
279
|
+
'stringify -> parse round-trips a nested dotted key with an object value'
|
|
263
280
|
);
|
|
264
|
-
|
|
265
281
|
st.deepEqual(
|
|
266
282
|
qs.parse(
|
|
267
|
-
qs.stringify({ a: {
|
|
283
|
+
qs.stringify({ 'a.b': { c: { d: 'e' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
268
284
|
{ allowDots: true, decodeDotInKeys: true }
|
|
269
285
|
),
|
|
270
|
-
{ 'a.b': { c:
|
|
271
|
-
'
|
|
286
|
+
{ 'a.b': { c: { d: 'e' } } },
|
|
287
|
+
'stringify -> parse round-trips a top-level dotted key with deep nesting'
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
st.end();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
t.test('with allowDots false, encodeDotInKeys leaves nested-key dots literal, consistently for object and primitive values', function (st) {
|
|
294
|
+
st.equal(
|
|
295
|
+
qs.stringify({ a: { 'b.c': { d: 'e' } } }, { allowDots: false, encodeDotInKeys: true }),
|
|
296
|
+
'a%5Bb.c%5D%5Bd%5D=e',
|
|
297
|
+
'nested dotted key with an object value'
|
|
298
|
+
);
|
|
299
|
+
st.equal(
|
|
300
|
+
qs.stringify({ a: { 'b.c': 'e' } }, { allowDots: false, encodeDotInKeys: true }),
|
|
301
|
+
'a%5Bb.c%5D=e',
|
|
302
|
+
'nested dotted key with a primitive value matches the object-value case'
|
|
272
303
|
);
|
|
273
304
|
|
|
274
305
|
st.end();
|
|
@@ -394,6 +425,41 @@ test('stringify()', function (t) {
|
|
|
394
425
|
st.end();
|
|
395
426
|
});
|
|
396
427
|
|
|
428
|
+
t.test('ignores inherited array format names', function (st) {
|
|
429
|
+
var array = { a: ['b', 'c'] };
|
|
430
|
+
var indices = 'a%5B0%5D=b&a%5B1%5D=c';
|
|
431
|
+
var repeat = 'a=b&a=c';
|
|
432
|
+
|
|
433
|
+
st.equal(qs.stringify(array, { arrayFormat: 'unsupported' }), indices, 'unknown format falls back to `indices`');
|
|
434
|
+
|
|
435
|
+
var names = [
|
|
436
|
+
'constructor',
|
|
437
|
+
'toString',
|
|
438
|
+
'__proto__',
|
|
439
|
+
'valueOf',
|
|
440
|
+
'hasOwnProperty',
|
|
441
|
+
'isPrototypeOf',
|
|
442
|
+
'propertyIsEnumerable',
|
|
443
|
+
'toLocaleString'
|
|
444
|
+
];
|
|
445
|
+
for (var i = 0; i < names.length; i++) {
|
|
446
|
+
var name = names[i];
|
|
447
|
+
st.equal(qs.stringify(array, { arrayFormat: name }), indices, '`' + name + '` falls back to `indices`');
|
|
448
|
+
st.equal(
|
|
449
|
+
qs.stringify(array, { arrayFormat: name, indices: true }),
|
|
450
|
+
indices,
|
|
451
|
+
'`' + name + '` with `indices: true` falls back to `indices`'
|
|
452
|
+
);
|
|
453
|
+
st.equal(
|
|
454
|
+
qs.stringify(array, { arrayFormat: name, indices: false }),
|
|
455
|
+
repeat,
|
|
456
|
+
'`' + name + '` with `indices: false` falls back to `repeat`'
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
st.end();
|
|
461
|
+
});
|
|
462
|
+
|
|
397
463
|
t.test('`skipNulls` option', function (st) {
|
|
398
464
|
st.equal(
|
|
399
465
|
qs.stringify({ a: 'b', c: null }, { skipNulls: true }),
|
|
@@ -569,6 +635,53 @@ test('stringify()', function (t) {
|
|
|
569
635
|
st.end();
|
|
570
636
|
});
|
|
571
637
|
|
|
638
|
+
t.test('encodes comma array values with the same encoder arguments as other values, with encodeValuesOnly', function (st) {
|
|
639
|
+
var commaValuesOnly = { arrayFormat: 'comma', encodeValuesOnly: true };
|
|
640
|
+
|
|
641
|
+
st.equal(
|
|
642
|
+
qs.stringify({ a: ['é', 'x'], b: 'é' }, utils.assign({ charset: 'iso-8859-1' }, commaValuesOnly)),
|
|
643
|
+
'a=%E9,x&b=%E9',
|
|
644
|
+
'respects charset iso-8859-1'
|
|
645
|
+
);
|
|
646
|
+
st.deepEqual(
|
|
647
|
+
qs.parse(qs.stringify({ a: ['é', 'x'] }, utils.assign({ charset: 'iso-8859-1' }, commaValuesOnly)), { charset: 'iso-8859-1', comma: true }),
|
|
648
|
+
{ a: ['é', 'x'] },
|
|
649
|
+
'round-trips in iso-8859-1'
|
|
650
|
+
);
|
|
651
|
+
st.equal(
|
|
652
|
+
qs.stringify({ a: ['☺'] }, utils.assign({ charset: 'iso-8859-1' }, commaValuesOnly)),
|
|
653
|
+
'a=%26%239786%3B',
|
|
654
|
+
'encodes unrepresentable chars as numeric entities in iso-8859-1'
|
|
655
|
+
);
|
|
656
|
+
st.equal(
|
|
657
|
+
qs.stringify({ a: ['(x)', 'y z'], b: '(x)' }, utils.assign({ format: qs.formats.RFC1738 }, commaValuesOnly)),
|
|
658
|
+
'a=(x),y+z&b=(x)',
|
|
659
|
+
'respects format RFC1738'
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
var calls = [];
|
|
663
|
+
st.equal(
|
|
664
|
+
qs.stringify({ a: ['b', 'c'] }, utils.assign({
|
|
665
|
+
encoder: function (str, defaultEncoder, charset, kind, format) {
|
|
666
|
+
calls[calls.length] = [str, typeof defaultEncoder, charset, kind, format];
|
|
667
|
+
return defaultEncoder(str, defaultEncoder, charset, kind, format).toUpperCase();
|
|
668
|
+
}
|
|
669
|
+
}, commaValuesOnly)),
|
|
670
|
+
'a=B,C',
|
|
671
|
+
'a custom encoder can call defaultEncoder'
|
|
672
|
+
);
|
|
673
|
+
st.deepEqual(
|
|
674
|
+
calls,
|
|
675
|
+
[
|
|
676
|
+
['b', 'function', 'utf-8', 'value', qs.formats.RFC3986],
|
|
677
|
+
['c', 'function', 'utf-8', 'value', qs.formats.RFC3986]
|
|
678
|
+
],
|
|
679
|
+
'a custom encoder receives defaultEncoder, charset, kind, and format'
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
st.end();
|
|
683
|
+
});
|
|
684
|
+
|
|
572
685
|
t.test('stringifies comma and empty non-array values', function (st) {
|
|
573
686
|
st.equal(qs.stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'indices' }), 'a=,&b=&c=c,d%');
|
|
574
687
|
st.equal(qs.stringify({ a: ',', b: '', c: 'c,d%' }, { encode: false, arrayFormat: 'brackets' }), 'a=,&b=&c=c,d%');
|
|
@@ -865,6 +978,112 @@ test('stringify()', function (t) {
|
|
|
865
978
|
st.end();
|
|
866
979
|
});
|
|
867
980
|
|
|
981
|
+
t.test('arrayFormat=comma: does not crash on entries with no callable `toString`', function (st) {
|
|
982
|
+
var json = JSON.parse('{ "a": ["b", { "toString": "x" }, "c"] }');
|
|
983
|
+
st.equal(qs.stringify(json, { arrayFormat: 'comma' }), 'a=b%2C%5Bobject%20Object%5D%2Cc', 'JSON own `toString`');
|
|
984
|
+
st.equal(qs.stringify(json, { arrayFormat: 'comma', encode: false }), 'a=b,[object Object],c', 'with encode: false');
|
|
985
|
+
st.equal(
|
|
986
|
+
qs.stringify(json, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
987
|
+
'a=b,%5Bobject%20Object%5D,c',
|
|
988
|
+
'with encodeValuesOnly'
|
|
989
|
+
);
|
|
990
|
+
st.equal(
|
|
991
|
+
qs.stringify(JSON.parse('{ "a": [{ "toString": "x" }] }'), { arrayFormat: 'comma', commaRoundTrip: true }),
|
|
992
|
+
'a%5B%5D=%5Bobject%20Object%5D',
|
|
993
|
+
'with commaRoundTrip'
|
|
994
|
+
);
|
|
995
|
+
st.equal(
|
|
996
|
+
qs.stringify(JSON.parse('{ "a": [{ "toString": "x" }] }'), { arrayFormat: 'comma' }),
|
|
997
|
+
qs.stringify({ a: [{ b: 1 }] }, { arrayFormat: 'comma' }),
|
|
998
|
+
'renders the same as a plain object entry'
|
|
999
|
+
);
|
|
1000
|
+
|
|
1001
|
+
st.equal(
|
|
1002
|
+
qs.stringify(qs.parse('a[][b]=c', { plainObjects: true }), { arrayFormat: 'comma' }),
|
|
1003
|
+
'a=%5Bobject%20Object%5D',
|
|
1004
|
+
'null-prototype entry from `parse` with plainObjects'
|
|
1005
|
+
);
|
|
1006
|
+
st.equal(
|
|
1007
|
+
qs.stringify(qs.parse('a[][b]=c', { plainObjects: true }), { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
1008
|
+
'a=%5Bobject%20Object%5D',
|
|
1009
|
+
'null-prototype entry from `parse` with plainObjects, with encodeValuesOnly'
|
|
1010
|
+
);
|
|
1011
|
+
st.equal(
|
|
1012
|
+
qs.stringify(qs.parse('a[0][toString]=x', { allowPrototypes: true }), { arrayFormat: 'comma' }),
|
|
1013
|
+
'a=%5Bobject%20Object%5D',
|
|
1014
|
+
'own `toString` entry from `parse` with allowPrototypes'
|
|
1015
|
+
);
|
|
1016
|
+
|
|
1017
|
+
var nested = JSON.parse('{ "a": [[1, [{ "toString": "x" }]], "y"] }');
|
|
1018
|
+
st.equal(qs.stringify(nested, { arrayFormat: 'comma', encode: false }), 'a=1,[object Object],y', 'nested array');
|
|
1019
|
+
st.equal(
|
|
1020
|
+
qs.stringify(nested, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
1021
|
+
'a=1%2C%5Bobject%20Object%5D,y',
|
|
1022
|
+
'nested array, with encodeValuesOnly'
|
|
1023
|
+
);
|
|
1024
|
+
st.equal(qs.stringify({ a: [[1, 2], 3] }, { arrayFormat: 'comma', encode: false }), 'a=1,2,3', 'nested array is still flattened');
|
|
1025
|
+
|
|
1026
|
+
var cyclic = [{ toString: 'x' }];
|
|
1027
|
+
cyclic[cyclic.length] = cyclic;
|
|
1028
|
+
st.equal(
|
|
1029
|
+
qs.stringify({ a: cyclic }, { arrayFormat: 'comma', encode: false }),
|
|
1030
|
+
'a=[object Object],[object Object],',
|
|
1031
|
+
'cyclic nested array joins its cycle as empty'
|
|
1032
|
+
);
|
|
1033
|
+
|
|
1034
|
+
var customJoin = [{ toString: 'x' }];
|
|
1035
|
+
customJoin.join = function () { return 'j'; };
|
|
1036
|
+
st.equal(qs.stringify({ a: [customJoin, 'y'] }, { arrayFormat: 'comma', encode: false }), 'a=j,y', 'own `join` is still used');
|
|
1037
|
+
|
|
1038
|
+
st.equal(
|
|
1039
|
+
qs.stringify({ a: [{ toString: 'x', valueOf: function () { return 7; } }] }, { arrayFormat: 'comma' }),
|
|
1040
|
+
'a=7',
|
|
1041
|
+
'primitive `valueOf` is still used'
|
|
1042
|
+
);
|
|
1043
|
+
st.equal(
|
|
1044
|
+
qs.stringify({ a: [{ toString: 'x', valueOf: function () { return null; } }] }, { arrayFormat: 'comma' }),
|
|
1045
|
+
'a=null',
|
|
1046
|
+
'null `valueOf` is still used'
|
|
1047
|
+
);
|
|
1048
|
+
st.equal(
|
|
1049
|
+
qs.stringify({ a: [{ toString: 'x', valueOf: function () { return {}; } }] }, { arrayFormat: 'comma' }),
|
|
1050
|
+
'a=%5Bobject%20Object%5D',
|
|
1051
|
+
'non-primitive `valueOf` falls back'
|
|
1052
|
+
);
|
|
1053
|
+
|
|
1054
|
+
st.equal(
|
|
1055
|
+
qs.stringify({ a: [{ toString: function () { return 't'; } }, new Date(0)] }, { arrayFormat: 'comma', encode: false }),
|
|
1056
|
+
'a=t,1970-01-01T00:00:00.000Z',
|
|
1057
|
+
'callable `toString` and dates are unaffected'
|
|
1058
|
+
);
|
|
1059
|
+
|
|
1060
|
+
st['throws'](
|
|
1061
|
+
function () {
|
|
1062
|
+
qs.stringify({ a: [{ toString: function () { throw new SyntaxError('mine'); } }] }, { arrayFormat: 'comma' });
|
|
1063
|
+
},
|
|
1064
|
+
SyntaxError,
|
|
1065
|
+
'a throwing `toString` still throws'
|
|
1066
|
+
);
|
|
1067
|
+
st['throws'](
|
|
1068
|
+
function () {
|
|
1069
|
+
qs.stringify({ a: [{ toString: 'x', valueOf: function () { throw new EvalError('mine'); } }] }, { arrayFormat: 'comma' });
|
|
1070
|
+
},
|
|
1071
|
+
EvalError,
|
|
1072
|
+
'a throwing `valueOf` still throws'
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
st.end();
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
t.test('arrayFormat=comma: entries with `Symbol.toPrimitive` are unaffected', { skip: !hasSymbols() || typeof Symbol.toPrimitive !== 'symbol' }, function (st) {
|
|
1079
|
+
var entry = { toString: 'x' };
|
|
1080
|
+
entry[Symbol.toPrimitive] = function () { return 'p'; };
|
|
1081
|
+
st.equal(qs.stringify({ a: [entry] }, { arrayFormat: 'comma' }), 'a=p');
|
|
1082
|
+
st.equal(qs.stringify({ a: [entry] }, { arrayFormat: 'comma', encodeValuesOnly: true }), 'a=p');
|
|
1083
|
+
|
|
1084
|
+
st.end();
|
|
1085
|
+
});
|
|
1086
|
+
|
|
868
1087
|
t.test('stringifies a null object', { skip: !hasProto }, function (st) {
|
|
869
1088
|
st.equal(qs.stringify({ __proto__: null, a: 'b' }), 'a=b');
|
|
870
1089
|
st.end();
|
|
@@ -1480,6 +1699,17 @@ test('stringify()', function (t) {
|
|
|
1480
1699
|
st.end();
|
|
1481
1700
|
});
|
|
1482
1701
|
|
|
1702
|
+
t.test('encodes a literal `+` as `%2B` in iso-8859-1 mode so it round-trips', function (st) {
|
|
1703
|
+
st.equal(qs.stringify({ a: 'b+c' }, { charset: 'iso-8859-1' }), 'a=b%2Bc', 'a `+` in a value is percent-encoded');
|
|
1704
|
+
st.equal(qs.stringify({ 'a+b': 'c' }, { charset: 'iso-8859-1' }), 'a%2Bb=c', 'a `+` in a key is percent-encoded');
|
|
1705
|
+
st.deepEqual(
|
|
1706
|
+
qs.parse(qs.stringify({ a: 'b+c' }, { charset: 'iso-8859-1' }), { charset: 'iso-8859-1' }),
|
|
1707
|
+
{ a: 'b+c' },
|
|
1708
|
+
'the `+` survives a round-trip instead of decoding to a space'
|
|
1709
|
+
);
|
|
1710
|
+
st.end();
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1483
1713
|
t.test('respects an explicit charset of utf-8 (the default)', function (st) {
|
|
1484
1714
|
st.equal(qs.stringify({ a: 'æ' }, { charset: 'utf-8' }), 'a=%C3%A6');
|
|
1485
1715
|
st.end();
|
|
@@ -106,6 +106,146 @@ test('merge()', function (t) {
|
|
|
106
106
|
s2t.end();
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
st.test('with strictMerge, repeated conflicts append instead of nesting a new wrapper', function (s2t) {
|
|
110
|
+
var opts = { strictMerge: true };
|
|
111
|
+
var wrapped = utils.merge({ foo: 'bar' }, 'baz', opts);
|
|
112
|
+
s2t.deepEqual(wrapped, [{ foo: 'bar' }, 'baz'], 'first conflict wraps in an array');
|
|
113
|
+
|
|
114
|
+
var withObject = utils.merge(wrapped, { qux: 'quux' }, opts);
|
|
115
|
+
s2t.deepEqual(
|
|
116
|
+
withObject,
|
|
117
|
+
{ 0: { foo: 'bar' }, 1: 'baz', qux: 'quux' },
|
|
118
|
+
'merging an object converts the wrapper to an object'
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
var second = utils.merge(withObject, 'corge', opts);
|
|
122
|
+
s2t.deepEqual(
|
|
123
|
+
second,
|
|
124
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', qux: 'quux' },
|
|
125
|
+
'a second conflict appends at the next index'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
var third = utils.merge(second, 'grault', opts);
|
|
129
|
+
s2t.deepEqual(
|
|
130
|
+
third,
|
|
131
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', 3: 'grault', qux: 'quux' },
|
|
132
|
+
'a third conflict appends again, so nesting depth stays constant'
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
s2t.end();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
st.test('with strictMerge, a caller-supplied array is still wrapped, not appended to', function (s2t) {
|
|
139
|
+
var merged = utils.merge({ 0: 'a', 1: 'b' }, 'c', { strictMerge: true });
|
|
140
|
+
s2t.deepEqual(merged, [{ 0: 'a', 1: 'b' }, 'c'], 'an unmarked object with numeric keys is wrapped');
|
|
141
|
+
s2t.end();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
st.test('with strictMerge, throws instead of appending a conflict past arrayLimit with throwOnLimitExceeded', function (s2t) {
|
|
145
|
+
var appendConflict = function (options) {
|
|
146
|
+
var wrapped = utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true });
|
|
147
|
+
var container = utils.merge(wrapped, { qux: 'quux' }, { strictMerge: true });
|
|
148
|
+
return utils.merge(container, 'corge', options);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
s2t['throws'](
|
|
152
|
+
function () { appendConflict({ strictMerge: true, arrayLimit: 1, throwOnLimitExceeded: true }); },
|
|
153
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
154
|
+
'appending to a two-element container past arrayLimit throws'
|
|
155
|
+
);
|
|
156
|
+
s2t['throws'](
|
|
157
|
+
function () { appendConflict({ strictMerge: true, arrayLimit: 2, throwOnLimitExceeded: true }); },
|
|
158
|
+
new RangeError('Array limit exceeded. Only 2 elements allowed in an array.'),
|
|
159
|
+
'uses the plural message when arrayLimit is not 1'
|
|
160
|
+
);
|
|
161
|
+
s2t.deepEqual(
|
|
162
|
+
appendConflict({ strictMerge: true, arrayLimit: 3, throwOnLimitExceeded: true }),
|
|
163
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', qux: 'quux' },
|
|
164
|
+
'appends while the container stays within arrayLimit'
|
|
165
|
+
);
|
|
166
|
+
s2t.deepEqual(
|
|
167
|
+
appendConflict({ strictMerge: true, arrayLimit: 1 }),
|
|
168
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', qux: 'quux' },
|
|
169
|
+
'still appends past arrayLimit without throwOnLimitExceeded, since the container is already an object'
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
s2t.end();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
st.test('with strictMerge, a conflict appends after numeric keys already in its container', function (s2t) {
|
|
176
|
+
var opts = { strictMerge: true };
|
|
177
|
+
|
|
178
|
+
var container = utils.merge(utils.merge({ foo: 'bar' }, 'baz', opts), { qux: 'quux' }, opts);
|
|
179
|
+
var withIndex = utils.merge(container, { 2: 'keep' }, opts);
|
|
180
|
+
s2t.deepEqual(
|
|
181
|
+
utils.merge(withIndex, 'corge', opts),
|
|
182
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'keep', 3: 'corge', qux: 'quux' },
|
|
183
|
+
'a numeric key merged into the container is not overwritten'
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
var sparse = [];
|
|
187
|
+
sparse[2] = 'keep';
|
|
188
|
+
var extended = utils.merge(utils.merge({ foo: 'bar' }, 'baz', opts), sparse, opts);
|
|
189
|
+
s2t.deepEqual(extended, [{ foo: 'bar' }, 'baz', 'keep'], 'an indexed value extends the container while it is an array');
|
|
190
|
+
var converted = utils.merge(extended, { qux: 'quux' }, opts);
|
|
191
|
+
s2t.deepEqual(
|
|
192
|
+
utils.merge(converted, 'corge', opts),
|
|
193
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'keep', 3: 'corge', qux: 'quux' },
|
|
194
|
+
'converting the container to an object keeps its last index'
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
s2t.end();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
st.test('enforces arrayLimit on the two-element array built for colliding values', function (s2t) {
|
|
201
|
+
s2t['throws'](
|
|
202
|
+
function () { utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true, arrayLimit: 1, throwOnLimitExceeded: true }); },
|
|
203
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
204
|
+
'a strictMerge conflict throws at arrayLimit 1'
|
|
205
|
+
);
|
|
206
|
+
s2t['throws'](
|
|
207
|
+
function () { utils.merge('a', 'b', { arrayLimit: 0, throwOnLimitExceeded: true }); },
|
|
208
|
+
new RangeError('Array limit exceeded. Only 0 elements allowed in an array.'),
|
|
209
|
+
'two colliding primitives throw at arrayLimit 0'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
var conflict = utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true, arrayLimit: 1 });
|
|
213
|
+
s2t.ok(utils.isOverflow(conflict), 'without throwOnLimitExceeded, a strictMerge conflict converts to an overflow object');
|
|
214
|
+
s2t.deepEqual(conflict, { 0: { foo: 'bar' }, 1: 'baz' }, 'holding both values');
|
|
215
|
+
|
|
216
|
+
var pair = utils.merge('a', 'b', { arrayLimit: 1 });
|
|
217
|
+
s2t.ok(utils.isOverflow(pair), 'without throwOnLimitExceeded, two colliding primitives convert to an overflow object');
|
|
218
|
+
s2t.deepEqual(pair, { 0: 'a', 1: 'b' }, 'holding both values');
|
|
219
|
+
|
|
220
|
+
s2t.deepEqual(
|
|
221
|
+
utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true, arrayLimit: 2, throwOnLimitExceeded: true }),
|
|
222
|
+
[{ foo: 'bar' }, 'baz'],
|
|
223
|
+
'a strictMerge conflict within arrayLimit is still an array'
|
|
224
|
+
);
|
|
225
|
+
s2t.deepEqual(
|
|
226
|
+
utils.merge('a', 'b', { arrayLimit: 2, throwOnLimitExceeded: true }),
|
|
227
|
+
['a', 'b'],
|
|
228
|
+
'two colliding primitives within arrayLimit are still an array'
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
s2t.end();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
st.test('seeds an overflow container index from the keys it already holds', function (s2t) {
|
|
235
|
+
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
236
|
+
s2t.ok(utils.isOverflow(overflow), 'the source is an overflow object');
|
|
237
|
+
|
|
238
|
+
var merged = utils.merge({ 2: 'keep', z: 'zz' }, overflow);
|
|
239
|
+
s2t.ok(utils.isOverflow(merged), 'the result is marked as overflow');
|
|
240
|
+
s2t.deepEqual(
|
|
241
|
+
utils.merge(merged, 'c'),
|
|
242
|
+
{ 0: 'a', 1: 'b', 2: 'keep', 3: 'c', z: 'zz' },
|
|
243
|
+
'the next value lands after the highest numeric key the container already held'
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
s2t.end();
|
|
247
|
+
});
|
|
248
|
+
|
|
109
249
|
st.test('merges overflow object into primitive', function (s2t) {
|
|
110
250
|
// Create an overflow object via combine: 2 elements (indices 0-1) with limit 0
|
|
111
251
|
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
@@ -441,6 +581,50 @@ test('decode', function (t) {
|
|
|
441
581
|
'decodes a string in iso-8859-1'
|
|
442
582
|
);
|
|
443
583
|
|
|
584
|
+
t.test('strings with no percent sign', function (st) {
|
|
585
|
+
st.equal(utils.decode(''), '', 'empty string');
|
|
586
|
+
st.equal(utils.decode('abc'), 'abc', 'returns a plain string unchanged');
|
|
587
|
+
st.equal(utils.decode('abc', null, 'iso-8859-1'), 'abc', 'returns a plain string unchanged in iso-8859-1');
|
|
588
|
+
st.equal(utils.decode('a++b+'), 'a b ', 'decodes every + to a space');
|
|
589
|
+
st.equal(utils.decode('a++b+', null, 'iso-8859-1'), 'a b ', 'decodes every + to a space in iso-8859-1');
|
|
590
|
+
st.equal(utils.decode('\uD800x\uDC00'), '\uD800x\uDC00', 'leaves lone surrogates unchanged');
|
|
591
|
+
st.equal(utils.decode('😀+'), '😀 ', 'leaves surrogate pairs unchanged');
|
|
592
|
+
st.equal(utils.decode('a=b&c'), 'a=b&c', 'leaves delimiters unchanged');
|
|
593
|
+
|
|
594
|
+
st.end();
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
t.test('strings with a percent sign', function (st) {
|
|
598
|
+
st.equal(utils.decode('a+%20b'), 'a b', 'decodes both + and an escape');
|
|
599
|
+
st.equal(utils.decode('%E9+x', null, 'iso-8859-1'), 'é x', 'decodes both + and an escape in iso-8859-1');
|
|
600
|
+
st.equal(utils.decode('a+%'), 'a %', 'decodes + even when the escape is malformed');
|
|
601
|
+
st.equal(utils.decode('a+%E0%A4%A'), 'a %E0%A4%A', 'decodes + even when the escape is truncated');
|
|
602
|
+
|
|
603
|
+
st.end();
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
t.test('non-strings', function (st) {
|
|
607
|
+
forEach(v.nullPrimitives, function (nullish) {
|
|
608
|
+
st['throws'](
|
|
609
|
+
function () { utils.decode(nullish); },
|
|
610
|
+
TypeError,
|
|
611
|
+
inspect(nullish) + ' is not a string'
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
st['throws'](
|
|
615
|
+
function () { utils.decode(['a']); },
|
|
616
|
+
TypeError,
|
|
617
|
+
'an array is not a string'
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
/* eslint no-new-wrappers: 0 */
|
|
621
|
+
st.equal(utils.decode(new String('ab')), 'ab', 'a boxed string decodes to a primitive');
|
|
622
|
+
st.equal(utils.decode(new String('a+b')), 'a b', 'a boxed string with + decodes to a primitive');
|
|
623
|
+
st.equal(utils.decode(new String('a%20b')), 'a b', 'a boxed string with an escape decodes to a primitive');
|
|
624
|
+
|
|
625
|
+
st.end();
|
|
626
|
+
});
|
|
627
|
+
|
|
444
628
|
t.end();
|
|
445
629
|
});
|
|
446
630
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tickernelz/paperclip-pro-server",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.1015.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://github.com/tickernelz/paperclip-pro",
|
|
6
6
|
"bugs": {
|
|
@@ -54,25 +54,25 @@
|
|
|
54
54
|
"@openrouter/ai-sdk-provider": "3.1.0",
|
|
55
55
|
"@opentelemetry/api": "^1.9.0",
|
|
56
56
|
"@photon-ai/advanced-imessage": "2.1.0",
|
|
57
|
-
"@tickernelz/paperclip-pro-adapter-claude-local": "2026.
|
|
58
|
-
"@tickernelz/paperclip-pro-adapter-codex-local": "2026.
|
|
59
|
-
"@tickernelz/paperclip-pro-adapter-cursor-cloud": "2026.
|
|
60
|
-
"@tickernelz/paperclip-pro-adapter-cursor-local": "2026.
|
|
61
|
-
"@tickernelz/paperclip-pro-adapter-gemini-local": "2026.
|
|
62
|
-
"@tickernelz/paperclip-pro-adapter-grok-local": "2026.
|
|
63
|
-
"@tickernelz/paperclip-pro-adapter-kimi-local": "2026.
|
|
64
|
-
"@tickernelz/paperclip-pro-adapter-omp-local": "2026.
|
|
65
|
-
"@tickernelz/paperclip-pro-adapter-openclaw-gateway": "2026.
|
|
66
|
-
"@tickernelz/paperclip-pro-adapter-opencode-local": "2026.
|
|
67
|
-
"@tickernelz/paperclip-pro-adapter-pi-local": "2026.
|
|
68
|
-
"@tickernelz/paperclip-pro-adapter-utils": "2026.
|
|
69
|
-
"@tickernelz/paperclip-pro-db": "2026.
|
|
70
|
-
"@tickernelz/paperclip-pro-hermes-paperclip-adapter": "2026.
|
|
71
|
-
"@tickernelz/paperclip-pro-mcp-server": "2026.
|
|
72
|
-
"@tickernelz/paperclip-pro-plugin-sdk": "2026.
|
|
73
|
-
"@tickernelz/paperclip-pro-shared": "2026.
|
|
74
|
-
"@tickernelz/paperclip-pro-skills-catalog": "2026.
|
|
75
|
-
"@tickernelz/paperclip-pro-teams-catalog": "2026.
|
|
57
|
+
"@tickernelz/paperclip-pro-adapter-claude-local": "2026.1015.0",
|
|
58
|
+
"@tickernelz/paperclip-pro-adapter-codex-local": "2026.1015.0",
|
|
59
|
+
"@tickernelz/paperclip-pro-adapter-cursor-cloud": "2026.1015.0",
|
|
60
|
+
"@tickernelz/paperclip-pro-adapter-cursor-local": "2026.1015.0",
|
|
61
|
+
"@tickernelz/paperclip-pro-adapter-gemini-local": "2026.1015.0",
|
|
62
|
+
"@tickernelz/paperclip-pro-adapter-grok-local": "2026.1015.0",
|
|
63
|
+
"@tickernelz/paperclip-pro-adapter-kimi-local": "2026.1015.0",
|
|
64
|
+
"@tickernelz/paperclip-pro-adapter-omp-local": "2026.1015.0",
|
|
65
|
+
"@tickernelz/paperclip-pro-adapter-openclaw-gateway": "2026.1015.0",
|
|
66
|
+
"@tickernelz/paperclip-pro-adapter-opencode-local": "2026.1015.0",
|
|
67
|
+
"@tickernelz/paperclip-pro-adapter-pi-local": "2026.1015.0",
|
|
68
|
+
"@tickernelz/paperclip-pro-adapter-utils": "2026.1015.0",
|
|
69
|
+
"@tickernelz/paperclip-pro-db": "2026.1015.0",
|
|
70
|
+
"@tickernelz/paperclip-pro-hermes-paperclip-adapter": "2026.1015.0",
|
|
71
|
+
"@tickernelz/paperclip-pro-mcp-server": "2026.1015.0",
|
|
72
|
+
"@tickernelz/paperclip-pro-plugin-sdk": "2026.1015.0",
|
|
73
|
+
"@tickernelz/paperclip-pro-shared": "2026.1015.0",
|
|
74
|
+
"@tickernelz/paperclip-pro-skills-catalog": "2026.1015.0",
|
|
75
|
+
"@tickernelz/paperclip-pro-teams-catalog": "2026.1015.0",
|
|
76
76
|
"@vercel/connect": "2.3.3",
|
|
77
77
|
"acorn": "8.18.0",
|
|
78
78
|
"acpx": "0.13.1",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"acpx"
|
|
129
129
|
],
|
|
130
130
|
"devDependencies": {
|
|
131
|
-
"@tickernelz/paperclip-pro-paperclip-runner": "2026.
|
|
131
|
+
"@tickernelz/paperclip-pro-paperclip-runner": "2026.1015.0",
|
|
132
132
|
"@types/compression": "^1.8.1",
|
|
133
133
|
"@types/express": "^5.0.0",
|
|
134
134
|
"@types/express-serve-static-core": "^5.1.3",
|