@lizardbyte/contribkit 2025.1130.1103 → 2026.518.124816

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ContribKit
2
2
 
3
3
  [![GitHub stars](https://img.shields.io/github/stars/lizardbyte/contribkit.svg?logo=github&style=for-the-badge)](https://github.com/LizardByte/contribkit)
4
- [![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/contribkit/_ci-node.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/contribkit/actions/workflows/CI.yml?query=branch%3Amaster)
4
+ [![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/contribkit/_ci-node.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/contribkit/actions/workflows/_ci-node.yml?query=branch%3Amaster)
5
5
  [![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/contribkit?token=WBivqQDwFw&style=for-the-badge&logo=codecov&label=codecov)](https://codecov.io/gh/LizardByte/contribkit)
6
6
  [![NPM Monthly Downloads](https://img.shields.io/npm/dm/%40lizardbyte%2Fcontribkit?style=for-the-badge&logo=npm&label=npm%20downloads/m)](https://www.npmjs.com/package/@lizardbyte/contribkit)
7
7
  [![NPM Version](https://img.shields.io/npm/v/%40lizardbyte%2Fcontribkit?style=for-the-badge&logo=npm&label=npm%20version)](https://www.npmjs.com/package/@lizardbyte/contribkit)
@@ -90,61 +90,70 @@ class Queue {
90
90
  }
91
91
 
92
92
  function pLimit(concurrency) {
93
+ let rejectOnClear = false;
94
+
95
+ if (typeof concurrency === 'object') {
96
+ ({concurrency, rejectOnClear = false} = concurrency);
97
+ }
98
+
93
99
  validateConcurrency(concurrency);
94
100
 
101
+ if (typeof rejectOnClear !== 'boolean') {
102
+ throw new TypeError('Expected `rejectOnClear` to be a boolean');
103
+ }
104
+
95
105
  const queue = new Queue();
96
106
  let activeCount = 0;
97
107
 
98
108
  const resumeNext = () => {
109
+ // Process the next queued function if we're under the concurrency limit
99
110
  if (activeCount < concurrency && queue.size > 0) {
100
- queue.dequeue()();
101
- // Since `pendingCount` has been decreased by one, increase `activeCount` by one.
102
111
  activeCount++;
112
+ queue.dequeue().run();
103
113
  }
104
114
  };
105
115
 
106
116
  const next = () => {
107
117
  activeCount--;
108
-
109
118
  resumeNext();
110
119
  };
111
120
 
112
121
  const run = async (function_, resolve, arguments_) => {
122
+ // Execute the function and capture the result promise
113
123
  const result = (async () => function_(...arguments_))();
114
124
 
125
+ // Resolve immediately with the promise (don't wait for completion)
115
126
  resolve(result);
116
127
 
128
+ // Wait for the function to complete (success or failure)
129
+ // We catch errors here to prevent unhandled rejections,
130
+ // but the original promise rejection is preserved for the caller
117
131
  try {
118
132
  await result;
119
133
  } catch {}
120
134
 
135
+ // Decrement active count and process next queued function
121
136
  next();
122
137
  };
123
138
 
124
- const enqueue = (function_, resolve, arguments_) => {
125
- // Queue `internalResolve` instead of the `run` function
126
- // to preserve asynchronous context.
127
- new Promise(internalResolve => {
128
- queue.enqueue(internalResolve);
129
- }).then(
130
- run.bind(undefined, function_, resolve, arguments_),
131
- );
132
-
133
- (async () => {
134
- // This function needs to wait until the next microtask before comparing
135
- // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
136
- // after the `internalResolve` function is dequeued and called. The comparison in the if-statement
137
- // needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
138
- await Promise.resolve();
139
-
140
- if (activeCount < concurrency) {
141
- resumeNext();
142
- }
143
- })();
139
+ const enqueue = (function_, resolve, reject, arguments_) => {
140
+ const queueItem = {reject};
141
+
142
+ // Queue the internal resolve function instead of the run function
143
+ // to preserve the asynchronous execution context.
144
+ new Promise(internalResolve => { // eslint-disable-line promise/param-names
145
+ queueItem.run = internalResolve;
146
+ queue.enqueue(queueItem);
147
+ }).then(run.bind(undefined, function_, resolve, arguments_)); // eslint-disable-line promise/prefer-await-to-then
148
+
149
+ // Start processing immediately if we haven't reached the concurrency limit
150
+ if (activeCount < concurrency) {
151
+ resumeNext();
152
+ }
144
153
  };
145
154
 
146
- const generator = (function_, ...arguments_) => new Promise(resolve => {
147
- enqueue(function_, resolve, arguments_);
155
+ const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
156
+ enqueue(function_, resolve, reject, arguments_);
148
157
  });
149
158
 
150
159
  Object.defineProperties(generator, {
@@ -156,7 +165,16 @@ function pLimit(concurrency) {
156
165
  },
157
166
  clearQueue: {
158
167
  value() {
159
- queue.clear();
168
+ if (!rejectOnClear) {
169
+ queue.clear();
170
+ return;
171
+ }
172
+
173
+ const abortError = AbortSignal.abort().reason;
174
+
175
+ while (queue.size > 0) {
176
+ queue.dequeue().reject(abortError);
177
+ }
160
178
  },
161
179
  },
162
180
  concurrency: {
@@ -174,6 +192,12 @@ function pLimit(concurrency) {
174
192
  });
175
193
  },
176
194
  },
195
+ map: {
196
+ async value(iterable, function_) {
197
+ const promises = Array.from(iterable, (value, index) => this(function_, value, index));
198
+ return Promise.all(promises);
199
+ },
200
+ },
177
201
  });
178
202
 
179
203
  return generator;
@@ -193,48 +193,6 @@ function requireLodash_escaperegexp () {
193
193
  return lodash_escaperegexp;
194
194
  }
195
195
 
196
- /**
197
- * lodash 4.0.0 (Custom Build) <https://lodash.com/>
198
- * Build: `lodash modularize exports="npm" -o ./`
199
- * Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>
200
- * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
201
- * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
202
- * Available under MIT license <https://lodash.com/license>
203
- */
204
-
205
- var lodash_isnil;
206
- var hasRequiredLodash_isnil;
207
-
208
- function requireLodash_isnil () {
209
- if (hasRequiredLodash_isnil) return lodash_isnil;
210
- hasRequiredLodash_isnil = 1;
211
- /**
212
- * Checks if `value` is `null` or `undefined`.
213
- *
214
- * @static
215
- * @memberOf _
216
- * @category Lang
217
- * @param {*} value The value to check.
218
- * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
219
- * @example
220
- *
221
- * _.isNil(null);
222
- * // => true
223
- *
224
- * _.isNil(void 0);
225
- * // => true
226
- *
227
- * _.isNil(NaN);
228
- * // => false
229
- */
230
- function isNil(value) {
231
- return value == null;
232
- }
233
-
234
- lodash_isnil = isNil;
235
- return lodash_isnil;
236
- }
237
-
238
196
  var hasRequiredParserOptions;
239
197
 
240
198
  function requireParserOptions () {
@@ -246,7 +204,6 @@ function requireParserOptions () {
246
204
  Object.defineProperty(ParserOptions, "__esModule", { value: true });
247
205
  ParserOptions.ParserOptions = void 0;
248
206
  const lodash_escaperegexp_1 = __importDefault(requireLodash_escaperegexp());
249
- const lodash_isnil_1 = __importDefault(requireLodash_isnil());
250
207
  let ParserOptions$1 = class ParserOptions {
251
208
  escapedDelimiter;
252
209
  objectMode = true;
@@ -278,7 +235,7 @@ function requireParserOptions () {
278
235
  }
279
236
  this.escapedDelimiter = (0, lodash_escaperegexp_1.default)(this.delimiter);
280
237
  this.escapeChar = this.escape ?? this.quote;
281
- this.supportsComments = !(0, lodash_isnil_1.default)(this.comment);
238
+ this.supportsComments = this.comment != null;
282
239
  this.NEXT_TOKEN_REGEXP = new RegExp(`([^\\s]|\\r\\n|\\n|\\r|${this.escapedDelimiter})`);
283
240
  if (this.maxRows > 0) {
284
241
  this.limitRows = true;
@@ -296,170 +253,6 @@ var transforms = {};
296
253
 
297
254
  var RowTransformerValidator = {};
298
255
 
299
- /**
300
- * Lodash (Custom Build) <https://lodash.com/>
301
- * Build: `lodash modularize exports="npm" -o ./`
302
- * Copyright JS Foundation and other contributors <https://js.foundation/>
303
- * Released under MIT license <https://lodash.com/license>
304
- * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
305
- * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
306
- */
307
-
308
- var lodash_isfunction;
309
- var hasRequiredLodash_isfunction;
310
-
311
- function requireLodash_isfunction () {
312
- if (hasRequiredLodash_isfunction) return lodash_isfunction;
313
- hasRequiredLodash_isfunction = 1;
314
- /** `Object#toString` result references. */
315
- var asyncTag = '[object AsyncFunction]',
316
- funcTag = '[object Function]',
317
- genTag = '[object GeneratorFunction]',
318
- nullTag = '[object Null]',
319
- proxyTag = '[object Proxy]',
320
- undefinedTag = '[object Undefined]';
321
-
322
- /** Detect free variable `global` from Node.js. */
323
- var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
324
-
325
- /** Detect free variable `self`. */
326
- var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
327
-
328
- /** Used as a reference to the global object. */
329
- var root = freeGlobal || freeSelf || Function('return this')();
330
-
331
- /** Used for built-in method references. */
332
- var objectProto = Object.prototype;
333
-
334
- /** Used to check objects for own properties. */
335
- var hasOwnProperty = objectProto.hasOwnProperty;
336
-
337
- /**
338
- * Used to resolve the
339
- * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
340
- * of values.
341
- */
342
- var nativeObjectToString = objectProto.toString;
343
-
344
- /** Built-in value references. */
345
- var Symbol = root.Symbol,
346
- symToStringTag = Symbol ? Symbol.toStringTag : undefined;
347
-
348
- /**
349
- * The base implementation of `getTag` without fallbacks for buggy environments.
350
- *
351
- * @private
352
- * @param {*} value The value to query.
353
- * @returns {string} Returns the `toStringTag`.
354
- */
355
- function baseGetTag(value) {
356
- if (value == null) {
357
- return value === undefined ? undefinedTag : nullTag;
358
- }
359
- return (symToStringTag && symToStringTag in Object(value))
360
- ? getRawTag(value)
361
- : objectToString(value);
362
- }
363
-
364
- /**
365
- * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
366
- *
367
- * @private
368
- * @param {*} value The value to query.
369
- * @returns {string} Returns the raw `toStringTag`.
370
- */
371
- function getRawTag(value) {
372
- var isOwn = hasOwnProperty.call(value, symToStringTag),
373
- tag = value[symToStringTag];
374
-
375
- try {
376
- value[symToStringTag] = undefined;
377
- var unmasked = true;
378
- } catch (e) {}
379
-
380
- var result = nativeObjectToString.call(value);
381
- if (unmasked) {
382
- if (isOwn) {
383
- value[symToStringTag] = tag;
384
- } else {
385
- delete value[symToStringTag];
386
- }
387
- }
388
- return result;
389
- }
390
-
391
- /**
392
- * Converts `value` to a string using `Object.prototype.toString`.
393
- *
394
- * @private
395
- * @param {*} value The value to convert.
396
- * @returns {string} Returns the converted string.
397
- */
398
- function objectToString(value) {
399
- return nativeObjectToString.call(value);
400
- }
401
-
402
- /**
403
- * Checks if `value` is classified as a `Function` object.
404
- *
405
- * @static
406
- * @memberOf _
407
- * @since 0.1.0
408
- * @category Lang
409
- * @param {*} value The value to check.
410
- * @returns {boolean} Returns `true` if `value` is a function, else `false`.
411
- * @example
412
- *
413
- * _.isFunction(_);
414
- * // => true
415
- *
416
- * _.isFunction(/abc/);
417
- * // => false
418
- */
419
- function isFunction(value) {
420
- if (!isObject(value)) {
421
- return false;
422
- }
423
- // The use of `Object#toString` avoids issues with the `typeof` operator
424
- // in Safari 9 which returns 'object' for typed arrays and other constructors.
425
- var tag = baseGetTag(value);
426
- return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
427
- }
428
-
429
- /**
430
- * Checks if `value` is the
431
- * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
432
- * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
433
- *
434
- * @static
435
- * @memberOf _
436
- * @since 0.1.0
437
- * @category Lang
438
- * @param {*} value The value to check.
439
- * @returns {boolean} Returns `true` if `value` is an object, else `false`.
440
- * @example
441
- *
442
- * _.isObject({});
443
- * // => true
444
- *
445
- * _.isObject([1, 2, 3]);
446
- * // => true
447
- *
448
- * _.isObject(_.noop);
449
- * // => true
450
- *
451
- * _.isObject(null);
452
- * // => false
453
- */
454
- function isObject(value) {
455
- var type = typeof value;
456
- return value != null && (type == 'object' || type == 'function');
457
- }
458
-
459
- lodash_isfunction = isFunction;
460
- return lodash_isfunction;
461
- }
462
-
463
256
  var types = {};
464
257
 
465
258
  var hasRequiredTypes;
@@ -486,15 +279,10 @@ var hasRequiredRowTransformerValidator;
486
279
  function requireRowTransformerValidator () {
487
280
  if (hasRequiredRowTransformerValidator) return RowTransformerValidator;
488
281
  hasRequiredRowTransformerValidator = 1;
489
- var __importDefault = (RowTransformerValidator && RowTransformerValidator.__importDefault) || function (mod) {
490
- return (mod && mod.__esModule) ? mod : { "default": mod };
491
- };
492
282
  Object.defineProperty(RowTransformerValidator, "__esModule", { value: true });
493
283
  RowTransformerValidator.RowTransformerValidator = void 0;
494
- const lodash_isfunction_1 = __importDefault(requireLodash_isfunction());
495
284
  const types_1 = requireTypes();
496
285
  let RowTransformerValidator$1 = class RowTransformerValidator {
497
- // eslint-disable-next-line @typescript-eslint/no-shadow
498
286
  static createTransform(transformFunction) {
499
287
  if ((0, types_1.isSyncTransform)(transformFunction)) {
500
288
  return (row, cb) => {
@@ -531,13 +319,13 @@ function requireRowTransformerValidator () {
531
319
  _rowTransform = null;
532
320
  _rowValidator = null;
533
321
  set rowTransform(transformFunction) {
534
- if (!(0, lodash_isfunction_1.default)(transformFunction)) {
322
+ if (typeof transformFunction !== 'function') {
535
323
  throw new TypeError('The transform should be a function');
536
324
  }
537
325
  this._rowTransform = RowTransformerValidator.createTransform(transformFunction);
538
326
  }
539
327
  set rowValidator(validateFunction) {
540
- if (!(0, lodash_isfunction_1.default)(validateFunction)) {
328
+ if (typeof validateFunction !== 'function') {
541
329
  throw new TypeError('The validate should be a function');
542
330
  }
543
331
  this._rowValidator = RowTransformerValidator.createValidator(validateFunction);
@@ -581,45 +369,6 @@ function requireRowTransformerValidator () {
581
369
 
582
370
  var HeaderTransformer = {};
583
371
 
584
- /**
585
- * lodash 3.0.1 (Custom Build) <https://lodash.com/>
586
- * Build: `lodash modern modularize exports="npm" -o ./`
587
- * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
588
- * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
589
- * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
590
- * Available under MIT license <https://lodash.com/license>
591
- */
592
-
593
- var lodash_isundefined;
594
- var hasRequiredLodash_isundefined;
595
-
596
- function requireLodash_isundefined () {
597
- if (hasRequiredLodash_isundefined) return lodash_isundefined;
598
- hasRequiredLodash_isundefined = 1;
599
- /**
600
- * Checks if `value` is `undefined`.
601
- *
602
- * @static
603
- * @memberOf _
604
- * @category Lang
605
- * @param {*} value The value to check.
606
- * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
607
- * @example
608
- *
609
- * _.isUndefined(void 0);
610
- * // => true
611
- *
612
- * _.isUndefined(null);
613
- * // => false
614
- */
615
- function isUndefined(value) {
616
- return value === undefined;
617
- }
618
-
619
- lodash_isundefined = isUndefined;
620
- return lodash_isundefined;
621
- }
622
-
623
372
  /**
624
373
  * lodash (Custom Build) <https://lodash.com/>
625
374
  * Build: `lodash modularize exports="npm" -o ./`
@@ -1514,7 +1263,7 @@ var hasRequiredLodash_groupby;
1514
1263
  function requireLodash_groupby () {
1515
1264
  if (hasRequiredLodash_groupby) return lodash_groupby.exports;
1516
1265
  hasRequiredLodash_groupby = 1;
1517
- (function (module, exports$1) {
1266
+ (function (module, exports) {
1518
1267
  /** Used as the size to enable large array optimizations. */
1519
1268
  var LARGE_ARRAY_SIZE = 200;
1520
1269
 
@@ -1608,7 +1357,7 @@ function requireLodash_groupby () {
1608
1357
  var root = freeGlobal || freeSelf || Function('return this')();
1609
1358
 
1610
1359
  /** Detect free variable `exports`. */
1611
- var freeExports = exports$1 && !exports$1.nodeType && exports$1;
1360
+ var freeExports = exports && !exports.nodeType && exports;
1612
1361
 
1613
1362
  /** Detect free variable `module`. */
1614
1363
  var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
@@ -3885,8 +3634,6 @@ function requireHeaderTransformer () {
3885
3634
  };
3886
3635
  Object.defineProperty(HeaderTransformer, "__esModule", { value: true });
3887
3636
  HeaderTransformer.HeaderTransformer = void 0;
3888
- const lodash_isundefined_1 = __importDefault(requireLodash_isundefined());
3889
- const lodash_isfunction_1 = __importDefault(requireLodash_isfunction());
3890
3637
  const lodash_uniq_1 = __importDefault(requireLodash_uniq());
3891
3638
  const lodash_groupby_1 = __importDefault(requireLodash_groupby());
3892
3639
  let HeaderTransformer$1 = class HeaderTransformer {
@@ -3905,7 +3652,7 @@ function requireHeaderTransformer () {
3905
3652
  else if (Array.isArray(parserOptions.headers)) {
3906
3653
  this.setHeaders(parserOptions.headers);
3907
3654
  }
3908
- else if ((0, lodash_isfunction_1.default)(parserOptions.headers)) {
3655
+ else if (typeof parserOptions.headers === 'function') {
3909
3656
  this.headersTransform = parserOptions.headers;
3910
3657
  }
3911
3658
  }
@@ -3968,10 +3715,9 @@ function requireHeaderTransformer () {
3968
3715
  const { headers, headersLength } = this;
3969
3716
  for (let i = 0; i < headersLength; i += 1) {
3970
3717
  const header = headers[i];
3971
- if (!(0, lodash_isundefined_1.default)(header)) {
3718
+ if (header !== undefined) {
3972
3719
  const val = row[i];
3973
- // eslint-disable-next-line no-param-reassign
3974
- if ((0, lodash_isundefined_1.default)(val)) {
3720
+ if (val === undefined) {
3975
3721
  rowMap[header] = '';
3976
3722
  }
3977
3723
  else {
@@ -4007,13 +3753,13 @@ var hasRequiredTransforms;
4007
3753
  function requireTransforms () {
4008
3754
  if (hasRequiredTransforms) return transforms;
4009
3755
  hasRequiredTransforms = 1;
4010
- (function (exports$1) {
4011
- Object.defineProperty(exports$1, "__esModule", { value: true });
4012
- exports$1.HeaderTransformer = exports$1.RowTransformerValidator = void 0;
3756
+ (function (exports) {
3757
+ Object.defineProperty(exports, "__esModule", { value: true });
3758
+ exports.HeaderTransformer = exports.RowTransformerValidator = void 0;
4013
3759
  var RowTransformerValidator_1 = requireRowTransformerValidator();
4014
- Object.defineProperty(exports$1, "RowTransformerValidator", { enumerable: true, get: function () { return RowTransformerValidator_1.RowTransformerValidator; } });
3760
+ Object.defineProperty(exports, "RowTransformerValidator", { enumerable: true, get: function () { return RowTransformerValidator_1.RowTransformerValidator; } });
4015
3761
  var HeaderTransformer_1 = requireHeaderTransformer();
4016
- Object.defineProperty(exports$1, "HeaderTransformer", { enumerable: true, get: function () { return HeaderTransformer_1.HeaderTransformer; } });
3762
+ Object.defineProperty(exports, "HeaderTransformer", { enumerable: true, get: function () { return HeaderTransformer_1.HeaderTransformer; } });
4017
3763
 
4018
3764
  } (transforms));
4019
3765
  return transforms;
@@ -4392,17 +4138,17 @@ var hasRequiredColumn;
4392
4138
  function requireColumn () {
4393
4139
  if (hasRequiredColumn) return column;
4394
4140
  hasRequiredColumn = 1;
4395
- (function (exports$1) {
4396
- Object.defineProperty(exports$1, "__esModule", { value: true });
4397
- exports$1.ColumnFormatter = exports$1.QuotedColumnParser = exports$1.NonQuotedColumnParser = exports$1.ColumnParser = void 0;
4141
+ (function (exports) {
4142
+ Object.defineProperty(exports, "__esModule", { value: true });
4143
+ exports.ColumnFormatter = exports.QuotedColumnParser = exports.NonQuotedColumnParser = exports.ColumnParser = void 0;
4398
4144
  var ColumnParser_1 = requireColumnParser();
4399
- Object.defineProperty(exports$1, "ColumnParser", { enumerable: true, get: function () { return ColumnParser_1.ColumnParser; } });
4145
+ Object.defineProperty(exports, "ColumnParser", { enumerable: true, get: function () { return ColumnParser_1.ColumnParser; } });
4400
4146
  var NonQuotedColumnParser_1 = requireNonQuotedColumnParser();
4401
- Object.defineProperty(exports$1, "NonQuotedColumnParser", { enumerable: true, get: function () { return NonQuotedColumnParser_1.NonQuotedColumnParser; } });
4147
+ Object.defineProperty(exports, "NonQuotedColumnParser", { enumerable: true, get: function () { return NonQuotedColumnParser_1.NonQuotedColumnParser; } });
4402
4148
  var QuotedColumnParser_1 = requireQuotedColumnParser();
4403
- Object.defineProperty(exports$1, "QuotedColumnParser", { enumerable: true, get: function () { return QuotedColumnParser_1.QuotedColumnParser; } });
4149
+ Object.defineProperty(exports, "QuotedColumnParser", { enumerable: true, get: function () { return QuotedColumnParser_1.QuotedColumnParser; } });
4404
4150
  var ColumnFormatter_1 = requireColumnFormatter();
4405
- Object.defineProperty(exports$1, "ColumnFormatter", { enumerable: true, get: function () { return ColumnFormatter_1.ColumnFormatter; } });
4151
+ Object.defineProperty(exports, "ColumnFormatter", { enumerable: true, get: function () { return ColumnFormatter_1.ColumnFormatter; } });
4406
4152
 
4407
4153
  } (column));
4408
4154
  return column;
@@ -4583,21 +4329,21 @@ var hasRequiredParser;
4583
4329
  function requireParser () {
4584
4330
  if (hasRequiredParser) return parser;
4585
4331
  hasRequiredParser = 1;
4586
- (function (exports$1) {
4587
- Object.defineProperty(exports$1, "__esModule", { value: true });
4588
- exports$1.QuotedColumnParser = exports$1.NonQuotedColumnParser = exports$1.ColumnParser = exports$1.Token = exports$1.Scanner = exports$1.RowParser = exports$1.Parser = void 0;
4332
+ (function (exports) {
4333
+ Object.defineProperty(exports, "__esModule", { value: true });
4334
+ exports.QuotedColumnParser = exports.NonQuotedColumnParser = exports.ColumnParser = exports.Token = exports.Scanner = exports.RowParser = exports.Parser = void 0;
4589
4335
  var Parser_1 = requireParser$1();
4590
- Object.defineProperty(exports$1, "Parser", { enumerable: true, get: function () { return Parser_1.Parser; } });
4336
+ Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return Parser_1.Parser; } });
4591
4337
  var RowParser_1 = requireRowParser();
4592
- Object.defineProperty(exports$1, "RowParser", { enumerable: true, get: function () { return RowParser_1.RowParser; } });
4338
+ Object.defineProperty(exports, "RowParser", { enumerable: true, get: function () { return RowParser_1.RowParser; } });
4593
4339
  var Scanner_1 = requireScanner();
4594
- Object.defineProperty(exports$1, "Scanner", { enumerable: true, get: function () { return Scanner_1.Scanner; } });
4340
+ Object.defineProperty(exports, "Scanner", { enumerable: true, get: function () { return Scanner_1.Scanner; } });
4595
4341
  var Token_1 = requireToken();
4596
- Object.defineProperty(exports$1, "Token", { enumerable: true, get: function () { return Token_1.Token; } });
4342
+ Object.defineProperty(exports, "Token", { enumerable: true, get: function () { return Token_1.Token; } });
4597
4343
  var column_1 = requireColumn();
4598
- Object.defineProperty(exports$1, "ColumnParser", { enumerable: true, get: function () { return column_1.ColumnParser; } });
4599
- Object.defineProperty(exports$1, "NonQuotedColumnParser", { enumerable: true, get: function () { return column_1.NonQuotedColumnParser; } });
4600
- Object.defineProperty(exports$1, "QuotedColumnParser", { enumerable: true, get: function () { return column_1.QuotedColumnParser; } });
4344
+ Object.defineProperty(exports, "ColumnParser", { enumerable: true, get: function () { return column_1.ColumnParser; } });
4345
+ Object.defineProperty(exports, "NonQuotedColumnParser", { enumerable: true, get: function () { return column_1.NonQuotedColumnParser; } });
4346
+ Object.defineProperty(exports, "QuotedColumnParser", { enumerable: true, get: function () { return column_1.QuotedColumnParser; } });
4601
4347
 
4602
4348
  } (parser));
4603
4349
  return parser;
@@ -4834,7 +4580,7 @@ var hasRequiredSrc;
4834
4580
  function requireSrc () {
4835
4581
  if (hasRequiredSrc) return src;
4836
4582
  hasRequiredSrc = 1;
4837
- (function (exports$1) {
4583
+ (function (exports) {
4838
4584
  var __createBinding = (src && src.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4839
4585
  if (k2 === undefined) k2 = k;
4840
4586
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -4851,46 +4597,56 @@ function requireSrc () {
4851
4597
  }) : function(o, v) {
4852
4598
  o["default"] = v;
4853
4599
  });
4854
- var __importStar = (src && src.__importStar) || function (mod) {
4855
- if (mod && mod.__esModule) return mod;
4856
- var result = {};
4857
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
4858
- __setModuleDefault(result, mod);
4859
- return result;
4860
- };
4861
- var __exportStar = (src && src.__exportStar) || function(m, exports$1) {
4862
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
4600
+ var __importStar = (src && src.__importStar) || (function () {
4601
+ var ownKeys = function(o) {
4602
+ ownKeys = Object.getOwnPropertyNames || function (o) {
4603
+ var ar = [];
4604
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
4605
+ return ar;
4606
+ };
4607
+ return ownKeys(o);
4608
+ };
4609
+ return function (mod) {
4610
+ if (mod && mod.__esModule) return mod;
4611
+ var result = {};
4612
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
4613
+ __setModuleDefault(result, mod);
4614
+ return result;
4615
+ };
4616
+ })();
4617
+ var __exportStar = (src && src.__exportStar) || function(m, exports) {
4618
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
4863
4619
  };
4864
- Object.defineProperty(exports$1, "__esModule", { value: true });
4865
- exports$1.parseString = exports$1.parseFile = exports$1.parseStream = exports$1.parse = exports$1.ParserOptions = exports$1.CsvParserStream = void 0;
4620
+ Object.defineProperty(exports, "__esModule", { value: true });
4621
+ exports.parseString = exports.parseFile = exports.parseStream = exports.parse = exports.ParserOptions = exports.CsvParserStream = void 0;
4866
4622
  const fs = __importStar(require$$0$1);
4867
4623
  const stream_1 = require$$1;
4868
4624
  const ParserOptions_1 = requireParserOptions();
4869
4625
  const CsvParserStream_1 = requireCsvParserStream();
4870
- __exportStar(requireTypes(), exports$1);
4626
+ __exportStar(requireTypes(), exports);
4871
4627
  var CsvParserStream_2 = requireCsvParserStream();
4872
- Object.defineProperty(exports$1, "CsvParserStream", { enumerable: true, get: function () { return CsvParserStream_2.CsvParserStream; } });
4628
+ Object.defineProperty(exports, "CsvParserStream", { enumerable: true, get: function () { return CsvParserStream_2.CsvParserStream; } });
4873
4629
  var ParserOptions_2 = requireParserOptions();
4874
- Object.defineProperty(exports$1, "ParserOptions", { enumerable: true, get: function () { return ParserOptions_2.ParserOptions; } });
4630
+ Object.defineProperty(exports, "ParserOptions", { enumerable: true, get: function () { return ParserOptions_2.ParserOptions; } });
4875
4631
  const parse = (args) => {
4876
4632
  return new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(args));
4877
4633
  };
4878
- exports$1.parse = parse;
4634
+ exports.parse = parse;
4879
4635
  const parseStream = (stream, options) => {
4880
4636
  return stream.pipe(new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(options)));
4881
4637
  };
4882
- exports$1.parseStream = parseStream;
4638
+ exports.parseStream = parseStream;
4883
4639
  const parseFile = (location, options = {}) => {
4884
4640
  return fs.createReadStream(location).pipe(new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(options)));
4885
4641
  };
4886
- exports$1.parseFile = parseFile;
4642
+ exports.parseFile = parseFile;
4887
4643
  const parseString = (string, options) => {
4888
4644
  const rs = new stream_1.Readable();
4889
4645
  rs.push(string);
4890
4646
  rs.push(null);
4891
4647
  return rs.pipe(new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(options)));
4892
4648
  };
4893
- exports$1.parseString = parseString;
4649
+ exports.parseString = parseString;
4894
4650
 
4895
4651
  } (src));
4896
4652
  return src;
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import cac from 'cac';
2
- import { S as SvgComposer, i as generateBadge, p as partitionTiers, t as tierPresets, v as version, l as loadConfig, k as resolveProviders, j as guessProviders, r as resolveAvatars, q as outputFormats, s as svgToPng, g as svgToWebp } from './shared/contribkit.DLdCPKip.mjs';
2
+ import { S as SvgComposer, h as generateBadge, p as partitionTiers, t as tierPresets, v as version, l as loadConfig, n as resolveProviders, i as guessProviders, k as resolveAvatars, o as outputFormats, s as svgToPng, q as svgToWebp } from './shared/contribkit.DYospa65.mjs';
3
3
  import fs from 'node:fs';
4
4
  import fsp from 'node:fs/promises';
5
5
  import { resolve, dirname, relative, join } from 'node:path';
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { F as FALLBACK_AVATAR, G as GitHubProvider, P as ProvidersMap, S as SvgComposer, c as defaultConfig, b as defaultInlineCSS, a as defaultTiers, d as defineConfig, n as fetchGitHubSponsors, m as fetchSponsors, h as genSvgImage, i as generateBadge, j as guessProviders, l as loadConfig, o as makeQuery, q as outputFormats, p as partitionTiers, e as presets, f as resizeImage, r as resolveAvatars, k as resolveProviders, s as svgToPng, g as svgToWebp, t as tierPresets } from './shared/contribkit.DLdCPKip.mjs';
1
+ export { F as FALLBACK_AVATAR, G as GitHubProvider, P as ProvidersMap, S as SvgComposer, d as defaultConfig, a as defaultInlineCSS, b as defaultTiers, c as defineConfig, f as fetchGitHubSponsors, e as fetchSponsors, g as genSvgImage, h as generateBadge, i as guessProviders, l as loadConfig, m as makeQuery, o as outputFormats, p as partitionTiers, j as presets, r as resizeImage, k as resolveAvatars, n as resolveProviders, s as svgToPng, q as svgToWebp, t as tierPresets } from './shared/contribkit.DYospa65.mjs';
2
2
  import 'unconfig';
3
3
  import 'node:process';
4
4
  import 'dotenv';
@@ -207,7 +207,7 @@ function loadEnv() {
207
207
  return JSON.parse(JSON.stringify(config));
208
208
  }
209
209
 
210
- const version = "2025.1130.1103";
210
+ const version = "2026.518.124816";
211
211
 
212
212
  async function fetchImage(url) {
213
213
  const arrayBuffer = await $fetch(url, {
@@ -595,7 +595,23 @@ async function fetchCrowdinContributors(token, projectId, minTranslations = 1) {
595
595
  const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain';
596
596
  const DATA_URL_DEFAULT_CHARSET = 'us-ascii';
597
597
 
598
- const testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);
598
+ const encodedReservedCharactersPattern = '%(?:3A|2F|3F|23|5B|5D|40|21|24|26|27|28|29|2A|2B|2C|3B|3D)';
599
+ const temporaryEncodedReservedTokenBase = '__normalize_url_encoded_reserved__';
600
+ const temporaryEncodedReservedTokenPattern = /__normalize_url_encoded_reserved__(\d+)__/g;
601
+ const hasEncodedReservedCharactersRegex = new RegExp(encodedReservedCharactersPattern, 'i');
602
+ const encodedReservedCharactersRegex = new RegExp(encodedReservedCharactersPattern, 'gi');
603
+
604
+ const testParameter = (name, filters) => Array.isArray(filters) && filters.some(filter => {
605
+ if (filter instanceof RegExp) {
606
+ if (filter.flags.includes('g') || filter.flags.includes('y')) {
607
+ return new RegExp(filter.source, filter.flags.replaceAll(/[gy]/g, '')).test(name);
608
+ }
609
+
610
+ return filter.test(name);
611
+ }
612
+
613
+ return filter === name;
614
+ });
599
615
 
600
616
  const supportedProtocols = new Set([
601
617
  'https:',
@@ -603,16 +619,155 @@ const supportedProtocols = new Set([
603
619
  'file:',
604
620
  ]);
605
621
 
606
- const hasCustomProtocol = urlString => {
622
+ const normalizeCustomProtocolOption = protocol => {
623
+ if (typeof protocol !== 'string') {
624
+ return undefined;
625
+ }
626
+
627
+ const normalizedProtocol = protocol.trim().toLowerCase().replace(/:$/, '');
628
+ return normalizedProtocol === '' ? undefined : `${normalizedProtocol}:`;
629
+ };
630
+
631
+ const getCustomProtocol = urlString => {
607
632
  try {
608
633
  const {protocol} = new URL(urlString);
634
+ const hasAuthority = urlString.slice(0, protocol.length + 2).toLowerCase() === `${protocol}//`;
635
+
636
+ if (protocol.endsWith(':')
637
+ && (!protocol.includes('.') || hasAuthority)
638
+ && !supportedProtocols.has(protocol)) {
639
+ return protocol;
640
+ }
641
+ } catch {}
642
+
643
+ return undefined;
644
+ };
645
+
646
+ const decodeQueryKey = value => {
647
+ try {
648
+ return decodeURIComponent(value.replaceAll('+', '%20'));
649
+ } catch {
650
+ // Match URLSearchParams behavior for malformed percent-encoding.
651
+ return new URLSearchParams(`${value}=`).keys().next().value;
652
+ }
653
+ };
654
+
655
+ const getKeysWithoutEquals = search => {
656
+ const keys = new Set();
657
+ if (!search) {
658
+ return keys;
659
+ }
660
+
661
+ for (const part of search.slice(1).split('&')) {
662
+ if (part && !part.includes('=')) {
663
+ keys.add(decodeQueryKey(part));
664
+ }
665
+ }
666
+
667
+ return keys;
668
+ };
669
+
670
+ const getTemporaryEncodedReservedTokenPrefix = search => {
671
+ let decodedSearch = search;
609
672
 
610
- return protocol.endsWith(':')
611
- && !protocol.includes('.')
612
- && !supportedProtocols.has(protocol);
673
+ try {
674
+ decodedSearch = decodeURIComponent(search);
613
675
  } catch {
614
- return false;
676
+ decodedSearch = new URLSearchParams(search).toString();
677
+ }
678
+
679
+ const getUsedTokenIndexes = value => {
680
+ const indexes = new Set();
681
+
682
+ for (const match of value.matchAll(temporaryEncodedReservedTokenPattern)) {
683
+ indexes.add(Number.parseInt(match[1], 10));
684
+ }
685
+
686
+ return indexes;
687
+ };
688
+
689
+ const usedTokenIndexes = getUsedTokenIndexes(search);
690
+ for (const tokenIndex of getUsedTokenIndexes(decodedSearch)) {
691
+ usedTokenIndexes.add(tokenIndex);
692
+ }
693
+
694
+ let tokenIndex = 0;
695
+ while (usedTokenIndexes.has(tokenIndex)) {
696
+ tokenIndex++;
697
+ }
698
+
699
+ return `${temporaryEncodedReservedTokenBase}${tokenIndex}__`;
700
+ };
701
+
702
+ const sortSearchParameters = (searchParameters, encodedReservedTokenRegex) => {
703
+ if (!encodedReservedTokenRegex) {
704
+ searchParameters.sort();
705
+ return searchParameters.toString();
706
+ }
707
+
708
+ const getSortableKey = key => key.replace(encodedReservedTokenRegex, (_, hexCode) => String.fromCodePoint(Number.parseInt(hexCode, 16)));
709
+ const entries = [...searchParameters.entries()];
710
+ entries.sort(([leftKey], [rightKey]) => {
711
+ const left = getSortableKey(leftKey);
712
+ const right = getSortableKey(rightKey);
713
+ return left < right ? -1 : (left > right ? 1 : 0);
714
+ });
715
+
716
+ return new URLSearchParams(entries).toString();
717
+ };
718
+
719
+ const decodeReservedTokens = (value, encodedReservedTokenRegex) => {
720
+ if (!encodedReservedTokenRegex) {
721
+ return value;
615
722
  }
723
+
724
+ return value.replace(encodedReservedTokenRegex, (_, hexCode) => String.fromCodePoint(Number.parseInt(hexCode, 16)));
725
+ };
726
+
727
+ const normalizeEmptyQueryParameters = (search, emptyQueryValue, originalSearch) => {
728
+ const isAlways = emptyQueryValue === 'always';
729
+ const isNever = emptyQueryValue === 'never';
730
+ const keysWithoutEquals = (isAlways || isNever) ? undefined : getKeysWithoutEquals(originalSearch);
731
+
732
+ const normalizeKey = key => key.replaceAll('+', '%20');
733
+ const formatEmptyValue = normalizedKey => {
734
+ if (isAlways) {
735
+ return `${normalizedKey}=`;
736
+ }
737
+
738
+ if (isNever) {
739
+ return normalizedKey;
740
+ }
741
+
742
+ return keysWithoutEquals.has(decodeQueryKey(normalizedKey)) ? normalizedKey : `${normalizedKey}=`;
743
+ };
744
+
745
+ const normalizeParameter = parameter => {
746
+ const equalIndex = parameter.indexOf('=');
747
+
748
+ if (equalIndex === -1) {
749
+ // Normalize + to %20 (+ means space in query strings)
750
+ return formatEmptyValue(normalizeKey(parameter));
751
+ }
752
+
753
+ const key = parameter.slice(0, equalIndex);
754
+ const value = parameter.slice(equalIndex + 1);
755
+
756
+ if (value === '') {
757
+ if (key === '') {
758
+ return '=';
759
+ }
760
+
761
+ // Normalize + to %20 (+ means space in query strings)
762
+ return formatEmptyValue(normalizeKey(key));
763
+ }
764
+
765
+ // Normalize + to %20 in key.
766
+ return `${normalizeKey(key)}=${value}`;
767
+ };
768
+
769
+ const parameters = search.slice(1).split('&').filter(Boolean);
770
+ return parameters.length === 0 ? '' : `?${parameters.map(x => normalizeParameter(x)).join('&')}`;
616
771
  };
617
772
 
618
773
  const normalizeDataURL = (urlString, {stripHash}) => {
@@ -622,18 +777,16 @@ const normalizeDataURL = (urlString, {stripHash}) => {
622
777
  throw new Error(`Invalid URL: ${urlString}`);
623
778
  }
624
779
 
625
- let {type, data, hash} = match.groups;
780
+ const {type, data, hash} = match.groups;
626
781
  const mediaType = type.split(';');
627
- hash = stripHash ? '' : hash;
628
782
 
629
- let isBase64 = false;
630
- if (mediaType[mediaType.length - 1] === 'base64') {
783
+ const isBase64 = mediaType.at(-1) === 'base64';
784
+ if (isBase64) {
631
785
  mediaType.pop();
632
- isBase64 = true;
633
786
  }
634
787
 
635
788
  // Lowercase MIME type
636
- const mimeType = mediaType.shift()?.toLowerCase() ?? '';
789
+ const mimeType = mediaType.shift().toLowerCase();
637
790
  const attributes = mediaType
638
791
  .map(attribute => {
639
792
  let [key, value = ''] = attribute.split('=').map(string => string.trim());
@@ -651,9 +804,7 @@ const normalizeDataURL = (urlString, {stripHash}) => {
651
804
  })
652
805
  .filter(Boolean);
653
806
 
654
- const normalizedMediaType = [
655
- ...attributes,
656
- ];
807
+ const normalizedMediaType = [...attributes];
657
808
 
658
809
  if (isBase64) {
659
810
  normalizedMediaType.push('base64');
@@ -663,7 +814,8 @@ const normalizeDataURL = (urlString, {stripHash}) => {
663
814
  normalizedMediaType.unshift(mimeType);
664
815
  }
665
816
 
666
- return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`;
817
+ const hashPart = stripHash || !hash ? '' : `#${hash}`;
818
+ return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hashPart}`;
667
819
  };
668
820
 
669
821
  function normalizeUrl$1(urlString, options) {
@@ -684,6 +836,7 @@ function normalizeUrl$1(urlString, options) {
684
836
  sortQueryParameters: true,
685
837
  removePath: false,
686
838
  transformPath: false,
839
+ emptyQueryValue: 'preserve',
687
840
  ...options,
688
841
  };
689
842
 
@@ -699,7 +852,13 @@ function normalizeUrl$1(urlString, options) {
699
852
  return normalizeDataURL(urlString, options);
700
853
  }
701
854
 
702
- if (hasCustomProtocol(urlString)) {
855
+ const customProtocols = Array.isArray(options.customProtocols) ? options.customProtocols : [];
856
+ const normalizedCustomProtocols = new Set(customProtocols
857
+ .map(protocol => normalizeCustomProtocolOption(protocol))
858
+ .filter(Boolean));
859
+
860
+ const customProtocol = getCustomProtocol(urlString);
861
+ if (customProtocol && !normalizedCustomProtocols.has(customProtocol)) {
703
862
  return urlString;
704
863
  }
705
864
 
@@ -707,7 +866,7 @@ function normalizeUrl$1(urlString, options) {
707
866
  const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString);
708
867
 
709
868
  // Prepend protocol
710
- if (!isRelativeUrl) {
869
+ if (!isRelativeUrl && !customProtocol) {
711
870
  urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
712
871
  }
713
872
 
@@ -762,13 +921,13 @@ function normalizeUrl$1(urlString, options) {
762
921
  const protocolAtIndex = match.index;
763
922
  const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);
764
923
 
765
- result += intermediate.replace(/\/{2,}/g, '/');
924
+ result += intermediate.replaceAll(/\/{2,}/g, '/');
766
925
  result += protocol;
767
926
  lastIndex = protocolAtIndex + protocol.length;
768
927
  }
769
928
 
770
- const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);
771
- result += remnant.replace(/\/{2,}/g, '/');
929
+ const remnant = urlObject.pathname.slice(lastIndex);
930
+ result += remnant.replaceAll(/\/{2,}/g, '/');
772
931
 
773
932
  urlObject.pathname = result;
774
933
  }
@@ -776,7 +935,7 @@ function normalizeUrl$1(urlString, options) {
776
935
  // Decode URI octets
777
936
  if (urlObject.pathname) {
778
937
  try {
779
- urlObject.pathname = decodeURI(urlObject.pathname).replace(/\\/g, '%5C');
938
+ urlObject.pathname = decodeURI(urlObject.pathname).replaceAll('\\', '%5C');
780
939
  } catch {}
781
940
  }
782
941
 
@@ -786,12 +945,12 @@ function normalizeUrl$1(urlString, options) {
786
945
  }
787
946
 
788
947
  if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {
789
- let pathComponents = urlObject.pathname.split('/');
790
- const lastComponent = pathComponents[pathComponents.length - 1];
948
+ const pathComponents = urlObject.pathname.split('/').filter(Boolean);
949
+ const lastComponent = pathComponents.at(-1);
791
950
 
792
- if (testParameter(lastComponent, options.removeDirectoryIndex)) {
793
- pathComponents = pathComponents.slice(0, -1);
794
- urlObject.pathname = pathComponents.slice(1).join('/') + '/';
951
+ if (lastComponent && testParameter(lastComponent, options.removeDirectoryIndex)) {
952
+ pathComponents.pop();
953
+ urlObject.pathname = pathComponents.length > 0 ? `/${pathComponents.join('/')}/` : '/';
795
954
  }
796
955
  }
797
956
 
@@ -821,49 +980,61 @@ function normalizeUrl$1(urlString, options) {
821
980
  }
822
981
  }
823
982
 
983
+ // Capture original query params format before any searchParams modifications
984
+ const originalSearch = urlObject.search;
985
+ let encodedReservedTokenRegex;
986
+
987
+ if (options.sortQueryParameters && hasEncodedReservedCharactersRegex.test(originalSearch)) {
988
+ const encodedReservedTokenPrefix = getTemporaryEncodedReservedTokenPrefix(originalSearch);
989
+ urlObject.search = originalSearch.replaceAll(encodedReservedCharactersRegex, match => `${encodedReservedTokenPrefix}${match.slice(1).toUpperCase()}`);
990
+ encodedReservedTokenRegex = new RegExp(`${encodedReservedTokenPrefix}([0-9A-F]{2})`, 'g');
991
+ }
992
+
993
+ const hasKeepQueryParameters = Array.isArray(options.keepQueryParameters);
994
+ const {searchParams} = urlObject;
995
+
824
996
  // Remove query unwanted parameters
825
- if (Array.isArray(options.removeQueryParameters)) {
997
+ if (!hasKeepQueryParameters && Array.isArray(options.removeQueryParameters) && options.removeQueryParameters.length > 0) {
826
998
  // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.
827
- for (const key of [...urlObject.searchParams.keys()]) {
828
- if (testParameter(key, options.removeQueryParameters)) {
829
- urlObject.searchParams.delete(key);
999
+ for (const key of [...searchParams.keys()]) {
1000
+ if (testParameter(decodeReservedTokens(key, encodedReservedTokenRegex), options.removeQueryParameters)) {
1001
+ searchParams.delete(key);
830
1002
  }
831
1003
  }
832
1004
  }
833
1005
 
834
- if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {
1006
+ if (!hasKeepQueryParameters && options.removeQueryParameters === true) {
835
1007
  urlObject.search = '';
836
1008
  }
837
1009
 
838
1010
  // Keep wanted query parameters
839
- if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {
1011
+ if (hasKeepQueryParameters && options.keepQueryParameters.length > 0) {
840
1012
  // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy.
841
- for (const key of [...urlObject.searchParams.keys()]) {
842
- if (!testParameter(key, options.keepQueryParameters)) {
843
- urlObject.searchParams.delete(key);
1013
+ for (const key of [...searchParams.keys()]) {
1014
+ if (!testParameter(decodeReservedTokens(key, encodedReservedTokenRegex), options.keepQueryParameters)) {
1015
+ searchParams.delete(key);
844
1016
  }
845
1017
  }
1018
+ } else if (hasKeepQueryParameters) {
1019
+ urlObject.search = '';
846
1020
  }
847
1021
 
848
1022
  // Sort query parameters
849
1023
  if (options.sortQueryParameters) {
850
- const originalSearch = urlObject.search;
851
- urlObject.searchParams.sort();
1024
+ urlObject.search = sortSearchParameters(urlObject.searchParams, encodedReservedTokenRegex);
852
1025
 
853
- // Calling `.sort()` encodes the search parameters, so we need to decode them again.
854
- try {
855
- urlObject.search = decodeURIComponent(urlObject.search);
856
- } catch {}
1026
+ // Sorting and serializing encode the search parameters, so we need to decode them again.
1027
+ // Protect &%#? and %2B from decoding (would break URL structure or change meaning) by double-encoding them first.
1028
+ urlObject.search = decodeURIComponent(urlObject.search.replaceAll(/%(?:26|23|3f|25|2b)/gi, match => `%25${match.slice(1)}`));
857
1029
 
858
- // Fix parameters that originally had no equals sign but got one added by URLSearchParams
859
- const partsWithoutEquals = originalSearch.slice(1).split('&').filter(p => p && !p.includes('='));
860
- for (const part of partsWithoutEquals) {
861
- const decoded = decodeURIComponent(part);
862
- // Only replace at word boundaries to avoid partial matches
863
- urlObject.search = urlObject.search.replace(`?${decoded}=`, `?${decoded}`).replace(`&${decoded}=`, `&${decoded}`);
1030
+ if (encodedReservedTokenRegex) {
1031
+ urlObject.search = urlObject.search.replace(encodedReservedTokenRegex, '%$1');
864
1032
  }
865
1033
  }
866
1034
 
1035
+ // Normalize empty query parameter values
1036
+ urlObject.search = normalizeEmptyQueryParameters(urlObject.search, options.emptyQueryValue, originalSearch);
1037
+
867
1038
  if (options.removeTrailingSlash) {
868
1039
  urlObject.pathname = urlObject.pathname.replace(/\/$/, '');
869
1040
  }
@@ -1963,4 +2134,4 @@ async function fetchSponsors(config) {
1963
2134
 
1964
2135
  const outputFormats = ["svg", "png", "webp", "json"];
1965
2136
 
1966
- export { FALLBACK_AVATAR as F, GitHubProvider as G, ProvidersMap as P, SvgComposer as S, defaultTiers as a, defaultInlineCSS as b, defaultConfig as c, defineConfig as d, presets as e, resizeImage as f, svgToWebp as g, genSvgImage as h, generateBadge as i, guessProviders as j, resolveProviders as k, loadConfig as l, fetchSponsors as m, fetchGitHubSponsors as n, makeQuery as o, partitionTiers as p, outputFormats as q, resolveAvatars as r, svgToPng as s, tierPresets as t, version as v };
2137
+ export { FALLBACK_AVATAR as F, GitHubProvider as G, ProvidersMap as P, SvgComposer as S, defaultInlineCSS as a, defaultTiers as b, defineConfig as c, defaultConfig as d, fetchSponsors as e, fetchGitHubSponsors as f, genSvgImage as g, generateBadge as h, guessProviders as i, presets as j, resolveAvatars as k, loadConfig as l, makeQuery as m, resolveProviders as n, outputFormats as o, partitionTiers as p, svgToWebp as q, resizeImage as r, svgToPng as s, tierPresets as t, version as v };
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/LizardByte/contribkit.git"
7
7
  },
8
- "version": "2025.1130.1103",
8
+ "version": "2026.518.124816",
9
9
  "description": "Toolkit for generating contributor images",
10
10
  "license": "MIT",
11
11
  "funding": "https://app.lizardbyte.dev",
@@ -27,7 +27,7 @@
27
27
  "module": "./dist/index.mjs",
28
28
  "types": "./dist/index.d.mts",
29
29
  "bin": {
30
- "contribkit": "./bin/contribkit.mjs"
30
+ "contribkit": "bin/contribkit.mjs"
31
31
  },
32
32
  "files": [
33
33
  "bin",
@@ -37,45 +37,46 @@
37
37
  "build": "unbuild",
38
38
  "stub": "unbuild --stub",
39
39
  "dev": "esno src/cli.ts",
40
- "test": "npm-run-all test:unit test:report test:lint",
40
+ "test": "npm-run-all test:unit test:report test:lint test:typecheck",
41
41
  "test:unit": "jest --coverage",
42
42
  "test:report": "jest --reporters=jest-junit",
43
43
  "test:lint": "eslint .",
44
- "typecheck": "tsc --noEmit",
44
+ "test:typecheck": "tsc --noEmit",
45
45
  "prepublishOnly": "nr build",
46
46
  "release": "bumpp && pnpm publish"
47
47
  },
48
48
  "dependencies": {
49
49
  "@crowdin/crowdin-api-client": "^1.41.2",
50
- "ansis": "^3.17.0",
50
+ "ansis": "^4.2.0",
51
51
  "cac": "^6.7.14",
52
52
  "consola": "^3.4.0",
53
- "dotenv": "^16.4.7",
53
+ "dotenv": "^17.3.1",
54
54
  "ofetch": "^1.4.1",
55
- "sharp": "^0.33.5",
55
+ "sharp": "^0.34.5",
56
56
  "unconfig": "^7.3.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@antfu/ni": "^23.3.1",
59
+ "@antfu/ni": "^30.0.0",
60
60
  "@antfu/utils": "^9.1.0",
61
- "@babel/core": "7.26.9",
62
- "@babel/preset-env": "7.26.9",
63
- "@codecov/webpack-plugin": "1.9.0",
61
+ "@babel/core": "7.29.0",
62
+ "@babel/preset-env": "7.29.5",
63
+ "@codecov/webpack-plugin": "2.0.1",
64
+ "@eslint/js": "^10.0.1",
64
65
  "@fast-csv/parse": "^5.0.2",
65
66
  "@types/d3-hierarchy": "^3.1.7",
66
- "@types/node": "^22.13.10",
67
- "bumpp": "^10.0.3",
67
+ "@types/node": "^25.2.3",
68
+ "bumpp": "^11.0.1",
68
69
  "d3-hierarchy": "^3.1.2",
69
- "eslint": "^9.22.0",
70
- "eslint-plugin-jest": "28.11.0",
71
- "globals": "16.0.0",
72
- "jest": "29.7.0",
73
- "jest-environment-jsdom": "29.7.0",
74
- "jest-junit": "16.0.0",
70
+ "eslint": "^10.0.0",
71
+ "eslint-plugin-jest": "29.15.2",
72
+ "globals": "17.6.0",
73
+ "jest": "30.4.2",
74
+ "jest-environment-jsdom": "30.4.1",
75
+ "jest-junit": "17.0.0",
75
76
  "jiti": "^2.4.2",
76
- "normalize-url": "^8.0.1",
77
- "npm-run-all": "4.1.5",
78
- "p-limit": "^6.2.0",
77
+ "normalize-url": "^9.0.0",
78
+ "npm-run-all2": "8.0.4",
79
+ "p-limit": "^7.3.0",
79
80
  "tsx": "^4.19.3",
80
81
  "typescript": "^5.8.2",
81
82
  "unbuild": "^3.5.0"