speexjs-core 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/CONTRIBUTING.md +55 -0
  3. package/PUBLISH.md +45 -0
  4. package/README.md +174 -0
  5. package/ROADMAP.md +72 -0
  6. package/SECURITY.md +35 -0
  7. package/SUMMARY.md +321 -0
  8. package/dist/async/index.d.ts +232 -0
  9. package/dist/async/index.js +366 -0
  10. package/dist/async/index.js.map +1 -0
  11. package/dist/collection/index.d.ts +230 -0
  12. package/dist/collection/index.js +375 -0
  13. package/dist/collection/index.js.map +1 -0
  14. package/dist/color/index.d.ts +128 -0
  15. package/dist/color/index.js +167 -0
  16. package/dist/color/index.js.map +1 -0
  17. package/dist/core/index.d.ts +119 -0
  18. package/dist/core/index.js +324 -0
  19. package/dist/core/index.js.map +1 -0
  20. package/dist/crypto/index.d.ts +84 -0
  21. package/dist/crypto/index.js +144 -0
  22. package/dist/crypto/index.js.map +1 -0
  23. package/dist/date/index.d.ts +588 -0
  24. package/dist/date/index.js +737 -0
  25. package/dist/date/index.js.map +1 -0
  26. package/dist/dep-exray/analyzer/index.d.ts +7 -0
  27. package/dist/dep-exray/analyzer/index.js +68 -0
  28. package/dist/dep-exray/analyzer/index.js.map +1 -0
  29. package/dist/dep-exray/cli.d.ts +1 -0
  30. package/dist/dep-exray/cli.js +441 -0
  31. package/dist/dep-exray/cli.js.map +1 -0
  32. package/dist/dep-exray/index.d.ts +5 -0
  33. package/dist/dep-exray/index.js +454 -0
  34. package/dist/dep-exray/index.js.map +1 -0
  35. package/dist/dep-exray/known-mappings.d.ts +17 -0
  36. package/dist/dep-exray/known-mappings.js +122 -0
  37. package/dist/dep-exray/known-mappings.js.map +1 -0
  38. package/dist/dep-exray/reporter/index.d.ts +5 -0
  39. package/dist/dep-exray/reporter/index.js +89 -0
  40. package/dist/dep-exray/reporter/index.js.map +1 -0
  41. package/dist/dep-exray/scanner/index.d.ts +5 -0
  42. package/dist/dep-exray/scanner/index.js +299 -0
  43. package/dist/dep-exray/scanner/index.js.map +1 -0
  44. package/dist/dep-exray/types.d.ts +38 -0
  45. package/dist/dep-exray/types.js +1 -0
  46. package/dist/dep-exray/types.js.map +1 -0
  47. package/dist/error/index.d.ts +148 -0
  48. package/dist/error/index.js +115 -0
  49. package/dist/error/index.js.map +1 -0
  50. package/dist/index-BgG21uJC.d.ts +166 -0
  51. package/dist/index.d.ts +19 -0
  52. package/dist/index.js +4378 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/io/index.d.ts +39 -0
  55. package/dist/io/index.js +111 -0
  56. package/dist/io/index.js.map +1 -0
  57. package/dist/logger/index.d.ts +1 -0
  58. package/dist/logger/index.js +214 -0
  59. package/dist/logger/index.js.map +1 -0
  60. package/dist/logger/transports.d.ts +1 -0
  61. package/dist/logger/transports.js +122 -0
  62. package/dist/logger/transports.js.map +1 -0
  63. package/dist/math/index.d.ts +362 -0
  64. package/dist/math/index.js +372 -0
  65. package/dist/math/index.js.map +1 -0
  66. package/dist/path/index.d.ts +81 -0
  67. package/dist/path/index.js +134 -0
  68. package/dist/path/index.js.map +1 -0
  69. package/dist/string/index.d.ts +234 -0
  70. package/dist/string/index.js +411 -0
  71. package/dist/string/index.js.map +1 -0
  72. package/dist/type/index.d.ts +85 -0
  73. package/dist/type/index.js +107 -0
  74. package/dist/type/index.js.map +1 -0
  75. package/dist/validation/index.d.ts +203 -0
  76. package/dist/validation/index.js +402 -0
  77. package/dist/validation/index.js.map +1 -0
  78. package/package.json +172 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/math/index.ts","../../src/string/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the median of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The median value.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function median(values: number[]): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute median of an empty array')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const mid = Math.floor(sorted.length / 2)\r\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\r\n}\r\n\r\n/**\r\n * Computes the population standard deviation.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function stddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length)\r\n}\r\n\r\n/**\r\n * Computes the sample standard deviation (Bessel's correction).\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The sample standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function sampleStddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for sample stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1))\r\n}\r\n\r\n/**\r\n * Computes the percentile value (0-100) using linear interpolation.\r\n *\r\n * @param values - Array of numbers.\r\n * @param p - Percentile (0-100).\r\n * @returns The percentile value.\r\n * @throws {RangeError} If p is outside [0, 100] or array is empty.\r\n */\r\nexport function percentile(values: number[], p: number): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute percentile of empty array')\r\n if (p < 0 || p > 100) throw new RangeError('Percentile must be between 0 and 100')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const rank = (p / 100) * (sorted.length - 1)\r\n const lower = Math.floor(rank)\r\n const upper = Math.ceil(rank)\r\n if (lower === upper) return sorted[lower]!\r\n return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower)\r\n}\r\n\r\n/**\r\n * Computes the Pearson correlation coefficient between two arrays.\r\n *\r\n * @param x - First array.\r\n * @param y - Second array.\r\n * @returns The correlation coefficient (-1 to 1).\r\n * @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.\r\n */\r\nexport function correlation(x: number[], y: number[]): number {\r\n if (x.length !== y.length) throw new RangeError('Arrays must have the same length')\r\n if (x.length < 2) throw new RangeError('Need at least 2 pairs for correlation')\r\n const n = x.length\r\n const meanX = sum(x) / n\r\n const meanY = sum(y) / n\r\n let num = 0\r\n let denX = 0\r\n let denY = 0\r\n for (let i = 0; i < n; i++) {\r\n const dx = x[i]! - meanX\r\n const dy = y[i]! - meanY\r\n num += dx * dy\r\n denX += dx * dx\r\n denY += dy * dy\r\n }\r\n if (denX === 0 || denY === 0) return 0\r\n return num / Math.sqrt(denX * denY)\r\n}\r\n\r\n/**\r\n * Formats a number as a currency string with locale support.\r\n *\r\n * @example formatCurrency(1500000) // \"Rp1.500.000\"\r\n * @example formatCurrency(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n * @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // \"$99.99\"\r\n *\r\n * @param value - The number to format.\r\n * @param options - Formatting options.\r\n * @returns The formatted currency string.\r\n */\r\nexport function formatCurrency(\r\n value: number,\r\n options?: { locale?: string; currency?: string; notation?: 'standard' | 'compact' },\r\n): string {\r\n const locale = options?.locale ?? 'id-ID'\r\n const currency = options?.currency ?? 'IDR'\r\n const notation = options?.notation ?? 'standard'\r\n\r\n try {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n notation,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n }).format(value)\r\n } catch {\r\n return `${currency} ${value.toLocaleString(locale)}`\r\n }\r\n}\r\n\r\n// ─── Parity ─────────────────────────────────────────────\r\n\r\n/**\r\n * Checks if a number is even.\r\n *\r\n * @example isEven(4) // true\r\n * @example isEven(7) // false\r\n *\r\n * @param n - The number to check.\r\n * @returns Whether the number is even.\r\n */\r\nexport function isEven(n: number): boolean {\r\n return n % 2 === 0\r\n}\r\n\r\n/**\r\n * Checks if a number is odd.\r\n *\r\n * @example isOdd(3) // true\r\n * @example isOdd(8) // false\r\n *\r\n * @param n - The number to check.\r\n * @returns Whether the number is odd.\r\n */\r\nexport function isOdd(n: number): boolean {\r\n return n % 2 !== 0\r\n}\r\n\r\n// ─── Number Theory ──────────────────────────────────────\r\n\r\n/**\r\n * Computes the greatest common divisor (GCD) using the Euclidean algorithm.\r\n *\r\n * @example gcd(12, 8) // 4\r\n * @example gcd(17, 5) // 1\r\n *\r\n * @param a - First integer.\r\n * @param b - Second integer.\r\n * @returns The GCD.\r\n */\r\nexport function gcd(a: number, b: number): number {\r\n if (!Number.isInteger(a) || !Number.isInteger(b)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n a = Math.abs(a)\r\n b = Math.abs(b)\r\n while (b !== 0) {\r\n const t = b\r\n b = a % b\r\n a = t\r\n }\r\n return a\r\n}\r\n\r\n/**\r\n * Computes the least common multiple (LCM).\r\n *\r\n * @example lcm(4, 6) // 12\r\n * @example lcm(7, 5) // 35\r\n *\r\n * @param a - First integer.\r\n * @param b - Second integer.\r\n * @returns The LCM.\r\n * @throws {RangeError} If either argument is zero.\r\n */\r\nexport function lcm(a: number, b: number): number {\r\n if (!Number.isInteger(a) || !Number.isInteger(b)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (a === 0 || b === 0) {\r\n throw new RangeError('Arguments must be non-zero')\r\n }\r\n return Math.abs(a * b) / gcd(a, b)\r\n}\r\n\r\n/**\r\n * Computes the factorial of a non-negative integer.\r\n *\r\n * @example factorial(5) // 120\r\n * @example factorial(0) // 1\r\n *\r\n * @param n - Non-negative integer.\r\n * @returns The factorial.\r\n * @throws {RangeError} If n is negative or not an integer.\r\n */\r\nexport function factorial(n: number): number {\r\n if (!Number.isInteger(n)) {\r\n throw new RangeError('Argument must be an integer')\r\n }\r\n if (n < 0) {\r\n throw new RangeError('Factorial is not defined for negative numbers')\r\n }\r\n let result = 1\r\n for (let i = 2; i <= n; i++) {\r\n result *= i\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Tests whether a number is prime (optimized trial division).\r\n *\r\n * @example isPrime(7) // true\r\n * @example isPrime(10) // false\r\n *\r\n * @param n - Positive integer.\r\n * @returns Whether the number is prime.\r\n * @throws {RangeError} If n is not an integer or is less than 2.\r\n */\r\nexport function isPrime(n: number): boolean {\r\n if (!Number.isInteger(n)) {\r\n throw new RangeError('Argument must be an integer')\r\n }\r\n if (n < 2) return false\r\n if (n === 2 || n === 3) return true\r\n if (n % 2 === 0 || n % 3 === 0) return false\r\n const limit = Math.sqrt(n)\r\n for (let i = 5; i <= limit; i += 6) {\r\n if (n % i === 0 || n % (i + 2) === 0) return false\r\n }\r\n return true\r\n}\r\n\r\n// ─── Angle Conversion ───────────────────────────────────\r\n\r\n/**\r\n * Converts degrees to radians.\r\n *\r\n * @example toRadians(180) // ~3.14159\r\n *\r\n * @param degrees - Angle in degrees.\r\n * @returns Angle in radians.\r\n */\r\nexport function toRadians(degrees: number): number {\r\n return (degrees * Math.PI) / 180\r\n}\r\n\r\n/**\r\n * Converts radians to degrees.\r\n *\r\n * @example toDegrees(Math.PI) // 180\r\n *\r\n * @param radians - Angle in radians.\r\n * @returns Angle in degrees.\r\n */\r\nexport function toDegrees(radians: number): number {\r\n return (radians * 180) / Math.PI\r\n}\r\n\r\n// ─── Interpolation & Mapping ────────────────────────────\r\n\r\n/**\r\n * Linearly interpolates between `a` and `b` by `t`.\r\n *\r\n * @example lerp(0, 100, 0.5) // 50\r\n *\r\n * @param a - Start value.\r\n * @param b - End value.\r\n * @param t - Interpolation factor (typically 0–1).\r\n * @returns The interpolated value.\r\n */\r\nexport function lerp(a: number, b: number, t: number): number {\r\n return a + (b - a) * t\r\n}\r\n\r\n/**\r\n * Calculates what percentage `value` is of `total`.\r\n *\r\n * @example percentageOf(25, 200) // 12.5\r\n *\r\n * @param value - The part value.\r\n * @param total - The total value.\r\n * @returns The percentage.\r\n * @throws {RangeError} If total is zero.\r\n */\r\nexport function percentageOf(value: number, total: number): number {\r\n if (total === 0) {\r\n throw new RangeError('Total must be non-zero')\r\n }\r\n return (value / total) * 100\r\n}\r\n\r\n/**\r\n * Maps a value from one range to another.\r\n *\r\n * @example mapRange(0.5, 0, 1, 0, 100) // 50\r\n *\r\n * @param value - The value to map.\r\n * @param inMin - Lower bound of the input range.\r\n * @param inMax - Upper bound of the input range.\r\n * @param outMin - Lower bound of the output range.\r\n * @param outMax - Upper bound of the output range.\r\n * @returns The mapped value.\r\n * @throws {RangeError} If the input range is zero.\r\n */\r\nexport function mapRange(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number {\r\n if (inMin === inMax) {\r\n throw new RangeError('Input range must not be zero')\r\n }\r\n return ((value - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the statistical mode(s) — the most frequently occurring value(s).\r\n *\r\n * @example mode([1, 2, 2, 3]) // [2]\r\n * @example mode([1, 1, 2, 2]) // [1, 2]\r\n *\r\n * @param values - Array of numbers.\r\n * @returns Array of mode value(s).\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function mode(values: number[]): number[] {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute mode of an empty array')\r\n }\r\n const freq = new Map<number, number>()\r\n let maxFreq = 0\r\n for (const v of values) {\r\n const count = (freq.get(v) ?? 0) + 1\r\n freq.set(v, count)\r\n if (count > maxFreq) maxFreq = count\r\n }\r\n const result: number[] = []\r\n for (const [v, count] of freq) {\r\n if (count === maxFreq) result.push(v)\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Generates an array of numbers from `start` to `end` (inclusive),\r\n * incremented by `step`.\r\n *\r\n * @example range(1, 5) // [1, 2, 3, 4, 5]\r\n * @example range(0, 10, 2) // [0, 2, 4, 6, 8, 10]\r\n *\r\n * @param start - Start of the range.\r\n * @param end - End of the range (inclusive).\r\n * @param step - Increment (defaults to 1 or -1 based on direction).\r\n * @returns Array of numbers.\r\n * @throws {RangeError} If step is zero.\r\n */\r\nexport function range(start: number, end: number, step?: number): number[] {\r\n const dir = end >= start ? 1 : -1\r\n const s = step ?? dir\r\n if (s === 0) {\r\n throw new RangeError('Step must not be zero')\r\n }\r\n if ((end - start) * s < 0) {\r\n return []\r\n }\r\n const result: number[] = []\r\n let i = start\r\n if (s > 0) {\r\n while (i <= end) {\r\n result.push(i)\r\n i += s\r\n }\r\n } else {\r\n while (i >= end) {\r\n result.push(i)\r\n i += s\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Computes the weighted mean of values with corresponding weights.\r\n *\r\n * @example weightedAverage([10, 20], [1, 4]) // 18\r\n *\r\n * @param values - Array of values.\r\n * @param weights - Array of weights.\r\n * @returns The weighted average.\r\n * @throws {RangeError} If arrays are empty, differ in length, or sum of weights is zero.\r\n */\r\nexport function weightedAverage(values: number[], weights: number[]): number {\r\n if (values.length === 0 || weights.length === 0) {\r\n throw new RangeError('Arrays must not be empty')\r\n }\r\n if (values.length !== weights.length) {\r\n throw new RangeError('Values and weights must have the same length')\r\n }\r\n let weightedSum = 0\r\n let weightSum = 0\r\n for (let i = 0; i < values.length; i++) {\r\n weightedSum += values[i]! * weights[i]!\r\n weightSum += weights[i]!\r\n }\r\n if (weightSum === 0) {\r\n throw new RangeError('Sum of weights must be non-zero')\r\n }\r\n return weightedSum / weightSum\r\n}\r\n\r\n/**\r\n * Computes the geometric mean of an array of numbers.\r\n *\r\n * @example geometricMean([4, 9]) // 6\r\n *\r\n * @param values - Array of positive numbers.\r\n * @returns The geometric mean.\r\n * @throws {RangeError} If the array is empty or contains negative values.\r\n */\r\nexport function geometricMean(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute geometric mean of an empty array')\r\n }\r\n for (const v of values) {\r\n if (v < 0) {\r\n throw new RangeError('Values must be non-negative for geometric mean')\r\n }\r\n }\r\n const logSum = values.reduce((acc, v) => (v === 0 ? acc : acc + Math.log(v)), 0)\r\n if (logSum === -Infinity) return 0\r\n return Math.exp(logSum / values.length)\r\n}\r\n\r\n/**\r\n * Computes the number of combinations (n choose k) — selecting k items from n without order.\r\n *\r\n * @example combinations(5, 2) // 10\r\n *\r\n * @param n - Total items.\r\n * @param k - Chosen items.\r\n * @returns The number of combinations.\r\n * @throws {RangeError} If n < k, or either is negative / non-integer.\r\n */\r\nexport function combinations(n: number, k: number): number {\r\n if (!Number.isInteger(n) || !Number.isInteger(k)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (n < 0 || k < 0) {\r\n throw new RangeError('Arguments must be non-negative')\r\n }\r\n if (k > n) {\r\n throw new RangeError('k must not exceed n')\r\n }\r\n if (k === 0 || k === n) return 1\r\n const r = Math.min(k, n - k)\r\n let result = 1\r\n for (let i = 1; i <= r; i++) {\r\n result = (result * (n - r + i)) / i\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Computes the number of permutations — selecting k items from n in order.\r\n *\r\n * @example permutations(5, 2) // 20\r\n *\r\n * @param n - Total items.\r\n * @param k - Chosen items.\r\n * @returns The number of permutations.\r\n * @throws {RangeError} If n < k, or either is negative / non-integer.\r\n */\r\nexport function permutations(n: number, k: number): number {\r\n if (!Number.isInteger(n) || !Number.isInteger(k)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (n < 0 || k < 0) {\r\n throw new RangeError('Arguments must be non-negative')\r\n }\r\n if (k > n) {\r\n throw new RangeError('k must not exceed n')\r\n }\r\n let result = 1\r\n for (let i = n; i > n - k; i--) {\r\n result *= i\r\n }\r\n return result\r\n}\r\n","import { formatCurrency } from '../math/index.js'\r\n\r\nconst WORD_SPLIT_RE = /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|\\d|\\b)|\\d+/g\r\n\r\nfunction splitWords(str: string): string[] {\r\n return str.match(WORD_SPLIT_RE) ?? []\r\n}\r\n\r\n/**\r\n * Capitalizes the first character and lowercases the rest.\r\n */\r\nexport function capitalize(str: string): string {\r\n if (str.length === 0) return str\r\n return str[0]!.toUpperCase() + str.slice(1).toLowerCase()\r\n}\r\n\r\n/**\r\n * Converts a string to camelCase.\r\n */\r\nexport function camelCase(str: string): string {\r\n const words = splitWords(str)\r\n if (words.length === 0) return ''\r\n const [firstWord, ...rest] = words\r\n return firstWord!.toLowerCase() + rest.map(w => w[0]!.toUpperCase() + w.slice(1).toLowerCase()).join('')\r\n}\r\n\r\n/**\r\n * Converts a string to kebab-case.\r\n */\r\nexport function kebabCase(str: string): string {\r\n return splitWords(str).map(w => w.toLowerCase()).join('-')\r\n}\r\n\r\n/**\r\n * Converts a string to snake_case.\r\n */\r\nexport function snakeCase(str: string): string {\r\n return splitWords(str).map(w => w.toLowerCase()).join('_')\r\n}\r\n\r\n/**\r\n * Converts a string to PascalCase.\r\n */\r\nexport function pascalCase(str: string): string {\r\n return splitWords(str).map(w => w[0]!.toUpperCase() + w.slice(1).toLowerCase()).join('')\r\n}\r\n\r\n/**\r\n * Truncates a string to the specified length, appending a suffix (default \"...\").\r\n */\r\nexport function truncate(str: string, maxLength: number, suffix = '...'): string {\r\n if (str.length <= maxLength) return str\r\n return str.slice(0, Math.max(0, maxLength - suffix.length)) + suffix\r\n}\r\n\r\n/**\r\n * Simple string interpolation using {{key}} syntax.\r\n *\r\n * @example template(\"Hello {{name}}\", { name: \"world\" }) // => \"Hello world\"\r\n */\r\nexport function template(str: string, data: Record<string, string | number>): string {\r\n return str.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key: string) => {\r\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') return `{{${key}}}`\r\n const value = data[key]\r\n return value !== undefined ? String(value) : `{{${key}}}`\r\n })\r\n}\r\n\r\n/**\r\n * Generates a UUID v4 string.\r\n * Uses crypto.randomUUID when available, falls back to manual implementation.\r\n */\r\nexport function uuid(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID()\r\n }\r\n const hex = '0123456789abcdef'\r\n const chars: string[] = []\r\n for (let i = 0; i < 36; i++) {\r\n if (i === 8 || i === 13 || i === 18 || i === 23) {\r\n chars.push('-')\r\n } else if (i === 14) {\r\n chars.push('4')\r\n } else if (i === 19) {\r\n chars.push(hex[Math.floor(Math.random() * 4) + 8]!)\r\n } else {\r\n chars.push(hex[Math.floor(Math.random() * 16)]!)\r\n }\r\n }\r\n return chars.join('')\r\n}\r\n\r\n/**\r\n * Generates a short random ID with configurable length and alphabet.\r\n *\r\n * @default size = 21, alphabet = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-\"\r\n */\r\nexport function nanoid(size = 21, alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-'): string {\r\n const len = alphabet.length\r\n const bytes = new Uint8Array(size)\r\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\r\n crypto.getRandomValues(bytes)\r\n } else {\r\n throw new Error('Crypto API unavailable. Cannot generate secure random ID.')\r\n }\r\n let result = ''\r\n for (let i = 0; i < size; i++) {\r\n result += alphabet[bytes[i]! % len]!\r\n }\r\n return result\r\n}\r\n\r\nconst HTML_ESCAPE_MAP: Record<string, string> = {\r\n '&': '&amp;',\r\n '<': '&lt;',\r\n '>': '&gt;',\r\n '\"': '&quot;',\r\n \"'\": '&#39;',\r\n '`': '&#96;',\r\n '/': '&#x2F;',\r\n}\r\n\r\nconst HTML_UNESCAPE_MAP: Record<string, string> = {\r\n '&amp;': '&',\r\n '&lt;': '<',\r\n '&gt;': '>',\r\n '&quot;': '\"',\r\n '&#39;': \"'\",\r\n '&#x27;': \"'\",\r\n '&#96;': '`',\r\n '&#x2F;': '/',\r\n}\r\n\r\n/**\r\n * Escapes HTML special characters (&, <, >, \", ').\r\n */\r\nexport function escapeHtml(str: string): string {\r\n return str.replace(/[&<>\"'`\\/]/g, ch => HTML_ESCAPE_MAP[ch] ?? ch)\r\n}\r\n\r\n/**\r\n * Unescapes common HTML entities.\r\n */\r\nexport function unescapeHtml(str: string): string {\r\n return str.replace(/&(?:amp|lt|gt|quot|#39|#x27|#96|#x2F);/g, entity => HTML_UNESCAPE_MAP[entity] ?? entity)\r\n}\r\n\r\n/**\r\n * Removes whitespace from both ends of a string.\r\n */\r\nexport function trim(str: string): string {\r\n return str.trim()\r\n}\r\n\r\n/**\r\n * Removes whitespace from the start of a string.\r\n */\r\nexport function trimStart(str: string): string {\r\n return str.trimStart()\r\n}\r\n\r\n/**\r\n * Removes whitespace from the end of a string.\r\n */\r\nexport function trimEnd(str: string): string {\r\n return str.trimEnd()\r\n}\r\n\r\n/**\r\n * Pads a string to the given length by adding characters to both sides.\r\n */\r\nexport function pad(str: string, length: number, char = ' '): string {\r\n const totalPadding = Math.max(0, length - str.length)\r\n const leftPad = Math.floor(totalPadding / 2)\r\n const rightPad = totalPadding - leftPad\r\n return char.repeat(leftPad) + str + char.repeat(rightPad)\r\n}\r\n\r\n/**\r\n * Pads the start of a string to the given length.\r\n */\r\nexport function padStart(str: string, length: number, char = ' '): string {\r\n return str.padStart(length, char)\r\n}\r\n\r\n/**\r\n * Pads the end of a string to the given length.\r\n */\r\nexport function padEnd(str: string, length: number, char = ' '): string {\r\n return str.padEnd(length, char)\r\n}\r\n\r\n/**\r\n * Reverses a string.\r\n */\r\nexport function reverse(str: string): string {\r\n return str.split('').reverse().join('')\r\n}\r\n\r\n/**\r\n * Splits a string into words.\r\n */\r\nexport function words(str: string): string[] {\r\n return splitWords(str)\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-friendly slug.\r\n */\r\nexport function slugify(str: string): string {\r\n return str\r\n .toLowerCase()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/[\\s_]+/g, '-')\r\n .replace(/-+/g, '-')\r\n .replace(/^-+/, '')\r\n .replace(/-+$/, '')\r\n}\r\n\r\n/**\r\n * Counts occurrences of a substring in a string.\r\n */\r\nexport function countOccurrences(str: string, substring: string): number {\r\n if (substring.length === 0 || str.length === 0) return 0\r\n let count = 0\r\n let pos = 0\r\n while ((pos = str.indexOf(substring, pos)) !== -1) {\r\n count++\r\n pos += substring.length\r\n }\r\n return count\r\n}\r\n\r\n/**\r\n * Computes the Levenshtein distance between two strings.\r\n * Uses iterative DP with O(min(m,n)) space.\r\n */\r\nexport function levenshtein(a: string, b: string): number {\r\n const an = a.length\r\n const bn = b.length\r\n if (an === 0) return bn\r\n if (bn === 0) return an\r\n if (an < bn) return levenshtein(b, a)\r\n\r\n let prev = new Uint32Array(bn + 1)\r\n let curr = new Uint32Array(bn + 1)\r\n for (let j = 0; j <= bn; j++) prev[j] = j\r\n\r\n for (let i = 1; i <= an; i++) {\r\n curr[0] = i\r\n for (let j = 1; j <= bn; j++) {\r\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\r\n curr[j] = Math.min(\r\n prev[j]! + 1,\r\n curr[j - 1]! + 1,\r\n prev[j - 1]! + cost,\r\n )\r\n }\r\n ;[prev, curr] = [curr, prev]\r\n }\r\n return prev[bn]!\r\n}\r\n\r\n/**\r\n * Performs a simple fuzzy match: checks if all characters of query\r\n * appear in str in order (case-insensitive).\r\n */\r\nexport function fuzzyMatch(str: string, query: string): boolean {\r\n if (query.length === 0) return true\r\n if (str.length === 0) return false\r\n const sl = str.toLowerCase()\r\n const ql = query.toLowerCase()\r\n let si = 0\r\n for (let qi = 0; qi < ql.length; qi++) {\r\n si = sl.indexOf(ql[qi]!, si)\r\n if (si === -1) return false\r\n si++\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Masks parts of a string, useful for data compliance (PDPA/GDPR).\r\n *\r\n * @example maskString('08123456789') // \"0812****789\"\r\n * @example maskString('hello@email.com') // \"h***@e***.com\"\r\n * @example maskString('1234567890', { start: 0, end: 4, char: '#' }) // \"####567890\"\r\n */\r\nexport function maskString(\r\n str: string,\r\n options?: {\r\n start?: number\r\n end?: number\r\n char?: string\r\n },\r\n): string {\r\n if (str.length === 0) return str\r\n const maskChar = options?.char ?? '*'\r\n const start = options?.start ?? Math.ceil(str.length * 0.25)\r\n const end = options?.end ?? Math.floor(str.length * 0.75)\r\n\r\n if (start >= end || start < 0) return str\r\n const clampedStart = Math.max(0, start)\r\n const clampedEnd = Math.min(str.length, end)\r\n return (\r\n str.slice(0, clampedStart) +\r\n maskChar.repeat(clampedEnd - clampedStart) +\r\n str.slice(clampedEnd)\r\n )\r\n}\r\n\r\n// ─── Indonesian Locale Utilities ────────────────────────\r\n\r\nconst SATUAN = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan'] as const\r\nconst BELASAN = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'delapan belas', 'sembilan belas'] as const\r\nconst PULUHAN = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'delapan puluh', 'sembilan puluh'] as const\r\n\r\nfunction _terbilang(n: number): string {\r\n if (n < 0) return 'minus ' + _terbilang(-n)\r\n if (n === 0) return 'nol'\r\n if (n < 10) return SATUAN[n]!\r\n if (n < 20) return n === 10 ? 'sepuluh' : n === 11 ? 'sebelas' : BELASAN[n - 10]!\r\n if (n < 100) {\r\n const pul = Math.floor(n / 10)\r\n const sat = n % 10\r\n return PULUHAN[pul]! + (sat > 0 ? ' ' + SATUAN[sat]! : '')\r\n }\r\n if (n < 1000) {\r\n const ratus = Math.floor(n / 100)\r\n const sis = n % 100\r\n const ratusStr = ratus === 1 ? 'seratus' : SATUAN[ratus]! + ' ratus'\r\n return ratusStr + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000) {\r\n const rib = Math.floor(n / 1000)\r\n const sis = n % 1000\r\n const ribStr = rib === 1 ? 'seribu' : _terbilang(rib) + ' ribu'\r\n return ribStr + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000_000) {\r\n const jut = Math.floor(n / 1_000_000)\r\n const sis = n % 1_000_000\r\n return _terbilang(jut) + ' juta' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000_000_000) {\r\n const mil = Math.floor(n / 1_000_000_000)\r\n const sis = n % 1_000_000_000\r\n return _terbilang(mil) + ' miliar' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n const tril = Math.floor(n / 1_000_000_000_000)\r\n const sis = n % 1_000_000_000_000\r\n return _terbilang(tril) + ' triliun' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n}\r\n\r\n/**\r\n * Converts a number to Indonesian words (terbilang).\r\n *\r\n * @example terbilang(1500000) // \"satu juta lima ratus ribu\"\r\n * @example terbilang(2024) // \"dua ribu dua puluh empat\"\r\n * @example terbilang(11) // \"sebelas\"\r\n * @example terbilang(100) // \"seratus\"\r\n */\r\nexport function terbilang(value: number): string {\r\n if (!Number.isFinite(value)) throw new RangeError('Input must be a finite number')\r\n if (value > Number.MAX_SAFE_INTEGER) throw new RangeError('Input terlalu besar')\r\n return _terbilang(Math.floor(Math.abs(value)))\r\n}\r\n\r\n/**\r\n * Formats a number as Indonesian Rupiah string.\r\n *\r\n * @example formatRupiah(1500000) // \"Rp1.500.000\"\r\n * @example formatRupiah(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n */\r\nexport function formatRupiah(\r\n value: number,\r\n options?: { notation?: 'standard' | 'compact' },\r\n): string {\r\n return formatCurrency(value, { locale: 'id-ID', currency: 'IDR', notation: options?.notation })\r\n}\r\n\r\n/**\r\n * Formats a byte count into a human-readable string.\r\n *\r\n * @example formatBytes(1024) // \"1 KB\"\r\n * @example formatBytes(1536) // \"1.5 KB\"\r\n * @example formatBytes(1048576) // \"1 MB\"\r\n * @example formatBytes(0) // \"0 B\"\r\n */\r\nexport function formatBytes(bytes: number, options?: { decimals?: number }): string {\r\n if (bytes === 0) return '0 B'\r\n if (!Number.isFinite(bytes) || bytes < 0) return '0 B'\r\n const k = 1024\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']\r\n const i = Math.floor(Math.log(bytes) / Math.log(k))\r\n const dm = options?.decimals ?? (i === 0 ? 0 : 1)\r\n const index = Math.min(i, sizes.length - 1)\r\n return parseFloat((bytes / Math.pow(k, index)).toFixed(dm)) + ' ' + sizes[index]\r\n}\r\n\r\n/**\r\n * Generates a random alphanumeric string of the specified length.\r\n *\r\n * @example randomString() // \"a3F8k2...\"\r\n * @example randomString(8) // \"X7j2K9mQ\"\r\n */\r\nexport function randomString(length: number = 16): string {\r\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\r\n const bytes = new Uint8Array(length)\r\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\r\n crypto.getRandomValues(bytes)\r\n } else {\r\n throw new Error('Crypto API unavailable. Cannot generate secure random string.')\r\n }\r\n let result = ''\r\n for (let i = 0; i < length; i++) {\r\n result += chars[bytes[i]! % chars.length]\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a random boolean value.\r\n *\r\n * @example randomBoolean() // true or false\r\n */\r\nexport function randomBoolean(): boolean {\r\n return Math.random() >= 0.5\r\n}\r\n\r\n/**\r\n * Basic English pluralization helper. Adds 's' or 'es' based on simple rules.\r\n *\r\n * @example pluralize(1, 'apple') // \"apple\"\r\n * @example pluralize(3, 'apple') // \"apples\"\r\n * @example pluralize(0, 'box') // \"boxes\"\r\n * @example pluralize(1, 'box') // \"box\"\r\n */\r\nexport function pluralize(count: number, singular: string): string {\r\n if (count === 1) return singular\r\n const last = singular[singular.length - 1]\r\n const lastTwo = singular.slice(-2)\r\n if (last === 's' || last === 'x' || last === 'z' || lastTwo === 'ch' || lastTwo === 'sh') {\r\n return singular + 'es'\r\n }\r\n if (last === 'y' && singular.length > 2 && !'aeiou'.includes(singular[singular.length - 2]!)) {\r\n return singular.slice(0, -1) + 'ies'\r\n }\r\n return singular + 's'\r\n}\r\n\r\n/**\r\n * Removes all HTML tags from a string, including script and style content.\r\n *\r\n * @example stripHtml('<p>Hello <b>world</b></p>') // \"Hello world\"\r\n * @example stripHtml('<script>alert(\"x\")</script>hi') // \"hi\"\r\n */\r\nexport function stripHtml(str: string): string {\r\n return str.replace(/<[^>]*>/g, '')\r\n}\r\n\r\n/**\r\n * Truncates a string by word count, appending a suffix when truncated.\r\n *\r\n * @example truncateWords('hello world foo bar', 2) // \"hello world...\"\r\n * @example truncateWords('hello world foo bar', 2, '…') // \"hello world…\"\r\n */\r\nexport function truncateWords(str: string, count: number, suffix = '...'): string {\r\n const words = str.split(/\\s+/).filter(Boolean)\r\n if (words.length <= count) return str\r\n return words.slice(0, count).join(' ') + suffix\r\n}\r\n\r\n/**\r\n * Checks if a string is a palindrome, ignoring case, spaces, and punctuation.\r\n *\r\n * @example isPalindrome('A man, a plan, a canal: Panama') // true\r\n * @example isPalindrome('race a car') // false\r\n */\r\nexport function isPalindrome(str: string): boolean {\r\n const cleaned = str.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()\r\n if (cleaned.length <= 1) return true\r\n let left = 0\r\n let right = cleaned.length - 1\r\n while (left < right) {\r\n if (cleaned[left] !== cleaned[right]) return false\r\n left++\r\n right--\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Checks if two strings are anagrams, ignoring case and spaces.\r\n *\r\n * @example isAnagram('listen', 'silent') // true\r\n * @example isAnagram('hello', 'world') // false\r\n */\r\nexport function isAnagram(str1: string, str2: string): boolean {\r\n const clean = (s: string) => s.replace(/\\s/g, '').toLowerCase().split('').sort().join('')\r\n return clean(str1) === clean(str2)\r\n}\r\n\r\n/**\r\n * Computes the Dice coefficient similarity between two strings (0-1).\r\n *\r\n * @example similarity('hello', 'hallo') // 0.666...\r\n * @example similarity('abc', 'xyz') // 0\r\n */\r\nexport function similarity(a: string, b: string): number {\r\n if (a === b) return 1\r\n if (a.length < 2 || b.length < 2) return 0\r\n const bigrams = new Map<string, number>()\r\n for (let i = 0; i < a.length - 1; i++) {\r\n const bg = a.slice(i, i + 2)\r\n bigrams.set(bg, (bigrams.get(bg) ?? 0) + 1)\r\n }\r\n let intersection = 0\r\n for (let i = 0; i < b.length - 1; i++) {\r\n const bg = b.slice(i, i + 2)\r\n const count = bigrams.get(bg) ?? 0\r\n if (count > 0) {\r\n bigrams.set(bg, count - 1)\r\n intersection++\r\n }\r\n }\r\n return (2 * intersection) / (a.length + b.length - 2)\r\n}\r\n\r\n/**\r\n * Removes common leading whitespace from each line in a multi-line string.\r\n *\r\n * @example dedent(' hello\\n world') // \"hello\\nworld\"\r\n * @example dedent(' foo\\n bar') // \"foo\\n bar\"\r\n */\r\nexport function dedent(str: string): string {\r\n const lines = str.split('\\n')\r\n if (lines.length === 0) return str\r\n const indent = lines.reduce<number | null>((min, line) => {\r\n if (line.trim().length === 0) return min\r\n const leading = line.match(/^[ \\t]*/)?.[0]?.length ?? 0\r\n return min === null ? leading : Math.min(min, leading)\r\n }, null)\r\n if (indent === null || indent === 0) return str\r\n return lines.map(line => line.slice(indent)).join('\\n')\r\n}\r\n\r\n/**\r\n * Counts the number of words in a string, handling multiple spaces and punctuation.\r\n *\r\n * @example wordCount('hello world') // 2\r\n * @example wordCount(' hello world! ') // 2\r\n */\r\nexport function wordCount(str: string): number {\r\n const match = str.match(/[a-zA-Z0-9]+(?:['\\u2019][a-zA-Z]+)?/g)\r\n return match ? match.length : 0\r\n}\r\n\r\n/**\r\n * Swaps the case of each character in a string (upper to lower, lower to upper).\r\n *\r\n * @example swapCase('Hello World') // \"hELLO wORLD\"\r\n * @example swapCase('ABC123') // \"abc123\"\r\n */\r\nexport function swapCase(str: string): string {\r\n let result = ''\r\n for (let i = 0; i < str.length; i++) {\r\n const ch = str[i]!\r\n if (ch >= 'a' && ch <= 'z') {\r\n result += ch.toUpperCase()\r\n } else if (ch >= 'A' && ch <= 'Z') {\r\n result += ch.toLowerCase()\r\n } else {\r\n result += ch\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Converts a string to COBOL / SCREAMING_SNAKE_CASE.\r\n *\r\n * @example toCobolCase('helloWorld') // \"HELLO_WORLD\"\r\n * @example toCobolCase('getUserByID') // \"GET_USER_BY_ID\"\r\n */\r\nexport function toCobolCase(str: string): string {\r\n const words = splitWords(str)\r\n return words.map(w => w.toUpperCase()).join('_')\r\n}\r\n\r\n/**\r\n * Counts character frequency in a string.\r\n *\r\n * @example charCount('hello') // { h: 1, e: 1, l: 2, o: 1 }\r\n * @example charCount('aabb') // { a: 2, b: 2 }\r\n */\r\nexport function charCount(str: string): Record<string, number> {\r\n const result: Record<string, number> = {}\r\n for (let i = 0; i < str.length; i++) {\r\n const ch = str[i]!\r\n result[ch] = (result[ch] ?? 0) + 1\r\n }\r\n return result\r\n}\r\n"],"mappings":"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","names":["words","sis"]}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Checks if a value is a string.
3
+ */
4
+ declare function isString(value: unknown): value is string;
5
+ /**
6
+ * Checks if a value is a number (not NaN, not Infinity).
7
+ */
8
+ declare function isNumber(value: unknown): value is number;
9
+ /**
10
+ * Checks if a value is a boolean.
11
+ */
12
+ declare function isBoolean(value: unknown): value is boolean;
13
+ /**
14
+ * Checks if a value is a plain object (not null, not array, typeof 'object').
15
+ */
16
+ declare function isObject(value: unknown): value is Record<string, unknown>;
17
+ /**
18
+ * Checks if a value is an array.
19
+ */
20
+ declare function isArray(value: unknown): value is unknown[];
21
+ /**
22
+ * Checks if a value is a function.
23
+ */
24
+ declare function isFunction(value: unknown): value is (...args: unknown[]) => unknown;
25
+ /**
26
+ * Checks if a value is a Date.
27
+ */
28
+ declare function isDate(value: unknown): value is Date;
29
+ /**
30
+ * Checks if a value is a RegExp.
31
+ */
32
+ declare function isRegExp(value: unknown): value is RegExp;
33
+ /**
34
+ * Checks if a value is a Map.
35
+ */
36
+ declare function isMap(value: unknown): value is Map<unknown, unknown>;
37
+ /**
38
+ * Checks if a value is a Set.
39
+ */
40
+ declare function isSet(value: unknown): value is Set<unknown>;
41
+ /**
42
+ * Checks if a value is a Promise.
43
+ */
44
+ declare function isPromise(value: unknown): value is Promise<unknown>;
45
+ /**
46
+ * Checks if a value is null.
47
+ */
48
+ declare function isNull(value: unknown): value is null;
49
+ /**
50
+ * Checks if a value is undefined.
51
+ */
52
+ declare function isUndefined(value: unknown): value is undefined;
53
+ /**
54
+ * Checks if a value is null or undefined.
55
+ */
56
+ declare function isNil(value: unknown): value is null | undefined;
57
+ /**
58
+ * Checks if a value is empty.
59
+ * Works for strings, arrays, objects, Map, and Set.
60
+ */
61
+ declare function isEmpty(value: unknown): boolean;
62
+ /**
63
+ * Asserts that a value is defined (not null or undefined).
64
+ * Throws if the value is null or undefined.
65
+ */
66
+ declare function assertDefined<T>(value: T, message?: string): asserts value is NonNullable<T>;
67
+ /**
68
+ * Asserts that a value matches a type guard. Throws if it doesn't.
69
+ */
70
+ declare function assertType<T>(value: unknown, guard: (v: unknown) => v is T, message?: string): asserts value is T;
71
+ /**
72
+ * Wraps a value in an array if it is not already one.
73
+ */
74
+ declare function ensureArray<T>(value: T | T[]): T[];
75
+ /** Alias for ensureArray. Wraps a value in an array if it is not already one. */
76
+ declare const castArray: typeof ensureArray;
77
+ /**
78
+ * Returns a string representation of the value's type.
79
+ *
80
+ * Possible values: "string", "number", "boolean", "array", "object", "function",
81
+ * "date", "regexp", "map", "set", "promise", "null", "undefined", "nan", "infinity"
82
+ */
83
+ declare function getType(value: unknown): string;
84
+
85
+ export { assertDefined, assertType, castArray, ensureArray, getType, isArray, isBoolean, isDate, isEmpty, isFunction, isMap, isNil, isNull, isNumber, isObject, isPromise, isRegExp, isSet, isString, isUndefined };
@@ -0,0 +1,107 @@
1
+ // src/type/index.ts
2
+ function isString(value) {
3
+ return typeof value === "string";
4
+ }
5
+ function isNumber(value) {
6
+ return typeof value === "number" && Number.isFinite(value);
7
+ }
8
+ function isBoolean(value) {
9
+ return typeof value === "boolean";
10
+ }
11
+ function isObject(value) {
12
+ return value !== null && typeof value === "object" && !Array.isArray(value);
13
+ }
14
+ function isArray(value) {
15
+ return Array.isArray(value);
16
+ }
17
+ function isFunction(value) {
18
+ return typeof value === "function";
19
+ }
20
+ function isDate(value) {
21
+ return value instanceof Date && !Number.isNaN(value.getTime());
22
+ }
23
+ function isRegExp(value) {
24
+ return value instanceof RegExp;
25
+ }
26
+ function isMap(value) {
27
+ return value instanceof Map;
28
+ }
29
+ function isSet(value) {
30
+ return value instanceof Set;
31
+ }
32
+ function isPromise(value) {
33
+ return value instanceof Promise;
34
+ }
35
+ function isNull(value) {
36
+ return value === null;
37
+ }
38
+ function isUndefined(value) {
39
+ return value === void 0;
40
+ }
41
+ function isNil(value) {
42
+ return value === null || value === void 0;
43
+ }
44
+ function isEmpty(value) {
45
+ if (value === null || value === void 0) return true;
46
+ if (typeof value === "string") return value.length === 0;
47
+ if (Array.isArray(value)) return value.length === 0;
48
+ if (value instanceof Map || value instanceof Set) return value.size === 0;
49
+ if (typeof value === "object") return Object.keys(value).length === 0;
50
+ return false;
51
+ }
52
+ function assertDefined(value, message) {
53
+ if (value === null || value === void 0) {
54
+ throw new Error(message ?? "Expected value to be defined");
55
+ }
56
+ }
57
+ function assertType(value, guard, message) {
58
+ if (!guard(value)) {
59
+ throw new Error(message ?? "Value does not match expected type");
60
+ }
61
+ }
62
+ function ensureArray(value) {
63
+ return Array.isArray(value) ? value : [value];
64
+ }
65
+ var castArray = ensureArray;
66
+ function getType(value) {
67
+ if (value === null) return "null";
68
+ if (value === void 0) return "undefined";
69
+ if (typeof value === "string") return "string";
70
+ if (typeof value === "boolean") return "boolean";
71
+ if (typeof value === "function") return "function";
72
+ if (typeof value === "number") {
73
+ if (Number.isNaN(value)) return "nan";
74
+ if (!Number.isFinite(value)) return "infinity";
75
+ return "number";
76
+ }
77
+ if (Array.isArray(value)) return "array";
78
+ if (value instanceof Date) return "date";
79
+ if (value instanceof RegExp) return "regexp";
80
+ if (value instanceof Map) return "map";
81
+ if (value instanceof Set) return "set";
82
+ if (value instanceof Promise) return "promise";
83
+ return "object";
84
+ }
85
+ export {
86
+ assertDefined,
87
+ assertType,
88
+ castArray,
89
+ ensureArray,
90
+ getType,
91
+ isArray,
92
+ isBoolean,
93
+ isDate,
94
+ isEmpty,
95
+ isFunction,
96
+ isMap,
97
+ isNil,
98
+ isNull,
99
+ isNumber,
100
+ isObject,
101
+ isPromise,
102
+ isRegExp,
103
+ isSet,
104
+ isString,
105
+ isUndefined
106
+ };
107
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/type/index.ts"],"sourcesContent":["/**\r\n * Checks if a value is a string.\r\n */\r\nexport function isString(value: unknown): value is string {\r\n return typeof value === 'string'\r\n}\r\n\r\n/**\r\n * Checks if a value is a number (not NaN, not Infinity).\r\n */\r\nexport function isNumber(value: unknown): value is number {\r\n return typeof value === 'number' && Number.isFinite(value)\r\n}\r\n\r\n/**\r\n * Checks if a value is a boolean.\r\n */\r\nexport function isBoolean(value: unknown): value is boolean {\r\n return typeof value === 'boolean'\r\n}\r\n\r\n/**\r\n * Checks if a value is a plain object (not null, not array, typeof 'object').\r\n */\r\nexport function isObject(value: unknown): value is Record<string, unknown> {\r\n return value !== null && typeof value === 'object' && !Array.isArray(value)\r\n}\r\n\r\n/**\r\n * Checks if a value is an array.\r\n */\r\nexport function isArray(value: unknown): value is unknown[] {\r\n return Array.isArray(value)\r\n}\r\n\r\n/**\r\n * Checks if a value is a function.\r\n */\r\nexport function isFunction(value: unknown): value is (...args: unknown[]) => unknown {\r\n return typeof value === 'function'\r\n}\r\n\r\n/**\r\n * Checks if a value is a Date.\r\n */\r\nexport function isDate(value: unknown): value is Date {\r\n return value instanceof Date && !Number.isNaN(value.getTime())\r\n}\r\n\r\n/**\r\n * Checks if a value is a RegExp.\r\n */\r\nexport function isRegExp(value: unknown): value is RegExp {\r\n return value instanceof RegExp\r\n}\r\n\r\n/**\r\n * Checks if a value is a Map.\r\n */\r\nexport function isMap(value: unknown): value is Map<unknown, unknown> {\r\n return value instanceof Map\r\n}\r\n\r\n/**\r\n * Checks if a value is a Set.\r\n */\r\nexport function isSet(value: unknown): value is Set<unknown> {\r\n return value instanceof Set\r\n}\r\n\r\n/**\r\n * Checks if a value is a Promise.\r\n */\r\nexport function isPromise(value: unknown): value is Promise<unknown> {\r\n return value instanceof Promise\r\n}\r\n\r\n/**\r\n * Checks if a value is null.\r\n */\r\nexport function isNull(value: unknown): value is null {\r\n return value === null\r\n}\r\n\r\n/**\r\n * Checks if a value is undefined.\r\n */\r\nexport function isUndefined(value: unknown): value is undefined {\r\n return value === undefined\r\n}\r\n\r\n/**\r\n * Checks if a value is null or undefined.\r\n */\r\nexport function isNil(value: unknown): value is null | undefined {\r\n return value === null || value === undefined\r\n}\r\n\r\n/**\r\n * Checks if a value is empty.\r\n * Works for strings, arrays, objects, Map, and Set.\r\n */\r\nexport function isEmpty(value: unknown): boolean {\r\n if (value === null || value === undefined) return true\r\n if (typeof value === 'string') return value.length === 0\r\n if (Array.isArray(value)) return value.length === 0\r\n if (value instanceof Map || value instanceof Set) return value.size === 0\r\n if (typeof value === 'object') return Object.keys(value as Record<string, unknown>).length === 0\r\n return false\r\n}\r\n\r\n/**\r\n * Asserts that a value is defined (not null or undefined).\r\n * Throws if the value is null or undefined.\r\n */\r\nexport function assertDefined<T>(value: T, message?: string): asserts value is NonNullable<T> {\r\n if (value === null || value === undefined) {\r\n throw new Error(message ?? 'Expected value to be defined')\r\n }\r\n}\r\n\r\n/**\r\n * Asserts that a value matches a type guard. Throws if it doesn't.\r\n */\r\nexport function assertType<T>(value: unknown, guard: (v: unknown) => v is T, message?: string): asserts value is T {\r\n if (!guard(value)) {\r\n throw new Error(message ?? 'Value does not match expected type')\r\n }\r\n}\r\n\r\n/**\r\n * Wraps a value in an array if it is not already one.\r\n */\r\nexport function ensureArray<T>(value: T | T[]): T[] {\r\n return Array.isArray(value) ? value : [value]\r\n}\r\n\r\n/** Alias for ensureArray. Wraps a value in an array if it is not already one. */\r\nexport const castArray = ensureArray\r\n\r\n/**\r\n * Returns a string representation of the value's type.\r\n *\r\n * Possible values: \"string\", \"number\", \"boolean\", \"array\", \"object\", \"function\",\r\n * \"date\", \"regexp\", \"map\", \"set\", \"promise\", \"null\", \"undefined\", \"nan\", \"infinity\"\r\n */\r\nexport function getType(value: unknown): string {\r\n if (value === null) return 'null'\r\n if (value === undefined) return 'undefined'\r\n if (typeof value === 'string') return 'string'\r\n if (typeof value === 'boolean') return 'boolean'\r\n if (typeof value === 'function') return 'function'\r\n if (typeof value === 'number') {\r\n if (Number.isNaN(value)) return 'nan'\r\n if (!Number.isFinite(value)) return 'infinity'\r\n return 'number'\r\n }\r\n if (Array.isArray(value)) return 'array'\r\n if (value instanceof Date) return 'date'\r\n if (value instanceof RegExp) return 'regexp'\r\n if (value instanceof Map) return 'map'\r\n if (value instanceof Set) return 'set'\r\n if (value instanceof Promise) return 'promise'\r\n return 'object'\r\n}\r\n"],"mappings":";AAGO,SAAS,SAAS,OAAiC;AACxD,SAAO,OAAO,UAAU;AAC1B;AAKO,SAAS,SAAS,OAAiC;AACxD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAC3D;AAKO,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU;AAC1B;AAKO,SAAS,SAAS,OAAkD;AACzE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAKO,SAAS,QAAQ,OAAoC;AAC1D,SAAO,MAAM,QAAQ,KAAK;AAC5B;AAKO,SAAS,WAAW,OAA0D;AACnF,SAAO,OAAO,UAAU;AAC1B;AAKO,SAAS,OAAO,OAA+B;AACpD,SAAO,iBAAiB,QAAQ,CAAC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC/D;AAKO,SAAS,SAAS,OAAiC;AACxD,SAAO,iBAAiB;AAC1B;AAKO,SAAS,MAAM,OAAgD;AACpE,SAAO,iBAAiB;AAC1B;AAKO,SAAS,MAAM,OAAuC;AAC3D,SAAO,iBAAiB;AAC1B;AAKO,SAAS,UAAU,OAA2C;AACnE,SAAO,iBAAiB;AAC1B;AAKO,SAAS,OAAO,OAA+B;AACpD,SAAO,UAAU;AACnB;AAKO,SAAS,YAAY,OAAoC;AAC9D,SAAO,UAAU;AACnB;AAKO,SAAS,MAAM,OAA2C;AAC/D,SAAO,UAAU,QAAQ,UAAU;AACrC;AAMO,SAAS,QAAQ,OAAyB;AAC/C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,WAAW;AACvD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,WAAW;AAClD,MAAI,iBAAiB,OAAO,iBAAiB,IAAK,QAAO,MAAM,SAAS;AACxE,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK,KAAgC,EAAE,WAAW;AAC/F,SAAO;AACT;AAMO,SAAS,cAAiB,OAAU,SAAmD;AAC5F,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,UAAM,IAAI,MAAM,WAAW,8BAA8B;AAAA,EAC3D;AACF;AAKO,SAAS,WAAc,OAAgB,OAA+B,SAAsC;AACjH,MAAI,CAAC,MAAM,KAAK,GAAG;AACjB,UAAM,IAAI,MAAM,WAAW,oCAAoC;AAAA,EACjE;AACF;AAKO,SAAS,YAAe,OAAqB;AAClD,SAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;AAGO,IAAM,YAAY;AAQlB,SAAS,QAAQ,OAAwB;AAC9C,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,UAAW,QAAO;AACvC,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAChC,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,MAAI,iBAAiB,KAAM,QAAO;AAClC,MAAI,iBAAiB,OAAQ,QAAO;AACpC,MAAI,iBAAiB,IAAK,QAAO;AACjC,MAAI,iBAAiB,IAAK,QAAO;AACjC,MAAI,iBAAiB,QAAS,QAAO;AACrC,SAAO;AACT;","names":[]}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Validates an Indonesian NIK (Nomor Induk Kependudukan / Resident Identity Number).
3
+ *
4
+ * A valid NIK:
5
+ * - Must be exactly 16 digits
6
+ * - Structure: PP CC DD DDMMYY SSSS
7
+ * - PP: Province code (2 digits)
8
+ * - CC: City code (2 digits)
9
+ * - DD: District code (2 digits)
10
+ * - DDMMYY: Birth date (6 digits; for women the day is incremented by 40)
11
+ * - SSSS: Serial number (4 digits)
12
+ * - The birth date must correspond to a valid calendar date
13
+ *
14
+ * @param value - The NIK string (digits only or with dots)
15
+ * @returns `true` if the value is a valid NIK
16
+ *
17
+ * @example isNIK('3201010203940001') // => true (male, born 2 March 1994)
18
+ * @example isNIK('3201015203940001') // => true (female, born 12 March 1994)
19
+ * @example isNIK('1234567890123456') // => false (invalid birth date)
20
+ * @example isNIK('320101') // => false (too short)
21
+ */
22
+ declare function isNIK(value: string): boolean;
23
+
24
+ /**
25
+ * Validates an Indonesian NPWP (Nomor Pokok Wajib Pajak / Tax Identification Number).
26
+ *
27
+ * A valid NPWP:
28
+ * - Must be 15 or 16 digits (formatted: `XX.XXX.XXX.X-XXX.XXX` or plain digits)
29
+ * - The last digit is a checksum computed from the preceding digits
30
+ *
31
+ * The checksum uses a weighted-sum algorithm with a repeating weight pattern of
32
+ * `[3, 7, 1]`. The computed checksum must equal the last digit.
33
+ *
34
+ * @param value - The NPWP string (formatted with dots & dash, or plain digits)
35
+ * @returns `true` if the value is a valid NPWP
36
+ *
37
+ * @example isNPWP('12.345.678.9-012.344') // => true
38
+ * @example isNPWP('123456789012344') // => true (plain digits)
39
+ * @example isNPWP('12.345.678.9-012.345') // => false (invalid checksum)
40
+ */
41
+ declare function isNPWP(value: string): boolean;
42
+
43
+ /**
44
+ * Validates a phone number.
45
+ *
46
+ * For Indonesian numbers (`country = 'id'`):
47
+ * - Accepted formats: `08xx…`, `+628xx…`, `628xx…`
48
+ * - Must start with a valid operator prefix:
49
+ * 0811-0819, 0821-0829, 0851-0859, 0877-0879, 0895-0899
50
+ * - 10–13 digits after the country code
51
+ *
52
+ * For generic numbers (`country = 'any'`):
53
+ * - Any string with 10–15 digits is accepted
54
+ *
55
+ * @param value - The phone number string
56
+ * @param country - Country to validate against (`'id'` or `'any'`; default `'id'`)
57
+ * @returns `true` if the value is a valid phone number
58
+ *
59
+ * @example isPhone('08123456789') // => true
60
+ * @example isPhone('+628123456789') // => true
61
+ * @example isPhone('628123456789') // => true
62
+ * @example isPhone('081234567') // => false (too short)
63
+ * @example isPhone('089123456789') // => false (invalid prefix 91)
64
+ */
65
+ declare function isPhone(value: string, country?: 'id' | 'any'): boolean;
66
+
67
+ /**
68
+ * RFC‑compliant email address validation.
69
+ *
70
+ * Validation rules:
71
+ * - Total length ≤ 254 characters
72
+ * - Local part ≤ 64 characters; supports quoted strings (including escaped
73
+ * characters), unquoted letters / digits / `!#$%&'*+/=?^_`{|}~-`, and dots
74
+ * (no leading, trailing, or consecutive dots)
75
+ * - Domain part ≤ 255 characters; valid DNS labels separated by dots, each
76
+ * label ≤ 63 characters, no leading/trailing hyphens, at least two labels
77
+ *
78
+ * @param value - The email address string
79
+ * @returns `true` if the value is a syntactically valid email address
80
+ *
81
+ * @example isEmail('user@example.com') // => true
82
+ * @example isEmail('user.name+tag@example.co.id') // => true
83
+ * @example isEmail('"quoted@local"@example.com') // => true
84
+ * @example isEmail('not-an-email') // => false
85
+ */
86
+ declare function isEmail(value: string): boolean;
87
+
88
+ /**
89
+ * Validates a URL.
90
+ *
91
+ * A valid URL:
92
+ * - Must use the `http` or `https` protocol
93
+ * - Must have a valid hostname (DNS name, IPv4, IPv6 literal, or `localhost`)
94
+ * - May include an optional port, path, query string, and fragment
95
+ *
96
+ * @param value - The URL string
97
+ * @returns `true` if the value is a valid http/https URL
98
+ *
99
+ * @example isURL('https://example.com') // => true
100
+ * @example isURL('http://example.com:8080/path?q=1#f') // => true
101
+ * @example isURL('ftp://example.com') // => false
102
+ * @example isURL('not-a-url') // => false
103
+ */
104
+ declare function isURL(value: string): boolean;
105
+
106
+ /**
107
+ * Parse data dari NIK (Nomor Induk Kependudukan).
108
+ *
109
+ * @example parseNIK('3201010203940001')
110
+ * // { nik: '3201010203940001', valid: true, gender: 'LAKI-LAKI', birthDate: Date(1994-03-02), province: 'JAWA BARAT', city: 'BOGOR', district: 'CIAWI' }
111
+ *
112
+ * @example parseNIK('3201015203940001')
113
+ * // { nik: '3201015203940001', valid: true, gender: 'PEREMPUAN', birthDate: Date(1994-03-12), province: 'JAWA BARAT', ... }
114
+ */
115
+ declare function parseNIK(value: string): NIKInfo;
116
+ interface NIKInfo {
117
+ nik: string;
118
+ valid: boolean;
119
+ gender: 'LAKI-LAKI' | 'PEREMPUAN' | null;
120
+ birthDate: Date | null;
121
+ province: string | null;
122
+ provinceCode: string | null;
123
+ city: string | null;
124
+ cityCode: string | null;
125
+ district: string | null;
126
+ districtCode: string | null;
127
+ uniqueCode: string | null;
128
+ }
129
+
130
+ /**
131
+ * Validasi Plat Nomor Kendaraan Bermotor Indonesia.
132
+ *
133
+ * Format: [KODE_DEPAN] [ANGKA] [KODE_BELAKANG]
134
+ * - Kode depan: 1-2 huruf (kode daerah)
135
+ * - Angka: 1-4 digit
136
+ * - Kode belakang: 1-3 huruf (opsional)
137
+ *
138
+ * @example isPlatNomor('B 1234 CD') // true (Jakarta)
139
+ * @example isPlatNomor('AB 5678 XYZ') // true
140
+ * @example isPlatNomor('B 1 A') // true
141
+ * @example isPlatNomor('INVALID') // false
142
+ */
143
+ declare function isPlatNomor(value: string): boolean;
144
+
145
+ /**
146
+ * Validasi kode pos Indonesia.
147
+ * Kode pos Indonesia terdiri dari 5 digit angka.
148
+ *
149
+ * @example isKodepos('16110') // true (Bogor)
150
+ * @example isKodepos('12345') // true
151
+ * @example isKodepos('1234') // false (kurang)
152
+ * @example isKodepos('ABCDE') // false
153
+ */
154
+ declare function isKodepos(value: string): boolean;
155
+
156
+ /**
157
+ * Validasi nomor rekening bank Indonesia.
158
+ * Format: 8-16 digit angka (tergantung bank).
159
+ *
160
+ * @example isNoRekening('1234567890') // true (10 digit)
161
+ * @example isNoRekening('12345678') // true (8 digit - minimum)
162
+ * @example isNoRekening('1234567890123456') // true (16 digit - maksimum)
163
+ * @example isNoRekening('12345') // false (kurang dari 8 digit)
164
+ */
165
+ declare function isNoRekening(value: string): boolean;
166
+
167
+ /**
168
+ * Validasi Nomor SIM Indonesia.
169
+ * Format: 12 digit angka (2 digit golongan + 6 digit tanggal lahir + 4 digit nomor seri).
170
+ *
171
+ * @example isNoSIM('123456789012') // true (12 digit)
172
+ * @example isNoSIM('12345') // false
173
+ */
174
+ declare function isNoSIM(value: string): boolean;
175
+
176
+ /**
177
+ * Validasi nomor paspor Indonesia.
178
+ * Format: 2 huruf + 7 digit angka (total 9 karakter).
179
+ *
180
+ * @example isPassport('AB1234567') // true
181
+ * @example isPassport('123456789') // false (harus ada huruf)
182
+ */
183
+ declare function isPassport(value: string): boolean;
184
+
185
+ /**
186
+ * Validasi nomor BPJS Kesehatan Indonesia.
187
+ * Format: 13 digit angka.
188
+ *
189
+ * @example isNoBPJS('1234567890123') // true
190
+ * @example isNoBPJS('12345') // false
191
+ */
192
+ declare function isNoBPJS(value: string): boolean;
193
+
194
+ /**
195
+ * Validasi Nomor Kartu Keluarga (KK) Indonesia.
196
+ * Format: 16 digit angka.
197
+ *
198
+ * @example isNoKK('1234567890123456') // true
199
+ * @example isNoKK('12345') // false
200
+ */
201
+ declare function isNoKK(value: string): boolean;
202
+
203
+ export { type NIKInfo, isEmail, isKodepos, isNIK, isNPWP, isNoBPJS, isNoKK, isNoRekening, isNoSIM, isPassport, isPhone, isPlatNomor, isURL, parseNIK };