@quantiya/codevibe-claude-plugin 1.0.36 → 1.0.38

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.
Files changed (201) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/codevibe-claude +17 -3
  3. package/dist/server.js +13 -13
  4. package/hooks/stop.sh +30 -10
  5. package/node_modules/@quantiya/codevibe-core/dist/auth/auth-telemetry.d.ts +56 -0
  6. package/node_modules/@quantiya/codevibe-core/dist/index.js +30 -30
  7. package/node_modules/@quantiya/codevibe-core/dist/keychain/keychain-manager.d.ts +16 -2
  8. package/node_modules/@quantiya/codevibe-core/dist/session/session-rekey.d.ts +40 -0
  9. package/node_modules/@quantiya/codevibe-core/dist/session/session-resume.d.ts +1 -0
  10. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  11. package/node_modules/body-parser/README.md +18 -18
  12. package/node_modules/body-parser/index.js +6 -15
  13. package/node_modules/body-parser/lib/read.js +17 -20
  14. package/node_modules/body-parser/lib/types/json.js +8 -16
  15. package/node_modules/body-parser/lib/types/raw.js +3 -4
  16. package/node_modules/body-parser/lib/types/text.js +3 -4
  17. package/node_modules/body-parser/lib/types/urlencoded.js +8 -8
  18. package/node_modules/body-parser/lib/utils.js +11 -9
  19. package/node_modules/body-parser/package.json +2 -2
  20. package/node_modules/content-disposition/README.md +7 -8
  21. package/node_modules/content-disposition/index.js +118 -40
  22. package/node_modules/content-disposition/package.json +8 -11
  23. package/node_modules/express/Readme.md +39 -29
  24. package/node_modules/express/lib/application.js +1 -1
  25. package/node_modules/express/lib/request.js +5 -6
  26. package/node_modules/express/lib/response.js +14 -0
  27. package/node_modules/express/lib/utils.js +3 -1
  28. package/node_modules/express/package.json +6 -5
  29. package/node_modules/finalhandler/HISTORY.md +6 -0
  30. package/node_modules/finalhandler/README.md +26 -23
  31. package/node_modules/finalhandler/package.json +13 -9
  32. package/node_modules/graphql/execution/execute.d.ts +14 -1
  33. package/node_modules/graphql/execution/execute.js +63 -13
  34. package/node_modules/graphql/execution/execute.mjs +63 -13
  35. package/node_modules/graphql/execution/subscribe.js +1 -0
  36. package/node_modules/graphql/execution/subscribe.mjs +2 -0
  37. package/node_modules/graphql/execution/values.js +4 -4
  38. package/node_modules/graphql/execution/values.mjs +4 -4
  39. package/node_modules/graphql/index.d.ts +1 -0
  40. package/node_modules/graphql/language/ast.d.ts +10 -1
  41. package/node_modules/graphql/language/ast.js +8 -1
  42. package/node_modules/graphql/language/ast.mjs +8 -1
  43. package/node_modules/graphql/language/directiveLocation.d.ts +1 -0
  44. package/node_modules/graphql/language/directiveLocation.js +1 -0
  45. package/node_modules/graphql/language/directiveLocation.mjs +1 -0
  46. package/node_modules/graphql/language/index.d.ts +1 -0
  47. package/node_modules/graphql/language/kinds.d.ts +1 -0
  48. package/node_modules/graphql/language/kinds.js +1 -0
  49. package/node_modules/graphql/language/kinds.mjs +1 -0
  50. package/node_modules/graphql/language/parser.d.ts +14 -0
  51. package/node_modules/graphql/language/parser.js +33 -0
  52. package/node_modules/graphql/language/parser.mjs +33 -0
  53. package/node_modules/graphql/language/predicates.js +3 -1
  54. package/node_modules/graphql/language/predicates.mjs +5 -1
  55. package/node_modules/graphql/language/printer.js +13 -1
  56. package/node_modules/graphql/language/printer.mjs +13 -1
  57. package/node_modules/graphql/package.json +1 -1
  58. package/node_modules/graphql/type/directives.d.ts +9 -1
  59. package/node_modules/graphql/type/directives.js +10 -1
  60. package/node_modules/graphql/type/directives.mjs +10 -1
  61. package/node_modules/graphql/type/introspection.js +24 -1
  62. package/node_modules/graphql/type/introspection.mjs +24 -1
  63. package/node_modules/graphql/utilities/buildASTSchema.js +4 -0
  64. package/node_modules/graphql/utilities/buildASTSchema.mjs +4 -0
  65. package/node_modules/graphql/utilities/buildClientSchema.js +1 -0
  66. package/node_modules/graphql/utilities/buildClientSchema.mjs +1 -0
  67. package/node_modules/graphql/utilities/coerceInputValue.js +2 -2
  68. package/node_modules/graphql/utilities/coerceInputValue.mjs +2 -2
  69. package/node_modules/graphql/utilities/extendSchema.js +58 -3
  70. package/node_modules/graphql/utilities/extendSchema.mjs +58 -3
  71. package/node_modules/graphql/utilities/getIntrospectionQuery.d.ts +16 -0
  72. package/node_modules/graphql/utilities/getIntrospectionQuery.js +31 -38
  73. package/node_modules/graphql/utilities/getIntrospectionQuery.mjs +31 -38
  74. package/node_modules/graphql/utilities/introspectionFromSchema.js +1 -0
  75. package/node_modules/graphql/utilities/introspectionFromSchema.mjs +1 -0
  76. package/node_modules/graphql/utilities/printSchema.js +1 -0
  77. package/node_modules/graphql/utilities/printSchema.mjs +1 -0
  78. package/node_modules/graphql/utilities/valueFromAST.js +12 -2
  79. package/node_modules/graphql/utilities/valueFromAST.mjs +12 -2
  80. package/node_modules/graphql/validation/rules/KnownDirectivesRule.js +4 -0
  81. package/node_modules/graphql/validation/rules/KnownDirectivesRule.mjs +4 -0
  82. package/node_modules/graphql/validation/rules/UniqueDirectivesPerLocationRule.js +12 -0
  83. package/node_modules/graphql/validation/rules/UniqueDirectivesPerLocationRule.mjs +12 -0
  84. package/node_modules/graphql/validation/rules/ValuesOfCorrectTypeRule.js +5 -11
  85. package/node_modules/graphql/validation/rules/ValuesOfCorrectTypeRule.mjs +5 -11
  86. package/node_modules/graphql/validation/validate.js +12 -0
  87. package/node_modules/graphql/validation/validate.mjs +13 -2
  88. package/node_modules/graphql/version.js +2 -2
  89. package/node_modules/graphql/version.mjs +2 -2
  90. package/node_modules/hasown/CHANGELOG.md +11 -0
  91. package/node_modules/hasown/eslint.config.mjs +6 -0
  92. package/node_modules/hasown/index.d.ts +1 -0
  93. package/node_modules/hasown/package.json +14 -14
  94. package/node_modules/iconv-lite/lib/index.d.ts +114 -26
  95. package/node_modules/iconv-lite/lib/index.js +39 -40
  96. package/node_modules/iconv-lite/package.json +13 -2
  97. package/node_modules/iconv-lite/types/encodings.d.ts +423 -0
  98. package/node_modules/node-abi/abi_registry.json +10 -3
  99. package/node_modules/{semver → node-abi/node_modules/semver}/README.md +19 -4
  100. package/node_modules/{semver → node-abi/node_modules/semver}/bin/semver.js +14 -10
  101. package/node_modules/node-abi/node_modules/semver/functions/truncate.js +48 -0
  102. package/node_modules/{semver → node-abi/node_modules/semver}/index.js +2 -0
  103. package/node_modules/{semver → node-abi/node_modules/semver}/internal/re.js +1 -1
  104. package/node_modules/{semver → node-abi/node_modules/semver}/package.json +3 -3
  105. package/node_modules/{semver → node-abi/node_modules/semver}/range.bnf +5 -4
  106. package/node_modules/node-abi/package.json +1 -1
  107. package/node_modules/path-to-regexp/Readme.md +3 -3
  108. package/node_modules/path-to-regexp/dist/index.d.ts +3 -0
  109. package/node_modules/path-to-regexp/dist/index.js +215 -193
  110. package/node_modules/path-to-regexp/dist/index.js.map +1 -1
  111. package/node_modules/path-to-regexp/package.json +2 -2
  112. package/node_modules/qs/.editorconfig +1 -1
  113. package/node_modules/qs/.github/SECURITY.md +11 -0
  114. package/node_modules/qs/.github/THREAT_MODEL.md +78 -0
  115. package/node_modules/qs/CHANGELOG.md +190 -0
  116. package/node_modules/qs/README.md +29 -4
  117. package/node_modules/qs/dist/qs.js +21 -21
  118. package/node_modules/qs/eslint.config.mjs +56 -0
  119. package/node_modules/qs/lib/parse.js +94 -49
  120. package/node_modules/qs/lib/utils.js +85 -11
  121. package/node_modules/qs/package.json +10 -9
  122. package/node_modules/qs/test/parse.js +391 -13
  123. package/node_modules/qs/test/stringify.js +16 -3
  124. package/node_modules/qs/test/utils.js +173 -3
  125. package/node_modules/send/package.json +11 -8
  126. package/node_modules/serve-static/README.md +23 -23
  127. package/node_modules/serve-static/package.json +6 -3
  128. package/node_modules/side-channel-list/CHANGELOG.md +25 -4
  129. package/node_modules/side-channel-list/index.js +1 -3
  130. package/node_modules/side-channel-list/package.json +8 -8
  131. package/node_modules/side-channel-list/test/index.js +50 -0
  132. package/node_modules/uuid/dist/v35.js +3 -0
  133. package/node_modules/uuid/dist/v6.js +3 -0
  134. package/node_modules/uuid/dist-node/v35.js +3 -0
  135. package/node_modules/uuid/dist-node/v6.js +3 -0
  136. package/node_modules/uuid/package.json +1 -1
  137. package/node_modules/ws/index.js +15 -6
  138. package/node_modules/ws/lib/constants.js +1 -0
  139. package/node_modules/ws/lib/permessage-deflate.js +6 -6
  140. package/node_modules/ws/lib/websocket-server.js +10 -6
  141. package/node_modules/ws/lib/websocket.js +19 -14
  142. package/node_modules/ws/package.json +4 -3
  143. package/node_modules/ws/wrapper.mjs +14 -1
  144. package/package.json +2 -2
  145. package/node_modules/content-disposition/HISTORY.md +0 -72
  146. package/node_modules/express/History.md +0 -3858
  147. package/node_modules/hasown/.eslintrc +0 -5
  148. package/node_modules/iconv-lite/Changelog.md +0 -236
  149. package/node_modules/qs/.eslintrc +0 -39
  150. package/node_modules/send/HISTORY.md +0 -580
  151. package/node_modules/serve-static/HISTORY.md +0 -516
  152. /package/node_modules/{semver → node-abi/node_modules/semver}/LICENSE +0 -0
  153. /package/node_modules/{semver → node-abi/node_modules/semver}/classes/comparator.js +0 -0
  154. /package/node_modules/{semver → node-abi/node_modules/semver}/classes/index.js +0 -0
  155. /package/node_modules/{semver → node-abi/node_modules/semver}/classes/range.js +0 -0
  156. /package/node_modules/{semver → node-abi/node_modules/semver}/classes/semver.js +0 -0
  157. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/clean.js +0 -0
  158. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/cmp.js +0 -0
  159. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/coerce.js +0 -0
  160. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/compare-build.js +0 -0
  161. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/compare-loose.js +0 -0
  162. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/compare.js +0 -0
  163. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/diff.js +0 -0
  164. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/eq.js +0 -0
  165. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/gt.js +0 -0
  166. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/gte.js +0 -0
  167. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/inc.js +0 -0
  168. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/lt.js +0 -0
  169. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/lte.js +0 -0
  170. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/major.js +0 -0
  171. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/minor.js +0 -0
  172. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/neq.js +0 -0
  173. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/parse.js +0 -0
  174. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/patch.js +0 -0
  175. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/prerelease.js +0 -0
  176. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/rcompare.js +0 -0
  177. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/rsort.js +0 -0
  178. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/satisfies.js +0 -0
  179. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/sort.js +0 -0
  180. /package/node_modules/{semver → node-abi/node_modules/semver}/functions/valid.js +0 -0
  181. /package/node_modules/{semver → node-abi/node_modules/semver}/internal/constants.js +0 -0
  182. /package/node_modules/{semver → node-abi/node_modules/semver}/internal/debug.js +0 -0
  183. /package/node_modules/{semver → node-abi/node_modules/semver}/internal/identifiers.js +0 -0
  184. /package/node_modules/{semver → node-abi/node_modules/semver}/internal/lrucache.js +0 -0
  185. /package/node_modules/{semver → node-abi/node_modules/semver}/internal/parse-options.js +0 -0
  186. /package/node_modules/{semver → node-abi/node_modules/semver}/preload.js +0 -0
  187. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/gtr.js +0 -0
  188. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/intersects.js +0 -0
  189. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/ltr.js +0 -0
  190. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/max-satisfying.js +0 -0
  191. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/min-satisfying.js +0 -0
  192. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/min-version.js +0 -0
  193. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/outside.js +0 -0
  194. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/simplify.js +0 -0
  195. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/subset.js +0 -0
  196. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/to-comparators.js +0 -0
  197. /package/node_modules/{semver → node-abi/node_modules/semver}/ranges/valid.js +0 -0
  198. /package/node_modules/{strip-json-comments → rc/node_modules/strip-json-comments}/index.js +0 -0
  199. /package/node_modules/{strip-json-comments → rc/node_modules/strip-json-comments}/license +0 -0
  200. /package/node_modules/{strip-json-comments → rc/node_modules/strip-json-comments}/package.json +0 -0
  201. /package/node_modules/{strip-json-comments → rc/node_modules/strip-json-comments}/readme.md +0 -0
