{"version":3,"file":"alphanumeric-segmenter.cjs","sources":["../../../src/utils/alphanumeric-segmenter.ts"],"sourcesContent":["/**\n * Alphanumeric Segmentation Utilities\n * \n * Segments strings into alphabetic and numeric parts for better fuzzy matching\n * of identifiers like \"servicehandler14568\" or \"api_manager_3254\"\n */\n\nexport type SegmentType = 'alpha' | 'numeric' | 'other';\n\nexport interface Segment {\n  /** Type of segment */\n  type: SegmentType;\n  /** The actual text content */\n  value: string;\n  /** Start position in original string */\n  start: number;\n  /** End position in original string */\n  end: number;\n}\n\n/**\n * Check if a string contains both letters and numbers\n * \n * @param str - String to check\n * @returns True if string is alphanumeric (contains both letters and numbers)\n */\nexport function isAlphanumeric(str: string): boolean {\n  const hasLetters = /[a-zA-Z]/.test(str);\n  const hasNumbers = /[0-9]/.test(str);\n  return hasLetters && hasNumbers;\n}\n\n/**\n * Segment a string into alphabetic, numeric, and other parts\n * \n * @param str - String to segment\n * @returns Array of segments\n * \n * @example\n * ```typescript\n * segmentString(\"servicehandler14568\")\n * // [\n * //   { type: 'alpha', value: 'servicehandler', start: 0, end: 14 },\n * //   { type: 'numeric', value: '14568', start: 14, end: 19 }\n * // ]\n * \n * segmentString(\"api_manager_3254\")\n * // [\n * //   { type: 'alpha', value: 'api', start: 0, end: 3 },\n * //   { type: 'other', value: '_', start: 3, end: 4 },\n * //   { type: 'alpha', value: 'manager', start: 4, end: 11 },\n * //   { type: 'other', value: '_', start: 11, end: 12 },\n * //   { type: 'numeric', value: '3254', start: 12, end: 16 }\n * // ]\n * ```\n */\nexport function segmentString(str: string): Segment[] {\n  const segments: Segment[] = [];\n  let currentType: SegmentType | null = null;\n  let currentValue = '';\n  let currentStart = 0;\n\n  for (let i = 0; i < str.length; i++) {\n    const char = str[i];\n    let charType: SegmentType;\n\n    if (/[a-zA-Z]/.test(char)) {\n      charType = 'alpha';\n    } else if (/[0-9]/.test(char)) {\n      charType = 'numeric';\n    } else {\n      charType = 'other';\n    }\n\n    if (currentType === null) {\n      // Start first segment\n      currentType = charType;\n      currentValue = char;\n      currentStart = i;\n    } else if (currentType === charType) {\n      // Continue current segment\n      currentValue += char;\n    } else {\n      // End current segment and start new one\n      segments.push({\n        type: currentType,\n        value: currentValue,\n        start: currentStart,\n        end: i,\n      });\n      currentType = charType;\n      currentValue = char;\n      currentStart = i;\n    }\n  }\n\n  // Add final segment\n  if (currentType !== null && currentValue.length > 0) {\n    segments.push({\n      type: currentType,\n      value: currentValue,\n      start: currentStart,\n      end: str.length,\n    });\n  }\n\n  return segments;\n}\n\n/**\n * Get only alphabetic segments from a string\n * \n * @param str - String to segment\n * @returns Array of alphabetic segments\n */\nexport function getAlphaSegments(str: string): Segment[] {\n  return segmentString(str).filter(s => s.type === 'alpha');\n}\n\n/**\n * Get only numeric segments from a string\n * \n * @param str - String to segment\n * @returns Array of numeric segments\n */\nexport function getNumericSegments(str: string): Segment[] {\n  return segmentString(str).filter(s => s.type === 'numeric');\n}\n\n/**\n * Extract just the alphabetic parts of a string\n * \n * @param str - String to process\n * @returns Concatenated alphabetic parts\n * \n * @example\n * ```typescript\n * extractAlphaPart(\"servicehandler14568\") // \"servicehandler\"\n * extractAlphaPart(\"api_manager_3254\") // \"apimanager\"\n * ```\n */\nexport function extractAlphaPart(str: string): string {\n  return getAlphaSegments(str).map(s => s.value).join('');\n}\n\n/**\n * Extract just the numeric parts of a string\n * \n * @param str - String to process\n * @returns Concatenated numeric parts\n * \n * @example\n * ```typescript\n * extractNumericPart(\"servicehandler14568\") // \"14568\"\n * extractNumericPart(\"api_manager_3254\") // \"3254\"\n * ```\n */\nexport function extractNumericPart(str: string): string {\n  return getNumericSegments(str).map(s => s.value).join('');\n}\n\n/**\n * Compare two strings segment by segment\n * Returns similarity scores for alpha and numeric parts separately\n * \n * @param str1 - First string\n * @param str2 - Second string\n * @returns Object with alpha and numeric similarity scores (0-1)\n */\nexport function compareSegments(\n  str1: string,\n  str2: string\n): {\n  alphaSimilarity: number;\n  numericSimilarity: number;\n  hasAlpha: boolean;\n  hasNumeric: boolean;\n} {\n  const alpha1 = extractAlphaPart(str1);\n  const alpha2 = extractAlphaPart(str2);\n  const numeric1 = extractNumericPart(str1);\n  const numeric2 = extractNumericPart(str2);\n\n  const hasAlpha = alpha1.length > 0 || alpha2.length > 0;\n  const hasNumeric = numeric1.length > 0 || numeric2.length > 0;\n\n  // Calculate alpha similarity (exact match for now, can be enhanced with edit distance)\n  let alphaSimilarity = 0;\n  if (hasAlpha) {\n    if (alpha1 === alpha2) {\n      alphaSimilarity = 1.0;\n    } else if (alpha1.length === 0 || alpha2.length === 0) {\n      alphaSimilarity = 0;\n    } else {\n      // Basic similarity based on common prefix\n      let commonLength = 0;\n      const minLen = Math.min(alpha1.length, alpha2.length);\n      for (let i = 0; i < minLen; i++) {\n        if (alpha1[i].toLowerCase() === alpha2[i].toLowerCase()) {\n          commonLength++;\n        } else {\n          break;\n        }\n      }\n      const maxLen = Math.max(alpha1.length, alpha2.length);\n      alphaSimilarity = commonLength / maxLen;\n    }\n  }\n\n  // Calculate numeric similarity\n  let numericSimilarity = 0;\n  if (hasNumeric) {\n    if (numeric1 === numeric2) {\n      numericSimilarity = 1.0;\n    } else if (numeric1.length === 0 || numeric2.length === 0) {\n      numericSimilarity = 0;\n    } else {\n      // For numbers, we're more lenient - partial match is ok\n      const longer = numeric1.length > numeric2.length ? numeric1 : numeric2;\n      const shorter = numeric1.length > numeric2.length ? numeric2 : numeric1;\n      if (longer.includes(shorter)) {\n        numericSimilarity = shorter.length / longer.length;\n      } else {\n        // Count matching digits\n        let matchingDigits = 0;\n        const minLen = Math.min(numeric1.length, numeric2.length);\n        for (let i = 0; i < minLen; i++) {\n          if (numeric1[i] === numeric2[i]) {\n            matchingDigits++;\n          }\n        }\n        numericSimilarity = matchingDigits / Math.max(numeric1.length, numeric2.length);\n      }\n    }\n  }\n\n  return {\n    alphaSimilarity,\n    numericSimilarity,\n    hasAlpha,\n    hasNumeric,\n  };\n}\n"],"names":[],"mappings":"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"}