@visulima/string 3.0.0-alpha.1 → 3.0.0-alpha.10

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 (83) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/LICENSE.md +429 -1
  3. package/README.md +72 -20
  4. package/dist/align-text.d.ts +7 -10
  5. package/dist/align-text.js +7 -5
  6. package/dist/case/camel-case.d.ts +3 -5
  7. package/dist/case/camel-case.js +7 -7
  8. package/dist/case/capital-case.d.ts +3 -5
  9. package/dist/case/capital-case.js +3 -3
  10. package/dist/case/constant-case.d.ts +3 -5
  11. package/dist/case/dot-case.d.ts +3 -5
  12. package/dist/case/flat-case.d.ts +3 -5
  13. package/dist/case/flip-case.d.ts +4 -7
  14. package/dist/case/flip-case.js +8 -2
  15. package/dist/case/identify-case.d.ts +3 -5
  16. package/dist/case/identify-case.js +2 -1
  17. package/dist/case/index.d.ts +23 -25
  18. package/dist/case/index.js +1 -1
  19. package/dist/case/kebab-case.d.ts +6 -9
  20. package/dist/case/kebab-case.js +3 -3
  21. package/dist/case/lower-first.d.ts +3 -5
  22. package/dist/case/no-case.d.ts +3 -5
  23. package/dist/case/pascal-case.d.ts +3 -5
  24. package/dist/case/pascal-case.js +3 -3
  25. package/dist/case/pascal-snake-case.d.ts +3 -5
  26. package/dist/case/path-case.d.ts +3 -5
  27. package/dist/case/sentence-case.d.ts +3 -5
  28. package/dist/case/sentence-case.js +7 -7
  29. package/dist/case/snake-case.d.ts +3 -5
  30. package/dist/case/split-by-case.d.ts +11 -14
  31. package/dist/case/split-by-case.js +171 -52
  32. package/dist/case/title-case.d.ts +3 -5
  33. package/dist/case/title-case.js +3 -5
  34. package/dist/case/train-case.d.ts +3 -5
  35. package/dist/case/train-case.js +1 -1
  36. package/dist/case/upper-first.d.ts +3 -5
  37. package/dist/closest-string.d.ts +3 -5
  38. package/dist/closest-string.js +5 -8
  39. package/dist/compare-similarity.d.ts +3 -5
  40. package/dist/compare-similarity.js +2 -2
  41. package/dist/get-string-truncated-width.d.ts +18 -20
  42. package/dist/get-string-truncated-width.js +2 -33
  43. package/dist/get-string-width.d.ts +2 -5
  44. package/dist/index.d.ts +49 -45
  45. package/dist/index.js +12 -4
  46. package/dist/native-string-types.d.ts +145 -128
  47. package/dist/outdent.d.ts +9 -11
  48. package/dist/outdent.js +7 -15
  49. package/dist/packem_shared/closestN-DUsjgiXb.js +25 -0
  50. package/dist/packem_shared/{constants-CTzC6Gjg.js → constants-BsZS5f0z.js} +1 -1
  51. package/dist/packem_shared/countOccurrences-CS9XprQN.js +22 -0
  52. package/dist/packem_shared/direction-C9gfWWTK.js +16 -0
  53. package/dist/packem_shared/eastAsianWidthType-SD3YniiT.js +20 -0
  54. package/dist/packem_shared/excerpt-COsVNttN.js +1502 -0
  55. package/dist/packem_shared/indent-D3DSCa6s.js +33 -0
  56. package/dist/packem_shared/isFullwidthCodePoint-IFXZxNjq.js +10 -0
  57. package/dist/packem_shared/{joinSegments-CuU8RGpP.js → joinSegments-CvxSgKz0.js} +1 -1
  58. package/dist/packem_shared/lookup-BE8lEqy4.js +104 -0
  59. package/dist/packem_shared/lru-cache.d-BsP6kbVJ.d.ts +13 -0
  60. package/dist/packem_shared/{normalize-german-eszett-DEB8-ZPT.js → normalize-german-eszett-4p3yKEzn.js} +2 -1
  61. package/dist/packem_shared/redent-DSb1IJWn.js +8 -0
  62. package/dist/packem_shared/{replaceString-vYvRBMfC.js → replaceString-Ce8X2ZQ-.js} +19 -17
  63. package/dist/packem_shared/stripIndent-D_TRjxZ_.js +20 -0
  64. package/dist/packem_shared/{transliterate-CYnKKT8f.js → transliterate-CKt9VK7D.js} +10 -12
  65. package/dist/packem_shared/{types-Daae0hGf.d.ts → types.d-BVNv675q.d.ts} +18 -21
  66. package/dist/packem_shared/{types-CfPsw9ix.d.ts → types.d-DfgbcNKh.d.ts} +19 -21
  67. package/dist/slice.d.ts +5 -8
  68. package/dist/slice.js +9 -7
  69. package/dist/slugify.d.ts +1 -3
  70. package/dist/slugify.js +1 -1
  71. package/dist/test/utils.d.ts +20 -22
  72. package/dist/test/utils.js +2 -2
  73. package/dist/test/vitest.d.ts +3 -6
  74. package/dist/truncate.d.ts +7 -10
  75. package/dist/truncate.js +49 -26
  76. package/dist/utilities.d.ts +2 -4
  77. package/dist/utilities.js +6 -8
  78. package/dist/word-similarity-sort.d.ts +3 -5
  79. package/dist/word-wrap.d.ts +9 -11
  80. package/dist/word-wrap.js +37 -35
  81. package/package.json +2 -2
  82. package/dist/packem_shared/closestN-BREy4E7C.js +0 -19
  83. package/dist/packem_shared/lru-cache-B_t27XwI.d.ts +0 -14
