@zelgadis87/utils-core 5.3.5 → 5.3.7

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/.rollup/index.cjs CHANGED
@@ -1080,6 +1080,20 @@ function randomNumberInInterval(min, max) {
1080
1080
  const randomId = (length) => {
1081
1081
  return Math.random().toString(36).substring(2, length + 2);
1082
1082
  };
1083
+ function randomPick(arr) {
1084
+ return first$1(randomPicks(arr, 1));
1085
+ }
1086
+ function randomPicks(arr, count) {
1087
+ const available = [...arr];
1088
+ const result = [];
1089
+ while (available.length > 0 && count > 0) {
1090
+ const randomIndex = randomNumberInInterval(0, available.length - 1);
1091
+ result.push(available[randomIndex]);
1092
+ available.splice(randomIndex, 1);
1093
+ count--;
1094
+ }
1095
+ return result;
1096
+ }
1083
1097
 
1084
1098
  function dictToEntries(obj) {
1085
1099
  return Object.entries(obj);
@@ -2345,8 +2359,48 @@ function normalizeMonthName(name) {
2345
2359
  // Trim whitespace
2346
2360
  .trim();
2347
2361
  }
2348
- const PATTERN_REGEX = /(M|y|d|D|h|H|m|s|S|G|Z|P|a)+/g;
2349
2362
  const ESCAPE_REGEX = /\\"|"((?:\\"|[^"])*)"/g;
2363
+ /**
2364
+ * Tokenize a date/time pattern by detecting character changes.
2365
+ *
2366
+ * This function uses a character-change detection algorithm to split a pattern into tokens:
2367
+ * 1. Iterates through the pattern character by character
2368
+ * 2. Detects when the character changes (e.g., 'y' → 'M' → 'd')
2369
+ * 3. Creates tokens based on consecutive identical characters
2370
+ * 4. Marks tokens as pattern tokens only if they consist of repeated pattern characters
2371
+ *
2372
+ * This approach works correctly for patterns with or without separators:
2373
+ * - "yyyy-MM-dd" → [{token:"yyyy", type:"y"}, {token:"-", type:null}, {token:"MM", type:"M"}, {token:"-", type:null}, {token:"dd", type:"d"}]
2374
+ * - "yyyyMMdd" → [{token:"yyyy", type:"y"}, {token:"MM", type:"M"}, {token:"dd", type:"d"}]
2375
+ *
2376
+ * @param pattern - The date/time pattern to tokenize
2377
+ * @returns Array of tokens with their types (null for separators/literals, pattern char for pattern tokens)
2378
+ */
2379
+ function tokenizePattern(pattern) {
2380
+ const result = [];
2381
+ let currentToken = '';
2382
+ let currentChar = '';
2383
+ for (const char of pattern) {
2384
+ if (char === currentChar) {
2385
+ // Same character, extend current token
2386
+ currentToken += char;
2387
+ }
2388
+ else {
2389
+ // Character changed, end current token and start new one
2390
+ if (currentToken) {
2391
+ // Mark the token with its character (caller will decide if it's a valid pattern)
2392
+ result.push({ token: currentToken, type: currentChar });
2393
+ }
2394
+ currentToken = char;
2395
+ currentChar = char;
2396
+ }
2397
+ }
2398
+ // Don't forget the last token
2399
+ if (currentToken) {
2400
+ result.push({ token: currentToken, type: currentChar });
2401
+ }
2402
+ return result;
2403
+ }
2350
2404
  // Formatter configuration mapping: token pattern -> configuration with optional extraction rules
2351
2405
  const formatterConfigs = {
2352
2406
  // Year
@@ -2428,11 +2482,16 @@ function formatTimeInstant(instant, pattern, config = {}) {
2428
2482
  if (index % 2 !== 0) {
2429
2483
  return sub;
2430
2484
  }
2431
- return sub.replace(PATTERN_REGEX, (match) => {
2432
- const type = match.charAt(0);
2433
- const length = match.length;
2434
- return formatType(type, length, date, locale, timeZone) || match;
2435
- });
2485
+ // Tokenize the pattern by detecting character changes
2486
+ const tokens = tokenizePattern(sub);
2487
+ // Format each token and join the results
2488
+ return tokens.map(({ token, type }) => {
2489
+ // If type is null or formatting fails, keep token as-is
2490
+ if (!type)
2491
+ return token;
2492
+ const formatted = formatType(type, token.length, date, locale, timeZone);
2493
+ return formatted !== undefined ? formatted : token;
2494
+ }).join('');
2436
2495
  })
2437
2496
  .join('');
2438
2497
  }
