expensify-common 2.0.209 → 2.0.212

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/str.js CHANGED
@@ -15,6 +15,126 @@ function resultFn(parameter, ...args) {
15
15
  }
16
16
  return parameter;
17
17
  }
18
+ // Str methods that other Str methods call are plain module-level functions instead of being reached through
19
+ // `this`: react-native-worklets 0.13 no longer keeps an object literal together on a Worklet Runtime, so `this`
20
+ // inside a workletized method does not point to Str there.
21
+ function compare(strA, strB) {
22
+ if (strA < strB) {
23
+ return -1;
24
+ }
25
+ if (strA > strB) {
26
+ return 1;
27
+ }
28
+ return 0;
29
+ }
30
+ function caseInsensitiveCompare(strA, strB) {
31
+ const lowerCaseStrA = strA.toLocaleLowerCase();
32
+ const lowerCaseStrB = strB.toLocaleLowerCase();
33
+ return compare(lowerCaseStrA, lowerCaseStrB);
34
+ }
35
+ function contains(haystack, needle) {
36
+ return haystack.indexOf(needle) !== -1;
37
+ }
38
+ function cutAfter(str, substr) {
39
+ const index = str.indexOf(substr);
40
+ if (index !== -1) {
41
+ return str.substring(0, index);
42
+ }
43
+ return str;
44
+ }
45
+ function cutBefore(str, substr) {
46
+ const index = str.indexOf(substr);
47
+ if (index !== -1) {
48
+ return str.substring(index + substr.length);
49
+ }
50
+ return str;
51
+ }
52
+ function extractEmailDomain(email) {
53
+ return cutBefore(email, '@');
54
+ }
55
+ function getExtension(url) {
56
+ var _a, _b;
57
+ if (typeof url !== 'string') {
58
+ Log.warn('Str.getExtension: url is not a string', { url });
59
+ return undefined;
60
+ }
61
+ return (_b = (_a = url.split('.').pop()) === null || _a === void 0 ? void 0 : _a.split('?')[0]) === null || _b === void 0 ? void 0 : _b.toLowerCase();
62
+ }
63
+ function getRawByteSize(inputChar) {
64
+ const onlyChar = String(inputChar);
65
+ const c = onlyChar.charCodeAt(0);
66
+ // If we are grabbing the byte size, we need to temporarily diable no-bitwise for linting
67
+ /* eslint-disable no-bitwise */
68
+ if (c < 1 << 7) {
69
+ return 1;
70
+ }
71
+ if (c < 1 << 11) {
72
+ return 2;
73
+ }
74
+ if (c < 1 << 16) {
75
+ return 3;
76
+ }
77
+ if (c < 1 << 21) {
78
+ return 4;
79
+ }
80
+ if (c < 1 << 26) {
81
+ return 5;
82
+ }
83
+ if (c < 1 << 31) {
84
+ return 6;
85
+ }
86
+ /* eslint-enable no-bitwise */
87
+ return Number.NaN;
88
+ }
89
+ function htmlDecode(s) {
90
+ return HtmlEntities.decode(s);
91
+ }
92
+ function htmlEncode(s) {
93
+ return HtmlEntities.encode(s);
94
+ }
95
+ function isTypeOf(obj, type) {
96
+ return Object.prototype.toString.call(obj) === `[object ${type}]`;
97
+ }
98
+ function isNumber(obj) {
99
+ return isTypeOf(obj, 'Number');
100
+ }
101
+ function isString(obj) {
102
+ return isTypeOf(obj, 'String');
103
+ }
104
+ function isUndefined(obj) {
105
+ // eslint-disable-next-line no-void
106
+ return obj === void 0;
107
+ }
108
+ function isValidE164Phone(phone) {
109
+ return Constants.CONST.SMS.E164_REGEX.test(phone);
110
+ }
111
+ function isValidEmail(str) {
112
+ if (!str || typeof str !== 'string') {
113
+ return false;
114
+ }
115
+ const unicodeVersion = Punycode.toUnicode(str);
116
+ if (String(unicodeVersion).match(Constants.CONST.REG_EXP.EMOJI_RULE)) {
117
+ return false;
118
+ }
119
+ return !!String(str).match(Constants.CONST.REG_EXP.EMAIL);
120
+ }
121
+ function maskFirstNCharacters(str, num, mask) {
122
+ // if str is empty, str or mask aren't strings,
123
+ // or n is not a number, do nothing
124
+ if (!isString(str) || !isString(mask) || str.length === 0 || !isNumber(num)) {
125
+ return str;
126
+ }
127
+ return str.substring(0, num).replaceAll(/./g, mask) + str.substring(num);
128
+ }
129
+ function removeSMSDomain(text) {
130
+ return text.replaceAll(REMOVE_SMS_DOMAIN_PATTERN, '');
131
+ }
132
+ function removeTrailingComma(str) {
133
+ return str.trim().replaceAll(/(,$)/g, '');
134
+ }
135
+ function startsWith(haystack, needle) {
136
+ return isString(haystack) && isString(needle) && haystack.substring(0, needle.length) === needle;
137
+ }
18
138
  const Str = {
19
139
  /**
20
140
  * Return true if the string is ending with the provided suffix
@@ -72,18 +192,14 @@ const Str = {
72
192
  * @param s The string to decode.
73
193
  * @returns The decoded string.
74
194
  */
75
- htmlDecode(s) {
76
- return HtmlEntities.decode(s);
77
- },
195
+ htmlDecode,
78
196
  /**
79
197
  * HTML encodes the given string.
80
198
  *
81
199
  * @param s The string to encode.
82
200
  * @return string @p s HTML encoded.
83
201
  */
84
- htmlEncode(s) {
85
- return HtmlEntities.encode(s);
86
- },
202
+ htmlEncode,
87
203
  /**
88
204
  * Decodes the given HTML encoded string.
89
205
  *
@@ -127,7 +243,7 @@ const Str = {
127
243
  * @returns True when first === second, ignoring HTML encoding
128
244
  */
129
245
  htmlEncodingInsensitiveEquals(first, second) {
130
- return first === second || this.htmlDecode(first) === second || this.htmlEncode(first) === second;
246
+ return first === second || htmlDecode(first) === second || htmlEncode(first) === second;
131
247
  },
132
248
  /**
133
249
  * Creates an ID that can be used as an HTML attribute from @p str.
@@ -213,32 +329,7 @@ const Str = {
213
329
  * one.
214
330
  * @returns Byte size of the character
215
331
  */
216
- getRawByteSize(inputChar) {
217
- const onlyChar = String(inputChar);
218
- const c = onlyChar.charCodeAt(0);
219
- // If we are grabbing the byte size, we need to temporarily diable no-bitwise for linting
220
- /* eslint-disable no-bitwise */
221
- if (c < 1 << 7) {
222
- return 1;
223
- }
224
- if (c < 1 << 11) {
225
- return 2;
226
- }
227
- if (c < 1 << 16) {
228
- return 3;
229
- }
230
- if (c < 1 << 21) {
231
- return 4;
232
- }
233
- if (c < 1 << 26) {
234
- return 5;
235
- }
236
- if (c < 1 << 31) {
237
- return 6;
238
- }
239
- /* eslint-enable no-bitwise */
240
- return Number.NaN;
241
- },
332
+ getRawByteSize,
242
333
  /**
243
334
  * Gets the length of a string in bytes, including non-ASCII characters
244
335
  * @returns The number of bytes used by string
@@ -246,7 +337,7 @@ const Str = {
246
337
  getByteLength(input) {
247
338
  // Force string type
248
339
  const stringInput = String(input);
249
- const byteLength = Array.from(stringInput).reduce((acc, char) => acc + this.getRawByteSize(char), 0);
340
+ const byteLength = Array.from(stringInput).reduce((acc, char) => acc + getRawByteSize(char), 0);
250
341
  return byteLength;
251
342
  },
252
343
  /**
@@ -258,7 +349,7 @@ const Str = {
258
349
  const stringInput = String(input);
259
350
  let totalByteLength = 0;
260
351
  for (let i = 0; i < stringInput.length; i++) {
261
- const charByteSize = this.getRawByteSize(stringInput[i]);
352
+ const charByteSize = getRawByteSize(stringInput[i]);
262
353
  if (charByteSize + totalByteLength > maxSize) {
263
354
  // If the next character exceeds the limit, stop and return the truncated string.
264
355
  return `${stringInput.substr(0, i - 3)}...`;
@@ -274,9 +365,7 @@ const Str = {
274
365
  * @param needle The case-sensitive string to search for
275
366
  * @returns True if the haystack starts with the needle.
276
367
  */
277
- startsWith(haystack, needle) {
278
- return this.isString(haystack) && this.isString(needle) && haystack.substring(0, needle.length) === needle;
279
- },
368
+ startsWith,
280
369
  /**
281
370
  * Gets the textual value of the given string.
282
371
  *
@@ -284,7 +373,7 @@ const Str = {
284
373
  * @returns The text from within the HTML string.
285
374
  */
286
375
  stripHTML(str) {
287
- if (!this.isString(str)) {
376
+ if (!isString(str)) {
288
377
  return '';
289
378
  }
290
379
  return str.replaceAll(/<[^>]*>?/gm, '');
@@ -307,13 +396,7 @@ const Str = {
307
396
  * @param substr The substring to search for.
308
397
  * @returns The cut/trimmed string.
309
398
  */
310
- cutAfter(str, substr) {
311
- const index = str.indexOf(substr);
312
- if (index !== -1) {
313
- return str.substring(0, index);
314
- }
315
- return str;
316
- },
399
+ cutAfter,
317
400
  /**
318
401
  * Returns a string containing all the characters str from after the first
319
402
  * occurrence of substr to the end of the string.
@@ -323,13 +406,7 @@ const Str = {
323
406
  * @param substr The substring to search for.
324
407
  * @returns The cut/trimmed string.
325
408
  */
326
- cutBefore(str, substr) {
327
- const index = str.indexOf(substr);
328
- if (index !== -1) {
329
- return str.substring(index + substr.length);
330
- }
331
- return str;
332
- },
409
+ cutBefore,
333
410
  /**
334
411
  * Checks that the string is a domain name (e.g. example.com)
335
412
  *
@@ -356,16 +433,7 @@ const Str = {
356
433
  *
357
434
  * @returns True if the string is an email
358
435
  */
359
- isValidEmail(str) {
360
- if (!str || typeof str !== 'string') {
361
- return false;
362
- }
363
- const unicodeVersion = Punycode.toUnicode(str);
364
- if (String(unicodeVersion).match(Constants.CONST.REG_EXP.EMOJI_RULE)) {
365
- return false;
366
- }
367
- return !!String(str).match(Constants.CONST.REG_EXP.EMAIL);
368
- },
436
+ isValidEmail,
369
437
  /**
370
438
  * Checks if the string is an valid email address formed during comment markdown formation.
371
439
  *
@@ -383,9 +451,7 @@ const Str = {
383
451
  *
384
452
  * @returns string with the trailing comma removed
385
453
  */
386
- removeTrailingComma(str) {
387
- return str.trim().replaceAll(/(,$)/g, '');
388
- },
454
+ removeTrailingComma,
389
455
  /**
390
456
  * Checks that the string is a list of coma separated email addresss.
391
457
  *
@@ -394,12 +460,12 @@ const Str = {
394
460
  * @returns True if all emails are valid or if input is empty
395
461
  */
396
462
  areValidEmails(str) {
397
- const string = this.removeTrailingComma(str);
463
+ const string = removeTrailingComma(str);
398
464
  if (string === '') {
399
465
  return true;
400
466
  }
401
467
  const emails = string.split(',');
402
- const result = emails.every((email) => this.isValidEmail(email.trim()));
468
+ const result = emails.every((email) => isValidEmail(email.trim()));
403
469
  return result;
404
470
  },
405
471
  /**
@@ -416,9 +482,7 @@ const Str = {
416
482
  *
417
483
  * @returns The domain name in the email address.
418
484
  */
419
- extractEmailDomain(email) {
420
- return this.cutBefore(email, '@');
421
- },
485
+ extractEmailDomain,
422
486
  /**
423
487
  * Tries to extract the company name from the given email address
424
488
  * (e.g. "yelp" for "joe@yelp.co.uk").
@@ -428,7 +492,7 @@ const Str = {
428
492
  * @returns The company name in the email address or null.
429
493
  */
430
494
  extractCompanyNameFromEmailDomain(email) {
431
- const domain = this.extractEmailDomain(email);
495
+ const domain = extractEmailDomain(email);
432
496
  if (!domain) {
433
497
  return null;
434
498
  }
@@ -447,7 +511,7 @@ const Str = {
447
511
  * @returns The local part in the email address.
448
512
  */
449
513
  extractEmailLocalPart(email) {
450
- return this.cutAfter(email, '@');
514
+ return cutAfter(email, '@');
451
515
  },
452
516
  /**
453
517
  * Sanitize phone number to return only numbers. Return null if non valid phone number.
@@ -505,13 +569,13 @@ const Str = {
505
569
  * @returns true if the length is in the range, false otherwise
506
570
  */
507
571
  isOfLength(str, minimumLength, maximumLength) {
508
- if (!this.isString(str)) {
572
+ if (!isString(str)) {
509
573
  return false;
510
574
  }
511
575
  if (str.length < minimumLength) {
512
576
  return false;
513
577
  }
514
- if (!this.isUndefined(maximumLength) && str.length > maximumLength) {
578
+ if (!isUndefined(maximumLength) && str.length > maximumLength) {
515
579
  return false;
516
580
  }
517
581
  return true;
@@ -565,9 +629,7 @@ const Str = {
565
629
  *
566
630
  * @returns Returns true if the haystack contains the needle
567
631
  */
568
- contains(haystack, needle) {
569
- return haystack.indexOf(needle) !== -1;
570
- },
632
+ contains,
571
633
  /**
572
634
  * Returns true if the haystack contains the needle, ignoring case
573
635
  *
@@ -577,7 +639,7 @@ const Str = {
577
639
  * @returns Returns true if the haystack contains the needle, ignoring case
578
640
  */
579
641
  caseInsensitiveContains(haystack, needle) {
580
- return this.contains(haystack.toLowerCase(), needle.toLowerCase());
642
+ return contains(haystack.toLowerCase(), needle.toLowerCase());
581
643
  },
582
644
  /**
583
645
  * Case insensitive compare function
@@ -589,11 +651,7 @@ const Str = {
589
651
  * 1 if first string > second string
590
652
  * 0 if first string = second string
591
653
  */
592
- caseInsensitiveCompare(strA, strB) {
593
- const lowerCaseStrA = strA.toLocaleLowerCase();
594
- const lowerCaseStrB = strB.toLocaleLowerCase();
595
- return this.compare(lowerCaseStrA, lowerCaseStrB);
596
- },
654
+ caseInsensitiveCompare,
597
655
  /**
598
656
  * Case insensitive equals
599
657
  *
@@ -602,7 +660,7 @@ const Str = {
602
660
  * @returns true when first == second except for case
603
661
  */
604
662
  caseInsensitiveEquals(strA, strB) {
605
- return this.caseInsensitiveCompare(strA, strB) === 0;
663
+ return caseInsensitiveCompare(strA, strB) === 0;
606
664
  },
607
665
  /**
608
666
  * Compare function
@@ -614,15 +672,7 @@ const Str = {
614
672
  * 1 if first string > second string
615
673
  * 0 if first string = second string
616
674
  */
617
- compare(strA, strB) {
618
- if (strA < strB) {
619
- return -1;
620
- }
621
- if (strA > strB) {
622
- return 1;
623
- }
624
- return 0;
625
- },
675
+ compare,
626
676
  /**
627
677
  * Check if a file extension is supported by SmartReports
628
678
  */
@@ -646,48 +696,39 @@ const Str = {
646
696
  // Hide these numbers completely
647
697
  // We should not be getting account numbers this small or large
648
698
  if (len < 6 || len > 20) {
649
- return this.maskFirstNCharacters(accountNumber, len, 'X');
699
+ return maskFirstNCharacters(accountNumber, len, 'X');
650
700
  }
651
701
  // Can show last 4
652
702
  if (len < 14) {
653
- return this.maskFirstNCharacters(accountNumber, len - 4, 'X');
703
+ return maskFirstNCharacters(accountNumber, len - 4, 'X');
654
704
  }
655
705
  // Can show first 6 and last 4
656
706
  const first = accountNumber.substr(0, 6);
657
707
  const last = accountNumber.substr(7);
658
- const masked = this.maskFirstNCharacters(last, len - 11, 'X');
708
+ const masked = maskFirstNCharacters(last, len - 11, 'X');
659
709
  return `${first}${masked}`;
660
710
  },
661
711
  /**
662
712
  * Checks if something is a string
663
713
  * Stolen from underscore
664
714
  */
665
- isString(obj) {
666
- return this.isTypeOf(obj, 'String');
667
- },
715
+ isString,
668
716
  /**
669
717
  * Checks if something is a number
670
718
  * Stolen from underscore
671
719
  * @param obj
672
720
  */
673
- isNumber(obj) {
674
- return this.isTypeOf(obj, 'Number');
675
- },
721
+ isNumber,
676
722
  /**
677
723
  * Checks if something is a certain type
678
724
  * Stolen from underscore
679
725
  */
680
- isTypeOf(obj, type) {
681
- return Object.prototype.toString.call(obj) === `[object ${type}]`;
682
- },
726
+ isTypeOf,
683
727
  /**
684
728
  * Checks to see if something is undefined
685
729
  * Stolen from underscore
686
730
  */
687
- isUndefined(obj) {
688
- // eslint-disable-next-line no-void
689
- return obj === void 0;
690
- },
731
+ isUndefined,
691
732
  /**
692
733
  * Replace first N characters of the string with maskChar
693
734
  * eg: maskFirstNCharacters( '1234567890', 6, 'X' ) yields XXXXXX7890
@@ -696,14 +737,7 @@ const Str = {
696
737
  * @param mask String we want replace the first N chars with
697
738
  * @returns Masked string
698
739
  */
699
- maskFirstNCharacters(str, num, mask) {
700
- // if str is empty, str or mask aren't strings,
701
- // or n is not a number, do nothing
702
- if (!this.isString(str) || !this.isString(mask) || str.length === 0 || !this.isNumber(num)) {
703
- return str;
704
- }
705
- return str.substring(0, num).replaceAll(/./g, mask) + str.substring(num);
706
- },
740
+ maskFirstNCharacters,
707
741
  /**
708
742
  * Trim a string
709
743
  */
@@ -715,7 +749,7 @@ const Str = {
715
749
  * @param percentageString The percentage as a string
716
750
  */
717
751
  percentageStringToNumber(percentageString) {
718
- return Number(this.cutAfter(percentageString, '%'));
752
+ return Number(cutAfter(percentageString, '%'));
719
753
  },
720
754
  /**
721
755
  * Remove all the spaces from a string
@@ -761,7 +795,7 @@ const Str = {
761
795
  * Converts a value to boolean, case-insensitive.
762
796
  */
763
797
  toBool(value) {
764
- if (this.isString(value)) {
798
+ if (isString(value)) {
765
799
  return value.toLowerCase() === 'true';
766
800
  }
767
801
  return !!value;
@@ -811,9 +845,7 @@ const Str = {
811
845
  /**
812
846
  * Check for whether a phone number is valid according to E.164 standard.
813
847
  */
814
- isValidE164Phone(phone) {
815
- return Constants.CONST.SMS.E164_REGEX.test(phone);
816
- },
848
+ isValidE164Phone,
817
849
  /**
818
850
  * Check for whether a phone number is valid in different formats/standards. For example:
819
851
  * significant: 4404589784
@@ -842,14 +874,12 @@ const Str = {
842
874
  /**
843
875
  * Returns text without our SMS domain
844
876
  */
845
- removeSMSDomain(text) {
846
- return text.replaceAll(REMOVE_SMS_DOMAIN_PATTERN, '');
847
- },
877
+ removeSMSDomain,
848
878
  /**
849
879
  * Returns true if the text is a valid E.164 phone number with our SMS domain removed
850
880
  */
851
881
  isSMSLogin(text) {
852
- return this.isValidE164Phone(this.removeSMSDomain(text));
882
+ return isValidE164Phone(removeSMSDomain(text));
853
883
  },
854
884
  /**
855
885
  * This method will return all matches of a single regex like preg_match_all() in PHP. This is not a common part of
@@ -907,7 +937,7 @@ const Str = {
907
937
  return url;
908
938
  }
909
939
  const website = match[3] ? match[2] : `${defaultScheme}://${match[2]}`;
910
- return website.toLowerCase() + this.cutBefore(match[1], match[2]);
940
+ return website.toLowerCase() + cutBefore(match[1], match[2]);
911
941
  },
912
942
  /**
913
943
  * Checks if parameter is a string or function
@@ -919,14 +949,7 @@ const Str = {
919
949
  * Get file extension for a given url with or
920
950
  * without query parameters
921
951
  */
922
- getExtension(url) {
923
- var _a, _b;
924
- if (typeof url !== 'string') {
925
- Log.warn('Str.getExtension: url is not a string', { url });
926
- return undefined;
927
- }
928
- return (_b = (_a = url.split('.').pop()) === null || _a === void 0 ? void 0 : _a.split('?')[0]) === null || _b === void 0 ? void 0 : _b.toLowerCase();
929
- },
952
+ getExtension,
930
953
  /**
931
954
  * Takes in a URL and checks if the file extension is PDF
932
955
  *
@@ -934,7 +957,7 @@ const Str = {
934
957
  * @returns Whether file path is PDF or not
935
958
  */
936
959
  isPDF(url) {
937
- return this.getExtension(url) === 'pdf';
960
+ return getExtension(url) === 'pdf';
938
961
  },
939
962
  /**
940
963
  * Takes in a URL and checks if the file extension is an image
@@ -945,7 +968,7 @@ const Str = {
945
968
  * https://reactnative.dev/docs/image#source
946
969
  */
947
970
  isImage(url) {
948
- const extension = this.getExtension(url);
971
+ const extension = getExtension(url);
949
972
  if (!extension) {
950
973
  return false;
951
974
  }
@@ -961,7 +984,7 @@ const Str = {
961
984
  * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs
962
985
  */
963
986
  isVideo(url) {
964
- const extension = this.getExtension(url);
987
+ const extension = getExtension(url);
965
988
  if (!extension) {
966
989
  return false;
967
990
  }
@@ -974,7 +997,7 @@ const Str = {
974
997
  * @returns True if is a domain account email, otherwise false.
975
998
  */
976
999
  isDomainEmail(email) {
977
- return this.startsWith(email, '+@');
1000
+ return startsWith(email, '+@');
978
1001
  },
979
1002
  /**
980
1003
  * Find the minimum indentation of any line in the string,