@@ -1,31 +1,29 @@
1
1
  declare const formatAnsiString: (ansiString: string) => {
2
- ansi: string;
3
- json: string;
4
- lengthDifference: number;
5
- stripped: string;
6
- visible: string;
2
+ ansi: string;
3
+ json: string;
4
+ lengthDifference: number;
5
+ stripped: string;
6
+ visible: string;
7
7
  };
8
8
  interface ExpectationResult {
9
- actual?: unknown;
10
- expected?: unknown;
11
- message: () => string;
12
- pass: boolean;
9
+ actual?: unknown;
10
+ expected?: unknown;
11
+ message: () => string;
12
+ pass: boolean;
13
13
  }
14
14
  declare const expectAnsiStrings: (actual: string, expected: string) => ExpectationResult;
15
15
  declare const compareAnsiStrings: (actual: string, expected: string) => {
16
- actual: ReturnType<typeof formatAnsiString>;
16
+ actual: ReturnType<typeof formatAnsiString>;
17
+ ansiEqual: boolean;
18
+ expected: ReturnType<typeof formatAnsiString>;
19
+ strippedEqual: boolean;
20
+ summary: {
21
+ actualLength: number;
22
+ actualStrippedLength: number;
17
23
  ansiEqual: boolean;
18
- expected: ReturnType<typeof formatAnsiString>;
24
+ expectedLength: number;
25
+ expectedStrippedLength: number;
19
26
  strippedEqual: boolean;
20
- summary: {
21
- actualLength: number;
22
- actualStrippedLength: number;
23
- ansiEqual: boolean;
24
- expectedLength: number;
25
- expectedStrippedLength: number;
26
- strippedEqual: boolean;
27
- };
27
+ };
28
28
  };
29
-
30
- export { compareAnsiStrings, expectAnsiStrings, formatAnsiString };
31
- export type { ExpectationResult };
29
+ export { ExpectationResult, compareAnsiStrings, expectAnsiStrings, formatAnsiString };
@@ -18,8 +18,8 @@ const __cjs_getBuiltinModule = (module) => {
18
18
  };
19
19
 
20
20
  const {
21
- stripVTControlCharacters,
22
- format
21
+ format,
22
+ stripVTControlCharacters
23
23
  } = __cjs_getBuiltinModule("node:util");
24
24
  import { getStringWidth } from '../get-string-width.js';
25
25
 
@@ -1,9 +1,6 @@
1
- import { ExpectationResult } from './utils.js';
2
-
1
+ import { ExpectationResult } from "./utils.js";
3
2
  interface CustomMatchers {
4
- toEqualAnsi: (expected: string) => ExpectationResult;
3
+ toEqualAnsi: (expected: string) => ExpectationResult;
5
4
  }
6
5
  declare const toEqualAnsi: (actual: string, expected: string) => ExpectationResult;
