{"version":3,"file":"encoding.cjs","names":[],"sources":["../../../src/batteries/tools/encoding/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for common text encodings and decoding operations.\n *\n * @module @nhtio/adk/batteries/tools/encoding\n *\n * @remarks\n * Pre-constructed bundled tools for the `encoding` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\nconst utf8ToBase64 = (text: string): string => {\n  const bytes = new TextEncoder().encode(text)\n  let binary = ''\n  for (const byte of bytes) binary += String.fromCharCode(byte)\n  return globalThis.btoa(binary)\n}\n\nconst base64ToUtf8 = (b64: string): string => {\n  const binary = globalThis.atob(b64)\n  const bytes = new Uint8Array(binary.length)\n  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n  return new TextDecoder().decode(bytes)\n}\n\n/**\n * Convert a numeric HTML-entity code point to its string, handling astral characters correctly.\n * Returns the original entity text `fallback` for code points outside the valid Unicode range\n * (0 – 0x10FFFF) so `String.fromCodePoint` never throws a RangeError.\n */\nconst codePointToString = (cp: number, fallback: string): string => {\n  if (!Number.isInteger(cp) || cp < 0 || cp > 0x10ffff) return fallback\n  return String.fromCodePoint(cp)\n}\n\n/**\n * Encode or decode text using common schemes: base64, url (percent-encoding), html_entities.\n *\n * @remarks\n * `base64` uses the portable `globalThis.btoa`/`atob` pair so the tool works in browsers, Node,\n * and edge runtimes. Encoding/decoding errors are returned as `Error:` strings rather than\n * thrown so the model can react in-line.\n */\nexport const encodeTextTool = new Tool({\n  name: 'encode_text',\n  description:\n    'Encode or decode text using common schemes: base64, url (percent-encoding), html_entities. Specify direction: encode or decode.',\n  inputSchema: validator.object({\n    text: validator.string().required().description('Text to encode or decode'),\n    scheme: validator\n      .string()\n      .valid('base64', 'url', 'html_entities')\n      .required()\n      .description('Encoding scheme'),\n    direction: validator\n      .string()\n      .valid('encode', 'decode')\n      .default('encode')\n      .description('Whether to encode or decode (default: encode)'),\n  }),\n  handler: async (args) => {\n    const { text, scheme, direction } = args as {\n      text: string\n      scheme: string\n      direction: string\n    }\n\n    try {\n      if (scheme === 'base64') {\n        if (direction === 'encode') return utf8ToBase64(text)\n        return base64ToUtf8(text)\n      }\n\n      if (scheme === 'url') {\n        if (direction === 'encode') return encodeURIComponent(text)\n        return decodeURIComponent(text)\n      }\n\n      if (scheme === 'html_entities') {\n        if (direction === 'encode') {\n          return text\n            .replace(/&/g, '&amp;')\n            .replace(/</g, '&lt;')\n            .replace(/>/g, '&gt;')\n            .replace(/\"/g, '&quot;')\n            .replace(/'/g, '&#39;')\n        }\n        return (\n          text\n            .replace(/&amp;/g, '&')\n            .replace(/&lt;/g, '<')\n            .replace(/&gt;/g, '>')\n            .replace(/&quot;/g, '\"')\n            .replace(/&#39;/g, \"'\")\n            // Use fromCodePoint, not fromCharCode: a numeric entity above U+FFFF (e.g. an emoji like\n            // &#127881; = 🎉) is an astral code point. fromCharCode truncates to 16 bits and yields a\n            // broken/empty character; fromCodePoint produces the correct surrogate pair. Out-of-range\n            // code points (> U+10FFFF) are left as-is rather than throwing.\n            .replace(/&#(\\d+);/g, (m, code: string) =>\n              codePointToString(Number.parseInt(code, 10), m)\n            )\n            .replace(/&#x([0-9a-fA-F]+);/g, (m, hex: string) =>\n              codePointToString(Number.parseInt(hex, 16), m)\n            )\n        )\n      }\n\n      return `Error: Unknown scheme \"${scheme}\".`\n    } catch (err) {\n      return `Error: ${isError(err) ? err.message : String(err)}`\n    }\n  },\n})\n\n/**\n * Escape or unescape special characters for a target context.\n *\n * @remarks\n * Supports `json_string`, `regex`, `csv_field`, `sql_like`, and `markdown` targets. Returns\n * an error string for unknown targets rather than throwing.\n */\nexport const textEscapeTool = new Tool({\n  name: 'text_escape',\n  description:\n    'Escape or unescape special characters for a target context: json_string, regex, csv_field, sql_like, markdown.',\n  inputSchema: validator.object({\n    text: validator.string().required().description('Text to escape or unescape'),\n    target: validator\n      .string()\n      .valid('json_string', 'regex', 'csv_field', 'sql_like', 'markdown')\n      .required()\n      .description('Target context for escaping'),\n    direction: validator\n      .string()\n      .valid('escape', 'unescape')\n      .default('escape')\n      .description('Whether to escape or unescape (default: escape)'),\n  }),\n  handler: async (args) => {\n    const { text, target, direction } = args as {\n      text: string\n      target: string\n      direction: string\n    }\n\n    try {\n      if (target === 'json_string') {\n        if (direction === 'escape') return JSON.stringify(text).slice(1, -1)\n        return JSON.parse(`\"${text}\"`) as string\n      }\n\n      if (target === 'regex') {\n        if (direction === 'escape') return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n        return text.replace(/\\\\([.*+?^${}()|[\\]\\\\])/g, '$1')\n      }\n\n      if (target === 'csv_field') {\n        if (direction === 'escape') {\n          if (/[,\"\\n\\r]/.test(text)) return `\"${text.replace(/\"/g, '\"\"')}\"`\n          return text\n        }\n        if (text.startsWith('\"') && text.endsWith('\"')) {\n          return text.slice(1, -1).replace(/\"\"/g, '\"')\n        }\n        return text\n      }\n\n      if (target === 'sql_like') {\n        if (direction === 'escape') return text.replace(/[%_\\\\]/g, '\\\\$&')\n        return text.replace(/\\\\([%_\\\\])/g, '$1')\n      }\n\n      if (target === 'markdown') {\n        const mdChars = /[\\\\`*_{}[\\]()#+\\-.!|]/g\n        if (direction === 'escape') return text.replace(mdChars, '\\\\$&')\n        return text.replace(/\\\\([\\\\`*_{}[\\]()#+\\-.!|])/g, '$1')\n      }\n\n      return `Error: Unknown target \"${target}\".`\n    } catch (err) {\n      return `Error: ${isError(err) ? err.message : String(err)}`\n    }\n  },\n})\n\n/**\n * Normalize Unicode text or extract code points.\n *\n * @remarks\n * Operations: NFC, NFD, NFKC, NFKD Unicode normalization forms (via `String.prototype.normalize`),\n * `strip_accents` (NFD-decompose, remove combining diacritics, re-compose to NFC), or\n * `code_points` (one `U+HHHH (char)` per line).\n */\nexport const unicodeNormalizeTool = new Tool({\n  name: 'unicode_normalize',\n  description:\n    'Normalize Unicode text (NFC, NFD, NFKC, NFKD), strip accents/diacritics, or get Unicode code points for each character.',\n  inputSchema: validator.object({\n    text: validator.string().required().description('Text to process'),\n    operation: validator\n      .string()\n      .valid('nfc', 'nfd', 'nfkc', 'nfkd', 'strip_accents', 'code_points')\n      .required()\n      .description(\n        'Operation: NFC/NFD/NFKC/NFKD normalization, strip_accents (remove diacritics), or code_points (list hex values)'\n      ),\n  }),\n  handler: async (args) => {\n    const { text, operation } = args as { text: string; operation: string }\n\n    try {\n      if (operation === 'strip_accents') {\n        return text.normalize('NFD').replace(/[̀-ͯ]/g, '').normalize('NFC')\n      }\n\n      if (operation === 'code_points') {\n        return [...text]\n          .map((ch) => {\n            const cp = ch.codePointAt(0)!\n            const hex = cp.toString(16).toUpperCase().padStart(4, '0')\n            return `U+${hex} (${ch})`\n          })\n          .join('\\n')\n      }\n\n      const formMap: Record<string, string> = { nfc: 'NFC', nfd: 'NFD', nfkc: 'NFKC', nfkd: 'NFKD' }\n      const form = formMap[operation]\n      if (!form) return `Error: Unknown operation \"${operation}\".`\n      return text.normalize(form)\n    } catch (err) {\n      return `Error: ${isError(err) ? err.message : String(err)}`\n    }\n  },\n})\n"],"mappings":"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"}