{"version":3,"sources":["../../src/nlp/constants.ts","../../src/nlp/stemmer.ts","../../src/nlp/phonetics.ts","../../src/nlp/tokenizer.ts","../../src/nlp/normalize.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * ============================================================================\n * INDONESIAN NLP ENGINE - CONSTANTS\n * ============================================================================\n *\n * This file contains constants for Indonesian text processing:\n * - AFFIX_PATTERNS: Indonesian prefix and suffix patterns for stemming\n * - ABBREVIATIONS: Common Indonesian abbreviations to preserve in tokenization\n * - PHONETIC_MAP: Character mappings for phonetic encoding\n *\n * ============================================================================\n */\n\n/**\n * Common Indonesian abbreviations that should NOT be split on periods\n * during sentence tokenization.\n */\nexport const SENTENCE_ABBREVIATIONS = [\n  'Yth.',    // Yth. - Kepada (Attention)\n  'Bpk.',    // Bpk. - Bapak (Mr.)\n  'Ibu.',    // Ibu - Mrs./Ms.\n  'Sdr.',    // Sdr. - Saudara (Brother/Sister)\n  'S.Kom.',  // S.Kom. - Sarjana Komputer\n  'M.Kom.',  // M.Kom. - Magister Komputer\n  'Dr.',     // Dr. - Doktor\n  'Sp.',     // Sp. - Spesialis\n  'Mk.',     // Mk. - Magister\n  'Jl.',     // Jl. - Jalan (Street)\n  'D.a.',    // D.a. - Dengan alat\n  'D.l.',    // D.l. - Dalam lingkup\n] as const;\n\n/**\n * Indonesian prefix patterns for algorithmic stemming.\n * Order matters - longer patterns should be checked first.\n */\nexport const PREFIX_PATTERNS = [\n  // 4-char prefixes\n  { pattern: /^meng/i, replacement: '' },        // meng- → Ø\n  { pattern: /^peny/i, replacement: 's' },       // peny- → s-\n  { pattern: /^pen/i, replacement: '' },         // pen- → Ø\n  { pattern: /^pem/i, replacement: '' },         // pem- → Ø\n  { pattern: /^peng/i, replacement: '' },        // peng- → Ø\n  { pattern: /^meny/i, replacement: 's' },       // meny- → s-\n  { pattern: /^men/i, replacement: '' },         // men- → Ø\n  { pattern: /^mem/i, replacement: '' },          // mem- → Ø\n\n  // 3-char prefixes\n  { pattern: /^ber/i, replacement: '' },          // ber- → Ø\n  { pattern: /^bel/i, replacement: '' },          // bel- → Ø\n  { pattern: /^ter/i, replacement: '' },          // ter- → Ø\n\n  // 2-char prefixes\n  { pattern: /^di/i, replacement: '' },           // di- → Ø\n  { pattern: /^ke/i, replacement: '' },           // ke- → Ø\n  { pattern: /^me/i, replacement: '' },          // me- → Ø\n  { pattern: /^pe/i, replacement: '' },          // pe- → Ø\n  { pattern: /^se/i, replacement: '' },          // se- → Ø\n] as const;\n\n/**\n * Indonesian suffix patterns for algorithmic stemming.\n * Order matters - longer patterns should be checked first.\n */\nexport const SUFFIX_PATTERNS = [\n  // 4-char suffixes\n  { pattern: /nya$/i, replacement: '' },         // -nya → Ø\n  { pattern: /lah$/i, replacement: '' },         // -lah → Ø\n  { pattern: /kan$/i, replacement: '' },        // -kan → Ø\n  { pattern: /pun$/i, replacement: '' },        // -pun → Ø\n\n  // 3-char suffixes\n  { pattern: /an$/i, replacement: '' },         // -an → Ø\n  { pattern: /ah$/i, replacement: '' },         // -ah → Ø\n  { pattern: /oh$/i, replacement: '' },         // -oh → Ø\n  { pattern: /eh$/i, replacement: '' },         // -eh → Ø\n\n  // 2-char suffixes\n  { pattern: /ku$/i, replacement: '' },          // -ku → Ø\n  { pattern: /mu$/i, replacement: '' },          // -mu → Ø\n  { pattern: /na$/i, replacement: '' },          // -na → Ø\n] as const;\n\n/**\n * Phonetic encoding character mappings.\n * Maps similar-sounding characters to the same code digit.\n */\nexport const PHONETIC_CODES: Record<string, string> = {\n  // Vowels map to 0\n  a: '0', e: '0', i: '0', o: '0', u: '0',\n  // Certain consonants map to specific codes\n  b: '1', f: '1', p: '1', v: '1',\n  c: '2', g: '2', j: '2', k: '2', q: '2', x: '2',\n  d: '3', t: '3',\n  l: '4',\n  m: '5', n: '5',\n  r: '6',\n  s: '6', z: '6',\n  // h is often silent, drops encoding\n  // w, y are sometimes vowels, handled specially\n};\n\n/**\n * Dutch spelling normalizations (oe → u, etc.)\n * Common in older Indonesian spellings and names.\n */\nexport const DUTCH_NORMALIZATIONS: Record<string, string> = {\n  oe: 'u',\n  dj: 'j',\n  dz: 'z',\n  j: 'j',   // already j\n  ch: 'h',  // dutch ch → h\n};\n\n/**\n * Arabic loan phoneme normalizations.\n * Common in Arabic-derived Indonesian words.\n */\nexport const ARABIC_NORMALIZATIONS: Record<string, string> = {\n  sy: 's',  // sy → s (as in Syafar)\n  kh: 'k',  // kh → k (as in Khair)\n  sh: 's',  // sh → s (as in Sharon)\n};\n\n/**\n * Regex pattern for Unicode whitespace normalization.\n */\nexport const WHITESPACE_PATTERN = /\\s+/g;\n\n/**\n * Regex pattern for non-alphanumeric characters (except spaces).\n */\nexport const NON_ALPHANUMERIC_PATTERN = /[^a-zA-Z0-9\\s]/g;","import {\n  PREFIX_PATTERNS,\n  SUFFIX_PATTERNS,\n} from './constants';\n\n/**\n * Removes Indonesian affixes (prefixes and suffixes) from a word.\n *\n * Strips common Indonesian morphological affixes such as:\n * - Prefixes: me-, di-, ber-, pe-, pe-, ku-, kau-, men-, men-, per-, dll\n * - Suffixes: -kan, -an, -nya, -lah, -tah, -pun\n *\n * Used for text normalization, search indexing, and linguistic analysis.\n *\n * @param text - Word or text to strip affixes from\n * @returns Text with affixes removed\n *\n * @example\n * ```typescript\n * stemText('mempertanggungjawabkan'); // 'tanggung jawab'\n * stemText('berbicara');               // 'bicara'\n * stemText('mahasiswanya');             // 'mahasisw'\n * ```\n *\n * @example\n * For search optimization:\n * ```typescript\n * const query = 'mempertanggungjawabkan';\n * const normalized = stemText(query); // 'tanggung jawab'\n * // Use normalized for Indonesian full-text search\n * ```\n */\nexport function stemText(text: string): string {\n  if (!text || typeof text !== 'string') return text;\n  if (text.length < 4) return text;\n\n  let result = text;\n\n  for (const { pattern, replacement } of PREFIX_PATTERNS) {\n    const match = result.match(pattern);\n    if (match) {\n      result = result.replace(pattern, replacement);\n      break;\n    }\n  }\n\n  for (const { pattern, replacement } of SUFFIX_PATTERNS) {\n    const match = result.match(pattern);\n    if (match) {\n      result = result.replace(pattern, replacement);\n      break;\n    }\n  }\n\n  return result || text;\n}","import {\n  PHONETIC_CODES,\n  DUTCH_NORMALIZATIONS,\n  ARABIC_NORMALIZATIONS,\n} from './constants';\n\n/**\n * Encodes a name into a phonetic code for fuzzy matching.\n *\n * Uses Soundex-like algorithm adapted for Indonesian names.\n * Handles Dutch and Arabic name normalizations for better\n * matching accuracy in Indonesian context.\n *\n * @param text - Name to encode (e.g., person name)\n * @returns Phonetic code (1 letter + 3 digits, e.g., 'S635')\n *\n * @example\n * ```typescript\n * encodePhonetic('Syahruddin'); // 'S635'\n * encodePhonetic('Ahmad');     // 'A530'\n * ```\n *\n * @example\n * For fuzzy name matching:\n * ```typescript\n * const code1 = encodePhonetic('Budi');\n * const code2 = encodePhonetic('Budi'); // same code = match\n * // Useful for deduping names with spelling variations\n * ```\n */\nexport function encodePhonetic(text: string): string {\n  if (!text || typeof text !== 'string') return '';\n\n  let normalized = text.toLowerCase();\n\n  for (const [from, to] of Object.entries(DUTCH_NORMALIZATIONS)) {\n    normalized = normalized.split(from).join(to);\n  }\n\n  for (const [from, to] of Object.entries(ARABIC_NORMALIZATIONS)) {\n    normalized = normalized.split(from).join(to);\n  }\n\n  const firstLetter = normalized.charAt(0);\n\n  const consonants: string[] = [];\n  for (const char of normalized.slice(1)) {\n    const code = PHONETIC_CODES[char];\n    if (code && code !== '0') {\n      if (consonants.length < 3 && (consonants.length === 0 || consonants[consonants.length - 1] !== code)) {\n        consonants.push(code);\n      }\n    }\n  }\n\n  while (consonants.length < 3) {\n    consonants.push('0');\n  }\n\n  return firstLetter.toUpperCase() + consonants.slice(0, 3).join('');\n}\n\n/**\n * Checks if two names match phonetically.\n *\n * Compares the phonetic codes of two names to determine\n * if they likely refer to the same person despite spelling\n * variations (typos, nicknames, alternative spellings).\n *\n * @param text1 - First name to compare\n * @param text2 - Second name to compare\n * @returns `true` if names match phonetically, `false` otherwise\n *\n * @example\n * ```typescript\n * isPhoneticMatch('Syahruddin', 'Syahrudin'); // true\n * isPhoneticMatch('Budi', 'Budi');           // true\n * isPhoneticMatch('Andi', 'Budi');            // false\n * ```\n *\n * @example\n * For search-as-you-type:\n * ```typescript\n * const typed = 'Syahrudin';\n * const candidates = ['Syahruddin', 'Budi', 'Andi'];\n * const match = candidates.find(c => isPhoneticMatch(typed, c));\n * // Returns 'Syahruddin'\n * ```\n */\nexport function isPhoneticMatch(text1: string, text2: string): boolean {\n  if (!text1 || !text2) return false;\n\n  const code1 = encodePhonetic(text1);\n  const code2 = encodePhonetic(text2);\n\n  return code1 === code2 && code1.length > 0;\n}","import { SENTENCE_ABBREVIATIONS } from './constants';\n\n/**\n * Splits Indonesian text into sentences.\n *\n * Handles Indonesian abbreviations (Yth., Bpk., Sdr., dll)\n * so periods inside abbreviations don't create false sentence breaks.\n *\n * @param text - Text to tokenize into sentences\n * @returns Array of sentences (empty array if input is empty/invalid)\n *\n * @example\n * ```typescript\n * tokenizeIndo(\"Kpd Yth. Bpk. Budi.\"); // [\"Kpd Yth. Bpk. Budi.\"]\n * tokenizeIndo(\"Halo. Selamat pagi.\"); // [\"Halo.\", \"Selamat pagi.\"]\n * ```\n *\n * @example\n * For processing formal Indonesian letters:\n * ```typescript\n * const letter = \"Hormat kami, PT ABC.Jl. Merdeka No.10\";\n * const sentences = tokenizeIndo(letter);\n * // Properly handles \"Jl.\" (Jalan) abbreviation\n * ```\n */\nexport function tokenizeIndo(text: string): string[] {\n  if (!text || typeof text !== 'string') {\n    return [];\n  }\n\n  const abbreviations = SENTENCE_ABBREVIATIONS;\n\n  const abbrevPositions: Array<{ start: number; end: number }> = [];\n\n  for (const abbrev of abbreviations) {\n    let searchStart = 0;\n    let index = text.indexOf(abbrev, searchStart);\n\n    while (index !== -1) {\n      abbrevPositions.push({ start: index, end: index + abbrev.length });\n      searchStart = index + 1;\n      index = text.indexOf(abbrev, searchStart);\n    }\n  }\n\n  if (abbrevPositions.length === 0) {\n    return text\n      .split('.')\n      .map(s => s.trim())\n      .filter(s => s.length > 0);\n  }\n\n  abbrevPositions.sort((a, b) => a.start - b.start);\n\n  const sentenceEndPositions: number[] = [];\n\n  for (const period of text.matchAll(/\\./g)) {\n    const periodIndex = period.index!;\n    const isInsideAbbrev = abbrevPositions.some(\n      pos => periodIndex >= pos.start && periodIndex < pos.end\n    );\n\n    if (!isInsideAbbrev) {\n      sentenceEndPositions.push(periodIndex);\n    }\n  }\n\n  const sentences: string[] = [];\n  let prevEnd = 0;\n\n  for (const endPos of sentenceEndPositions) {\n    const sentence = text.substring(prevEnd, endPos).trim();\n    if (sentence.length > 0) {\n      sentences.push(sentence);\n    }\n    prevEnd = endPos + 1;\n  }\n\n  if (prevEnd < text.length) {\n    const remaining = text.substring(prevEnd).trim();\n    if (remaining.length > 0) {\n      sentences.push(remaining);\n    }\n  }\n\n  return sentences;\n}\n","import {\n  WHITESPACE_PATTERN,\n  NON_ALPHANUMERIC_PATTERN,\n} from './constants';\n\n/**\n * Collapses multiple spaces to single space and trims.\n *\n * @param text - Text to normalize\n * @returns Text with normalized whitespace\n *\n * @example\n * ```typescript\n * normalizeWhitespace(\"  Budi   pergi  ke   sekolah  \")\n * // \"Budi pergi ke sekolah\"\n * ```\n */\nexport function normalizeWhitespace(text: string): string {\n  if (!text || typeof text !== 'string') return text;\n  return text.trim().replace(WHITESPACE_PATTERN, ' ');\n}\n\n/**\n * Removes all non-letter/number characters except spaces.\n *\n * @param text - Text to clean\n * @returns Text with only letters, numbers, and spaces\n *\n * @example\n * ```typescript\n * stripNonAlphanumeric(\"Budi123@#$%\"); // \"Budi123\"\n * stripNonAlphanumeric(\"Hello! World?\"); // \"Hello World\"\n * ```\n */\nexport function stripNonAlphanumeric(text: string): string {\n  if (!text || typeof text !== 'string') return text;\n  return text.replace(NON_ALPHANUMERIC_PATTERN, '');\n}"]}