7
-
8
- export { toEqualAnsi };
9
- export type { CustomMatchers };
6
+ export { CustomMatchers, toEqualAnsi };
@@ -1,13 +1,10 @@
1
- import { StringTruncatedWidthOptions } from './get-string-truncated-width.js';
2
-
1
+ import { StringTruncatedWidthOptions } from "./get-string-truncated-width.js";
3
2
  type TruncateOptions = {
4
- ellipsis?: string;
5
- ellipsisWidth?: number;
6
- position?: "end" | "middle" | "start";
7
- preferTruncationOnSpace?: boolean;
8
- width?: Omit<StringTruncatedWidthOptions, "ellipsis" | "ellipsisWidth" | "limit">;
3
+ ellipsis?: string;
4
+ ellipsisWidth?: number;
5
+ position?: "end" | "middle" | "start";
6
+ preferTruncationOnSpace?: boolean;
7
+ width?: Omit<StringTruncatedWidthOptions, "ellipsis" | "ellipsisWidth" | "limit">;
9
8
  };
10
9
  declare const truncate: (input: string, limit: number, options?: TruncateOptions) => string;
11
-
12
- export { truncate };
13
- export type { TruncateOptions };
10
+ export { TruncateOptions, truncate };
package/dist/truncate.js CHANGED
@@ -1,22 +1,33 @@
1
1
  import { getStringTruncatedWidth } from './get-string-truncated-width.js';
2
+ import { getStringWidth } from './get-string-width.js';
2
3
  import { slice } from './slice.js';
3
4
 
4
5
  const DEFAULT_ELLIPSIS = "…";
5
6
  const MAX_SPACE_SEARCH_DISTANCE = 3;
