complete-common 2.28.0 → 2.30.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.
Files changed (52) hide show
  1. package/dist/functions/array.d.ts.map +1 -1
  2. package/dist/functions/assert.d.cts +4 -2
  3. package/dist/functions/assert.d.mts +4 -2
  4. package/dist/functions/assert.d.ts +4 -2
  5. package/dist/functions/assert.d.ts.map +1 -1
  6. package/dist/functions/enums.d.cts +2 -2
  7. package/dist/functions/enums.d.mts +2 -2
  8. package/dist/functions/enums.d.ts +2 -2
  9. package/dist/functions/set.d.cts +1 -1
  10. package/dist/functions/set.d.mts +1 -1
  11. package/dist/functions/set.d.ts +1 -1
  12. package/dist/functions/set.d.ts.map +1 -1
  13. package/dist/functions/sort.d.ts.map +1 -1
  14. package/dist/functions/string.d.ts.map +1 -1
  15. package/dist/functions/tuple.d.cts +2 -2
  16. package/dist/functions/tuple.d.mts +2 -2
  17. package/dist/functions/tuple.d.ts +2 -2
  18. package/dist/functions/types.d.cts +1 -1
  19. package/dist/functions/types.d.mts +1 -1
  20. package/dist/functions/types.d.ts +1 -1
  21. package/dist/functions/utils.d.cts +2 -4
  22. package/dist/functions/utils.d.mts +2 -4
  23. package/dist/functions/utils.d.ts +2 -4
  24. package/dist/functions/utils.d.ts.map +1 -1
  25. package/dist/index.cjs +42 -42
  26. package/dist/index.d.cts +2 -0
  27. package/dist/index.d.mts +2 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.mjs +42 -43
  31. package/dist/types/Expect.d.cts +6 -0
  32. package/dist/types/Expect.d.mts +6 -0
  33. package/dist/types/Expect.d.ts +6 -0
  34. package/dist/types/Expect.d.ts.map +1 -0
  35. package/dist/types/KeysMatch.d.cts +25 -0
  36. package/dist/types/KeysMatch.d.mts +25 -0
  37. package/dist/types/KeysMatch.d.ts +25 -0
  38. package/dist/types/KeysMatch.d.ts.map +1 -0
  39. package/package.json +4 -4
  40. package/src/functions/array.ts +2 -8
  41. package/src/functions/assert.ts +16 -4
  42. package/src/functions/enums.ts +2 -2
  43. package/src/functions/object.ts +1 -1
  44. package/src/functions/set.ts +2 -7
  45. package/src/functions/sort.ts +1 -3
  46. package/src/functions/string.ts +28 -23
  47. package/src/functions/tuple.ts +2 -2
  48. package/src/functions/types.ts +1 -1
  49. package/src/functions/utils.ts +13 -11
  50. package/src/index.ts +2 -0
  51. package/src/types/Expect.ts +5 -0
  52. package/src/types/KeysMatch.ts +28 -0
package/dist/index.d.ts CHANGED
@@ -17,8 +17,10 @@ export type * from "./interfaces/SemanticVersion.js";
17
17
  export type * from "./types/AddSubtract.js";
18
18
  export type * from "./types/CompositionTypeSatisfiesEnum.js";
19
19
  export type * from "./types/ERange.js";
20
+ export type * from "./types/Expect.js";
20
21
  export type * from "./types/Immutable.js";
21
22
  export type * from "./types/IRange.js";
23
+ export type * from "./types/KeysMatch.js";
22
24
  export type * from "./types/NaturalNumbersLessThan.js";
23
25
  export type * from "./types/NaturalNumbersLessThanOrEqualTo.js";
24
26
  export type * from "./types/ObjectValues.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,mBAAmB,iCAAiC,CAAC;AACrD,mBAAmB,wBAAwB,CAAC;AAC5C,mBAAmB,yCAAyC,CAAC;AAC7D,mBAAmB,mBAAmB,CAAC;AACvC,mBAAmB,sBAAsB,CAAC;AAC1C,mBAAmB,mBAAmB,CAAC;AACvC,mBAAmB,mCAAmC,CAAC;AACvD,mBAAmB,4CAA4C,CAAC;AAChE,mBAAmB,yBAAyB,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,mBAAmB,2BAA2B,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AACvC,mBAAmB,2BAA2B,CAAC;AAC/C,mBAAmB,kBAAkB,CAAC;AACtC,mBAAmB,yBAAyB,CAAC;AAC7C,mBAAmB,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,mBAAmB,iCAAiC,CAAC;AACrD,mBAAmB,wBAAwB,CAAC;AAC5C,mBAAmB,yCAAyC,CAAC;AAC7D,mBAAmB,mBAAmB,CAAC;AACvC,mBAAmB,mBAAmB,CAAC;AACvC,mBAAmB,sBAAsB,CAAC;AAC1C,mBAAmB,mBAAmB,CAAC;AACvC,mBAAmB,sBAAsB,CAAC;AAC1C,mBAAmB,mCAAmC,CAAC;AACvD,mBAAmB,4CAA4C,CAAC;AAChE,mBAAmB,yBAAyB,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,mBAAmB,2BAA2B,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AACvC,mBAAmB,2BAA2B,CAAC;AAC/C,mBAAmB,kBAAkB,CAAC;AACtC,mBAAmB,yBAAyB,CAAC;AAC7C,mBAAmB,sBAAsB,CAAC"}
