{"id":33180263,"url":"https://github.com/Zheruel/nano-string-utils","last_synced_at":"2025-11-20T21:03:37.978Z","repository":{"id":312415813,"uuid":"1047420836","full_name":"Zheruel/nano-string-utils","owner":"Zheruel","description":"Ultra-lightweight, zero-dependency string utilities for modern JavaScript. Tree-shakeable, TypeScript-first,    \u003c1KB per function.","archived":false,"fork":false,"pushed_at":"2025-11-20T16:33:33.000Z","size":653,"stargazers_count":53,"open_issues_count":2,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-20T18:20:59.890Z","etag":null,"topics":["camelcase","cjs","esc","javascript","lightweight","npm-package","slugify","string-manipulation","string-utils","tree-shakeable","typescript","typescript-library","utilities","zero-dependencies"],"latest_commit_sha":null,"homepage":"https://zheruel.github.io/nano-string-utils/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Zheruel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-30T11:37:24.000Z","updated_at":"2025-11-20T16:33:24.000Z","dependencies_parsed_at":"2025-08-30T14:17:40.318Z","dependency_job_id":"6eed29a6-c74d-43d3-9167-c136a12d2e0c","html_url":"https://github.com/Zheruel/nano-string-utils","commit_stats":null,"previous_names":["zheruel/nano-string-utils"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/Zheruel/nano-string-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zheruel%2Fnano-string-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zheruel%2Fnano-string-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zheruel%2Fnano-string-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zheruel%2Fnano-string-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Zheruel","download_url":"https://codeload.github.com/Zheruel/nano-string-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zheruel%2Fnano-string-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285511808,"owners_count":27184244,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-20T02:00:05.334Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["camelcase","cjs","esc","javascript","lightweight","npm-package","slugify","string-manipulation","string-utils","tree-shakeable","typescript","typescript-library","utilities","zero-dependencies"],"created_at":"2025-11-16T03:00:41.704Z","updated_at":"2025-11-20T21:03:37.971Z","avatar_url":"https://github.com/Zheruel.png","language":"TypeScript","readme":"# nano-string-utils\n\nUltra-lightweight string utilities with zero dependencies. Tree-shakeable, fully typed, and optimized for modern JavaScript.\n\n[![npm version](https://img.shields.io/npm/v/nano-string-utils.svg)](https://www.npmjs.com/package/nano-string-utils)\n[![JSR](https://jsr.io/badges/@zheruel/nano-string-utils)](https://jsr.io/@zheruel/nano-string-utils)\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/nano-string-utils)](https://bundlephobia.com/package/nano-string-utils)\n[![TypeScript](https://img.shields.io/badge/TypeScript-100%25-blue.svg)](https://www.typescriptlang.org/)\n[![CI/CD](https://github.com/Zheruel/nano-string-utils/actions/workflows/ci.yml/badge.svg)](https://github.com/Zheruel/nano-string-utils/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Node.js](https://img.shields.io/badge/Node.js-≥18-brightgreen.svg)](https://nodejs.org/)\n[![Deno](https://img.shields.io/badge/Deno-✓-blue.svg)](https://deno.land)\n[![Bun](https://img.shields.io/badge/Bun-✓-orange.svg)](https://bun.sh)\n\n## Documentation\n\n📚 **[View Full API Documentation](https://zheruel.github.io/nano-string-utils/)**\n\n🚀 **[Migration Guide](https://zheruel.github.io/nano-string-utils/#migration)** - Step-by-step guide for migrating from lodash/underscore\n\n## Features\n\n- 🚀 **Zero dependencies** - No bloat, just pure functions\n- 📦 **\u003c 1KB per function** - Minimal bundle impact\n- 🌳 **Tree-shakeable** - Only import what you need\n- 💪 **Fully typed** - Complete TypeScript support with function overloads and template literal types\n- ⚡ **Fast performance** - 2-25x faster than lodash for many operations\n- ⚡ **ESM \u0026 CJS** - Works everywhere\n- 🧪 **100% tested** - Reliable and production-ready\n- 🔒 **Type-safe** - Written in strict TypeScript with enhanced type inference and compile-time transformations\n- 🛡️ **Null-safe** - All functions handle null/undefined gracefully without throwing errors\n- 📝 **Well documented** - JSDoc comments for all functions\n\n## Runtime Compatibility\n\nnano-string-utils works seamlessly across all modern JavaScript runtimes:\n\n| Runtime     | Support | Installation                          | CLI |\n| ----------- | ------- | ------------------------------------- | --- |\n| Node.js ≥18 | ✅ Full | `npm install nano-string-utils`       | ✅  |\n| Deno        | ✅ Full | `deno add @zheruel/nano-string-utils` | ✅  |\n| Bun         | ✅ Full | `bun add nano-string-utils`           | ✅  |\n| Browser     | ✅ Full | Via bundler or CDN                    | ❌  |\n\nAll core functions use standard JavaScript APIs and work identically across runtimes. The CLI tool supports Node.js, Deno, and Bun.\n\n## Installation\n\n### Node.js\n\n```bash\nnpm install nano-string-utils\n# or\nyarn add nano-string-utils\n# or\npnpm add nano-string-utils\n```\n\n### Deno\n\n```typescript\n// From JSR (recommended)\nimport { slugify } from \"jsr:@zheruel/nano-string-utils\";\n\n// Or add to your project\ndeno add @zheruel/nano-string-utils\n```\n\n### Bun\n\n```bash\nbun add nano-string-utils\n```\n\n### Browser (CDN)\n\n```html\n\u003c!-- Latest version --\u003e\n\u003cscript src=\"https://unpkg.com/nano-string-utils/dist/index.iife.js\"\u003e\u003c/script\u003e\n\n\u003c!-- Or specific version --\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/nano-string-utils@0.16.0/dist/index.iife.js\"\u003e\u003c/script\u003e\n\n\u003cscript\u003e\n  // All functions available on global nanoStringUtils object\n  const slug = nanoStringUtils.slugify(\"Hello World!\");\n  console.log(slug); // 'hello-world'\n\u003c/script\u003e\n```\n\nFor modern browsers with ES modules:\n\n```html\n\u003cscript type=\"module\"\u003e\n  import {\n    slugify,\n    camelCase,\n  } from \"https://unpkg.com/nano-string-utils/dist/index.js\";\n\n  console.log(slugify(\"Hello World\")); // 'hello-world'\n  console.log(camelCase(\"hello-world\")); // 'helloWorld'\n\u003c/script\u003e\n```\n\n## Quick Start\n\n```javascript\nimport {\n  slugify,\n  camelCase,\n  truncate,\n  isEmail,\n  fuzzyMatch,\n} from \"nano-string-utils\";\n\n// Transform strings with ease\nslugify(\"Hello World!\"); // 'hello-world'\ncamelCase(\"hello-world\"); // 'helloWorld'\ntruncate(\"Long text here\", 10); // 'Long te...'\n\n// Validate inputs\nisEmail(\"user@example.com\"); // true\nisEmail(\"invalid.email\"); // false\n\n// Advanced string matching\nfuzzyMatch(\"gto\", \"goToLine\"); // { matched: true, score: 0.546 }\nfuzzyMatch(\"abc\", \"xyz\"); // null (no match)\n```\n\n### Most Popular Functions\n\n#### `slugify(str: string): string`\n\nConvert any string to a URL-safe slug.\n\n```javascript\nslugify(\"Hello World!\"); // 'hello-world'\nslugify(\"  Multiple   Spaces  \"); // 'multiple-spaces'\nslugify(\"Special@#Characters!\"); // 'special-characters'\n```\n\n#### `camelCase(str: string): string`\n\nConvert strings to camelCase for JavaScript variables.\n\n```javascript\ncamelCase(\"hello-world\"); // 'helloWorld'\ncamelCase(\"HELLO_WORLD\"); // 'helloWorld'\ncamelCase(\"Hello World\"); // 'helloWorld'\n```\n\n#### `truncate(str: string, length: number, suffix?: string): string`\n\nIntelligently truncate text with customizable suffix.\n\n```javascript\ntruncate(\"Long text here\", 10); // 'Long te...'\ntruncate(\"Long text here\", 10, \"→\"); // 'Long tex→'\n```\n\n#### `isEmail(str: string, options?: { allowInternational?: boolean }): boolean`\n\nValidate email addresses with a robust regex. Supports apostrophes by default (for names like O'Connor). International characters require opt-in.\n\n```javascript\nisEmail(\"user@example.com\"); // true\nisEmail(\"test@sub.domain.com\"); // true\nisEmail(\"o'connor@example.com\"); // true (apostrophes supported)\nisEmail(\"invalid.email\"); // false\n\n// International support (opt-in)\nisEmail(\"josé@example.com\"); // false\nisEmail(\"josé@example.com\", { allowInternational: true }); // true\n```\n\n#### `fuzzyMatch(query: string, target: string): FuzzyMatchResult | null`\n\nPerform fuzzy string matching for search features.\n\n```javascript\nfuzzyMatch(\"usrctrl\", \"userController.js\"); // { matched: true, score: 0.444 }\nfuzzyMatch(\"of\", \"openFile\"); // { matched: true, score: 0.75 }\n```\n\n\u003e 📖 **See all 52 functions in the API Reference below**\n\n## CLI\n\nNano String Utils includes a command-line interface for quick string transformations directly in your terminal. The CLI works with Node.js, Deno, and Bun!\n\n### Installation \u0026 Usage\n\n#### Node.js\n\n```bash\n# Global installation\nnpm install -g nano-string-utils\nnano-string slugify \"Hello World\"\n\n# Using npx (no installation required)\nnpx nano-string-utils slugify \"Hello World\"\n```\n\n#### Deno\n\n```bash\n# Direct execution (no installation required)\ndeno run --allow-read https://unpkg.com/nano-string-utils/bin/nano-string.js slugify \"Hello World\"\n\n# Or if installed locally\ndeno run --allow-read node_modules/nano-string-utils/bin/nano-string.js slugify \"Hello World\"\n```\n\n#### Bun\n\n```bash\n# Using bunx (no installation required)\nbunx nano-string-utils slugify \"Hello World\"\n\n# Or if installed\nbun run nano-string-utils slugify \"Hello World\"\n```\n\n### Basic Usage\n\n```bash\nnano-string \u003cfunction\u003e \u003cinput\u003e [options]\n```\n\n### Examples\n\n#### Simple transformations\n\n```bash\nnano-string slugify \"Hello World!\"           # hello-world\nnano-string camelCase \"hello-world\"          # helloWorld\nnano-string kebabCase \"hello_world\"          # hello-world\nnano-string capitalize \"hello\"               # Hello\nnano-string reverse \"hello\"                  # olleh\n```\n\n#### Using pipes\n\n```bash\necho \"Hello World\" | nano-string slugify     # hello-world\ncat file.txt | nano-string truncate --length 50\n```\n\n#### Functions with options\n\n```bash\n# Truncate with custom length\nnano-string truncate \"Long text here\" --length 10  # Long te...\n\n# Template interpolation\nnano-string template \"Hello {{name}}\" --data '{\"name\":\"World\"}'  # Hello World\n\n# Pad strings\nnano-string padStart \"hi\" --length 5 --char \"*\"  # ***hi\n\n# Generate random strings\nnano-string randomString --length 10  # Generates 10-character string\n\n# Text processing\nnano-string smartSplit \"Dr. Smith went to the store. He bought milk.\"  # ['Dr. Smith went to the store.', 'He bought milk.']\nnano-string humanizeList \"apple,banana,orange\" --conjunction \"or\"  # apple, banana, or orange\n```\n\n#### Validation functions\n\n```bash\nnano-string isEmail \"test@example.com\"              # true\nnano-string isUrl \"https://example.com\"             # true\nnano-string isASCII \"hello\"                         # true\nnano-string isHexColor \"#ff5733\"                    # true\nnano-string isNumeric \"42\"                          # true\nnano-string isInteger \"42\"                          # true\nnano-string isAlphanumeric \"user123\"                # true\nnano-string isUUID \"550e8400-e29b-41d4-a716-446655440000\"  # true\n```\n\n#### Analysis functions\n\n```bash\nnano-string wordCount \"hello world test\"     # 3\nnano-string levenshtein \"kitten\" \"sitting\"   # 3\nnano-string diff \"hello\" \"hallo\"             # Shows differences\n```\n\n### Available Commands\n\nTo see all available functions:\n\n```bash\nnano-string --help\n```\n\nFor help on a specific function:\n\n```bash\nnano-string slugify --help\n```\n\n## API Reference\n\nThe library provides 52 string utility functions organized by category. Click on any category to explore the available functions.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🔤 Case Conversion Functions (10 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### Case Conversion\n\nTransform strings between different naming conventions commonly used in programming.\n\n#### `slugify(str: string): string`\n\nConverts a string to a URL-safe slug.\n\n```javascript\nslugify(\"Hello World!\"); // 'hello-world'\nslugify(\"  Multiple   Spaces  \"); // 'multiple-spaces'\n```\n\n#### `camelCase(str: string): string`\n\nConverts a string to camelCase.\n\n```javascript\ncamelCase(\"hello world\"); // 'helloWorld'\ncamelCase(\"hello-world\"); // 'helloWorld'\ncamelCase(\"hello_world\"); // 'helloWorld'\n```\n\n#### `snakeCase(str: string): string`\n\nConverts a string to snake_case.\n\n```javascript\nsnakeCase(\"hello world\"); // 'hello_world'\nsnakeCase(\"helloWorld\"); // 'hello_world'\nsnakeCase(\"hello-world\"); // 'hello_world'\n```\n\n#### `kebabCase(str: string): string`\n\nConverts a string to kebab-case.\n\n```javascript\nkebabCase(\"hello world\"); // 'hello-world'\nkebabCase(\"helloWorld\"); // 'hello-world'\nkebabCase(\"hello_world\"); // 'hello-world'\n```\n\n#### `pascalCase(str: string): string`\n\nConverts a string to PascalCase.\n\n```javascript\npascalCase(\"hello world\"); // 'HelloWorld'\npascalCase(\"hello-world\"); // 'HelloWorld'\npascalCase(\"hello_world\"); // 'HelloWorld'\n```\n\n#### `constantCase(str: string): string`\n\nConverts a string to CONSTANT_CASE.\n\n```javascript\nconstantCase(\"hello world\"); // 'HELLO_WORLD'\nconstantCase(\"helloWorld\"); // 'HELLO_WORLD'\nconstantCase(\"hello-world\"); // 'HELLO_WORLD'\nconstantCase(\"XMLHttpRequest\"); // 'XML_HTTP_REQUEST'\n```\n\n#### `dotCase(str: string): string`\n\nConverts a string to dot.case.\n\n```javascript\ndotCase(\"hello world\"); // 'hello.world'\ndotCase(\"helloWorld\"); // 'hello.world'\ndotCase(\"hello-world\"); // 'hello.world'\ndotCase(\"XMLHttpRequest\"); // 'xml.http.request'\ndotCase(\"com/example/package\"); // 'com.example.package'\n```\n\n#### `pathCase(str: string): string`\n\nConverts a string to path/case (forward slash separated).\n\n```javascript\npathCase(\"hello world\"); // 'hello/world'\npathCase(\"helloWorld\"); // 'hello/world'\npathCase(\"hello-world\"); // 'hello/world'\npathCase(\"hello_world\"); // 'hello/world'\npathCase(\"XMLHttpRequest\"); // 'xml/http/request'\npathCase(\"src.components.Header\"); // 'src/components/header'\npathCase(\"com.example.package\"); // 'com/example/package'\n```\n\n#### `titleCase(str: string, options?: { exceptions?: string[] }): string`\n\nConverts a string to title case with proper capitalization rules.\n\n```javascript\ntitleCase(\"the quick brown fox\"); // 'The Quick Brown Fox'\ntitleCase(\"a tale of two cities\"); // 'A Tale of Two Cities'\ntitleCase(\"mother-in-law\"); // 'Mother-in-Law'\ntitleCase(\"don't stop believing\"); // \"Don't Stop Believing\"\ntitleCase(\"NASA launches rocket\"); // 'NASA Launches Rocket'\ntitleCase(\"2001: a space odyssey\"); // '2001: A Space Odyssey'\n\n// With custom exceptions\ntitleCase(\"the lord of the rings\", {\n  exceptions: [\"versus\"],\n}); // 'The Lord of the Rings'\n```\n\n#### `sentenceCase(str: string): string`\n\nConverts a string to sentence case (first letter of each sentence capitalized).\n\n```javascript\nsentenceCase(\"hello world\"); // 'Hello world'\nsentenceCase(\"HELLO WORLD\"); // 'Hello world'\nsentenceCase(\"hello. world! how are you?\"); // 'Hello. World! How are you?'\nsentenceCase(\"this is the first. this is the second.\"); // 'This is the first. This is the second.'\nsentenceCase(\"the u.s.a. is large\"); // 'The u.s.a. is large'\nsentenceCase(\"i love javascript\"); // 'I love javascript'\nsentenceCase(\"what? when? where?\"); // 'What? When? Where?'\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e✂️ String Manipulation (11 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### String Manipulation\n\nEssential functions for transforming and manipulating text content.\n\n#### `capitalize(str: string): string`\n\nCapitalizes the first letter of a string and lowercases the rest.\n\n```javascript\ncapitalize(\"hello world\"); // 'Hello world'\ncapitalize(\"HELLO\"); // 'Hello'\n```\n\n#### `reverse(str: string): string`\n\nReverses a string.\n\n```javascript\nreverse(\"hello\"); // 'olleh'\nreverse(\"world\"); // 'dlrow'\n```\n\n#### `truncate(str: string, length: number, suffix?: string): string`\n\nTruncates a string to a specified length with an optional suffix.\n\n```javascript\ntruncate(\"Long text here\", 10); // 'Long te...'\ntruncate(\"Long text here\", 10, \"→\"); // 'Long tex→'\n```\n\n#### `excerpt(str: string, length: number, suffix?: string): string`\n\nCreates a smart excerpt from text with word boundary awareness.\n\n```javascript\nexcerpt(\"The quick brown fox jumps over the lazy dog\", 20); // 'The quick brown fox...'\nexcerpt(\"Hello world. This is a test.\", 15); // 'Hello world...'\nexcerpt(\"Long technical documentation text here\", 25, \"…\"); // 'Long technical…'\nexcerpt(\"Supercalifragilisticexpialidocious\", 10); // 'Supercalif...'\n```\n\n#### `pad(str: string, length: number, chars?: string): string`\n\nPads a string to a given length by adding characters to both sides (centers the string).\n\n```javascript\npad(\"Hi\", 6); // '  Hi  '\npad(\"Hi\", 6, \"-\"); // '--Hi--'\npad(\"Hi\", 7, \"-\"); // '--Hi---'\n```\n\n#### `padStart(str: string, length: number, chars?: string): string`\n\nPads a string to a given length by adding characters to the left.\n\n```javascript\npadStart(\"5\", 3, \"0\"); // '005'\npadStart(\"Hi\", 5, \".\"); // '...Hi'\npadStart(\"Hi\", 6, \"=-\"); // '=-=-Hi'\n```\n\n#### `padEnd(str: string, length: number, chars?: string): string`\n\nPads a string to a given length by adding characters to the right.\n\n```javascript\npadEnd(\"Hi\", 5, \".\"); // 'Hi...'\npadEnd(\"Hi\", 6, \"=-\"); // 'Hi=-=-'\npadEnd(\"5\", 3, \"0\"); // '500'\n```\n\n#### `deburr(str: string): string`\n\nRemoves diacritics/accents from Latin characters.\n\n```javascript\ndeburr(\"café\"); // 'cafe'\ndeburr(\"naïve\"); // 'naive'\ndeburr(\"Bjørn\"); // 'Bjorn'\ndeburr(\"São Paulo\"); // 'Sao Paulo'\ndeburr(\"Müller\"); // 'Muller'\n```\n\n#### `wordCount(str: string): number`\n\nCounts the number of words in a string.\n\n```javascript\nwordCount(\"Hello world test\"); // 3\nwordCount(\"One-word counts as one\"); // 5\n```\n\n#### `randomString(length: number, charset?: string): string`\n\nGenerates a random string of specified length.\n\n```javascript\nrandomString(10); // 'aBc123XyZ9'\nrandomString(5, \"abc\"); // 'abcab'\nrandomString(8, \"0123456789\"); // '42318765'\n```\n\n#### `hashString(str: string): number`\n\nGenerates a simple hash from a string (non-cryptographic).\n\n```javascript\nhashString(\"hello\"); // 99162322\nhashString(\"world\"); // 113318802\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📝 Text Processing (14 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### Text Processing\n\nAdvanced text processing utilities for handling HTML, whitespace, special characters, and entity extraction.\n\n#### `stripHtml(str: string): string`\n\nRemoves HTML tags from a string.\n\n```javascript\nstripHtml(\"\u003cp\u003eHello \u003cb\u003eworld\u003c/b\u003e!\u003c/p\u003e\"); // 'Hello world!'\nstripHtml(\"\u003cdiv\u003eText\u003c/div\u003e\"); // 'Text'\n```\n\n#### `sanitize(str: string, options?: SanitizeOptions): string`\n\nSecurity-focused string sanitization for safe use in web applications by removing or escaping dangerous content.\n\n```javascript\nsanitize(\"\u003cscript\u003ealert('xss')\u003c/script\u003eHello\"); // 'Hello'\nsanitize(\"\u003cb\u003eBold\u003c/b\u003e text\", { allowedTags: [\"b\"] }); // '\u003cb\u003eBold\u003c/b\u003e text'\nsanitize(\"javascript:alert(1)\"); // ''\nsanitize(\"\u003cdiv onclick='alert(1)'\u003eClick\u003c/div\u003e\"); // 'Click'\n```\n\n#### `redact(text: string, options?: RedactOptions): string`\n\nRedacts sensitive information from text for UI/logging purposes. Supports SSN, credit cards, emails, and phone numbers with customizable redaction strategies.\n\n**⚠️ Security Notice**: This is for UI/logging purposes to prevent accidental exposure. Not a substitute for proper data security practices or encryption.\n\n```javascript\n// Default: redact all types with partial strategy (show last 4)\nredact(\"My SSN is 123-45-6789\"); // 'My SSN is ***-**-6789'\nredact(\"Card: 4532-1234-5678-9010\"); // 'Card: **** **** **** 9010'\nredact(\"Email: user@example.com\"); // 'Email: use***@example.com'\nredact(\"Phone: (555) 123-4567\"); // 'Phone: (***) ***-4567'\n\n// Selective type redaction\nredact(\"Email: user@example.com, SSN: 123-45-6789\", {\n  types: [\"email\"], // Only redact emails\n}); // 'Email: use***@example.com, SSN: 123-45-6789'\n\n// Full redaction (no partial reveal)\nredact(\"SSN: 123-45-6789\", { strategy: \"full\" }); // 'SSN: ***-**-****'\n\n// Custom patterns\nredact(\"Secret: ABC-123\", {\n  customPatterns: [{ pattern: /[A-Z]{3}-\\d{3}/g, replacement: \"[REDACTED]\" }],\n}); // 'Secret: [REDACTED]'\n```\n\n#### `escapeHtml(str: string): string`\n\nEscapes HTML special characters.\n\n```javascript\nescapeHtml('\u003cdiv\u003eHello \u0026 \"world\"\u003c/div\u003e'); // '\u0026lt;div\u0026gt;Hello \u0026amp; \u0026quot;world\u0026quot;\u0026lt;/div\u0026gt;'\nescapeHtml(\"It's \u003cb\u003ebold\u003c/b\u003e\"); // 'It\u0026#x27;s \u0026lt;b\u0026gt;bold\u0026lt;/b\u0026gt;'\n```\n\n#### `normalizeWhitespace(str: string, options?: NormalizeWhitespaceOptions): string`\n\nNormalizes various Unicode whitespace characters to regular spaces.\n\n```javascript\nnormalizeWhitespace(\"hello   world\"); // 'hello world'\nnormalizeWhitespace(\"hello\\u00A0world\"); // 'hello world' (non-breaking space)\nnormalizeWhitespace(\"  hello  \"); // 'hello'\nnormalizeWhitespace(\"hello\\n\\nworld\"); // 'hello world'\n\n// With options\nnormalizeWhitespace(\"  hello  \", { trim: false }); // ' hello '\nnormalizeWhitespace(\"a    b\", { collapse: false }); // 'a    b'\nnormalizeWhitespace(\"hello\\n\\nworld\", { preserveNewlines: true }); // 'hello\\n\\nworld'\n\n// Handles various Unicode spaces\nnormalizeWhitespace(\"café\\u2003test\"); // 'café test' (em space)\nnormalizeWhitespace(\"hello\\u200Bworld\"); // 'hello world' (zero-width space)\nnormalizeWhitespace(\"日本\\u3000語\"); // '日本 語' (ideographic space)\n```\n\n#### `removeNonPrintable(str: string, options?: RemoveNonPrintableOptions): string`\n\nRemoves non-printable control characters and formatting characters from strings.\n\n```javascript\nremoveNonPrintable(\"hello\\x00world\"); // 'helloworld' (removes NULL character)\nremoveNonPrintable(\"hello\\nworld\"); // 'helloworld' (removes newline by default)\nremoveNonPrintable(\"hello\\u200Bworld\"); // 'helloworld' (removes zero-width space)\nremoveNonPrintable(\"hello\\u202Dworld\"); // 'helloworld' (removes directional override)\n\n// With options\nremoveNonPrintable(\"hello\\nworld\", { keepNewlines: true }); // 'hello\\nworld'\nremoveNonPrintable(\"hello\\tworld\", { keepTabs: true }); // 'hello\\tworld'\nremoveNonPrintable(\"hello\\r\\nworld\", { keepCarriageReturns: true }); // 'hello\\rworld'\n\n// Preserves emoji with zero-width joiners\nremoveNonPrintable(\"👨‍👩‍👧‍👦\"); // '👨‍👩‍👧‍👦' (family emoji preserved)\nremoveNonPrintable(\"text\\x1B[32mgreen\\x1B[0m\"); // 'text[32mgreen[0m' (ANSI escapes removed)\n```\n\n#### `toASCII(str: string, options?: { placeholder?: string }): string`\n\nConverts a string to ASCII-safe representation by removing diacritics, converting common Unicode symbols, and optionally replacing non-ASCII characters.\n\n```javascript\ntoASCII(\"café\"); // 'cafe'\ntoASCII(\"Hello \"world\"\"); // 'Hello \"world\"'\ntoASCII(\"em—dash\"); // 'em-dash'\ntoASCII(\"€100\"); // 'EUR100'\ntoASCII(\"½ + ¼ = ¾\"); // '1/2 + 1/4 = 3/4'\ntoASCII(\"→ ← ↑ ↓\"); // '-\u003e \u003c- ^ v'\ntoASCII(\"α β γ\"); // 'a b g'\ntoASCII(\"Привет\"); // 'Privet'\ntoASCII(\"你好\"); // '' (removes non-convertible characters)\ntoASCII(\"你好\", { placeholder: \"?\" }); // '??'\ntoASCII(\"Hello 世界\", { placeholder: \"?\" }); // 'Hello ??'\ntoASCII(\"© 2024 Müller™\"); // '(c) 2024 Muller(TM)'\n```\n\n#### `pluralize(word: string, count?: number): string`\n\nConverts a singular word to its plural form using English pluralization rules. Optionally takes a count to conditionally pluralize.\n\n```javascript\npluralize(\"box\"); // 'boxes'\npluralize(\"baby\"); // 'babies'\npluralize(\"person\"); // 'people'\npluralize(\"analysis\"); // 'analyses'\npluralize(\"cactus\"); // 'cacti'\n\n// With count parameter\npluralize(\"item\", 1); // 'item' (singular for count of 1)\npluralize(\"item\", 0); // 'items' (plural for count of 0)\npluralize(\"item\", 5); // 'items' (plural for count \u003e 1)\n\n// Preserves casing\npluralize(\"Box\"); // 'Boxes'\npluralize(\"PERSON\"); // 'PEOPLE'\n```\n\n#### `singularize(word: string): string`\n\nConverts a plural word to its singular form using English singularization rules.\n\n```javascript\nsingularize(\"boxes\"); // 'box'\nsingularize(\"babies\"); // 'baby'\nsingularize(\"people\"); // 'person'\nsingularize(\"analyses\"); // 'analysis'\nsingularize(\"cacti\"); // 'cactus'\nsingularize(\"data\"); // 'datum'\n\n// Preserves casing\nsingularize(\"Boxes\"); // 'Box'\nsingularize(\"PEOPLE\"); // 'PERSON'\n```\n\n#### `extractEntities(text: string): ExtractedEntities`\n\nExtracts various entities from text including emails, URLs, mentions, hashtags, phones, dates, and prices.\n\n```javascript\n// Extract from mixed content\nconst text =\n  \"Contact @john at john@example.com or call (555) 123-4567. Check #updates at https://example.com. Price: $99.99\";\nconst entities = extractEntities(text);\n// Returns:\n// {\n//   emails: ['john@example.com'],\n//   urls: ['https://example.com'],\n//   mentions: ['@john'],\n//   hashtags: ['#updates'],\n//   phones: ['(555) 123-4567'],\n//   dates: [],\n//   prices: ['$99.99']\n// }\n\n// Extract from social media content\nconst tweet =\n  \"Hey @alice and @bob! Check out #javascript #typescript at https://github.com/example\";\nconst social = extractEntities(tweet);\n// social.mentions: ['@alice', '@bob']\n// social.hashtags: ['#javascript', '#typescript']\n// social.urls: ['https://github.com/example']\n\n// Extract contact information\nconst contact = \"Email: support@company.com, Phone: +1-800-555-0100\";\nconst info = extractEntities(contact);\n// info.emails: ['support@company.com']\n// info.phones: ['+1-800-555-0100']\n\n// Extract dates and prices\nconst invoice = \"Invoice Date: 2024-01-15, Due: 01/30/2024, Amount: $1,234.56\";\nconst billing = extractEntities(invoice);\n// billing.dates: ['2024-01-15', '01/30/2024']\n// billing.prices: ['$1,234.56']\n```\n\n#### `smartSplit(text: string): string[]`\n\nIntelligently splits text into sentences while properly handling abbreviations, ellipses, and decimal numbers.\n\n```javascript\n// Basic sentence splitting\nsmartSplit(\"Hello world. How are you? I'm fine!\");\n// ['Hello world.', 'How are you?', \"I'm fine!\"]\n\n// Handles abbreviations correctly\nsmartSplit(\"Dr. Smith went to the store. He bought milk.\");\n// ['Dr. Smith went to the store.', 'He bought milk.']\n\n// Preserves decimal numbers\nsmartSplit(\"The price is $10.50. That's expensive!\");\n// ['The price is $10.50.', \"That's expensive!\"]\n\n// Handles multiple abbreviations\nsmartSplit(\n  \"Mr. and Mrs. Johnson live on St. Paul Ave. They moved from the U.S.A. last year.\"\n);\n// ['Mr. and Mrs. Johnson live on St. Paul Ave.', 'They moved from the U.S.A. last year.']\n\n// Handles ellipses\nsmartSplit(\"I was thinking... Maybe we should go. What do you think?\");\n// ['I was thinking...', 'Maybe we should go.', 'What do you think?']\n```\n\n#### `humanizeList(items: unknown[], options?: HumanizeListOptions): string`\n\nConverts an array into a grammatically correct, human-readable list with proper conjunctions and optional Oxford comma.\n\n```javascript\n// Basic usage\nhumanizeList([\"apple\", \"banana\", \"orange\"]);\n// 'apple, banana, and orange'\n\n// Two items\nhumanizeList([\"yes\", \"no\"]);\n// 'yes and no'\n\n// With custom conjunction\nhumanizeList([\"red\", \"green\", \"blue\"], { conjunction: \"or\" });\n// 'red, green, or blue'\n\n// Without Oxford comma\nhumanizeList([\"a\", \"b\", \"c\"], { oxford: false });\n// 'a, b and c'\n\n// With quotes\nhumanizeList([\"run\", \"jump\", \"swim\"], { quotes: true });\n// '\"run\", \"jump\", and \"swim\"'\n\n// Handles mixed types and nulls\nhumanizeList([1, null, \"text\", undefined, true]);\n// '1, text, and true'\n\n// Empty arrays\nhumanizeList([]);\n// ''\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e✅ Validation Functions (9 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### String Validation\n\nUtilities for validating string formats and content.\n\n#### `isEmail(str: string, options?: { allowInternational?: boolean }): boolean`\n\nValidates if a string is a valid email format. Supports apostrophes by default (useful for names like O'Connor, D'Angelo). International (Unicode) characters require opt-in via the `allowInternational` option.\n\n```javascript\nisEmail(\"user@example.com\"); // true\nisEmail(\"invalid.email\"); // false\nisEmail(\"test@sub.domain.com\"); // true\n\n// Apostrophes supported by default\nisEmail(\"o'connor@example.com\"); // true\nisEmail(\"d'angelo@test.co\"); // true\n\n// International characters require opt-in\nisEmail(\"josé@example.com\"); // false (default behavior)\nisEmail(\"josé@example.com\", { allowInternational: true }); // true\nisEmail(\"müller@domain.de\", { allowInternational: true }); // true\nisEmail(\"user@café.com\", { allowInternational: true }); // true\n```\n\n#### `isUrl(str: string): boolean`\n\nValidates if a string is a valid URL format.\n\n```javascript\nisUrl(\"https://example.com\"); // true\nisUrl(\"http://localhost:3000\"); // true\nisUrl(\"not a url\"); // false\nisUrl(\"ftp://files.com/file.zip\"); // true\n```\n\n#### `isASCII(str: string): boolean`\n\nChecks if a string contains only ASCII characters (code points 0-127).\n\n```javascript\nisASCII(\"Hello World!\"); // true\nisASCII(\"café\"); // false\nisASCII(\"👍\"); // false\nisASCII(\"abc123!@#\"); // true\nisASCII(\"\"); // true\n```\n\n#### `isHexColor(str: string): boolean`\n\nValidates if a string is a valid hexadecimal color code.\n\n```javascript\nisHexColor(\"#fff\"); // true (3-digit)\nisHexColor(\"#ffffff\"); // true (6-digit)\nisHexColor(\"#fff8\"); // true (4-digit with alpha)\nisHexColor(\"#ffffff80\"); // true (8-digit with alpha)\nisHexColor(\"#FFF\"); // true (case-insensitive)\nisHexColor(\"fff\"); // false (missing #)\nisHexColor(\"#gggggg\"); // false (invalid characters)\n```\n\n#### `isNumeric(str: string): boolean`\n\nValidates if a string represents a numeric value (integer or decimal).\n\n```javascript\nisNumeric(\"42\"); // true\nisNumeric(\"-17\"); // true\nisNumeric(\"3.14\"); // true\nisNumeric(\"-0.5\"); // true\nisNumeric(\"  42  \"); // true (whitespace trimmed)\nisNumeric(\"abc\"); // false\nisNumeric(\"\"); // false\nisNumeric(\"Infinity\"); // false\nisNumeric(\"1e5\"); // false (scientific notation not supported)\n```\n\n#### `isInteger(str: string): boolean`\n\nValidates if a string represents an integer value (whole number without decimals). Useful for validating age fields, quantities, IDs, pagination parameters, and other inputs that should only accept whole numbers.\n\n```javascript\nisInteger(\"42\"); // true\nisInteger(\"-17\"); // true\nisInteger(\"0\"); // true\nisInteger(\"  42  \"); // true (whitespace trimmed)\nisInteger(\"007\"); // true (leading zeros allowed)\nisInteger(\"3.14\"); // false (decimal number)\nisInteger(\"42.0\"); // false (contains decimal point)\nisInteger(\"abc\"); // false\nisInteger(\"\"); // false\nisInteger(\"Infinity\"); // false\nisInteger(\"1e5\"); // false (scientific notation not supported)\n```\n\n#### `isAlphanumeric(str: string): boolean`\n\nValidates if a string contains only alphanumeric characters (a-z, A-Z, 0-9). Useful for validating usernames, identifiers, and other inputs that should not contain special characters or whitespace.\n\n```javascript\nisAlphanumeric(\"user123\"); // true\nisAlphanumeric(\"HelloWorld\"); // true\nisAlphanumeric(\"ABC123XYZ\"); // true\nisAlphanumeric(\"test\"); // true\nisAlphanumeric(\"123\"); // true\nisAlphanumeric(\"hello_world\"); // false (underscore not allowed)\nisAlphanumeric(\"hello world\"); // false (whitespace not allowed)\nisAlphanumeric(\"test-123\"); // false (hyphen not allowed)\nisAlphanumeric(\"café\"); // false (Unicode not allowed)\nisAlphanumeric(\"\"); // false (empty string)\n```\n\n#### `isUUID(str: string): boolean`\n\nValidates if a string is a valid UUID (Universally Unique Identifier) in the standard 8-4-4-4-12 format. Accepts all UUID versions (v1-v5), the NIL UUID, and is case-insensitive. Perfect for validating API identifiers, session tokens, and database IDs.\n\n```javascript\nisUUID(\"550e8400-e29b-41d4-a716-446655440000\"); // true (v4)\nisUUID(\"6ba7b810-9dad-11d1-80b4-00c04fd430c8\"); // true (v1)\nisUUID(\"00000000-0000-0000-0000-000000000000\"); // true (NIL UUID)\nisUUID(\"550E8400-E29B-41D4-A716-446655440000\"); // true (uppercase)\nisUUID(\"550e8400e29b41d4a716446655440000\"); // false (no hyphens)\nisUUID(\"550e8400-e29b-41d4-a716\"); // false (too short)\nisUUID(\"not-a-uuid\"); // false (invalid format)\nisUUID(\"\"); // false (empty string)\n```\n\n#### `detectScript(str: string): 'latin' | 'cjk' | 'arabic' | 'cyrillic' | 'hebrew' | 'devanagari' | 'greek' | 'thai' | 'unknown'`\n\nDetects the dominant writing system (script) in a text string.\n\n```javascript\ndetectScript(\"Hello World\"); // 'latin'\ndetectScript(\"你好世界\"); // 'cjk'\ndetectScript(\"مرحبا بالعالم\"); // 'arabic'\ndetectScript(\"Привет мир\"); // 'cyrillic'\ndetectScript(\"שלום עולם\"); // 'hebrew'\ndetectScript(\"नमस्ते दुनिया\"); // 'devanagari'\ndetectScript(\"Γειά σου κόσμε\"); // 'greek'\ndetectScript(\"สวัสดีชาวโลก\"); // 'thai'\ndetectScript(\"\"); // 'unknown'\n```\n\n#### `classifyText(str: string): { type: string, confidence: number }`\n\nClassifies text content by type (URL, email, code, JSON, markdown, HTML, question, phone, numeric, or plain text) with confidence scoring.\n\n```javascript\nclassifyText(\"https://example.com\"); // { type: 'url', confidence: 1 }\nclassifyText(\"user@example.com\"); // { type: 'email', confidence: 1 }\nclassifyText(\"What is TypeScript?\"); // { type: 'question', confidence: 1 }\nclassifyText('{\"key\": \"value\"}'); // { type: 'json', confidence: 1 }\nclassifyText(\"function hello() { return 42; }\"); // { type: 'code', confidence: 0.85 }\nclassifyText(\"\u003cdiv\u003eHello\u003c/div\u003e\"); // { type: 'html', confidence: 0.9 }\nclassifyText(\"# Title\\n\\nText\"); // { type: 'markdown', confidence: 0.7 }\nclassifyText(\"+1-555-123-4567\"); // { type: 'phone', confidence: 0.95 }\nclassifyText(\"42 + 17 = 59\"); // { type: 'numeric', confidence: 0.8 }\nclassifyText(\"Just plain text\"); // { type: 'text', confidence: 0.7 }\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🔍 String Analysis \u0026 Comparison (6 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### String Analysis \u0026 Comparison\n\nAdvanced utilities for analyzing and comparing strings.\n\n#### `diff(oldStr: string, newStr: string): string`\n\nComputes a simple string diff comparison showing additions and deletions.\n\n```javascript\ndiff(\"hello world\", \"hello beautiful world\"); // 'hello {+beautiful +}world'\ndiff(\"goodbye world\", \"hello world\"); // '[-goodbye-]{+hello+} world'\ndiff(\"v1.0.0\", \"v1.1.0\"); // 'v1.[-0-]{+1+}.0'\ndiff(\"debug: false\", \"debug: true\"); // 'debug: [-fals-]{+tru+}e'\ndiff(\"user@example.com\", \"admin@example.com\"); // '[-user-]{+admin+}@example.com'\n\n// Form field changes\ndiff(\"John Doe\", \"Jane Doe\"); // 'J[-ohn-]{+ane+} Doe'\n\n// Configuration changes\ndiff(\"port: 3000\", \"port: 8080\"); // 'port: [-300-]{+808+}0'\n\n// File extension changes\ndiff(\"app.js\", \"app.ts\"); // 'app.[-j-]{+t+}s'\n\n// No changes\ndiff(\"test\", \"test\"); // 'test'\n\n// Complete replacement\ndiff(\"hello\", \"world\"); // '[-hello-]{+world+}'\n```\n\nUses a simple prefix/suffix algorithm optimized for readability. The output format uses:\n\n- `[-text-]` for deleted text\n- `{+text+}` for added text\n\n#### `levenshtein(a: string, b: string, maxDistance?: number): number`\n\nCalculates the Levenshtein distance (edit distance) between two strings. Optimized with space-efficient algorithm and early termination support.\n\n```javascript\nlevenshtein(\"cat\", \"bat\"); // 1 (substitution)\nlevenshtein(\"cat\", \"cats\"); // 1 (insertion)\nlevenshtein(\"cats\", \"cat\"); // 1 (deletion)\nlevenshtein(\"kitten\", \"sitting\"); // 3\nlevenshtein(\"example\", \"exmaple\"); // 2 (transposition)\n\n// With maxDistance for early termination\nlevenshtein(\"hello\", \"helicopter\", 3); // Infinity (exceeds max)\nlevenshtein(\"hello\", \"hallo\", 3); // 1 (within max)\n\n// Unicode support\nlevenshtein(\"café\", \"cafe\"); // 1\nlevenshtein(\"😀\", \"😃\"); // 1\n```\n\n#### `levenshteinNormalized(a: string, b: string): number`\n\nCalculates normalized Levenshtein similarity score between 0 and 1. Perfect for fuzzy matching and similarity scoring.\n\n```javascript\nlevenshteinNormalized(\"hello\", \"hello\"); // 1 (identical)\nlevenshteinNormalized(\"cat\", \"bat\"); // 0.667 (fairly similar)\nlevenshteinNormalized(\"hello\", \"world\"); // 0.2 (dissimilar)\nlevenshteinNormalized(\"\", \"abc\"); // 0 (completely different)\n\n// Real-world typo detection\nlevenshteinNormalized(\"necessary\", \"neccessary\"); // 0.9\nlevenshteinNormalized(\"example\", \"exmaple\"); // 0.714\n\n// Fuzzy matching (common threshold: 0.8)\nconst threshold = 0.8;\nlevenshteinNormalized(\"test\", \"tests\") \u003e= threshold; // true (0.8)\nlevenshteinNormalized(\"hello\", \"goodbye\") \u003e= threshold; // false (0.143)\n```\n\n#### `fuzzyMatch(query: string, target: string, options?: FuzzyMatchOptions): FuzzyMatchResult | null`\n\nPerforms fuzzy string matching with a similarity score, ideal for command palettes, file finders, and search-as-you-type features.\n\n```javascript\n// Basic usage\nfuzzyMatch(\"gto\", \"goToLine\"); // { matched: true, score: 0.546 }\nfuzzyMatch(\"usrctrl\", \"userController.js\"); // { matched: true, score: 0.444 }\nfuzzyMatch(\"abc\", \"xyz\"); // null (no match)\n\n// Command palette style matching\nfuzzyMatch(\"of\", \"openFile\"); // { matched: true, score: 0.75 }\nfuzzyMatch(\"svf\", \"saveFile\"); // { matched: true, score: 0.619 }\n\n// File finder matching\nfuzzyMatch(\"index\", \"src/components/index.html\"); // { matched: true, score: 0.262 }\nfuzzyMatch(\"app.js\", \"src/app.js\"); // { matched: true, score: 0.85 }\n\n// Case sensitivity\nfuzzyMatch(\"ABC\", \"abc\"); // { matched: true, score: 0.95 }\nfuzzyMatch(\"ABC\", \"abc\", { caseSensitive: true }); // null\n\n// Minimum score threshold\nfuzzyMatch(\"ab\", \"a\" + \"x\".repeat(50) + \"b\", { threshold: 0.5 }); // null (score too low)\n\n// Acronym matching (matches at word boundaries score higher)\nfuzzyMatch(\"uc\", \"UserController\"); // { matched: true, score: 0.75 }\nfuzzyMatch(\"gc\", \"getUserController\"); // { matched: true, score: 0.75 }\n```\n\nOptions:\n\n- `caseSensitive` - Enable case-sensitive matching (default: false)\n- `threshold` - Minimum score to consider a match (default: 0)\n\nReturns:\n\n- `{ matched: true, score: number }` - When match found (score between 0-1)\n- `{ matched: false, score: 0 }` - For empty query\n- `null` - When no match found or score below threshold\n\nScoring algorithm prioritizes:\n\n- Exact matches (1.0)\n- Prefix matches (≥0.85)\n- Consecutive character matches\n- Matches at word boundaries (camelCase, snake_case, kebab-case, etc.)\n- Early matches in the string\n- Acronym-style matches\n\n#### `highlight(str: string, terms: string | string[], options?: HighlightOptions): string`\n\nHighlights search terms in text by wrapping them with markers.\n\n```javascript\nhighlight(\"The quick brown fox\", \"quick\"); // 'The \u003cmark\u003equick\u003c/mark\u003e brown fox'\nhighlight(\"Hello WORLD\", \"world\"); // '\u003cmark\u003eHello\u003c/mark\u003e \u003cmark\u003eWORLD\u003c/mark\u003e' (case-insensitive by default)\n\n// Multiple terms\nhighlight(\"The quick brown fox\", [\"quick\", \"fox\"]); // 'The \u003cmark\u003equick\u003c/mark\u003e brown \u003cmark\u003efox\u003c/mark\u003e'\n\n// Custom wrapper\nhighlight(\"Error: Connection failed\", [\"error\", \"failed\"], {\n  wrapper: [\"**\", \"**\"],\n}); // '**Error**: Connection **failed**'\n\n// Whole word matching\nhighlight(\"Java and JavaScript\", \"Java\", { wholeWord: true }); // '\u003cmark\u003eJava\u003c/mark\u003e and JavaScript'\n\n// With CSS class\nhighlight(\"Hello world\", \"Hello\", { className: \"highlight\" }); // '\u003cmark class=\"highlight\"\u003eHello\u003c/mark\u003e world'\n\n// HTML escaping for security\nhighlight(\"\u003cdiv\u003eHello\u003c/div\u003e\", \"Hello\", { escapeHtml: true }); // '\u0026lt;div\u0026gt;\u003cmark\u003eHello\u003c/mark\u003e\u0026lt;/div\u0026gt;'\n\n// Case-sensitive matching\nhighlight(\"Hello hello\", \"hello\", { caseSensitive: true }); // 'Hello \u003cmark\u003ehello\u003c/mark\u003e'\n```\n\nOptions:\n\n- `caseSensitive` - Enable case-sensitive matching (default: false)\n- `wholeWord` - Match whole words only (default: false)\n- `wrapper` - Custom wrapper tags (default: ['\u003cmark\u003e', '\u003c/mark\u003e'])\n- `className` - CSS class for mark tags\n- `escapeHtml` - Escape HTML in text before highlighting (default: false)\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🌍 Unicode \u0026 International (5 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### Unicode \u0026 International\n\nHandle complex Unicode characters, emoji, and international text.\n\n#### `graphemes(str: string): string[]`\n\nSplits a string into an array of grapheme clusters, properly handling emojis, combining characters, and complex Unicode.\n\n```javascript\ngraphemes(\"hello\"); // ['h', 'e', 'l', 'l', 'o']\ngraphemes(\"👨‍👩‍👧‍👦🎈\"); // ['👨‍👩‍👧‍👦', '🎈']\ngraphemes(\"café\"); // ['c', 'a', 'f', 'é']\ngraphemes(\"👍🏽\"); // ['👍🏽'] - emoji with skin tone\ngraphemes(\"🇺🇸\"); // ['🇺🇸'] - flag emoji\ngraphemes(\"hello👋world\"); // ['h', 'e', 'l', 'l', 'o', '👋', 'w', 'o', 'r', 'l', 'd']\n```\n\n#### `codePoints(str: string): number[]`\n\nConverts a string into an array of Unicode code points, properly handling surrogate pairs and complex characters.\n\n```javascript\ncodePoints(\"hello\"); // [104, 101, 108, 108, 111]\ncodePoints(\"👍\"); // [128077]\ncodePoints(\"€\"); // [8364]\ncodePoints(\"Hello 👋\"); // [72, 101, 108, 108, 111, 32, 128075]\ncodePoints(\"a👍b\"); // [97, 128077, 98]\ncodePoints(\"👨‍👩‍👧‍👦\"); // [128104, 8205, 128105, 8205, 128103, 8205, 128102]\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🎯 Templates \u0026 Interpolation (2 functions)\u003c/b\u003e\u003c/summary\u003e\n\n### Templates \u0026 Interpolation\n\nSafe and flexible string template interpolation.\n\n#### `template(str: string, data: Record\u003cstring, any\u003e, options?: TemplateOptions): string`\n\nInterpolates variables in a template string.\n\n```javascript\ntemplate(\"Hello {{name}}!\", { name: \"World\" }); // 'Hello World!'\ntemplate(\"{{user.name}} is {{user.age}}\", {\n  user: { name: \"Alice\", age: 30 },\n}); // 'Alice is 30'\ntemplate(\n  \"Hello ${name}!\",\n  { name: \"World\" },\n  {\n    delimiters: [\"${\", \"}\"],\n  }\n); // 'Hello World!'\n```\n\n#### `templateSafe(str: string, data: Record\u003cstring, any\u003e, options?: TemplateOptions): string`\n\nInterpolates variables with HTML escaping for safe output.\n\n```javascript\ntemplateSafe(\"Hello {{name}}!\", {\n  name: '\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e',\n}); // 'Hello \u0026lt;script\u0026gt;alert(\u0026quot;XSS\u0026quot;)\u0026lt;/script\u0026gt;!'\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e⚡ Performance Utilities (1 function)\u003c/b\u003e\u003c/summary\u003e\n\n### Performance Utilities\n\nOptimize expensive string operations with caching.\n\n#### `memoize\u003cT\u003e(fn: T, options?: MemoizeOptions): T`\n\nCreates a memoized version of a function with LRU (Least Recently Used) cache eviction. Ideal for optimizing expensive string operations like `levenshtein`, `fuzzyMatch`, or `diff` when processing repetitive data.\n\n```javascript\nimport { levenshtein, memoize } from \"nano-string-utils\";\n\n// Basic usage - memoize expensive string operations\nconst memoizedLevenshtein = memoize(levenshtein);\n\n// First call computes the result\nmemoizedLevenshtein(\"kitten\", \"sitting\"); // 3 (computed)\n\n// Subsequent calls with same arguments return cached result\nmemoizedLevenshtein(\"kitten\", \"sitting\"); // 3 (cached - instant)\n\n// Custom cache size (default is 100)\nconst limited = memoize(levenshtein, { maxSize: 50 });\n\n// Custom key generation for complex arguments\nconst processUser = (user) =\u003e expensive(user);\nconst memoizedProcess = memoize(processUser, {\n  getKey: (user) =\u003e user.id, // Cache by user ID only\n});\n\n// Real-world example: Fuzzy search with caching\nimport { fuzzyMatch, memoize } from \"nano-string-utils\";\n\nconst cachedFuzzyMatch = memoize(fuzzyMatch);\nconst searchResults = items.map((item) =\u003e cachedFuzzyMatch(query, item.name));\n\n// Batch processing with deduplication benefits\nconst words = [\"hello\", \"world\", \"hello\", \"test\", \"world\"];\nconst distances = words.map((word) =\u003e memoizedLevenshtein(\"example\", word)); // Only computes 3 times instead of 5\n```\n\nFeatures:\n\n- **LRU cache eviction** - Keeps most recently used results\n- **Configurable cache size** - Control memory usage (default: 100 entries)\n- **Custom key generation** - Support for complex argument types\n- **Type-safe** - Preserves function signatures and types\n- **Zero dependencies** - Pure JavaScript implementation\n\nBest used with:\n\n- `levenshtein()` - Expensive O(n×m) algorithm\n- `fuzzyMatch()` - Complex scoring with boundary detection\n- `diff()` - Character-by-character comparison\n- Any custom expensive string operations\n\nOptions:\n\n- `maxSize` - Maximum cached results (default: 100)\n- `getKey` - Custom cache key generator function\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e🔧 TypeScript Utilities\u003c/b\u003e\u003c/summary\u003e\n\n### Branded Types (TypeScript)\n\nNano-string-utils provides branded types for compile-time type safety with validated strings. These types add zero runtime overhead and are fully tree-shakeable.\n\n```typescript\nimport { branded } from \"nano-string-utils\";\n```\n\n#### Type Guards\n\nType guards narrow string types to branded types:\n\n```typescript\nconst input: string = getUserInput();\n\nif (branded.isValidEmail(input)) {\n  // input is now typed as Email\n  sendEmail(input);\n}\n\nif (branded.isValidUrl(input)) {\n  // input is now typed as URL\n  fetch(input);\n}\n\nif (branded.isSlug(input)) {\n  // input is now typed as Slug\n  useAsRoute(input);\n}\n```\n\n#### Builder Functions\n\nSafely create branded types with validation:\n\n```typescript\n// Returns Email | null\nconst email = branded.toEmail(\"user@example.com\");\nif (email) {\n  sendEmail(email); // email is typed as Email\n}\n\n// Returns URL | null\nconst url = branded.toUrl(\"https://example.com\");\nif (url) {\n  fetch(url); // url is typed as URL\n}\n\n// Always returns Slug (transforms input)\nconst slug = branded.toSlug(\"Hello World!\"); // 'hello-world' as Slug\ncreateRoute(slug);\n\n// Smart slug handling\nconst slug2 = branded.ensureSlug(\"already-a-slug\"); // returns as-is if valid\nconst slug3 = branded.ensureSlug(\"Not A Slug!\"); // transforms to 'not-a-slug'\n```\n\n#### Assertion Functions\n\nAssert types with runtime validation:\n\n```typescript\nconst input: string = getUserInput();\n\n// Throws BrandedTypeError if invalid\nbranded.assertEmail(input);\n// input is now typed as Email\nsendEmail(input);\n\n// Custom error messages\nbranded.assertUrl(input, \"Invalid webhook URL\");\n\n// All assertion functions available\nbranded.assertEmail(str);\nbranded.assertUrl(str);\nbranded.assertSlug(str);\n```\n\n#### Unsafe Variants\n\nFor trusted inputs where validation isn't needed:\n\n```typescript\n// Use only when you're certain the input is valid\nconst trustedEmail = branded.unsafeEmail(\"admin@system.local\");\nconst trustedUrl = branded.unsafeUrl(\"https://internal.api\");\nconst trustedSlug = branded.unsafeSlug(\"already-valid-slug\");\n```\n\n#### Available Types\n\n- `Email` - Validated email addresses\n- `URL` - Validated URLs (http/https/ftp/ftps)\n- `Slug` - URL-safe slugs (lowercase, hyphenated)\n- `Brand\u003cT, K\u003e` - Generic branding utility for custom types\n\n#### Benefits\n\n- **Zero runtime overhead** - Types are erased at compilation\n- **Type safety** - Prevent passing unvalidated strings to functions\n- **IntelliSense support** - Full autocomplete and type hints\n- **Tree-shakeable** - Only imported if used\n- **Composable** - Works with existing string functions\n\n```typescript\n// Example: Type-safe API\nfunction sendNewsletter(email: branded.Email) {\n  // Can only be called with validated emails\n  api.send(email);\n}\n\n// Won't compile without validation\nconst userInput = \"maybe@email.com\";\n// sendNewsletter(userInput); // ❌ Type error!\n\n// Must validate first\nconst validated = branded.toEmail(userInput);\nif (validated) {\n  sendNewsletter(validated); // ✅ Type safe!\n}\n```\n\n#### Extending with Custom Branded Types\n\nThe `Brand\u003cT, K\u003e` utility allows you to create your own custom branded types beyond the built-in ones. This is perfect for domain-specific validation without bloating the library.\n\n```typescript\nimport type { Brand } from \"nano-string-utils\";\nimport { sanitize } from \"nano-string-utils\";\n\n// Create custom branded types for your domain\ntype PhoneNumber = Brand\u003cstring, \"PhoneNumber\"\u003e;\ntype PostalCode = Brand\u003cstring, \"PostalCode\"\u003e;\ntype CreditCard = Brand\u003cstring, \"CreditCard\"\u003e;\n\n// Build type-safe constructors\nfunction toPhoneNumber(str: string): PhoneNumber | null {\n  const cleaned = str.replace(/\\D/g, \"\");\n  if (cleaned.length === 10 || cleaned.length === 11) {\n    return cleaned as PhoneNumber;\n  }\n  return null;\n}\n\nfunction toPostalCode(str: string): PostalCode | null {\n  // US ZIP code validation\n  if (/^\\d{5}(-\\d{4})?$/.test(str)) {\n    return str as PostalCode;\n  }\n  return null;\n}\n\n// Type guards for runtime checking\nfunction isPhoneNumber(str: string): str is PhoneNumber {\n  return toPhoneNumber(str) !== null;\n}\n\n// Use in your application\nfunction sendSMS(phone: PhoneNumber, message: string) {\n  // Can only be called with validated phone numbers\n  console.log(`Sending to ${phone}: ${message}`);\n}\n\nconst userInput = \"(555) 123-4567\";\nconst phone = toPhoneNumber(userInput);\nif (phone) {\n  sendSMS(phone); // ✅ Type safe!\n}\n// sendSMS(userInput); // ❌ Type error - string is not PhoneNumber\n```\n\nThis pattern gives you:\n\n- **Compile-time safety** - Prevent using unvalidated data\n- **Zero bundle cost** - Only import what you use from the library\n- **Domain modeling** - Express business rules in the type system\n- **Composability** - Mix custom types with built-in branded types\n\n### Template Literal Types (TypeScript)\n\nCase conversion functions now provide precise type inference for literal strings at compile time. This feature enhances IDE support with exact type transformations while maintaining full backward compatibility.\n\n```typescript\nimport { camelCase, kebabCase, snakeCase } from \"nano-string-utils\";\n\n// Literal strings get exact transformed types\nconst endpoint = kebabCase(\"getUserProfile\");\n// Type: \"get-user-profile\" (not just string!)\n\nconst column = snakeCase(\"firstName\");\n// Type: \"first_name\"\n\nconst methodName = camelCase(\"fetch-user-data\");\n// Type: \"fetchUserData\"\n\n// Runtime strings still return regular string type\nconst userInput: string = getUserInput();\nconst result = camelCase(userInput);\n// Type: string (backward compatible)\n```\n\n#### All Case Conversions Support Template Literals\n\n```typescript\ncamelCase(\"hello-world\"); // Type: \"helloWorld\"\nkebabCase(\"helloWorld\"); // Type: \"hello-world\"\nsnakeCase(\"HelloWorld\"); // Type: \"hello_world\"\npascalCase(\"hello-world\"); // Type: \"HelloWorld\"\nconstantCase(\"helloWorld\"); // Type: \"HELLO_WORLD\"\ndotCase(\"HelloWorld\"); // Type: \"hello.world\"\npathCase(\"helloWorld\"); // Type: \"hello/world\"\nsentenceCase(\"hello-world\"); // Type: \"Hello world\"\ntitleCase(\"hello-world\"); // Type: \"Hello World\"\n```\n\n#### Type-Safe Configuration Objects\n\nTransform configuration keys between naming conventions:\n\n```typescript\nconst config = {\n  \"api-base-url\": \"https://api.example.com\",\n  \"max-retries\": 3,\n} as const;\n\n// Convert keys to camelCase at type level\ntype ConfigCamelCase = {\n  [K in keyof typeof config as CamelCase\u003cK\u003e]: (typeof config)[K];\n};\n// Type: { apiBaseUrl: string; maxRetries: number; }\n```\n\n#### API Route Mapping\n\nCreate type-safe method names from API routes:\n\n```typescript\ntype ApiRoutes = \"user-profile\" | \"user-settings\" | \"admin-panel\";\n\ntype MethodNames = {\n  [K in ApiRoutes as `fetch${PascalCase\u003cK\u003e}`]: () =\u003e Promise\u003cvoid\u003e;\n};\n// Creates: fetchUserProfile(), fetchUserSettings(), fetchAdminPanel()\n```\n\nBenefits:\n\n- ✅ **Zero runtime cost** - All transformations happen at compile time\n- ✅ **Better IDE support** - Autocomplete shows exact transformed strings\n- ✅ **Type safety** - Catch typos and incorrect transformations during development\n- ✅ **Backward compatible** - Runtime strings work exactly as before\n\n\u003c/details\u003e\n\n### Null/Undefined Safety\n\nAll functions in nano-string-utils handle null and undefined inputs gracefully:\n\n```typescript\n// No more runtime errors!\nslugify(null); // Returns: null\nslugify(undefined); // Returns: undefined\nslugify(\"\"); // Returns: \"\"\n\n// Consistent behavior across all functions\nisEmail(null); // Returns: false (validation functions)\nwords(null); // Returns: [] (array functions)\nwordCount(null); // Returns: 0 (counting functions)\n\n// Safe to use without defensive checks\nconst userInput = getUserInput(); // might be null/undefined\nconst slug = slugify(userInput); // Won't throw!\n```\n\nThis means:\n\n- ✅ **No TypeErrors** - Functions never throw on null/undefined\n- ✅ **Predictable behavior** - Consistent handling across all utilities\n- ✅ **Cleaner code** - No need for defensive checks before calling functions\n- ✅ **Zero performance cost** - Minimal overhead from null checks\n\n## Bundle Size\n\nEach utility is optimized to be as small as possible:\n\n| Function              | Size (gzipped) |\n| --------------------- | -------------- |\n| slugify               | 138 bytes      |\n| camelCase             | 232 bytes      |\n| snakeCase             | 197 bytes      |\n| kebabCase             | 197 bytes      |\n| pascalCase            | 219 bytes      |\n| constantCase          | 228 bytes      |\n| dotCase               | 207 bytes      |\n| pathCase              | 207 bytes      |\n| sentenceCase          | 414 bytes      |\n| titleCase             | 562 bytes      |\n| capitalize            | 99 bytes       |\n| truncate              | 180 bytes      |\n| stripHtml             | 85 bytes       |\n| sanitize              | 812 bytes      |\n| redact                | 727 bytes      |\n| escapeHtml            | 136 bytes      |\n| excerpt               | 261 bytes      |\n| randomString          | 219 bytes      |\n| hashString            | 155 bytes      |\n| reverse               | 82 bytes       |\n| deburr                | 273 bytes      |\n| isEmail               | 148 bytes      |\n| isUrl                 | 155 bytes      |\n| isASCII               | 128 bytes      |\n| isHexColor            | 103 bytes      |\n| isNumeric             | 122 bytes      |\n| isInteger             | 120 bytes      |\n| isAlphanumeric        | 88 bytes       |\n| isUUID                | 89 bytes       |\n| toASCII               | 1.3 KB         |\n| wordCount             | 123 bytes      |\n| normalizeWhitespace   | 268 bytes      |\n| removeNonPrintable    | 304 bytes      |\n| template              | 302 bytes      |\n| templateSafe          | 502 bytes      |\n| pad                   | 209 bytes      |\n| padStart              | 179 bytes      |\n| padEnd                | 183 bytes      |\n| graphemes             | 171 bytes      |\n| codePoints            | 131 bytes      |\n| highlight             | 461 bytes      |\n| diff                  | 265 bytes      |\n| levenshtein           | 413 bytes      |\n| levenshteinNormalized | 471 bytes      |\n| fuzzyMatch            | 613 bytes      |\n| pluralize             | 459 bytes      |\n| singularize           | 562 bytes      |\n| smartSplit            | 566 bytes      |\n| humanizeList          | 251 bytes      |\n| memoize               | 334 bytes      |\n| extractEntities       | 573 bytes      |\n| detectScript          | 540 bytes      |\n| classifyText          | 898 bytes      |\n\nTotal package size: **\u003c 12KB** minified + gzipped\n\n## Requirements\n\n- Node.js \u003e= 18\n- TypeScript \u003e= 5.0 (for TypeScript users)\n\n## Development\n\n```bash\n# Clone the repository\ngit clone https://github.com/Zheruel/nano-string-utils.git\ncd nano-string-utils\n\n# Install dependencies\nnpm install\n\n# Run tests\nnpm test\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Build the library\nnpm run build\n\n# Type check\nnpm run typecheck\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nMIT © [Zheruel]\n\n---\n\n## Why nano-string-utils?\n\nIn a world of bloated dependencies, `nano-string-utils` stands out by providing exactly what you need and nothing more:\n\n- **Security First**: Zero dependencies means zero supply chain vulnerabilities\n- **Performance**: Optimized for both speed and size\n  - Ultra-fast case conversions (3.4M-4.3M ops/s)\n  - Efficient truncate operations (23.4M ops/s)\n  - Fast template interpolation (998K ops/s)\n- **Developer Experience**: Full TypeScript support with comprehensive JSDoc comments\n- **Production Ready**: 100% test coverage with extensive edge case handling\n- **Modern**: Built for ES2022+ with full ESM support and CommonJS compatibility\n\n## Benchmarks\n\nWe continuously benchmark nano-string-utils against popular alternatives (lodash and es-toolkit) to ensure optimal performance and bundle size.\n\n### Running Benchmarks\n\n```bash\n# Run vitest benchmark tests\nnpm run bench\n\n# Generate benchmark data for docs website\nnpm run bench:data\n```\n\n### Latest Results\n\n#### Bundle Size Comparison (gzipped)\n\n| Function   | nano-string-utils | lodash | es-toolkit | Winner  |\n| ---------- | ----------------- | ------ | ---------- | ------- |\n| camelCase  | 232B              | 3.4KB  | 273B       | nano ✅ |\n| capitalize | 99B               | 1.7KB  | 107B       | nano ✅ |\n| kebabCase  | 197B              | 2.8KB  | 197B       | tied    |\n| truncate   | 180B              | 2.9KB  | -          | nano ✅ |\n\n#### Performance Comparison\n\n| Function   | nano-string-utils | lodash | es-toolkit  | Winner     |\n| ---------- | ----------------- | ------ | ----------- | ---------- |\n| capitalize | 21.3M ops/s       | -      | 21.3M ops/s | tied ✅    |\n| truncate   | 23.4M ops/s       | -      | -           | nano ✅    |\n| deburr     | 2.25M ops/s       | -      | 2.34M ops/s | es-toolkit |\n| escapeHtml | 2.28M ops/s       | -      | 2.26M ops/s | nano ✅    |\n| camelCase  | 3.40M ops/s       | -      | 3.35M ops/s | nano ✅    |\n| kebabCase  | 4.32M ops/s       | -      | 4.34M ops/s | es-toolkit |\n| snakeCase  | 4.34M ops/s       | -      | 4.33M ops/s | nano ✅    |\n| pascalCase | 3.97M ops/s       | -      | 3.95M ops/s | nano ✅    |\n| pad        | 11.7M ops/s       | -      | 18.6M ops/s | es-toolkit |\n| padStart   | 7.97M ops/s       | -      | -           | nano ✅    |\n| padEnd     | 7.97M ops/s       | -      | -           | nano ✅    |\n| template   | 998K ops/s        | -      | -           | nano ✅    |\n\n**[📊 View full interactive performance benchmarks](https://zheruel.github.io/nano-string-utils/#performance)**\n\n### Key Findings\n\n- 🏆 **Smallest bundle sizes**: nano-string-utils wins 47 out of 48 tested functions (98% win rate)\n- ⚡ **Competitive performance**: Wins 10 out of 14 benchmarked functions against es-toolkit\n- 📊 **[View full interactive benchmarks](https://zheruel.github.io/nano-string-utils/#bundle-size)** with detailed comparison\n- ⚡ **Optimized performance**:\n  - **Case conversions**: 3.4M-4.3M ops/s, competitive with es-toolkit\n  - **Truncate**: 23.4M ops/s for fast string truncation\n  - **Template**: 998K ops/s for string interpolation\n- 🌳 **Superior tree-shaking**: Each function is independently importable with minimal overhead\n\n## Comparison with Alternatives\n\n| Library           | Bundle Size | Dependencies | Tree-shakeable        | TypeScript |\n| ----------------- | ----------- | ------------ | --------------------- | ---------- |\n| nano-string-utils | \u003c 12KB      | 0            | ✅                    | ✅         |\n| lodash            | ~70KB       | 0            | ⚠️ Requires lodash-es | ✅         |\n| underscore.string | ~20KB       | 0            | ❌                    | ❌         |\n| voca              | ~30KB       | 0            | ❌                    | ✅         |\n\n## Support\n\n- 🐛 [Report bugs](https://github.com/Zheruel/nano-string-utils/issues)\n- 💡 [Request features](https://github.com/Zheruel/nano-string-utils/issues)\n- 📖 [Read the docs](https://github.com/Zheruel/nano-string-utils#api-reference)\n- ⭐ [Star on GitHub](https://github.com/Zheruel/nano-string-utils)\n","funding_links":[],"categories":["Built with TypeScript"],"sub_categories":["Libraries"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZheruel%2Fnano-string-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FZheruel%2Fnano-string-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZheruel%2Fnano-string-utils/lists"}