6
- const findNearestSpace = (string_, startIndex, searchRight = false) => {
7
- if (string_.charAt(startIndex) === " ") {
7
+ const findNearestSpace = (value, startIndex, searchRight = false) => {
8
+ if (value.charAt(startIndex) === " ") {
8
9
  return startIndex;
9
10
  }
10
11
  const direction = searchRight ? 1 : -1;
11
- const limit = Math.min(MAX_SPACE_SEARCH_DISTANCE, searchRight ? string_.length - startIndex : startIndex);
12
+ const limit = Math.min(MAX_SPACE_SEARCH_DISTANCE, searchRight ? value.length - startIndex : startIndex);
12
13
  for (let offset = 1; offset <= limit; offset++) {
13
14
  const index = startIndex + offset * direction;
14
- if (string_.charAt(index) === " ") {
15
+ if (value.charAt(index) === " ") {
15
16
  return index;
16
17
  }
17
18
  }
18
19
  return startIndex;
19
20
  };
21
+ const widthToIndex = (input, targetWidth, widthOptions) => {
22
+ const result = getStringTruncatedWidth(input, {
23
+ ...widthOptions,
24
+ ellipsis: "",
25
+ ellipsisWidth: 0,
26
+ limit: targetWidth
27
+ });
28
+ return result.index;
29
+ };
30
+ const indexToWidth = (input, stringIndex, widthOptions) => getStringWidth(input.slice(0, stringIndex), widthOptions);
20
31
  const truncate = (input, limit, options = {}) => {
21
32
  if (typeof input !== "string") {
22
33
  throw new TypeError(`Expected \`input\` to be a string, got ${typeof input}`);
@@ -27,56 +38,68 @@ const truncate = (input, limit, options = {}) => {
27
38
  if (input === "" || limit <= 0) {
28
39
  return "";
29
40
  }
30
- input = input.normalize("NFC");
41
+ const normalizedInput = input.normalize("NFC");
31
42
  const { ellipsis = DEFAULT_ELLIPSIS, position = "end", preferTruncationOnSpace = false } = options;
32
43
  let ellipsisWidth = options.ellipsisWidth ?? ellipsis === DEFAULT_ELLIPSIS ? 1 : void 0;
33
- if (ellipsisWidth === void 0) {
34
- ellipsisWidth = getStringTruncatedWidth(ellipsis, {
35
- ...options.width,
36
- ellipsis: "",
37
- ellipsisWidth: 0,
38
- limit: Number.POSITIVE_INFINITY
39
- }).width;
40
- }
41
- const { width } = getStringTruncatedWidth(input, {
44
+ ellipsisWidth ??= getStringTruncatedWidth(ellipsis, {
45
+ ...options.width,
46
+ ellipsis: "",
47
+ ellipsisWidth: 0,
48
+ limit: Number.POSITIVE_INFINITY
49
+ }).width;
50
+ const { width } = getStringTruncatedWidth(normalizedInput, {
42
51
  ...options.width,
43
52
  ellipsis,
44
53
  ellipsisWidth
45
54
  });
46
55
  if (width <= limit) {
47
- return input;
56
+ return normalizedInput;
48
57
  }
49
58
  switch (position) {
50
59
  case "end": {
51
60
  if (preferTruncationOnSpace) {
52
- const nearestSpace = findNearestSpace(input, limit - 1);
53
- return slice(input, 0, nearestSpace, {
61
+ const targetWidth = limit - ellipsisWidth;
62
+ const stringIndex = widthToIndex(normalizedInput, targetWidth, options.width);
63
+ const nearestSpace = findNearestSpace(normalizedInput, stringIndex);
64
+ const nearestSpaceWidth = indexToWidth(normalizedInput, nearestSpace, options.width);
65
+ return slice(normalizedInput, 0, nearestSpaceWidth, {
54
66
  width: options.width
55
67
  }) + ellipsis;
56
68
  }
57
- return slice(input, 0, limit - ellipsisWidth, {
69
+ return slice(normalizedInput, 0, limit - ellipsisWidth, {
58
70
  width: options.width
59
71
  }) + ellipsis;
60
72
  }
61
73
  case "middle": {
62
74
  const half = Math.floor(limit / 2);
75
+ const totalTextWidth = Math.max(0, limit - ellipsisWidth);
76
+ const leftWidth = Math.min(half, totalTextWidth);
77
+ const rightWidth = Math.max(0, totalTextWidth - leftWidth);
63
78
  if (preferTruncationOnSpace) {
64
- const firstBreak = findNearestSpace(input, half);
65
- const secondBreak = findNearestSpace(input, width - (limit - half) + 1, true);
66
- return slice(input, 0, firstBreak) + ellipsis + slice(input, secondBreak, width).trim();
79
+ const firstBreakIndex = widthToIndex(normalizedInput, leftWidth, options.width);
80
+ const firstBreak = findNearestSpace(normalizedInput, firstBreakIndex);
81
+ const firstBreakWidth = indexToWidth(normalizedInput, firstBreak, options.width);
82
+ const secondBreakWidthTarget = width - rightWidth;
83
+ const secondBreakIndex = widthToIndex(normalizedInput, secondBreakWidthTarget, options.width);
84
+ const secondBreak = findNearestSpace(normalizedInput, secondBreakIndex, true);
85
+ const secondBreakWidthActual = indexToWidth(normalizedInput, secondBreak, options.width);
86
+ return slice(normalizedInput, 0, firstBreakWidth, { width: options.width }) + ellipsis + slice(normalizedInput, secondBreakWidthActual, width, { width: options.width }).trim();
67
87
  }
68
- return slice(input, 0, half, {
88
+ return slice(normalizedInput, 0, leftWidth, {
69
89
  width: options.width
70
- }) + ellipsis + slice(input, width - (limit - half) + ellipsisWidth, width, {
90
+ }) + ellipsis + slice(normalizedInput, width - rightWidth, width, {
71
91
  width: options.width
72
92
  });
73
93
  }
74
94
  case "start": {
75
95
  if (preferTruncationOnSpace) {
76
- const nearestSpace = findNearestSpace(input, width - limit + 1, true);
77
- return ellipsis + slice(input, nearestSpace, width).trim();
96
+ const targetWidth = width - limit + ellipsisWidth;
97
+ const stringIndex = widthToIndex(normalizedInput, targetWidth, options.width);
98
+ const nearestSpace = findNearestSpace(normalizedInput, stringIndex, true);
99
+ const nearestSpaceWidth = indexToWidth(normalizedInput, nearestSpace, options.width);
100
+ return ellipsis + slice(normalizedInput, nearestSpaceWidth, width, { width: options.width }).trim();
78
101
  }
79
- return ellipsis + slice(input, width - limit + ellipsisWidth, width, {
102
+ return ellipsis + slice(normalizedInput, width - limit + ellipsisWidth, width, {
80
103
  width: options.width
81
104
  });
82
105
  }
@@ -1,10 +1,8 @@
1
- import { l as IntervalArray } from './packem_shared/types-Daae0hGf.js';
2
- export { L as LRUCache } from './packem_shared/lru-cache-B_t27XwI.js';
3
-
1
+ import { a as IntervalArray } from "./packem_shared/types.d-BVNv675q.js";
2
+ export { L as LRUCache } from "./packem_shared/lru-cache.d-BsP6kbVJ.js";
4
3
  declare const escapeRegExp: (char: string) => string;
5
4
  declare const inRange: (number_: number, ranges: IntervalArray) => boolean;
6
5
  declare const hasChinese: (char: string) => boolean;
7
6
  declare const hasPunctuationOrSpace: (char: string) => boolean;
8
7
  declare const findStringOccurrences: (source: string, needles: string[]) => IntervalArray;
9
-
10
8
  export { escapeRegExp, findStringOccurrences, hasChinese, hasPunctuationOrSpace, inRange };
package/dist/utilities.js CHANGED
@@ -1,17 +1,15 @@
1
1
  export { default as LRUCache } from './packem_shared/LRUCache-3r3SovvR.js';
2
2
 
3
+ const RE_REGEXP_SPECIAL = /[.*+?^${}()|[\]\\]/g;
4
+ const RE_CHINESE = /[\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DBF\u4E00-\u9FFC\uF900-\uFA6D\uFA70-\uFAD9]|\uD81B[\uDFF0\uDFF1]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD869[\uDC00-\uDEDD\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A]/;
5
+ const RE_PUNCTUATION_OR_SPACE = /[\p{P}\p{Z}]/u;
3
6
  const escapeRegExp = (char) => (
4
7
  // $& means the whole matched string
5
- char.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)
8
+ char.replaceAll(RE_REGEXP_SPECIAL, String.raw`\$&`)
6
9
  );
7
10
  const inRange = (number_, ranges) => ranges.some((range) => number_ >= range[0] && number_ <= range[1]);
8
- const hasChinese = (char) => /[\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DBF\u4E00-\u9FFC\uF900-\uFA6D\uFA70-\uFAD9]|\uD81B[\uDFF0\uDFF1]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD869[\uDC00-\uDEDD\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A]/.test(
9
- char
10
- );
11
- const hasPunctuationOrSpace = (char) => (
12
- // Uses Unicode property escapes for Punctuation and general category for Space
13
- /[\p{P}\p{Z}]/u.test(char)
14
- );
11
+ const hasChinese = (char) => RE_CHINESE.test(char);
12
+ const hasPunctuationOrSpace = (char) => RE_PUNCTUATION_OR_SPACE.test(char);
15
13
  const findStringOccurrences = (source, needles) => {
16
14
  const ranges = [];
17
15
  if (!Array.isArray(needles)) {
@@ -1,8 +1,6 @@
1
1
  interface WordSimilaritySortOptions {
2
- caseSensitive?: boolean;
3
- compareFunction?: (a: string, b: string) => number;
2
+ caseSensitive?: boolean;
3
+ compareFunction?: (a: string, b: string) => number;
4
4
  }
5
5
  declare const wordSimilaritySort: (givenWord: string, possibleWords: ReadonlyArray<string>, options?: WordSimilaritySortOptions) => string[];
6
-
7
- export { wordSimilaritySort };
8
- export type { WordSimilaritySortOptions };
6
+ export { WordSimilaritySortOptions, wordSimilaritySort };
@@ -1,16 +1,14 @@
1
1
  declare const WrapMode: {
2
- readonly BREAK_AT_CHARACTERS: "BREAK_AT_CHARACTERS";
3
- readonly BREAK_WORDS: "BREAK_WORDS";
4
- readonly PRESERVE_WORDS: "PRESERVE_WORDS";
5
- readonly STRICT_WIDTH: "STRICT_WIDTH";
2
+ readonly BREAK_AT_CHARACTERS: "BREAK_AT_CHARACTERS";
3
+ readonly BREAK_WORDS: "BREAK_WORDS";
4
+ readonly PRESERVE_WORDS: "PRESERVE_WORDS";
5
+ readonly STRICT_WIDTH: "STRICT_WIDTH";
6
6
  };
7
7
  interface WordWrapOptions {
8
- removeZeroWidthCharacters?: boolean;
9
- trim?: boolean;
10
- width?: number;
11
- wrapMode?: keyof typeof WrapMode;
8
+ removeZeroWidthCharacters?: boolean;
9
+ trim?: boolean;
10
+ width?: number;
11
+ wrapMode?: keyof typeof WrapMode;
12
12
  }
13
13
  declare const wordWrap: (string: string, options?: WordWrapOptions) => string;
14
-
15
- export { WrapMode, wordWrap };
16
- export type { WordWrapOptions };
14
+ export { WordWrapOptions, WrapMode, wordWrap };
package/dist/word-wrap.js CHANGED
@@ -1,9 +1,10 @@
1
- import { O as ESCAPES, P as ANSI_ESCAPE_LINK, Q as ANSI_SGR_TERMINATOR, S as ANSI_ESCAPE_BELL, T as ANSI_CSI, U as RE_ESCAPE_PATTERN, V as ANSI_RESET_CODES, W as END_CODE, X as RE_ZERO_WIDTH } from './packem_shared/constants-CTzC6Gjg.js';
1
+ import { O as ESCAPES, P as ANSI_ESCAPE_LINK, Q as ANSI_SGR_TERMINATOR, S as ANSI_ESCAPE_BELL, T as ANSI_CSI, U as RE_ESCAPE_PATTERN, V as ANSI_RESET_CODES, W as END_CODE, X as RE_ZERO_WIDTH } from './packem_shared/constants-BsZS5f0z.js';
2
2
  import { getStringWidth } from './get-string-width.js';
3
3
 
4
+ const RE_ANSI_CODE = /\x1B\[(\d+)m/;
4
5
  class AnsiStateTracker {
5
- activeForeground = null;
6
- activeBackground = null;
6
+ activeForeground = void 0;
7
+ activeBackground = void 0;
7
8
  // Track other formatting (bold, italic, etc.)
8
9
  activeFormatting = [];
9
10
  /**
@@ -11,24 +12,24 @@ class AnsiStateTracker {
11
12
  * @param sequence The escape sequence to process
12
13
  */
13
14
  processEscape(sequence) {
14
- const match = /\x1B\[(\d+)m/.exec(sequence);
15
+ const match = RE_ANSI_CODE.exec(sequence);
15
16
  if (!match) {
16
17
  return;
17
18
  }
18
19
  const code = Number.parseInt(match[1], 10);
19
20
  switch (code) {
20
21
  case 0: {
21
- this.activeForeground = null;
22
- this.activeBackground = null;
22
+ this.activeForeground = void 0;
23
+ this.activeBackground = void 0;
23
24
  this.activeFormatting = [];
24
25
  break;
25
26
  }
26
27
  case 39: {
27
- this.activeForeground = null;
28
+ this.activeForeground = void 0;
28
29
  break;
29
30
  }
30
31
  case 49: {
31
- this.activeBackground = null;
32
+ this.activeBackground = void 0;
32
33
  break;
33
34
  }
34
35
  default: {
@@ -62,7 +63,7 @@ class AnsiStateTracker {
62
63
  }
63
64
  }
64
65
  /**
65
- * Gets all active escape sequences to apply
66
+ * Gets all active escape sequences to apply.
66
67
  * @returns String with all active escapes
67
68
  */
68
69
  getStartEscapesForAllActiveAttributes() {
@@ -92,7 +93,7 @@ class AnsiStateTracker {
92
93
  "\x1B[9m": "\x1B[29m"
93
94
  // Strikethrough
94
95
  };
95
- [...this.activeFormatting].reverse().forEach((formatCode) => {
96
+ [...this.activeFormatting].toReversed().forEach((formatCode) => {
96
97
  const resetCode = formatResetMap[formatCode];
97
98
  if (resetCode) {
98
99
  closingEscapes.push(resetCode);
@@ -122,7 +123,6 @@ const processAnsiString = (string, options = {}) => {
122
123
  const stateTracker = new AnsiStateTracker();
123
124
  let currentText = "";
124
125
  let isInsideEscape = false;
125
- let isInsideLinkEscape = false;
126
126
  let escapeBuffer = "";
127
127
  let currentUrl = "";
128
128
  let isInHyperlink = false;
@@ -150,7 +150,7 @@ const processAnsiString = (string, options = {}) => {
150
150
  isInsideEscape = true;
151
151
  escapeBuffer = character;
152
152
  const escapeInfo = checkEscapeSequence(chars, index);
153
- isInsideLinkEscape = escapeInfo.isInsideLinkEscape;
153
+ const { isInsideLinkEscape } = escapeInfo;
154
154
  if (isInsideLinkEscape) {
155
155
  let urlEnd = index + 1;
156
156
  currentUrl = "";
@@ -177,7 +177,6 @@ const processAnsiString = (string, options = {}) => {
177
177
  index = urlEnd;
178
178
  isInHyperlink = true;
179
179
  isInsideEscape = false;
180
- isInsideLinkEscape = false;
181
180
  escapeBuffer = "";
182
181
  continue;
183
182
  }
@@ -264,7 +263,7 @@ const processAnsiString = (string, options = {}) => {
264
263
 
265
264
  const wrapAnsiCode = (code) => {
266
265
  const escapeChar = ESCAPES.values().next().value;
267
- return `${escapeChar}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
266
+ return `${escapeChar}${ANSI_CSI}${String(code)}${ANSI_SGR_TERMINATOR}`;
268
267
  };
269
268
 
270
269
  const wrapAnsiHyperlink = (url) => {
@@ -318,6 +317,8 @@ const preserveAnsi = (rawLines) => {
318
317
  return returnValue;
319
318
  };
320
319
 
320
+ const RE_SPLIT_WHITESPACE = /(?=\s)|(?<=\s)/;
321
+ const RE_WHITESPACE_ONLY = /^\s+$/;
321
322
  const resetAnsiAtLineBreak = (currentLine) => {
322
323
  if (!currentLine.includes("\x1B")) {
323
324
  return currentLine;
@@ -356,7 +357,8 @@ const wrapWithBreakAtWidth = (string, width, trim) => {
356
357
  let isInsideEscape = false;
357
358
  let isInsideLinkEscape = false;
358
359
  let escapeBuffer = "";
359
- for (let index = 0; index < string.length; index++) {
360
+ let index = 0;
361
+ while (index < string.length) {
360
362
  const char = string[index];
361
363
  if (ESCAPES.has(char)) {
362
364
  isInsideEscape = true;
@@ -364,6 +366,7 @@ const wrapWithBreakAtWidth = (string, width, trim) => {
364
366
  currentLine += char;
365
367
  const escapeInfo = checkEscapeSequence([...string], index);
366
368
  isInsideLinkEscape = escapeInfo.isInsideLinkEscape;
369
+ index += 1;
367
370
  continue;
368
371
  }
369
372
  if (isInsideEscape) {
@@ -377,12 +380,14 @@ const wrapWithBreakAtWidth = (string, width, trim) => {
377
380
  isInsideEscape = false;
378
381
  ansiTracker.processEscape(escapeBuffer);
379
382
  }
383
+ index += 1;
380
384
  continue;
381
385
  }
382
386
  const charWidth = getStringWidth(char);
383
387
  const isSpace = char === " ";
384
388
  if (charWidth === 0) {
385
389
  currentLine += char;
390
+ index += 1;
386
391
  continue;
387
392
  }
388
393
  if (currentWidth + charWidth > width) {
@@ -394,12 +399,6 @@ const wrapWithBreakAtWidth = (string, width, trim) => {
394
399
  if (isSpace && trim) {
395
400
  while (index < string.length && string[index] === " ") {
396
401
  index += 1;
397
- if (index >= string.length) {
398
- break;
399
- }
400
- }
401
- if (index < string.length) {
402
- index--;
403
402
  }
404
403
  continue;
405
404
  }
@@ -415,9 +414,10 @@ const wrapWithBreakAtWidth = (string, width, trim) => {
415
414
  while (index < string.length && string[index] === " ") {
416
415
  index += 1;
417
416
  }
418
- index--;
417
+ continue;
419
418
  }
420
419
  }
420
+ index += 1;
421
421
  }
422
422
  if (currentLine) {
423
423
  rows.push(currentLine + ansiTracker.getEndEscapesForAllActiveAttributes());
@@ -437,13 +437,15 @@ const wrapCharByChar = (string, width, trim) => {
437
437
  let currentWidth = 0;
438
438
  processAnsiString(inputToProcess, {
439
439
  getWidth: getStringWidth,
440
+ // eslint-disable-next-line sonarjs/cognitive-complexity,sonarjs/no-invariant-returns
440
441
  onSegment: (segment, stateTracker) => {
442
+ const segText = segment.text ?? "";
441
443
  if (segment.isEscapeSequence) {
442
- currentLine += segment.text;
444
+ currentLine += segText;
443
445
  } else {
444
- const isSpace = segment.text === " ";
446
+ const isSpace = segText === " ";
445
447
  if (segment.width === 0) {
446
- currentLine += segment.text;
448
+ currentLine += segText;
447
449
  return true;
448
450
  }
449
451
  if (currentWidth + segment.width > width) {
@@ -456,11 +458,11 @@ const wrapCharByChar = (string, width, trim) => {
456
458
  if (trim) {
457
459
  return true;
458
460
  }
459
- rows.push(stateTracker.getStartEscapesForAllActiveAttributes() + segment.text);
461
+ rows.push(stateTracker.getStartEscapesForAllActiveAttributes() + segText);
460
462
  return true;
461
463
  }
462
464
  }
463
- currentLine += segment.text;
465
+ currentLine += segText;
464
466
  currentWidth += segment.width;
465
467
  }
466
468
  return true;
@@ -479,14 +481,14 @@ const wrapWithWordBoundaries = (string, width, trim) => {
479
481
  if (inputToProcess.length === 0) {
480
482
  return [];
481
483
  }
482
- const tokens = inputToProcess.split(/(?=\s)|(?<=\s)/);
484
+ const tokens = inputToProcess.split(RE_SPLIT_WHITESPACE);
483
485
  const rows = [];
484
486
  let currentLine = "";
485
487
  let currentWidth = 0;
486
488
  let index = 0;
487
489
  while (index < tokens.length) {
488
490
  const token = tokens[index];
489
- const isSpace = /^\s+$/.test(token);
491
+ const isSpace = RE_WHITESPACE_ONLY.test(token);
490
492
  const tokenVisibleWidth = getStringWidth(token);
491
493
  if (token.length === 0) {
492
494
  index += 1;
@@ -527,14 +529,14 @@ const wrapAndBreakWords = (string, width, trim) => {
527
529
  if (inputToProcess.length === 0) {
528
530
  return [];
529
531
  }
530
- const tokens = inputToProcess.split(/(?=\s)|(?<=\s)/);
532
+ const tokens = inputToProcess.split(RE_SPLIT_WHITESPACE);
531
533
  const rows = [];
532
534
  let currentLine = "";
533
535
  let currentWidth = 0;
534
536
  let index = 0;
535
537
  while (index < tokens.length) {
536
538
  const token = tokens[index];
537
- const isSpace = /^\s+$/.test(token);
539
+ const isSpace = RE_WHITESPACE_ONLY.test(token);
538
540
  const tokenVisibleWidth = getStringWidth(token);
539
541
  if (token.length === 0) {
540
542
  index += 1;
@@ -550,10 +552,10 @@ const wrapAndBreakWords = (string, width, trim) => {
550
552
  }
551
553
  const brokenLines = wrapWithBreakAtWidth(token, width, trim);
552
554
  if (brokenLines.length > 0) {
553
- for (let index_ = 0; index_ < brokenLines.length - 1; index_++) {
554
- rows.push(brokenLines[index_]);
555
+ for (let brokenLineIndex = 0; brokenLineIndex < brokenLines.length - 1; brokenLineIndex += 1) {
556
+ rows.push(brokenLines[brokenLineIndex]);
555
557
  }
556
- currentLine = brokenLines[brokenLines.length - 1];
558
+ currentLine = brokenLines.at(-1);
557
559
  currentWidth = getStringWidth(currentLine);
558
560
  } else {
559
561
  currentLine = "";
@@ -603,7 +605,7 @@ const wordWrap = (string, options = {}) => {
603
605
  if (trim && string.trim() === "") {
604
606
  return "";
605
607
  }
606
- let normalizedString = String(string).normalize("NFC").replaceAll("\r\n", "\n");
608
+ let normalizedString = string.normalize("NFC").replaceAll("\r\n", "\n");
607
609
  if (removeZeroWidthCharacters) {
608
610
  normalizedString = normalizedString.replaceAll(RE_ZERO_WIDTH, "");
609
611
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visulima/string",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0-alpha.10",
4
4
  "description": "Functions for manipulating strings.",
5
5
  "keywords": [
6
6
  "visulima",
@@ -228,7 +228,7 @@
228
228
  "CHANGELOG.md"
229
229
  ],
230
230
  "engines": {
231
- "node": ">=22.13 <=25.x"
231
+ "node": "^22.14.0 || >=24.10.0"
232
232
  },
233
233
  "publishConfig": {
234
234
  "access": "public",
@@ -1,19 +0,0 @@
1
- import { distance } from './closest-IHuSvnSn.js';
2
- export { closest } from './closest-IHuSvnSn.js';
3
-
4
- const closestN = (string_, array, n) => {
5
- const distances = new Array(n).fill(Infinity);
6
- const values = new Array(n).fill(null);
7
- for (let value of array) {
8
- let distribution = distance(string_, value);
9
- for (let index = 0; index < n; index++) {
10
- if (distribution < distances[index]) {
11
- [distribution, distances[index]] = [distances[index], distribution];
12
- [value, values[index]] = [values[index], value];
13
- }
14
- }
15
- }
16
- return values;
17
- };
18
-
19
- export { closestN, distance };
@@ -1,14 +0,0 @@
1
- declare class LRUCache<K, V> {
2
- private readonly capacity;
3
- private readonly cache;
4
- private keyOrder;
5
- constructor(capacity: number);
6
- get(key: K): V | undefined;
7
- has(key: K): boolean;
8
- set(key: K, value: V): void;
9
- delete(key: K): void;
10
- clear(): void;
11
- size(): number;
12
- }
13
-
14
- export { LRUCache as L };