package/dist/index.mjs CHANGED
@@ -100,7 +100,7 @@ function assertEnumValue(value, transpiledEnum, msg, set) {
100
100
  }
101
101
  }
102
102
  function assertInteger(value, msg) {
103
- if (!Number.isInteger(value)) {
103
+ if (!Number.isSafeInteger(value)) {
104
104
  throw new TypeError(msg);
105
105
  }
106
106
  }
@@ -124,6 +124,11 @@ function assertObject(value, ...[msg]) {
124
124
  throw new TypeError(msg);
125
125
  }
126
126
  }
127
+ function assertPositiveInteger(value, msg) {
128
+ if (!Number.isSafeInteger(value) || value <= 0) {
129
+ throw new TypeError(msg);
130
+ }
131
+ }
127
132
  function assertString(value, ...[msg]) {
128
133
  if (typeof value !== "string") {
129
134
  throw new TypeError(msg);
@@ -154,11 +159,7 @@ function getRandomInt(min, max, exceptions = []) {
154
159
  }
155
160
 
156
161
  function arrayCopyTwoDimensional(array) {
157
- const copiedArray = [];
158
- for (const subArray of array) {
159
- copiedArray.push([...subArray]);
160
- }
161
- return copiedArray;
162
+ return Array.from(array, (subArray) => [...subArray]);
162
163
  }
163
164
  function arrayEquals(array1, array2) {
164
165
  if (array1.length !== array2.length) {
@@ -205,7 +206,7 @@ function arrayRemoveInPlace(array, ...elementsToRemove) {
205
206
  return removedElements;
206
207
  }
207
208
  function emptyArray(array) {
208
- array.splice(0);
209
+ array.length = 0;
209
210
  }
210
211
  async function filterAsync(array, predicate) {
211
212
  const results = await Promise.all(
@@ -392,11 +393,7 @@ function combineSets(...sets) {
392
393
  return newSet;
393
394
  }
394
395
  function copySet(oldSet) {
395
- const newSet = /* @__PURE__ */ new Set();
396
- for (const value of oldSet) {
397
- newSet.add(value);
398
- }
399
- return newSet;
396
+ return new Set(oldSet);
400
397
  }
401
398
  function objectKeysToSet(object) {
402
399
  const set = /* @__PURE__ */ new Set();
@@ -424,15 +421,13 @@ function setHas(set, ...elements) {
424
421
  function sortCaseInsensitive(array) {
425
422
  const newArray = [...array];
426
423
  newArray.sort(
427
- (a, b) => a.localeCompare(b, void 0, {
428
- sensitivity: "base"
429
- })
424
+ (a, b) => a.localeCompare(b, void 0, { sensitivity: "base" })
430
425
  );
431
426
  return newArray;
432
427
  }
433
428
 
434
- const FLOAT_REGEX = /^-?\d*\.?\d+$/;
435
- const INTEGER_REGEX = /^-?\d+$/;
429
+ const FLOAT_REGEX = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/v;
430
+ const INTEGER_REGEX = /^-?\d+$/v;
436
431
  function* eRange(start, end, increment = 1) {
437
432
  if (end === void 0) {
438
433
  yield* eRange(0, start, increment);
@@ -451,7 +446,7 @@ function* iRange(start, end, increment = 1) {
451
446
  yield* eRange(start, exclusiveEnd, increment);
452
447
  }
453
448
  function isKeyOf(key, target) {
454
- return key in target;
449
+ return Object.hasOwn(target, key);
455
450
  }
456
451
  function noop() {
457
452
  }
@@ -460,10 +455,10 @@ function parseFloatSafe(string) {
460
455
  return void 0;
461
456
  }
462
457
  const trimmedString = string.trim();
463
- if (FLOAT_REGEX.exec(trimmedString) === null) {
458
+ if (!FLOAT_REGEX.test(trimmedString)) {
464
459
  return void 0;
465
460
  }
466
- const number = Number.parseFloat(trimmedString);
461
+ const number = Number(trimmedString);
467
462
  return Number.isNaN(number) ? void 0 : number;
468
463
  }
469
464
  function parseIntSafe(string) {
@@ -471,10 +466,10 @@ function parseIntSafe(string) {
471
466
  return void 0;
472
467
  }
473
468
  const trimmedString = string.trim();
474
- if (INTEGER_REGEX.exec(trimmedString) === null) {
469
+ if (!INTEGER_REGEX.test(trimmedString)) {
475
470
  return void 0;
476
471
  }
477
- const number = Number.parseInt(trimmedString, 10);
472
+ const number = Math.trunc(Number(trimmedString));
478
473
  return Number.isNaN(number) ? void 0 : number;
479
474
  }
480
475
  function repeat(num, func) {
@@ -485,17 +480,17 @@ function repeat(num, func) {
485
480
  function todo(...args) {
486
481
  }
487
482
 
488
- const ASCII_REGEX = /^[\u0000-\u007F]*$/;
489
- const DIACRITIC_REGEX = /\p{Diacritic}/u;
490
- const EMOJI_REGEX = /(\p{Extended_Pictographic}|[#*0-9]\uFE0F?\u20E3)/u;
491
- const FIRST_LETTER_CAPITALIZED_REGEX = /^\p{Lu}/u;
492
- const KEBAB_CASE_REGEX = /^[\da-z]+(?:-[\da-z]+)*$/;
493
- const SEMANTIC_VERSION_REGEX = /^v*(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/;
494
- const WHITESPACE_REGEX = /\s/;
495
- const WHITESPACE_GLOBAL_REGEX = /\s/g;
496
- const TITLE_CASE_BOUNDARY_REGEX = /(?<=[\da-z])(?=[A-Z])/g;
497
- const UPPERCASE_REGEX = /^[A-Z]*$/;
498
- const LOWERCASE_REGEX = /^[a-z]*$/;
483
+ const ASCII_REGEX = /^\p{ASCII}*$/v;
484
+ const DIACRITIC_REGEX = /\p{Diacritic}/v;
485
+ const FIRST_LETTER_CAPITALIZED_REGEX = /^\p{Lu}/v;
486
+ const EMOJI_REGEX = /\p{Extended_Pictographic}|[#*0-9]\u{FE0F}?\u{20E3}/v;
487
+ const KEBAB_CASE_REGEX = /^[\da-z]+(?:-[\da-z]+)*$/v;
488
+ const SEMANTIC_VERSION_REGEX = /^v*(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/v;
489
+ const WHITESPACE_REGEX = /\s/v;
490
+ const WHITESPACE_GLOBAL_REGEX = /\s/gv;
491
+ const TITLE_CASE_BOUNDARY_REGEX = /(?<=[\da-z])(?=[A-Z])/gv;
492
+ const UPPERCASE_REGEX = /^[A-Z]*$/v;
493
+ const LOWERCASE_REGEX = /^[a-z]*$/v;
499
494
  function capitalizeFirstLetter(string) {
500
495
  if (string === "") {
501
496
  return string;
@@ -553,8 +548,8 @@ function isUpperCase(string) {
553
548
  return UPPERCASE_REGEX.test(string);
554
549
  }
555
550
  function kebabCaseToCamelCase(string) {
556
- return string.replaceAll(/-./g, (match) => {
557
- const firstLetterOfWord = match[1];
551
+ return string.replaceAll(/-./gv, (match) => {
552
+ const firstLetterOfWord = match.at(1);
558
553
  return firstLetterOfWord === void 0 ? "" : firstLetterOfWord.toUpperCase();
559
554
  });
560
555
  }
@@ -566,14 +561,14 @@ function normalizeString(string) {
566
561
  let sanitizedString = string;
567
562
  sanitizedString = removeNonPrintableCharacters(sanitizedString);
568
563
  sanitizedString = sanitizedString.replaceAll("\n\r", "\n");
569
- sanitizedString = sanitizedString.replaceAll(/\p{Zl}/gu, "\n");
570
- sanitizedString = sanitizedString.replaceAll(/\p{Zp}/gu, "\n");
571
- sanitizedString = sanitizedString.replaceAll(/\p{Zs}/gu, " ");
564
+ sanitizedString = sanitizedString.replaceAll(/\p{Zl}/gv, "\n");
565
+ sanitizedString = sanitizedString.replaceAll(/\p{Zp}/gv, "\n");
566
+ sanitizedString = sanitizedString.replaceAll(/\p{Zs}/gv, " ");
572
567
  sanitizedString = sanitizedString.trim();
573
568
  return sanitizedString;
574
569
  }
575
570
  function parseSemanticVersion(versionString) {
576
- const match = versionString.match(SEMANTIC_VERSION_REGEX);
571
+ const match = SEMANTIC_VERSION_REGEX.exec(versionString);
577
572
  if (match === null || match.groups === void 0) {
578
573
  return void 0;
579
574
  }
@@ -587,7 +582,11 @@ function parseSemanticVersion(versionString) {
587
582
  if (majorVersion === void 0 || minorVersion === void 0 || patchVersion === void 0) {
588
583
  return void 0;
589
584
  }
590
- return { majorVersion, minorVersion, patchVersion };
585
+ return {
586
+ majorVersion,
587
+ minorVersion,
588
+ patchVersion
589
+ };
591
590
  }
592
591
  function removeLinesBetweenMarkers(string, marker) {
593
592
  const lines = string.split("\n");
@@ -614,7 +613,7 @@ function removeLinesMatching(string, match) {
614
613
  return newLines.join("\n");
615
614
  }
616
615
  function removeNonPrintableCharacters(string) {
617
- return string.replaceAll(/\p{C}/gu, "");
616
+ return string.replaceAll(/\p{C}/gv, "");
618
617
  }
619
618
  function removeWhitespace(string) {
620
619
  return string.replaceAll(WHITESPACE_GLOBAL_REGEX, "");
@@ -639,7 +638,7 @@ function satisfiesSemanticVersion(version, minimumVersion) {
639
638
  return majorVersion > minimumMajorVersion || majorVersion === minimumMajorVersion && minorVersion > minimumMinorVersion || majorVersion === minimumMajorVersion && minorVersion === minimumMinorVersion && patchVersion >= minimumPatchVersion;
640
639
  }
641
640
  function titleCaseToKebabCase(string) {
642
- return string.replaceAll(TITLE_CASE_BOUNDARY_REGEX, "-").replaceAll(/ +/g, "-").toLowerCase();
641
+ return string.replaceAll(TITLE_CASE_BOUNDARY_REGEX, "-").replaceAll(/ +/gv, "-").toLowerCase();
643
642
  }
644
643
  function trimPrefix(string, prefix, trimAll = false) {
645
644
  if (trimAll) {
@@ -681,4 +680,4 @@ function* tupleKeys(tuple) {
681
680
 
682
681
  const ReadonlyMap = Map;
683
682
 
684
- export { HOUR_IN_MILLISECONDS, MINUTE_IN_MILLISECONDS, ReadonlyMap, ReadonlySet, SECOND_IN_MILLISECONDS, addSetsToSet, arrayCopyTwoDimensional, arrayEquals, arrayRemove, arrayRemoveAllInPlace, arrayRemoveInPlace, assertArray, assertArrayBoolean, assertArrayLength, assertArrayNonEmpty, assertArrayNumber, assertArrayObject, assertArrayString, assertBoolean, assertDefined, assertEnumValue, assertInteger, assertIs, assertNotNull, assertNumber, assertObject, assertString, assertStringNotEmpty, capitalizeFirstLetter, clamp, combineSets, copySet, eRange, emptyArray, escapeHTMLCharacters, filterAsync, filterMap, filterMapAsync, getElapsedSeconds, getEnumEntries, getEnumKeys, getEnumValues, getNumConsecutiveDiacritics, getRandomArrayElement, getRandomArrayIndex, getRandomInt, getWidenedObjectValue, hasDiacritic, hasEmoji, hasWhitespace, iRange, includes, includesAny, interfaceSatisfiesEnum, isASCII, isArray, isArrayBoolean, isArrayNumber, isArrayObject, isArrayString, isEnumValue, isFirstLetterCapitalized, isKebabCase, isKeyOf, isLowerCase, isObject, isSemanticVersion, isUpperCase, kebabCaseToCamelCase, kebabCaseToPascalCase, mapAsync, mapFilter, mapFind, newArray, noop, normalizeString, objectFilter, objectKeysToSet, objectMap, objectToMap, objectToReverseMap, objectValuesToSet, parseFloatSafe, parseIntSafe, parseSemanticVersion, removeLinesBetweenMarkers, removeLinesMatching, removeNonPrintableCharacters, removeWhitespace, repeat, reverseObject, satisfiesSemanticVersion, setAdd, setHas, sortCaseInsensitive, sumArray, titleCaseToKebabCase, todo, trimPrefix, trimSuffix, truncateString, tupleEntries, tupleKeys, unique };
683
+ export { HOUR_IN_MILLISECONDS, MINUTE_IN_MILLISECONDS, ReadonlyMap, ReadonlySet, SECOND_IN_MILLISECONDS, addSetsToSet, arrayCopyTwoDimensional, arrayEquals, arrayRemove, arrayRemoveAllInPlace, arrayRemoveInPlace, assertArray, assertArrayBoolean, assertArrayLength, assertArrayNonEmpty, assertArrayNumber, assertArrayObject, assertArrayString, assertBoolean, assertDefined, assertEnumValue, assertInteger, assertIs, assertNotNull, assertNumber, assertObject, assertPositiveInteger, assertString, assertStringNotEmpty, capitalizeFirstLetter, clamp, combineSets, copySet, eRange, emptyArray, escapeHTMLCharacters, filterAsync, filterMap, filterMapAsync, getElapsedSeconds, getEnumEntries, getEnumKeys, getEnumValues, getNumConsecutiveDiacritics, getRandomArrayElement, getRandomArrayIndex, getRandomInt, getWidenedObjectValue, hasDiacritic, hasEmoji, hasWhitespace, iRange, includes, includesAny, interfaceSatisfiesEnum, isASCII, isArray, isArrayBoolean, isArrayNumber, isArrayObject, isArrayString, isEnumValue, isFirstLetterCapitalized, isKebabCase, isKeyOf, isLowerCase, isObject, isSemanticVersion, isUpperCase, kebabCaseToCamelCase, kebabCaseToPascalCase, mapAsync, mapFilter, mapFind, newArray, noop, normalizeString, objectFilter, objectKeysToSet, objectMap, objectToMap, objectToReverseMap, objectValuesToSet, parseFloatSafe, parseIntSafe, parseSemanticVersion, removeLinesBetweenMarkers, removeLinesMatching, removeNonPrintableCharacters, removeWhitespace, repeat, reverseObject, satisfiesSemanticVersion, setAdd, setHas, sortCaseInsensitive, sumArray, titleCaseToKebabCase, todo, trimPrefix, trimSuffix, truncateString, tupleEntries, tupleKeys, unique };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Helper type used for testing other interfaces/types. Only `true` is assignable, anything else
3
+ * errors.
4
+ */
5
+ export type Expect<T extends true> = T;
6
+ //# sourceMappingURL=Expect.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Helper type used for testing other interfaces/types. Only `true` is assignable, anything else
3
+ * errors.
4
+ */
5
+ export type Expect<T extends true> = T;
6
+ //# sourceMappingURL=Expect.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Helper type used for testing other interfaces/types. Only `true` is assignable, anything else
3
+ * errors.
4
+ */
5
+ export type Expect<T extends true> = T;
6
+ //# sourceMappingURL=Expect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Expect.d.ts","sourceRoot":"","sources":["../../src/types/Expect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Helper type to ensure that an array of keys matches an interface.
3
+ *
4
+ * For example:
5
+ *
6
+ * ```ts
7
+ * interface Foo {
8
+ * arg1: string;
9
+ * arg2: string;
10
+ * }
11
+ *
12
+ * const FOO_KEYS = ["arg1", "arg2"] as const;
13
+ * type _ = Expect<KeysMatch<Foo, typeof FOO_KEYS>>;
14
+ * ```
15
+ */
16
+ export type KeysMatch<T, Keys extends readonly PropertyKey[]> = [
17
+ keyof T
18
+ ] extends [Keys[number]] ? [Keys[number]] extends [keyof T] ? true : {
19
+ error: "Extra keys not in interface";
20
+ extra: Exclude<Keys[number], keyof T>;
21
+ } : {
22
+ error: "Missing keys from interface";
23
+ missing: Exclude<keyof T, Keys[number]>;
24
+ };
25
+ //# sourceMappingURL=KeysMatch.d.ts.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Helper type to ensure that an array of keys matches an interface.
3
+ *
4
+ * For example:
5
+ *
6
+ * ```ts
7
+ * interface Foo {
8
+ * arg1: string;
9
+ * arg2: string;
10
+ * }
11
+ *
12
+ * const FOO_KEYS = ["arg1", "arg2"] as const;
13
+ * type _ = Expect<KeysMatch<Foo, typeof FOO_KEYS>>;
14
+ * ```
15
+ */
16
+ export type KeysMatch<T, Keys extends readonly PropertyKey[]> = [
17
+ keyof T
18
+ ] extends [Keys[number]] ? [Keys[number]] extends [keyof T] ? true : {
19
+ error: "Extra keys not in interface";
20
+ extra: Exclude<Keys[number], keyof T>;
21
+ } : {
22
+ error: "Missing keys from interface";
23
+ missing: Exclude<keyof T, Keys[number]>;
24
+ };
25
+ //# sourceMappingURL=KeysMatch.d.ts.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Helper type to ensure that an array of keys matches an interface.
3
+ *
4
+ * For example:
5
+ *
6
+ * ```ts
7
+ * interface Foo {
8
+ * arg1: string;
9
+ * arg2: string;
10
+ * }
11
+ *
12
+ * const FOO_KEYS = ["arg1", "arg2"] as const;
13
+ * type _ = Expect<KeysMatch<Foo, typeof FOO_KEYS>>;
14
+ * ```
15
+ */
16
+ export type KeysMatch<T, Keys extends readonly PropertyKey[]> = [
17
+ keyof T
18
+ ] extends [Keys[number]] ? [Keys[number]] extends [keyof T] ? true : {
19
+ error: "Extra keys not in interface";
20
+ extra: Exclude<Keys[number], keyof T>;
21
+ } : {
22
+ error: "Missing keys from interface";
23
+ missing: Exclude<keyof T, Keys[number]>;
24
+ };
25
+ //# sourceMappingURL=KeysMatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeysMatch.d.ts","sourceRoot":"","sources":["../../src/types/KeysMatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,IAAI,SAAS,SAAS,WAAW,EAAE,IAAI;IAC9D,MAAM,CAAC;CACR,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GACpB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,GAC9B,IAAI,GACJ;IACE,KAAK,EAAE,6BAA6B,CAAC;IACrC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CACvC,GACH;IACE,KAAK,EAAE,6BAA6B,CAAC;IACrC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;CACzC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "complete-common",
3
- "version": "2.28.0",
3
+ "version": "2.30.0",
4
4
  "description": "Helper functions for TypeScript projects.",
5
5
  "homepage": "https://complete-ts.github.io/",
6
6
  "bugs": {
@@ -32,10 +32,10 @@
32
32
  "test": "bun test ./tests"
33
33
  },
34
34
  "devDependencies": {
35
- "@types/node": "25.6.0",
36
- "complete-node": "16.4.1",
35
+ "@types/node": "26.0.1",
36
+ "complete-node": "18.0.0",
37
37
  "typedoc": "0.28.19",
38
- "typedoc-plugin-markdown": "4.11.0",
38
+ "typedoc-plugin-markdown": "4.12.0",
39
39
  "typescript": "6.0.3",
40
40
  "unbuild": "3.6.1"
41
41
  }
@@ -17,13 +17,7 @@ import { isObject } from "./types.js";
17
17
  export function arrayCopyTwoDimensional<T>(
18
18
  array: ReadonlyArray<readonly T[]>,
19
19
  ): ReadonlyArray<readonly T[]> {
20
- const copiedArray: T[][] = [];
21
-
22
- for (const subArray of array) {
23
- copiedArray.push([...subArray]);
24
- }
25
-
26
- return copiedArray;
20
+ return Array.from(array, (subArray) => [...subArray]);
27
21
  }
28
22
 
29
23
  /**
@@ -134,7 +128,7 @@ export function arrayRemoveInPlace<T>(
134
128
  /** Helper function to remove all of the elements in an array in-place. */
135
129
  // eslint-disable-next-line complete/prefer-readonly-parameter-types
136
130
  export function emptyArray(array: unknown[]): void {
137
- array.splice(0);
131
+ array.length = 0; // eslint-disable-line no-param-reassign
138
132
  }
139
133
 
140
134
  /**
@@ -114,7 +114,7 @@ export function assertArrayNumber<T>(
114
114
 
115
115
  /**
116
116
  * Helper function to throw an error if the provided value is not an array with every element being
117
- * an object (i.e. a TypeScript record).
117
+ * an object (i.e., a TypeScript record).
118
118
  */
119
119
  export function assertArrayObject<T>(
120
120
  value: T,
@@ -212,9 +212,9 @@ export function assertInteger(
212
212
  value: unknown,
213
213
  msg: string,
214
214
  ): asserts value is number {
215
- // `Number.isInteger` will correctly return false for non-number variables such as strings,
215
+ // `Number.isSafeInteger` will correctly return false for non-number variables such as strings,
216
216
  // booleans, and so on.
217
- if (!Number.isInteger(value)) {
217
+ if (!Number.isSafeInteger(value)) {
218
218
  throw new TypeError(msg);
219
219
  }
220
220
  }
@@ -269,7 +269,7 @@ export function assertNumber<T>(
269
269
  }
270
270
 
271
271
  /**
272
- * Helper function to throw an error if the provided value is not an object (i.e. a TypeScript
272
+ * Helper function to throw an error if the provided value is not an object (i.e., a TypeScript
273
273
  * record).
274
274
  *
275
275
  * This is useful to have TypeScript narrow a `Record<string, unknown> | undefined` value to
@@ -290,6 +290,18 @@ export function assertObject<T>(
290
290
  }
291
291
  }
292
292
 
293
+ /** Helper function to throw an error if the provided value is not a positive integer. */
294
+ export function assertPositiveInteger(
295
+ value: unknown,
296
+ msg: string,
297
+ ): asserts value is number {
298
+ // `Number.isSafeInteger` will correctly return false for non-number variables such as strings,
299
+ // booleans, and so on.
300
+ if (!Number.isSafeInteger(value) || (value as number) <= 0) {
301
+ throw new TypeError(msg);
302
+ }
303
+ }
304
+
293
305
  /** Helper function to throw an error if the provided value is not a string. */
294
306
  export function assertString<T>(
295
307
  value: T,
@@ -57,7 +57,7 @@ export function getEnumValues<T extends TranspiledEnum>(
57
57
 
58
58
  /**
59
59
  * Helper function to validate that an interface contains all of the keys of an enum. You must
60
- * specify both generic parameters in order for this to work properly (i.e. the interface and then
60
+ * specify both generic parameters in order for this to work properly (i.e., the interface and then
61
61
  * the enum).
62
62
  *
63
63
  * For example:
@@ -78,7 +78,7 @@ export function getEnumValues<T extends TranspiledEnum>(
78
78
  * interfaceSatisfiesEnum<MyEnumToType, MyEnum>();
79
79
  * ```
80
80
  *
81
- * This function is only meant to be used with interfaces (i.e. types that will not exist at
81
+ * This function is only meant to be used with interfaces (i.e., types that will not exist at
82
82
  * run-time). If you are generating an object that will contain all of the keys of an enum, use the
83
83
  * `satisfies` operator with the `Record` type instead.
84
84
  */
@@ -35,7 +35,7 @@ export function objectFilter<K extends PropertyKey, V>(
35
35
 
36
36
  // eslint-disable-next-line complete/no-for-in
37
37
  for (const key in object) {
38
- const value = object[key];
38
+ const value = object[key]; // eslint-disable-line unicorn/no-unsafe-property-key
39
39
  const match = predicate(value);
40
40
  if (match) {
41
41
  array.push(value);
@@ -41,14 +41,9 @@ export function combineSets<T>(
41
41
  return newSet;
42
42
  }
43
43
 
44
- /** Helper function to copy a set. (You can also use a Set constructor to accomplish this task.) */
44
+ /** Helper function to copy a set. (Under the hood, this simply calls the `Set` constructor.) */
45
45
  export function copySet<T>(oldSet: ReadonlySet<T>): ReadonlySet<T> {
46
- const newSet = new Set<T>();
47
- for (const value of oldSet) {
48
- newSet.add(value);
49
- }
50
-
51
- return newSet;
46
+ return new Set(oldSet);
52
47
  }
53
48
 
54
49
  /** Helper function to convert the keys of an object to a set. */
@@ -16,9 +16,7 @@ export function sortCaseInsensitive(
16
16
  ): readonly string[] {
17
17
  const newArray = [...array];
18
18
  newArray.sort((a, b) =>
19
- a.localeCompare(b, undefined, {
20
- sensitivity: "base",
21
- }),
19
+ a.localeCompare(b, undefined, { sensitivity: "base" }),
22
20
  );
23
21
 
24
22
  return newArray;
@@ -14,25 +14,25 @@ import { parseIntSafe } from "./utils.js";
14
14
  // they will return inconsistent results due to the `lastIndex` property persisting between calls.
15
15
 
16
16
  /** We use a "*" instead of a "+" so that an empty string will match. */
17
- // eslint-disable-next-line no-control-regex
18
- const ASCII_REGEX = /^[\u0000-\u007F]*$/;
17
+ const ASCII_REGEX = /^\p{ASCII}*$/v;
19
18
 
20
- const DIACRITIC_REGEX = /\p{Diacritic}/u;
19
+ const DIACRITIC_REGEX = /\p{Diacritic}/v;
20
+ const FIRST_LETTER_CAPITALIZED_REGEX = /^\p{Lu}/v;
21
21
 
22
22
  /**
23
- * - We can't use `/\p{Emoji}/u` because it has a false positive on "#" characters.
24
- * - We can't use `/\p{Extended_Pictographic}/u` because it has a false negative on keycap emojis.
23
+ * - We can't use `/\p{Emoji}/v` because it has a false positive on "#" characters.
24
+ * - We can't use `/\p{Extended_Pictographic}/v` because it has a false negative on keycap emojis.
25
25
  */
26
- const EMOJI_REGEX = /(\p{Extended_Pictographic}|[#*0-9]\uFE0F?\u20E3)/u;
26
+ const EMOJI_REGEX = /\p{Extended_Pictographic}|[#*0-9]\u{FE0F}?\u{20E3}/v;
27
27
 
28
- const FIRST_LETTER_CAPITALIZED_REGEX = /^\p{Lu}/u;
29
- const KEBAB_CASE_REGEX = /^[\da-z]+(?:-[\da-z]+)*$/;
30
- const SEMANTIC_VERSION_REGEX = /^v*(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/;
31
- const WHITESPACE_REGEX = /\s/;
32
- const WHITESPACE_GLOBAL_REGEX = /\s/g;
33
- const TITLE_CASE_BOUNDARY_REGEX = /(?<=[\da-z])(?=[A-Z])/g;
34
- const UPPERCASE_REGEX = /^[A-Z]*$/;
35
- const LOWERCASE_REGEX = /^[a-z]*$/;
28
+ const KEBAB_CASE_REGEX = /^[\da-z]+(?:-[\da-z]+)*$/v;
29
+ const SEMANTIC_VERSION_REGEX =
30
+ /^v*(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/v;
31
+ const WHITESPACE_REGEX = /\s/v;
32
+ const WHITESPACE_GLOBAL_REGEX = /\s/gv;
33
+ const TITLE_CASE_BOUNDARY_REGEX = /(?<=[\da-z])(?=[A-Z])/gv;
34
+ const UPPERCASE_REGEX = /^[A-Z]*$/v;
35
+ const LOWERCASE_REGEX = /^[a-z]*$/v;
36
36
 
37
37
  /** Helper function to capitalize the first letter of a string. */
38
38
  export function capitalizeFirstLetter(string: string): string {
@@ -124,6 +124,7 @@ export function isASCII(str: string): boolean {
124
124
  * https://stackoverflow.com/questions/8334606/check-if-first-letter-of-word-is-a-capital-letter
125
125
  */
126
126
  export function isFirstLetterCapitalized(string: string): boolean {
127
+ // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
127
128
  return FIRST_LETTER_CAPITALIZED_REGEX.test(string);
128
129
  }
129
130
 
@@ -157,8 +158,8 @@ export function isUpperCase(string: string): boolean {
157
158
 
158
159
  /** Helper function to convert a string from kebab-case to camelCase. */
159
160
  export function kebabCaseToCamelCase(string: string): string {
160
- return string.replaceAll(/-./g, (match) => {
161
- const firstLetterOfWord = match[1];
161
+ return string.replaceAll(/-./gv, (match) => {
162
+ const firstLetterOfWord = match.at(1);
162
163
  return firstLetterOfWord === undefined
163
164
  ? ""
164
165
  : firstLetterOfWord.toUpperCase();
@@ -189,11 +190,11 @@ export function normalizeString(string: string): string {
189
190
 
190
191
  // Normalize newlines.
191
192
  sanitizedString = sanitizedString.replaceAll("\n\r", "\n");
192
- sanitizedString = sanitizedString.replaceAll(/\p{Zl}/gu, "\n");
193
- sanitizedString = sanitizedString.replaceAll(/\p{Zp}/gu, "\n");
193
+ sanitizedString = sanitizedString.replaceAll(/\p{Zl}/gv, "\n");
194
+ sanitizedString = sanitizedString.replaceAll(/\p{Zp}/gv, "\n");
194
195
 
195
196
  // Normalize spaces.
196
- sanitizedString = sanitizedString.replaceAll(/\p{Zs}/gu, " ");
197
+ sanitizedString = sanitizedString.replaceAll(/\p{Zs}/gv, " ");
197
198
 
198
199
  // Remove leading/trailing whitespace.
199
200
  sanitizedString = sanitizedString.trim();
@@ -210,7 +211,7 @@ export function normalizeString(string: string): string {
210
211
  export function parseSemanticVersion(
211
212
  versionString: string,
212
213
  ): SemanticVersion | undefined {
213
- const match = versionString.match(SEMANTIC_VERSION_REGEX);
214
+ const match = SEMANTIC_VERSION_REGEX.exec(versionString);
214
215
  if (match === null || match.groups === undefined) {
215
216
  return undefined;
216
217
  }
@@ -232,7 +233,11 @@ export function parseSemanticVersion(
232
233
  return undefined;
233
234
  }
234
235
 
235
- return { majorVersion, minorVersion, patchVersion };
236
+ return {
237
+ majorVersion,
238
+ minorVersion,
239
+ patchVersion,
240
+ };
236
241
  }
237
242
 
238
243
  /* eslint-disable jsdoc/escape-inline-tags */
@@ -301,7 +306,7 @@ export function removeLinesMatching(string: string, match: string): string {
301
306
  * https://stackoverflow.com/questions/11598786/how-to-replace-non-printable-unicode-characters-javascript
302
307
  */
303
308
  export function removeNonPrintableCharacters(string: string): string {
304
- return string.replaceAll(/\p{C}/gu, "");
309
+ return string.replaceAll(/\p{C}/gv, "");
305
310
  }
306
311
 
307
312
  /** Helper function to remove all whitespace characters from a string. */
@@ -353,7 +358,7 @@ export function satisfiesSemanticVersion(
353
358
  export function titleCaseToKebabCase(string: string): string {
354
359
  return string
355
360
  .replaceAll(TITLE_CASE_BOUNDARY_REGEX, "-")
356
- .replaceAll(/ +/g, "-")
361
+ .replaceAll(/ +/gv, "-")
357
362
  .toLowerCase();
358
363
  }
359
364
 
@@ -13,7 +13,7 @@ type TupleValue<T extends readonly unknown[]> = T[0];
13
13
  type TupleEntry<T extends readonly unknown[]> = [TupleKey<T>, TupleValue<T>]; // eslint-disable-line perfectionist/sort-modules
14
14
 
15
15
  /**
16
- * Helper function to get the entries (i.e. indexes and values) of a tuple in a type-safe way.
16
+ * Helper function to get the entries (i.e., indexes and values) of a tuple in a type-safe way.
17
17
  *
18
18
  * This is useful because the vanilla `Array.entries` method will always have the keys be of type
19
19
  * `number`.
@@ -25,7 +25,7 @@ export function* tupleEntries<T extends readonly unknown[]>(
25
25
  }
26
26
 
27
27
  /**
28
- * Helper function to get the keys (i.e. indexes) of a tuple in a type-safe way.
28
+ * Helper function to get the keys (i.e., indexes) of a tuple in a type-safe way.
29
29
  *
30
30
  * This is useful because the vanilla `Array.keys` method will always have the keys be of type
31
31
  * `number`.
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  /**
8
- * Helper function to narrow an unknown value to an object (i.e. a TypeScript record).
8
+ * Helper function to narrow an unknown value to an object (i.e., a TypeScript record).
9
9
  *
10
10
  * Under the hood, this checks for `typeof variable === "object"`, `variable !== null`, and
11
11
  * `!Array.isArray(variable)`.