@@ -213,6 +213,18 @@ test('parse()', function (t) {
213
213
  st.end();
214
214
  });
215
215
 
216
+ t.test('ignores prototype keys when depth = 0 and allowPrototypes is false', function (st) {
217
+ st.deepEqual(qs.parse('toString=foo', { depth: 0 }), {});
218
+ st.deepEqual(qs.parse('hasOwnProperty=bar', { depth: 0 }), {});
219
+ st.deepEqual(qs.parse('toString=foo&a=b', { depth: 0 }), { a: 'b' });
220
+ st.end();
221
+ });
222
+
223
+ t.test('allows prototype keys when depth = 0 and allowPrototypes is true', function (st) {
224
+ st.deepEqual(qs.parse('toString=foo', { depth: 0, allowPrototypes: true }), { toString: 'foo' });
225
+ st.end();
226
+ });
227
+
216
228
  t.test('uses original key when depth = false', function (st) {
217
229
  st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: false }), { 'a[0]': 'b', 'a[1]': 'c' });
218
230
  st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: false }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
@@ -235,11 +247,11 @@ test('parse()', function (t) {
235
247
  st.deepEqual(qs.parse('a=b&a[0]=c'), { a: ['b', 'c'] });
236
248
 
237
249
  st.deepEqual(qs.parse('a[1]=b&a=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
238
- st.deepEqual(qs.parse('a[]=b&a=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
250
+ st.deepEqual(qs.parse('a[]=b&a=c', { arrayLimit: 0 }), { a: { 0: 'b', 1: 'c' } });
239
251
  st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
240
252
 
241
253
  st.deepEqual(qs.parse('a=b&a[1]=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
242
- st.deepEqual(qs.parse('a=b&a[]=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
254
+ st.deepEqual(qs.parse('a=b&a[]=c', { arrayLimit: 0 }), { a: { 0: 'b', 1: 'c' } });
243
255
  st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
244
256
 
245
257
  st.end();
@@ -261,11 +273,11 @@ test('parse()', function (t) {
261
273
  });
262
274
 
263
275
  t.test('limits specific array indices to arrayLimit', function (st) {
264
- st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: ['a'] });
265
- st.deepEqual(qs.parse('a[21]=a', { arrayLimit: 20 }), { a: { 21: 'a' } });
276
+ st.deepEqual(qs.parse('a[19]=a', { arrayLimit: 20 }), { a: ['a'] });
277
+ st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: { 20: 'a' } });
266
278
 
267
- st.deepEqual(qs.parse('a[20]=a'), { a: ['a'] });
268
- st.deepEqual(qs.parse('a[21]=a'), { a: { 21: 'a' } });
279
+ st.deepEqual(qs.parse('a[19]=a'), { a: ['a'] });
280
+ st.deepEqual(qs.parse('a[20]=a'), { a: { 20: 'a' } });
269
281
  st.end();
270
282
  });
271
283
 
@@ -364,7 +376,7 @@ test('parse()', function (t) {
364
376
  );
365
377
  st.deepEqual(
366
378
  qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 }),
367
- { a: ['b', null, 'c', ''] },
379
+ { a: { 0: 'b', 1: null, 2: 'c', 3: '' } },
368
380
  'with arrayLimit 0 + array brackets: null then empty string works'
369
381
  );
370
382
 
@@ -375,7 +387,7 @@ test('parse()', function (t) {
375
387
  );
376
388
  st.deepEqual(
377
389
  qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 }),
378
- { a: ['b', '', 'c', null] },
390
+ { a: { 0: 'b', 1: '', 2: 'c', 3: null } },
379
391
  'with arrayLimit 0 + array brackets: empty string then null works'
380
392
  );
381
393
 
@@ -483,7 +495,7 @@ test('parse()', function (t) {
483
495
 
484
496
  t.test('allows overriding array limit', function (st) {
485
497
  st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { 0: 'b' } });
486
- st.deepEqual(qs.parse('a[0]=b', { arrayLimit: 0 }), { a: ['b'] });
498
+ st.deepEqual(qs.parse('a[0]=b', { arrayLimit: 0 }), { a: { 0: 'b' } });
487
499
 
488
500
  st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: -1 }), { a: { '-1': 'b' } });
489
501
  st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: 0 }), { a: { '-1': 'b' } });