@@ -2460,53 +2519,62 @@ function parseTimeInstantComponents(dateString, pattern, config = {}) {
2460
2519
  if (index % 2 !== 0) {
2461
2520
  return sub.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape special regex chars
2462
2521
  }
2463
- return sub.replace(PATTERN_REGEX, (match) => {
2464
- const type = match.charAt(0);
2465
- tokens.push({ type, length: match.length, position: position++ });
2522
+ // Tokenize the pattern by detecting character changes
2523
+ const patternTokens = tokenizePattern(sub);
2524
+ // Build regex pattern from tokens
2525
+ return patternTokens.map(({ token, type }) => {
2526
+ // Check if this is a valid pattern by looking it up in formatterConfigs
2527
+ const isValidPattern = type && formatterConfigs[token];
2528
+ if (!isValidPattern) {
2529
+ // This is a literal/separator, escape it for regex
2530
+ return token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2531
+ }
2532
+ // This is a pattern token, track it and create regex
2533
+ tokens.push({ type, length: token.length, position: position++ });
2466
2534
  // Create appropriate regex for each token type
2467
2535
  switch (type) {
2468
2536
  case 'y':
2469
- return match.length === 2 ? '(\\d{2})' : '(\\d{4})';
2537
+ return token.length === 2 ? '(\\d{2})' : '(\\d{4})';
2470
2538
  case 'M':
2471
- if (match.length === 1)
2539
+ if (token.length === 1)
2472
2540
  return '(\\d{1,2})';
2473
- if (match.length === 2)
2541
+ if (token.length === 2)
2474
2542
  return '(\\d{2})';
2475
- if (match.length === 3)
2543
+ if (token.length === 3)
2476
2544
  return '([A-Za-z.]{1,7})';
2477
2545
  return '([A-Za-z]+)';
2478
2546
  case 'd':
2479
- return match.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
2547
+ return token.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
2480
2548
  case 'H':
2481
2549
  case 'h':
2482
- return match.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
2550
+ return token.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
2483
2551
  case 'm':
2484
2552
  case 's':
2485
- return match.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
2553
+ return token.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
2486
2554
  case 'S':
2487
- return `(\\d{${match.length}})`;
2555
+ return `(\\d{${token.length}})`;
2488
2556
  case 'a':
2489
2557
  return '([aApP][mM])';
2490
2558
  case 'D':
2491
- if (match.length === 1)
2559
+ if (token.length === 1)
2492
2560
  return '([A-Za-z])';
2493
- if (match.length === 2)
2561
+ if (token.length === 2)
2494
2562
  return '([A-Za-z]{3})';
2495
2563
  return '([A-Za-z]+)';
2496
2564
  case 'G':
2497
- if (match.length === 1)
2565
+ if (token.length === 1)
2498
2566
  return '([A-Za-z])';
2499
- if (match.length === 2)
2567
+ if (token.length === 2)
2500
2568
  return '([A-Za-z]{2})';
2501
2569
  return '([A-Za-z\\s]+)';
2502
2570
  case 'Z':
2503
- return match.length === 1 ? '([A-Za-z0-9+\\-:]+)' : '([A-Za-z\\s]+)';
2571
+ return token.length === 1 ? '([A-Za-z0-9+\\-:]+)' : '([A-Za-z\\s]+)';
2504
2572
  case 'P':
2505
2573
  return '([A-Za-z\\s]+)';
2506
2574
  default:
2507
- return match;
2575
+ return token;
2508
2576
  }
2509
- });
2577
+ }).join('');
2510
2578
  }).join('');
2511
2579
  const regex = new RegExp('^' + regexPattern + '$');
2512
2580
  const matches = dateString.match(regex);
@@ -3658,6 +3726,8 @@ exports.pluralize = pluralize;
3658
3726
  exports.promiseSequence = promiseSequence;
3659
3727
  exports.randomId = randomId;
3660
3728
  exports.randomNumberInInterval = randomNumberInInterval;
3729
+ exports.randomPick = randomPick;
3730
+ exports.randomPicks = randomPicks;
3661
3731
  exports.range = range;
3662
3732
  exports.repeat = repeat;
3663
3733
  exports.reverse = reverse$1;