@@ -784,25 +796,25 @@ test('parse()', function (t) {
784
796
 
785
797
  t.test('add keys to objects', function (st) {
786
798
  st.deepEqual(
787
- qs.parse('a[b]=c&a=d'),
799
+ qs.parse('a[b]=c&a=d', { strictMerge: false }),
788
800
  { a: { b: 'c', d: true } },
789
801
  'can add keys to objects'
790
802
  );
791
803
 
792
804
  st.deepEqual(
793
- qs.parse('a[b]=c&a=toString'),
805
+ qs.parse('a[b]=c&a=toString', { strictMerge: false }),
794
806
  { a: { b: 'c' } },
795
807
  'can not overwrite prototype'
796
808
  );
797
809
 
798
810
  st.deepEqual(
799
- qs.parse('a[b]=c&a=toString', { allowPrototypes: true }),
811
+ qs.parse('a[b]=c&a=toString', { strictMerge: false, allowPrototypes: true }),
800
812
  { a: { b: 'c', toString: true } },
801
813
  'can overwrite prototype with allowPrototypes true'
802
814
  );
803
815
 
804
816
  st.deepEqual(
805
- qs.parse('a[b]=c&a=toString', { plainObjects: true }),
817
+ qs.parse('a[b]=c&a=toString', { strictMerge: false, plainObjects: true }),
806
818
  { __proto__: null, a: { __proto__: null, b: 'c', toString: true } },
807
819
  'can overwrite prototype with plainObjects true'
808
820
  );
@@ -810,6 +822,34 @@ test('parse()', function (t) {
810
822
  st.end();
811
823
  });
812
824
 
825
+ t.test('strictMerge wraps object and primitive into an array', function (st) {
826
+ st.deepEqual(
827
+ qs.parse('a[b]=c&a=d'),
828
+ { a: [{ b: 'c' }, 'd'] },
829
+ 'object then primitive produces array'
830
+ );
831
+
832
+ st.deepEqual(
833
+ qs.parse('a=d&a[b]=c'),
834
+ { a: ['d', { b: 'c' }] },
835
+ 'primitive then object produces array'
836
+ );
837
+
838
+ st.deepEqual(
839
+ qs.parse('a[b]=c&a=toString'),
840
+ { a: [{ b: 'c' }, 'toString'] },
841
+ 'prototype-colliding value is preserved in array'
842
+ );
843
+
844
+ st.deepEqual(
845
+ qs.parse('a[b]=c&a=toString', { plainObjects: true }),
846
+ { __proto__: null, a: [{ __proto__: null, b: 'c' }, 'toString'] },
847
+ 'plainObjects preserved in array wrapping'
848
+ );
849
+
850
+ st.end();
851
+ });
852
+
813
853
  t.test('dunder proto is ignored', function (st) {
814
854
  var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
815
855
  var result = qs.parse(payload, { allowPrototypes: true });
@@ -996,6 +1036,20 @@ test('parse()', function (t) {
996
1036
  st.end();
997
1037
  });
998
1038
 
1039
+ t.test('handles a custom decoder returning `null`, with a string key of `null`', function (st) {
1040
+ st.deepEqual(
1041
+ qs.parse('null=1&ToNull=2', {
1042
+ decoder: function (str, defaultDecoder, charset) {
1043
+ return str === 'ToNull' ? null : defaultDecoder(str, defaultDecoder, charset);
1044
+ }
1045
+ }),
1046
+ { 'null': '1' },
1047
+ '"null" key is not overridden by `null` decoder result'
1048
+ );
1049
+
1050
+ st.end();
1051
+ });
1052
+
999
1053
  t.test('does not interpret numeric entities in iso-8859-1 when `interpretNumericEntities` is absent', function (st) {
1000
1054
  st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1' }), { foo: '☺' });
1001
1055
  st.end();
@@ -1032,6 +1086,15 @@ test('parse()', function (t) {
1032
1086
  };
1033
1087
 
1034
1088
  st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
1089
+
1090
+ var noopDecoder = function () { return 'x'; };
1091
+ noopDecoder();
1092
+ st['throws'](
1093
+ function () { decoder('x', noopDecoder, 'utf-8', 'unknown'); },
1094
+ 'this should never happen! type: unknown',
1095
+ 'decoder throws for unexpected type'
1096
+ );
1097
+
1035
1098
  st.end();
1036
1099
  });
1037
1100
 
@@ -1061,6 +1124,14 @@ test('parse()', function (t) {
1061
1124
  new RangeError('Parameter limit exceeded. Only 3 parameters allowed.'),
1062
1125
  'throws error when parameter limit is exceeded'
1063
1126
  );
1127
+
1128
+ sst['throws'](
1129
+ function () {
1130
+ qs.parse('a=1&b=2', { parameterLimit: 1, throwOnLimitExceeded: true });
1131
+ },
1132
+ new RangeError('Parameter limit exceeded. Only 1 parameter allowed.'),
1133
+ 'throws error with singular "parameter" when parameterLimit is 1'
1134
+ );
1064
1135
  sst.end();
1065
1136
  });
1066
1137
 
@@ -1082,6 +1153,12 @@ test('parse()', function (t) {
1082
1153
  sst.end();
1083
1154
  });
1084
1155
 
1156
+ st.test('allows unlimited parameters when parameterLimit is Infinity and throwOnLimitExceeded is true', function (sst) {
1157
+ var result = qs.parse('a=1&b=2&c=3&d=4&e=5&f=6', { parameterLimit: Infinity, throwOnLimitExceeded: true });
1158
+ sst.deepEqual(result, { a: '1', b: '2', c: '3', d: '4', e: '5', f: '6' }, 'parses all parameters without truncation or throwing');
1159
+ sst.end();
1160
+ });
1161
+
1085
1162
  st.end();
1086
1163
  });
1087
1164
 
@@ -1104,6 +1181,7 @@ test('parse()', function (t) {
1104
1181
  });
1105
1182
 
1106
1183
  st.test('throws error when array limit exceeded', function (sst) {
1184
+ // 4 elements exceeds limit of 3
1107
1185
  sst['throws'](
1108
1186
  function () {
1109
1187
  qs.parse('a[]=1&a[]=2&a[]=3&a[]=4', { arrayLimit: 3, throwOnLimitExceeded: true });
@@ -1114,6 +1192,14 @@ test('parse()', function (t) {
1114
1192
  sst.end();
1115
1193
  });
1116
1194
 
1195
+ st.test('does not throw when at limit', function (sst) {
1196
+ // 3 elements = limit of 3, should not throw
1197
+ var result = qs.parse('a[]=1&a[]=2&a[]=3', { arrayLimit: 3, throwOnLimitExceeded: true });
1198
+ sst.ok(Array.isArray(result.a), 'result is an array');
1199
+ sst.deepEqual(result.a, ['1', '2', '3'], 'all values present');
1200
+ sst.end();
1201
+ });
1202
+
1117
1203
  st.test('converts array to object if length is greater than limit', function (sst) {
1118
1204
  var result = qs.parse('a[1]=1&a[2]=2&a[3]=3&a[4]=4&a[5]=5&a[6]=6', { arrayLimit: 5 });
1119
1205
 
@@ -1121,6 +1207,59 @@ test('parse()', function (t) {
1121
1207
  sst.end();
1122
1208
  });
1123
1209
 
1210
+ st.test('throws error when indexed notation exceeds arrayLimit with throwOnLimitExceeded', function (sst) {
1211
+ sst['throws'](
1212
+ function () {
1213
+ qs.parse('a[1001]=b', { arrayLimit: 1000, throwOnLimitExceeded: true });
1214
+ },
1215
+ new RangeError('Array limit exceeded. Only 1000 elements allowed in an array.'),
1216
+ 'throws error for a single index exceeding arrayLimit'
1217
+ );
1218
+
1219
+ sst['throws'](
1220
+ function () {
1221
+ qs.parse('a[0]=1&a[1]=2&a[2]=3&a[10]=4', { arrayLimit: 6, throwOnLimitExceeded: true, allowSparse: true });
1222
+ },
1223
+ new RangeError('Array limit exceeded. Only 6 elements allowed in an array.'),
1224
+ 'throws error when a sparse index exceeds arrayLimit'
1225
+ );
1226
+
1227
+ sst['throws'](
1228
+ function () {
1229
+ qs.parse('a[2]=b', { arrayLimit: 1, throwOnLimitExceeded: true });
1230
+ },
1231
+ new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
1232
+ 'throws error with singular "element" when arrayLimit is 1'
1233
+ );
1234
+
1235
+ sst.end();
1236
+ });
1237
+
1238
+ st.test('does not throw for indexed notation within arrayLimit with throwOnLimitExceeded', function (sst) {
1239
+ var result = qs.parse('a[4]=b', { arrayLimit: 5, throwOnLimitExceeded: true, allowSparse: true });
1240
+ sst.ok(Array.isArray(result.a), 'result is an array');
1241
+ sst.equal(result.a.length, 5, 'array has correct length');
1242
+ sst.equal(result.a[4], 'b', 'value at index 4 is correct');
1243
+ sst.end();
1244
+ });
1245
+
1246
+ st.test('silently converts to object for indexed notation exceeding arrayLimit without throwOnLimitExceeded', function (sst) {
1247
+ var result = qs.parse('a[1001]=b', { arrayLimit: 1000 });
1248
+ sst.deepEqual(result, { a: { 1001: 'b' } }, 'converts to object without throwing');
1249
+ sst.end();
1250
+ });
1251
+
1252
+ st.test('throws when duplicate bracket keys exceed arrayLimit with throwOnLimitExceeded', function (sst) {
1253
+ sst['throws'](
1254
+ function () {
1255
+ qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5, throwOnLimitExceeded: true });
1256
+ },
1257
+ new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
1258
+ 'throws error when duplicate bracket notation exceeds array limit'
1259
+ );
1260
+ sst.end();
1261
+ });
1262
+
1124
1263
  st.end();
1125
1264
  });
1126
1265
 
@@ -1172,6 +1311,34 @@ test('`duplicates` option', function (t) {
1172
1311
  'duplicates: last'
1173
1312
  );
1174
1313
 
1314
+ t.test('bracket notation always combines regardless of duplicates', function (st) {
1315
+ st.deepEqual(
1316
+ qs.parse('a=1&a=2&b[]=1&b[]=2', { duplicates: 'last' }),
1317
+ { a: '2', b: ['1', '2'] },
1318
+ 'duplicates last: unbracketed takes last, bracketed combines'
1319
+ );
1320
+
1321
+ st.deepEqual(
1322
+ qs.parse('b[]=1&b[]=2', { duplicates: 'last' }),
1323
+ { b: ['1', '2'] },
1324
+ 'duplicates last: bracketed always combines'
1325
+ );
1326
+
1327
+ st.deepEqual(
1328
+ qs.parse('b[]=1&b[]=2', { duplicates: 'first' }),
1329
+ { b: ['1', '2'] },
1330
+ 'duplicates first: bracketed always combines'
1331
+ );
1332
+
1333
+ st.deepEqual(
1334
+ qs.parse('a=1&a=2&b[]=1&b[]=2', { duplicates: 'first' }),
1335
+ { a: '1', b: ['1', '2'] },
1336
+ 'duplicates first: unbracketed takes first, bracketed combines'
1337
+ );
1338
+
1339
+ st.end();
1340
+ });
1341
+
1175
1342
  t.end();
1176
1343
  });
1177
1344
 
@@ -1274,3 +1441,214 @@ test('qs strictDepth option - non-throw cases', function (t) {
1274
1441
  st.end();
1275
1442
  });
1276
1443
  });
1444
+
1445
+ test('DOS', function (t) {
1446
+ var arr = [];
1447
+ for (var i = 0; i < 105; i++) {
1448
+ arr[arr.length] = 'x';
1449
+ }
1450
+ var attack = 'a[]=' + arr.join('&a[]=');
1451
+ var result = qs.parse(attack, { arrayLimit: 100 });
1452
+
1453
+ t.notOk(Array.isArray(result.a), 'arrayLimit is respected: result is an object, not an array');
1454
+ t.equal(Object.keys(result.a).length, 105, 'all values are preserved');
1455
+
1456
+ t.end();
1457
+ });
1458
+
1459
+ test('arrayLimit boundary conditions', function (t) {
1460
+ // arrayLimit is the max number of elements allowed in an array
1461
+ t.test('exactly at the limit stays as array', function (st) {
1462
+ // 3 elements = limit of 3
1463
+ var result = qs.parse('a[]=1&a[]=2&a[]=3', { arrayLimit: 3 });
1464
+ st.ok(Array.isArray(result.a), 'result is an array when count equals limit');
1465
+ st.deepEqual(result.a, ['1', '2', '3'], 'all values present');
1466
+ st.end();
1467
+ });
1468
+
1469
+ t.test('one over the limit converts to object', function (st) {
1470
+ // 4 elements exceeds limit of 3
1471
+ var result = qs.parse('a[]=1&a[]=2&a[]=3&a[]=4', { arrayLimit: 3 });
1472
+ st.notOk(Array.isArray(result.a), 'result is not an array when over limit');
1473
+ st.deepEqual(result.a, { 0: '1', 1: '2', 2: '3', 3: '4' }, 'all values preserved as object');
1474
+ st.end();
1475
+ });
1476
+
1477
+ t.test('arrayLimit 1 with one value', function (st) {
1478
+ // 1 element = limit of 1
1479
+ var result = qs.parse('a[]=1', { arrayLimit: 1 });
1480
+ st.ok(Array.isArray(result.a), 'result is an array when count equals limit');
1481
+ st.deepEqual(result.a, ['1'], 'value preserved as array');
1482
+ st.end();
1483
+ });
1484
+
1485
+ t.test('arrayLimit 1 with two values converts to object', function (st) {
1486
+ // 2 elements exceeds limit of 1
1487
+ var result = qs.parse('a[]=1&a[]=2', { arrayLimit: 1 });
1488
+ st.notOk(Array.isArray(result.a), 'result is not an array');
1489
+ st.deepEqual(result.a, { 0: '1', 1: '2' }, 'all values preserved as object');
1490
+ st.end();
1491
+ });
1492
+
1493
+ t.end();
1494
+ });
1495
+
1496
+ test('comma + arrayLimit', function (t) {
1497
+ t.test('comma-separated values within arrayLimit stay as array', function (st) {
1498
+ var result = qs.parse('a=1,2,3', { comma: true, arrayLimit: 5 });
1499
+ st.ok(Array.isArray(result.a), 'result is an array');
1500
+ st.deepEqual(result.a, ['1', '2', '3'], 'all values present');
1501
+ st.end();
1502
+ });
1503
+
1504
+ t.test('comma-separated values exceeding arrayLimit convert to object', function (st) {
1505
+ var result = qs.parse('a=1,2,3,4', { comma: true, arrayLimit: 3 });
1506
+ st.notOk(Array.isArray(result.a), 'result is not an array when over limit');
1507
+ st.deepEqual(result.a, { 0: '1', 1: '2', 2: '3', 3: '4' }, 'all values preserved as object');
1508
+ st.end();
1509
+ });
1510
+
1511
+ t.test('comma-separated values exceeding arrayLimit with throwOnLimitExceeded throws', function (st) {
1512
+ st['throws'](
1513
+ function () {
1514
+ qs.parse('a=1,2,3,4', { comma: true, arrayLimit: 3, throwOnLimitExceeded: true });
1515
+ },
1516
+ new RangeError('Array limit exceeded. Only 3 elements allowed in an array.'),
1517
+ 'throws error when comma-split exceeds array limit'
1518
+ );
1519
+
1520
+ st['throws'](
1521
+ function () {
1522
+ qs.parse('a=1,2,3', { comma: true, arrayLimit: 1, throwOnLimitExceeded: true });
1523
+ },
1524
+ new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
1525
+ 'throws error with singular "element" when arrayLimit is 1'
1526
+ );
1527
+ st.end();
1528
+ });
1529
+
1530
+ t.test('comma-separated values at exactly arrayLimit stay as array', function (st) {
1531
+ var result = qs.parse('a=1,2,3', { comma: true, arrayLimit: 3 });
1532
+ st.ok(Array.isArray(result.a), 'result is an array when exactly at limit');
1533
+ st.deepEqual(result.a, ['1', '2', '3'], 'all values present');
1534
+ st.end();
1535
+ });
1536
+
1537
+ t.end();
1538
+ });
1539
+
1540
+ test('mixed array and object notation', function (t) {
1541
+ t.test('array brackets with object key - under limit', function (st) {
1542
+ st.deepEqual(
1543
+ qs.parse('a[]=b&a[c]=d'),
1544
+ { a: { 0: 'b', c: 'd' } },
1545
+ 'mixing [] and [key] converts to object'
1546
+ );
1547
+ st.end();
1548
+ });
1549
+
1550
+ t.test('array index with object key - under limit', function (st) {
1551
+ st.deepEqual(
1552
+ qs.parse('a[0]=b&a[c]=d'),
1553
+ { a: { 0: 'b', c: 'd' } },
1554
+ 'mixing [0] and [key] produces object'
1555
+ );
1556
+ st.end();
1557
+ });
1558
+
1559
+ t.test('plain value with array brackets - under limit', function (st) {
1560
+ st.deepEqual(
1561
+ qs.parse('a=b&a[]=c', { arrayLimit: 20 }),
1562
+ { a: ['b', 'c'] },
1563
+ 'plain value combined with [] stays as array under limit'
1564
+ );
1565
+ st.end();
1566
+ });
1567
+
1568
+ t.test('array brackets with plain value - under limit', function (st) {
1569
+ st.deepEqual(
1570
+ qs.parse('a[]=b&a=c', { arrayLimit: 20 }),
1571
+ { a: ['b', 'c'] },
1572
+ '[] combined with plain value stays as array under limit'
1573
+ );
1574
+ st.end();
1575
+ });
1576
+
1577
+ t.test('plain value with array index - under limit', function (st) {
1578
+ st.deepEqual(
1579
+ qs.parse('a=b&a[0]=c', { arrayLimit: 20 }),
1580
+ { a: ['b', 'c'] },
1581
+ 'plain value combined with [0] stays as array under limit'
1582
+ );
1583
+ st.end();
1584
+ });
1585
+
1586
+ t.test('multiple plain values with duplicates combine', function (st) {
1587
+ st.deepEqual(
1588
+ qs.parse('a=b&a=c&a=d', { arrayLimit: 20 }),
1589
+ { a: ['b', 'c', 'd'] },
1590
+ 'duplicate plain keys combine into array'
1591
+ );
1592
+ st.end();
1593
+ });
1594
+
1595
+ t.test('multiple plain values exceeding limit', function (st) {
1596
+ // 3 elements (indices 0-2), max index 2 > limit 1
1597
+ st.deepEqual(
1598
+ qs.parse('a=b&a=c&a=d', { arrayLimit: 1 }),
1599
+ { a: { 0: 'b', 1: 'c', 2: 'd' } },
1600
+ 'duplicate plain keys convert to object when exceeding limit'
1601
+ );
1602
+ st.end();
1603
+ });
1604
+
1605
+ t.test('mixed notation produces consistent results when arrayLimit is exceeded', function (st) {
1606
+ var expected = { a: { 0: 'b', 1: 'c', 2: 'd' } };
1607
+
1608
+ st.deepEqual(
1609
+ qs.parse('a[]=b&a[1]=c&a=d', { arrayLimit: -1 }),
1610
+ expected,
1611
+ 'arrayLimit -1'
1612
+ );
1613
+
1614
+ st.deepEqual(
1615
+ qs.parse('a[]=b&a[1]=c&a=d', { arrayLimit: 0 }),
1616
+ expected,
1617
+ 'arrayLimit 0'
1618
+ );
1619
+
1620
+ st.deepEqual(
1621
+ qs.parse('a[]=b&a[1]=c&a=d', { arrayLimit: 1 }),
1622
+ expected,
1623
+ 'arrayLimit 1'
1624
+ );
1625
+
1626
+ st.end();
1627
+ });
1628
+
1629
+ t.test('uses existing array length for currentArrayLength when parsing object input with bracket keys', function (st) {
1630
+ var input = {};
1631
+ var arr = ['x', 'y'];
1632
+ arr.a = ['z', 'w'];
1633
+ input['a[]'] = arr;
1634
+ st.deepEqual(qs.parse(input), { a: ['x', 'y'] }, 'parses object input with bracket keys using existing array values');
1635
+ st.end();
1636
+ });
1637
+
1638
+ t.test('throws with singular message when object input bracket key exceeds arrayLimit of 1', function (st) {
1639
+ var input = {};
1640
+ var arr = ['x'];
1641
+ arr.a = ['z', 'w'];
1642
+ input['a[]'] = arr;
1643
+ st['throws'](
1644
+ function () {
1645
+ qs.parse(input, { throwOnLimitExceeded: true, arrayLimit: 1 });
1646
+ },
1647
+ new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
1648
+ 'throws singular error for object input exceeding arrayLimit 1'
1649
+ );
1650
+ st.end();
1651
+ });
1652
+
1653
+ t.end();
1654
+ });
@@ -1188,6 +1188,15 @@ test('stringify()', function (t) {
1188
1188
  };
1189
1189
 
1190
1190
  st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
1191
+
1192
+ var noopEncoder = function () { return 'x'; };
1193
+ noopEncoder();
1194
+ st['throws'](
1195
+ function () { encoder('x', noopEncoder, 'utf-8', 'unknown'); },
1196
+ 'this should never happen! type: unknown',
1197
+ 'encoder throws for unexpected type'
1198
+ );
1199
+
1191
1200
  st.end();
1192
1201
  });
1193
1202
 
@@ -1293,13 +1302,17 @@ test('stringifies empty keys', function (t) {
1293
1302
  });
1294
1303
 
1295
1304
  t.test('stringifies non-string keys', function (st) {
1296
- var actual = qs.stringify({ a: 'b', 'false': {} }, {
1297
- filter: ['a', false, null],
1305
+ var S = Object('abc');
1306
+ S.toString = function () {
1307
+ return 'd';
1308
+ };
1309
+ var actual = qs.stringify({ a: 'b', 'false': {}, 1e+22: 'c', d: 'e' }, {
1310
+ filter: ['a', false, null, 10000000000000000000000, S],
1298
1311
  allowDots: true,
1299
1312
  encodeDotInKeys: true
1300
1313
  });
1301
1314
 
1302
- st.equal(actual, 'a=b', 'stringifies correctly');
1315
+ st.equal(actual, 'a=b&1e%2B22=c&d=e', 'stringifies correctly');
1303
1316
 
1304
1317
  st.end();
1305
1318
  });