superjs-core 0.3.3 → 0.3.5
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.
- package/dist/async/index.d.ts +84 -1
- package/dist/async/index.js +151 -0
- package/dist/async/index.js.map +1 -1
- package/dist/collection/index.d.ts +7 -1
- package/dist/collection/index.js +55 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/date/index.d.ts +71 -1
- package/dist/date/index.js +121 -1
- package/dist/date/index.js.map +1 -1
- package/dist/error/index.d.ts +148 -0
- package/dist/error/index.js +115 -0
- package/dist/error/index.js.map +1 -0
- package/dist/index-BgG21uJC.d.ts +166 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +506 -0
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.js +214 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/transports.d.ts +1 -0
- package/dist/logger/transports.js +122 -0
- package/dist/logger/transports.js.map +1 -0
- package/dist/math/index.d.ts +59 -1
- package/dist/math/index.js +69 -0
- package/dist/math/index.js.map +1 -1
- package/dist/string/index.d.ts +41 -1
- package/dist/string/index.js +117 -0
- package/dist/string/index.js.map +1 -1
- package/dist/validation/index.d.ts +106 -0
- package/dist/validation/index.js +183 -0
- package/dist/validation/index.js.map +1 -0
- package/package.json +17 -1
package/dist/string/index.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
// src/math/index.ts
|
|
2
|
+
function formatCurrency(value, options) {
|
|
3
|
+
const locale = options?.locale ?? "id-ID";
|
|
4
|
+
const currency = options?.currency ?? "IDR";
|
|
5
|
+
const notation = options?.notation ?? "standard";
|
|
6
|
+
try {
|
|
7
|
+
return new Intl.NumberFormat(locale, {
|
|
8
|
+
style: "currency",
|
|
9
|
+
currency,
|
|
10
|
+
notation,
|
|
11
|
+
minimumFractionDigits: 0,
|
|
12
|
+
maximumFractionDigits: 2
|
|
13
|
+
}).format(value);
|
|
14
|
+
} catch {
|
|
15
|
+
return `${currency} ${value.toLocaleString(locale)}`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
1
19
|
// src/string/index.ts
|
|
2
20
|
var WORD_SPLIT_RE = /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|\d|\b)|\d+/g;
|
|
3
21
|
function splitWords(str) {
|
|
@@ -120,12 +138,110 @@ function countOccurrences(str, substring) {
|
|
|
120
138
|
}
|
|
121
139
|
return count;
|
|
122
140
|
}
|
|
141
|
+
function levenshtein(a, b) {
|
|
142
|
+
const an = a.length;
|
|
143
|
+
const bn = b.length;
|
|
144
|
+
if (an === 0) return bn;
|
|
145
|
+
if (bn === 0) return an;
|
|
146
|
+
if (an < bn) return levenshtein(b, a);
|
|
147
|
+
let prev = new Uint32Array(bn + 1);
|
|
148
|
+
let curr = new Uint32Array(bn + 1);
|
|
149
|
+
for (let j = 0; j <= bn; j++) prev[j] = j;
|
|
150
|
+
for (let i = 1; i <= an; i++) {
|
|
151
|
+
curr[0] = i;
|
|
152
|
+
for (let j = 1; j <= bn; j++) {
|
|
153
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
154
|
+
curr[j] = Math.min(
|
|
155
|
+
prev[j] + 1,
|
|
156
|
+
curr[j - 1] + 1,
|
|
157
|
+
prev[j - 1] + cost
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
;
|
|
161
|
+
[prev, curr] = [curr, prev];
|
|
162
|
+
}
|
|
163
|
+
return prev[bn];
|
|
164
|
+
}
|
|
165
|
+
function fuzzyMatch(str, query) {
|
|
166
|
+
if (query.length === 0) return true;
|
|
167
|
+
if (str.length === 0) return false;
|
|
168
|
+
const sl = str.toLowerCase();
|
|
169
|
+
const ql = query.toLowerCase();
|
|
170
|
+
let si = 0;
|
|
171
|
+
for (let qi = 0; qi < ql.length; qi++) {
|
|
172
|
+
si = sl.indexOf(ql[qi], si);
|
|
173
|
+
if (si === -1) return false;
|
|
174
|
+
si++;
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
function maskString(str, options) {
|
|
179
|
+
if (str.length === 0) return str;
|
|
180
|
+
const maskChar = options?.char ?? "*";
|
|
181
|
+
const start = options?.start ?? Math.ceil(str.length * 0.25);
|
|
182
|
+
const end = options?.end ?? Math.floor(str.length * 0.75);
|
|
183
|
+
if (start >= end || start < 0) return str;
|
|
184
|
+
const clampedStart = Math.max(0, start);
|
|
185
|
+
const clampedEnd = Math.min(str.length, end);
|
|
186
|
+
return str.slice(0, clampedStart) + maskChar.repeat(clampedEnd - clampedStart) + str.slice(clampedEnd);
|
|
187
|
+
}
|
|
188
|
+
var SATUAN = ["", "satu", "dua", "tiga", "empat", "lima", "enam", "tujuh", "delapan", "sembilan"];
|
|
189
|
+
var BELASAN = ["sepuluh", "sebelas", "dua belas", "tiga belas", "empat belas", "lima belas", "enam belas", "tujuh belas", "delapan belas", "sembilan belas"];
|
|
190
|
+
var PULUHAN = ["", "", "dua puluh", "tiga puluh", "empat puluh", "lima puluh", "enam puluh", "tujuh puluh", "delapan puluh", "sembilan puluh"];
|
|
191
|
+
function _terbilang(n) {
|
|
192
|
+
if (n < 0) return "minus " + _terbilang(-n);
|
|
193
|
+
if (n === 0) return "nol";
|
|
194
|
+
if (n < 10) return SATUAN[n];
|
|
195
|
+
if (n < 20) return n === 10 ? "sepuluh" : n === 11 ? "sebelas" : BELASAN[n - 10];
|
|
196
|
+
if (n < 100) {
|
|
197
|
+
const pul = Math.floor(n / 10);
|
|
198
|
+
const sat = n % 10;
|
|
199
|
+
return PULUHAN[pul] + (sat > 0 ? " " + SATUAN[sat] : "");
|
|
200
|
+
}
|
|
201
|
+
if (n < 1e3) {
|
|
202
|
+
const ratus = Math.floor(n / 100);
|
|
203
|
+
const sis2 = n % 100;
|
|
204
|
+
const ratusStr = ratus === 1 ? "seratus" : SATUAN[ratus] + " ratus";
|
|
205
|
+
return ratusStr + (sis2 > 0 ? " " + _terbilang(sis2) : "");
|
|
206
|
+
}
|
|
207
|
+
if (n < 1e6) {
|
|
208
|
+
const rib = Math.floor(n / 1e3);
|
|
209
|
+
const sis2 = n % 1e3;
|
|
210
|
+
const ribStr = rib === 1 ? "seribu" : _terbilang(rib) + " ribu";
|
|
211
|
+
return ribStr + (sis2 > 0 ? " " + _terbilang(sis2) : "");
|
|
212
|
+
}
|
|
213
|
+
if (n < 1e9) {
|
|
214
|
+
const jut = Math.floor(n / 1e6);
|
|
215
|
+
const sis2 = n % 1e6;
|
|
216
|
+
return _terbilang(jut) + " juta" + (sis2 > 0 ? " " + _terbilang(sis2) : "");
|
|
217
|
+
}
|
|
218
|
+
if (n < 1e12) {
|
|
219
|
+
const mil = Math.floor(n / 1e9);
|
|
220
|
+
const sis2 = n % 1e9;
|
|
221
|
+
return _terbilang(mil) + " miliar" + (sis2 > 0 ? " " + _terbilang(sis2) : "");
|
|
222
|
+
}
|
|
223
|
+
const tril = Math.floor(n / 1e12);
|
|
224
|
+
const sis = n % 1e12;
|
|
225
|
+
return _terbilang(tril) + " triliun" + (sis > 0 ? " " + _terbilang(sis) : "");
|
|
226
|
+
}
|
|
227
|
+
function terbilang(value) {
|
|
228
|
+
if (!Number.isFinite(value)) throw new RangeError("Input must be a finite number");
|
|
229
|
+
if (value > Number.MAX_SAFE_INTEGER) throw new RangeError("Input terlalu besar");
|
|
230
|
+
return _terbilang(Math.floor(Math.abs(value)));
|
|
231
|
+
}
|
|
232
|
+
function formatRupiah(value, options) {
|
|
233
|
+
return formatCurrency(value, { locale: "id-ID", currency: "IDR", notation: options?.notation });
|
|
234
|
+
}
|
|
123
235
|
export {
|
|
124
236
|
camelCase,
|
|
125
237
|
capitalize,
|
|
126
238
|
countOccurrences,
|
|
127
239
|
escapeHtml,
|
|
240
|
+
formatRupiah,
|
|
241
|
+
fuzzyMatch,
|
|
128
242
|
kebabCase,
|
|
243
|
+
levenshtein,
|
|
244
|
+
maskString,
|
|
129
245
|
nanoid,
|
|
130
246
|
pad,
|
|
131
247
|
padEnd,
|
|
@@ -135,6 +251,7 @@ export {
|
|
|
135
251
|
slugify,
|
|
136
252
|
snakeCase,
|
|
137
253
|
template,
|
|
254
|
+
terbilang,
|
|
138
255
|
trim,
|
|
139
256
|
trimEnd,
|
|
140
257
|
trimStart,
|
package/dist/string/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/string/index.ts"],"sourcesContent":["const 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 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 let result = ''\r\n for (let i = 0; i < size; i++) {\r\n result += alphabet[Math.floor(Math.random() * len)]!\r\n }\r\n return result\r\n}\r\n\r\nconst HTML_ESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n}\r\n\r\nconst HTML_UNESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '"': '\"',\r\n ''': \"'\",\r\n ''': \"'\",\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);/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"],"mappings":"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","names":["words"]}
|
|
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","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 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 let result = ''\r\n for (let i = 0; i < size; i++) {\r\n result += alphabet[Math.floor(Math.random() * len)]!\r\n }\r\n return result\r\n}\r\n\r\nconst HTML_ESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n}\r\n\r\nconst HTML_UNESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '"': '\"',\r\n ''': \"'\",\r\n ''': \"'\",\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);/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"],"mappings":";AAkTO,SAAS,eACd,OACA,SACQ;AACR,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,WAAW,SAAS,YAAY;AAEtC,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,MACnC,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,uBAAuB;AAAA,MACvB,uBAAuB;AAAA,IACzB,CAAC,EAAE,OAAO,KAAK;AAAA,EACjB,QAAQ;AACN,WAAO,GAAG,QAAQ,IAAI,MAAM,eAAe,MAAM,CAAC;AAAA,EACpD;AACF;;;ACnUA,IAAM,gBAAgB;AAEtB,SAAS,WAAW,KAAuB;AACzC,SAAO,IAAI,MAAM,aAAa,KAAK,CAAC;AACtC;AAKO,SAAS,WAAW,KAAqB;AAC9C,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,SAAO,IAAI,CAAC,EAAG,YAAY,IAAI,IAAI,MAAM,CAAC,EAAE,YAAY;AAC1D;AAKO,SAAS,UAAU,KAAqB;AAC7C,QAAMA,SAAQ,WAAW,GAAG;AAC5B,MAAIA,OAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,CAAC,WAAW,GAAG,IAAI,IAAIA;AAC7B,SAAO,UAAW,YAAY,IAAI,KAAK,IAAI,OAAK,EAAE,CAAC,EAAG,YAAY,IAAI,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,EAAE,KAAK,EAAE;AACzG;AAKO,SAAS,UAAU,KAAqB;AAC7C,SAAO,WAAW,GAAG,EAAE,IAAI,OAAK,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG;AAC3D;AAKO,SAAS,UAAU,KAAqB;AAC7C,SAAO,WAAW,GAAG,EAAE,IAAI,OAAK,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG;AAC3D;AAKO,SAAS,WAAW,KAAqB;AAC9C,SAAO,WAAW,GAAG,EAAE,IAAI,OAAK,EAAE,CAAC,EAAG,YAAY,IAAI,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,EAAE,KAAK,EAAE;AACzF;AAKO,SAAS,SAAS,KAAa,WAAmB,SAAS,OAAe;AAC/E,MAAI,IAAI,UAAU,UAAW,QAAO;AACpC,SAAO,IAAI,MAAM,GAAG,KAAK,IAAI,GAAG,YAAY,OAAO,MAAM,CAAC,IAAI;AAChE;AAOO,SAAS,SAAS,KAAa,MAA+C;AACnF,SAAO,IAAI,QAAQ,kBAAkB,CAAC,GAAG,QAAgB;AACvD,UAAM,QAAQ,KAAK,GAAG;AACtB,WAAO,UAAU,SAAY,OAAO,KAAK,IAAI,KAAK,GAAG;AAAA,EACvD,CAAC;AACH;AAMO,SAAS,OAAe;AAC7B,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,QAAM,MAAM;AACZ,QAAM,QAAkB,CAAC;AACzB,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,QAAI,MAAM,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI;AAC/C,YAAM,KAAK,GAAG;AAAA,IAChB,WAAW,MAAM,IAAI;AACnB,YAAM,KAAK,GAAG;AAAA,IAChB,WAAW,MAAM,IAAI;AACnB,YAAM,KAAK,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,CAAE;AAAA,IACpD,OAAO;AACL,YAAM,KAAK,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,CAAC,CAAE;AAAA,IACjD;AAAA,EACF;AACA,SAAO,MAAM,KAAK,EAAE;AACtB;AAOO,SAAS,OAAO,OAAO,IAAI,WAAW,oEAA4E;AACvH,QAAM,MAAM,SAAS;AACrB,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,cAAU,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG,CAAC;AAAA,EACpD;AACA,SAAO;AACT;AAEA,IAAM,kBAA0C;AAAA,EAC9C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEA,IAAM,oBAA4C;AAAA,EAChD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AACZ;AAKO,SAAS,WAAW,KAAqB;AAC9C,SAAO,IAAI,QAAQ,YAAY,QAAM,gBAAgB,EAAE,KAAK,EAAE;AAChE;AAKO,SAAS,aAAa,KAAqB;AAChD,SAAO,IAAI,QAAQ,kCAAkC,YAAU,kBAAkB,MAAM,KAAK,MAAM;AACpG;AAKO,SAAS,KAAK,KAAqB;AACxC,SAAO,IAAI,KAAK;AAClB;AAKO,SAAS,UAAU,KAAqB;AAC7C,SAAO,IAAI,UAAU;AACvB;AAKO,SAAS,QAAQ,KAAqB;AAC3C,SAAO,IAAI,QAAQ;AACrB;AAKO,SAAS,IAAI,KAAa,QAAgB,OAAO,KAAa;AACnE,QAAM,eAAe,KAAK,IAAI,GAAG,SAAS,IAAI,MAAM;AACpD,QAAM,UAAU,KAAK,MAAM,eAAe,CAAC;AAC3C,QAAM,WAAW,eAAe;AAChC,SAAO,KAAK,OAAO,OAAO,IAAI,MAAM,KAAK,OAAO,QAAQ;AAC1D;AAKO,SAAS,SAAS,KAAa,QAAgB,OAAO,KAAa;AACxE,SAAO,IAAI,SAAS,QAAQ,IAAI;AAClC;AAKO,SAAS,OAAO,KAAa,QAAgB,OAAO,KAAa;AACtE,SAAO,IAAI,OAAO,QAAQ,IAAI;AAChC;AAKO,SAAS,QAAQ,KAAqB;AAC3C,SAAO,IAAI,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;AACxC;AAKO,SAAS,MAAM,KAAuB;AAC3C,SAAO,WAAW,GAAG;AACvB;AAKO,SAAS,QAAQ,KAAqB;AAC3C,SAAO,IACJ,YAAY,EACZ,QAAQ,aAAa,EAAE,EACvB,QAAQ,WAAW,GAAG,EACtB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE,EACjB,QAAQ,OAAO,EAAE;AACtB;AAKO,SAAS,iBAAiB,KAAa,WAA2B;AACvE,MAAI,UAAU,WAAW,KAAK,IAAI,WAAW,EAAG,QAAO;AACvD,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,UAAQ,MAAM,IAAI,QAAQ,WAAW,GAAG,OAAO,IAAI;AACjD;AACA,WAAO,UAAU;AAAA,EACnB;AACA,SAAO;AACT;AAMO,SAAS,YAAY,GAAW,GAAmB;AACxD,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,EAAE;AACb,MAAI,OAAO,EAAG,QAAO;AACrB,MAAI,OAAO,EAAG,QAAO;AACrB,MAAI,KAAK,GAAI,QAAO,YAAY,GAAG,CAAC;AAEpC,MAAI,OAAO,IAAI,YAAY,KAAK,CAAC;AACjC,MAAI,OAAO,IAAI,YAAY,KAAK,CAAC;AACjC,WAAS,IAAI,GAAG,KAAK,IAAI,IAAK,MAAK,CAAC,IAAI;AAExC,WAAS,IAAI,GAAG,KAAK,IAAI,KAAK;AAC5B,SAAK,CAAC,IAAI;AACV,aAAS,IAAI,GAAG,KAAK,IAAI,KAAK;AAC5B,YAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI;AACzC,WAAK,CAAC,IAAI,KAAK;AAAA,QACb,KAAK,CAAC,IAAK;AAAA,QACX,KAAK,IAAI,CAAC,IAAK;AAAA,QACf,KAAK,IAAI,CAAC,IAAK;AAAA,MACjB;AAAA,IACF;AACA;AAAC,KAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI;AAAA,EAC7B;AACA,SAAO,KAAK,EAAE;AAChB;AAMO,SAAS,WAAW,KAAa,OAAwB;AAC9D,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,QAAM,KAAK,IAAI,YAAY;AAC3B,QAAM,KAAK,MAAM,YAAY;AAC7B,MAAI,KAAK;AACT,WAAS,KAAK,GAAG,KAAK,GAAG,QAAQ,MAAM;AACrC,SAAK,GAAG,QAAQ,GAAG,EAAE,GAAI,EAAE;AAC3B,QAAI,OAAO,GAAI,QAAO;AACtB;AAAA,EACF;AACA,SAAO;AACT;AASO,SAAS,WACd,KACA,SAKQ;AACR,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,QAAQ,SAAS,SAAS,KAAK,KAAK,IAAI,SAAS,IAAI;AAC3D,QAAM,MAAM,SAAS,OAAO,KAAK,MAAM,IAAI,SAAS,IAAI;AAExD,MAAI,SAAS,OAAO,QAAQ,EAAG,QAAO;AACtC,QAAM,eAAe,KAAK,IAAI,GAAG,KAAK;AACtC,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ,GAAG;AAC3C,SACE,IAAI,MAAM,GAAG,YAAY,IACzB,SAAS,OAAO,aAAa,YAAY,IACzC,IAAI,MAAM,UAAU;AAExB;AAIA,IAAM,SAAS,CAAC,IAAI,QAAQ,OAAO,QAAQ,SAAS,QAAQ,QAAQ,SAAS,WAAW,UAAU;AAClG,IAAM,UAAU,CAAC,WAAW,WAAW,aAAa,cAAc,eAAe,cAAc,cAAc,eAAe,iBAAiB,gBAAgB;AAC7J,IAAM,UAAU,CAAC,IAAI,IAAI,aAAa,cAAc,eAAe,cAAc,cAAc,eAAe,iBAAiB,gBAAgB;AAE/I,SAAS,WAAW,GAAmB;AACrC,MAAI,IAAI,EAAG,QAAO,WAAW,WAAW,CAAC,CAAC;AAC1C,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,IAAI,GAAI,QAAO,OAAO,CAAC;AAC3B,MAAI,IAAI,GAAI,QAAO,MAAM,KAAK,YAAY,MAAM,KAAK,YAAY,QAAQ,IAAI,EAAE;AAC/E,MAAI,IAAI,KAAK;AACX,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE;AAC7B,UAAM,MAAM,IAAI;AAChB,WAAO,QAAQ,GAAG,KAAM,MAAM,IAAI,MAAM,OAAO,GAAG,IAAK;AAAA,EACzD;AACA,MAAI,IAAI,KAAM;AACZ,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,UAAMC,OAAM,IAAI;AAChB,UAAM,WAAW,UAAU,IAAI,YAAY,OAAO,KAAK,IAAK;AAC5D,WAAO,YAAYA,OAAM,IAAI,MAAM,WAAWA,IAAG,IAAI;AAAA,EACvD;AACA,MAAI,IAAI,KAAW;AACjB,UAAM,MAAM,KAAK,MAAM,IAAI,GAAI;AAC/B,UAAMA,OAAM,IAAI;AAChB,UAAM,SAAS,QAAQ,IAAI,WAAW,WAAW,GAAG,IAAI;AACxD,WAAO,UAAUA,OAAM,IAAI,MAAM,WAAWA,IAAG,IAAI;AAAA,EACrD;AACA,MAAI,IAAI,KAAe;AACrB,UAAM,MAAM,KAAK,MAAM,IAAI,GAAS;AACpC,UAAMA,OAAM,IAAI;AAChB,WAAO,WAAW,GAAG,IAAI,WAAWA,OAAM,IAAI,MAAM,WAAWA,IAAG,IAAI;AAAA,EACxE;AACA,MAAI,IAAI,MAAmB;AACzB,UAAM,MAAM,KAAK,MAAM,IAAI,GAAa;AACxC,UAAMA,OAAM,IAAI;AAChB,WAAO,WAAW,GAAG,IAAI,aAAaA,OAAM,IAAI,MAAM,WAAWA,IAAG,IAAI;AAAA,EAC1E;AACA,QAAM,OAAO,KAAK,MAAM,IAAI,IAAiB;AAC7C,QAAM,MAAM,IAAI;AAChB,SAAO,WAAW,IAAI,IAAI,cAAc,MAAM,IAAI,MAAM,WAAW,GAAG,IAAI;AAC5E;AAUO,SAAS,UAAU,OAAuB;AAC/C,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,WAAW,+BAA+B;AACjF,MAAI,QAAQ,OAAO,iBAAkB,OAAM,IAAI,WAAW,qBAAqB;AAC/E,SAAO,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,CAAC;AAC/C;AAQO,SAAS,aACd,OACA,SACQ;AACR,SAAO,eAAe,OAAO,EAAE,QAAQ,SAAS,UAAU,OAAO,UAAU,SAAS,SAAS,CAAC;AAChG;","names":["words","sis"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
export { isEmail, isNIK, isNPWP, isPhone, isURL };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// src/validation/isNIK.ts
|
|
2
|
+
function isNIK(value) {
|
|
3
|
+
const digits = value.replace(/\D/g, "");
|
|
4
|
+
if (digits.length !== 16) return false;
|
|
5
|
+
const rawDay = Number.parseInt(digits.slice(6, 8), 10);
|
|
6
|
+
const month = Number.parseInt(digits.slice(8, 10), 10);
|
|
7
|
+
const year = Number.parseInt(digits.slice(10, 12), 10);
|
|
8
|
+
if (rawDay < 1 || rawDay > 71) return false;
|
|
9
|
+
if (month < 1 || month > 12) return false;
|
|
10
|
+
let day = rawDay;
|
|
11
|
+
if (day >= 41) day -= 40;
|
|
12
|
+
const fullYear = year < 70 ? 2e3 + year : 1900 + year;
|
|
13
|
+
const date = new Date(fullYear, month - 1, day);
|
|
14
|
+
return date.getFullYear() === fullYear && date.getMonth() === month - 1 && date.getDate() === day;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/validation/isNPWP.ts
|
|
18
|
+
function isNPWP(value) {
|
|
19
|
+
const digits = value.replace(/\D/g, "");
|
|
20
|
+
if (digits.length !== 15 && digits.length !== 16) return false;
|
|
21
|
+
const nums = [];
|
|
22
|
+
for (let i = 0; i < digits.length; i++) {
|
|
23
|
+
nums.push(Number.parseInt(digits[i], 10));
|
|
24
|
+
}
|
|
25
|
+
const checkDigit = nums[nums.length - 1];
|
|
26
|
+
let sum = 0;
|
|
27
|
+
for (let i = 0; i < nums.length - 1; i++) {
|
|
28
|
+
sum += nums[i] * [3, 7, 1][i % 3];
|
|
29
|
+
}
|
|
30
|
+
const computed = (11 - sum % 11) % 10;
|
|
31
|
+
return computed === checkDigit;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/validation/isPhone.ts
|
|
35
|
+
var INDONESIAN_PREFIXES = [
|
|
36
|
+
[11, 19],
|
|
37
|
+
[21, 29],
|
|
38
|
+
[51, 59],
|
|
39
|
+
[77, 79],
|
|
40
|
+
[95, 99]
|
|
41
|
+
];
|
|
42
|
+
function isValidIndonesianPrefix(prefix) {
|
|
43
|
+
for (const [min, max] of INDONESIAN_PREFIXES) {
|
|
44
|
+
if (prefix >= min && prefix <= max) return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
function isPhone(value, country = "id") {
|
|
49
|
+
const digits = value.replace(/\D/g, "");
|
|
50
|
+
if (country === "any") {
|
|
51
|
+
return digits.length >= 10 && digits.length <= 15;
|
|
52
|
+
}
|
|
53
|
+
if (digits.length < 10) return false;
|
|
54
|
+
let normalized;
|
|
55
|
+
if (digits.startsWith("62")) {
|
|
56
|
+
normalized = digits.slice(2);
|
|
57
|
+
} else if (digits.startsWith("0")) {
|
|
58
|
+
normalized = digits.slice(1);
|
|
59
|
+
} else {
|
|
60
|
+
normalized = digits;
|
|
61
|
+
}
|
|
62
|
+
if (normalized.length < 10 || normalized.length > 13) return false;
|
|
63
|
+
if (!normalized.startsWith("8")) return false;
|
|
64
|
+
const prefix = Number.parseInt(normalized.slice(1, 3), 10);
|
|
65
|
+
return isValidIndonesianPrefix(prefix);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/validation/isEmail.ts
|
|
69
|
+
var LOCAL_SPECIAL = "!#$%&'*+/=?^_`{|}~-";
|
|
70
|
+
function isQuotedLocalPart(local) {
|
|
71
|
+
if (local.length < 2) return false;
|
|
72
|
+
let i = 1;
|
|
73
|
+
while (i < local.length - 1) {
|
|
74
|
+
const ch = local[i];
|
|
75
|
+
if (ch === "\\") {
|
|
76
|
+
i++;
|
|
77
|
+
if (i >= local.length - 1) return false;
|
|
78
|
+
} else if (ch === '"') {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
i++;
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
function isUnquotedLocalPart(local) {
|
|
86
|
+
if (local.length === 0 || local.startsWith(".") || local.endsWith(".")) return false;
|
|
87
|
+
for (let i = 0; i < local.length; i++) {
|
|
88
|
+
const ch = local[i];
|
|
89
|
+
if (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "." || LOCAL_SPECIAL.includes(ch)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
for (let i = 1; i < local.length; i++) {
|
|
95
|
+
if (local[i] === "." && local[i - 1] === ".") return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
function isValidDomain(domain) {
|
|
100
|
+
if (domain.length === 0 || domain.startsWith(".") || domain.endsWith(".")) return false;
|
|
101
|
+
const labels = domain.split(".");
|
|
102
|
+
if (labels.length < 2) return false;
|
|
103
|
+
for (const label of labels) {
|
|
104
|
+
if (label.length === 0 || label.length > 63) return false;
|
|
105
|
+
if (label.startsWith("-") || label.endsWith("-")) return false;
|
|
106
|
+
for (let i = 0; i < label.length; i++) {
|
|
107
|
+
const ch = label[i];
|
|
108
|
+
if (!(ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "-")) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
function isEmail(value) {
|
|
116
|
+
if (value.length > 254) return false;
|
|
117
|
+
const atIndex = value.lastIndexOf("@");
|
|
118
|
+
if (atIndex < 1 || atIndex === value.length - 1) return false;
|
|
119
|
+
const localPart = value.slice(0, atIndex);
|
|
120
|
+
const domainPart = value.slice(atIndex + 1);
|
|
121
|
+
if (localPart.length > 64) return false;
|
|
122
|
+
if (domainPart.length > 255) return false;
|
|
123
|
+
if (localPart.startsWith('"') && localPart.endsWith('"')) {
|
|
124
|
+
if (!isQuotedLocalPart(localPart)) return false;
|
|
125
|
+
} else {
|
|
126
|
+
if (!isUnquotedLocalPart(localPart)) return false;
|
|
127
|
+
}
|
|
128
|
+
return isValidDomain(domainPart);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/validation/isURL.ts
|
|
132
|
+
var IPV4_OCTET = /^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
|
|
133
|
+
function isValidIPv4(hostname) {
|
|
134
|
+
const octets = hostname.split(".");
|
|
135
|
+
if (octets.length !== 4) return false;
|
|
136
|
+
return octets.every((octet) => IPV4_OCTET.test(octet));
|
|
137
|
+
}
|
|
138
|
+
function isValidDNSHostname(hostname) {
|
|
139
|
+
if (hostname.startsWith(".") || hostname.endsWith(".")) return false;
|
|
140
|
+
const labels = hostname.split(".");
|
|
141
|
+
if (labels.length < 2) return false;
|
|
142
|
+
for (const label of labels) {
|
|
143
|
+
if (label.length === 0 || label.length > 63) return false;
|
|
144
|
+
if (label.startsWith("-") || label.endsWith("-")) return false;
|
|
145
|
+
for (let i = 0; i < label.length; i++) {
|
|
146
|
+
const ch = label[i];
|
|
147
|
+
if (!(ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "-")) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
function isValidHostname(hostname) {
|
|
155
|
+
if (hostname.length === 0) return false;
|
|
156
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) {
|
|
157
|
+
return hostname.length > 2;
|
|
158
|
+
}
|
|
159
|
+
if (/^\d/.test(hostname) || /\d$/.test(hostname)) {
|
|
160
|
+
if (isValidIPv4(hostname)) return true;
|
|
161
|
+
}
|
|
162
|
+
if (hostname === "localhost") return true;
|
|
163
|
+
return isValidDNSHostname(hostname);
|
|
164
|
+
}
|
|
165
|
+
function isURL(value) {
|
|
166
|
+
try {
|
|
167
|
+
const url = new URL(value);
|
|
168
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return isValidHostname(url.hostname);
|
|
172
|
+
} catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
isEmail,
|
|
178
|
+
isNIK,
|
|
179
|
+
isNPWP,
|
|
180
|
+
isPhone,
|
|
181
|
+
isURL
|
|
182
|
+
};
|
|
183
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/validation/isNIK.ts","../../src/validation/isNPWP.ts","../../src/validation/isPhone.ts","../../src/validation/isEmail.ts","../../src/validation/isURL.ts"],"sourcesContent":["/**\n * Validates an Indonesian NIK (Nomor Induk Kependudukan / Resident Identity Number).\n *\n * A valid NIK:\n * - Must be exactly 16 digits\n * - Structure: PP CC DD DDMMYY SSSS\n * - PP: Province code (2 digits)\n * - CC: City code (2 digits)\n * - DD: District code (2 digits)\n * - DDMMYY: Birth date (6 digits; for women the day is incremented by 40)\n * - SSSS: Serial number (4 digits)\n * - The birth date must correspond to a valid calendar date\n *\n * @param value - The NIK string (digits only or with dots)\n * @returns `true` if the value is a valid NIK\n *\n * @example isNIK('3201010203940001') // => true (male, born 2 March 1994)\n * @example isNIK('3201015203940001') // => true (female, born 12 March 1994)\n * @example isNIK('1234567890123456') // => false (invalid birth date)\n * @example isNIK('320101') // => false (too short)\n */\nexport function isNIK(value: string): boolean {\n const digits = value.replace(/\\D/g, '')\n if (digits.length !== 16) return false\n\n const rawDay = Number.parseInt(digits.slice(6, 8), 10)\n const month = Number.parseInt(digits.slice(8, 10), 10)\n const year = Number.parseInt(digits.slice(10, 12), 10)\n\n if (rawDay < 1 || rawDay > 71) return false\n if (month < 1 || month > 12) return false\n\n let day = rawDay\n if (day >= 41) day -= 40\n\n const fullYear = year < 70 ? 2000 + year : 1900 + year\n const date = new Date(fullYear, month - 1, day)\n\n return (\n date.getFullYear() === fullYear &&\n date.getMonth() === month - 1 &&\n date.getDate() === day\n )\n}\n","/**\n * Validates an Indonesian NPWP (Nomor Pokok Wajib Pajak / Tax Identification Number).\n *\n * A valid NPWP:\n * - Must be 15 or 16 digits (formatted: `XX.XXX.XXX.X-XXX.XXX` or plain digits)\n * - The last digit is a checksum computed from the preceding digits\n *\n * The checksum uses a weighted-sum algorithm with a repeating weight pattern of\n * `[3, 7, 1]`. The computed checksum must equal the last digit.\n *\n * @param value - The NPWP string (formatted with dots & dash, or plain digits)\n * @returns `true` if the value is a valid NPWP\n *\n * @example isNPWP('12.345.678.9-012.344') // => true\n * @example isNPWP('123456789012344') // => true (plain digits)\n * @example isNPWP('12.345.678.9-012.345') // => false (invalid checksum)\n */\nexport function isNPWP(value: string): boolean {\n const digits = value.replace(/\\D/g, '')\n if (digits.length !== 15 && digits.length !== 16) return false\n\n const nums: number[] = []\n for (let i = 0; i < digits.length; i++) {\n nums.push(Number.parseInt(digits[i]!, 10))\n }\n\n const checkDigit = nums[nums.length - 1]!\n\n let sum = 0\n for (let i = 0; i < nums.length - 1; i++) {\n sum += nums[i]! * [3, 7, 1][i % 3]!\n }\n\n const computed = (11 - (sum % 11)) % 10\n return computed === checkDigit\n}\n","const INDONESIAN_PREFIXES: ReadonlyArray<[number, number]> = [\n [11, 19],\n [21, 29],\n [51, 59],\n [77, 79],\n [95, 99],\n]\n\nfunction isValidIndonesianPrefix(prefix: number): boolean {\n for (const [min, max] of INDONESIAN_PREFIXES) {\n if (prefix >= min && prefix <= max) return true\n }\n return false\n}\n\n/**\n * Validates a phone number.\n *\n * For Indonesian numbers (`country = 'id'`):\n * - Accepted formats: `08xx…`, `+628xx…`, `628xx…`\n * - Must start with a valid operator prefix:\n * 0811-0819, 0821-0829, 0851-0859, 0877-0879, 0895-0899\n * - 10–13 digits after the country code\n *\n * For generic numbers (`country = 'any'`):\n * - Any string with 10–15 digits is accepted\n *\n * @param value - The phone number string\n * @param country - Country to validate against (`'id'` or `'any'`; default `'id'`)\n * @returns `true` if the value is a valid phone number\n *\n * @example isPhone('08123456789') // => true\n * @example isPhone('+628123456789') // => true\n * @example isPhone('628123456789') // => true\n * @example isPhone('081234567') // => false (too short)\n * @example isPhone('089123456789') // => false (invalid prefix 91)\n */\nexport function isPhone(value: string, country: 'id' | 'any' = 'id'): boolean {\n const digits = value.replace(/\\D/g, '')\n\n if (country === 'any') {\n return digits.length >= 10 && digits.length <= 15\n }\n\n if (digits.length < 10) return false\n\n let normalized: string\n if (digits.startsWith('62')) {\n normalized = digits.slice(2)\n } else if (digits.startsWith('0')) {\n normalized = digits.slice(1)\n } else {\n normalized = digits\n }\n\n if (normalized.length < 10 || normalized.length > 13) return false\n if (!normalized.startsWith('8')) return false\n\n const prefix = Number.parseInt(normalized.slice(1, 3), 10)\n return isValidIndonesianPrefix(prefix)\n}\n","const LOCAL_SPECIAL = \"!#$%&'*+/=?^_`{|}~-\"\n\nfunction isQuotedLocalPart(local: string): boolean {\n if (local.length < 2) return false\n let i = 1\n while (i < local.length - 1) {\n const ch = local[i]!\n if (ch === '\\\\') {\n i++\n if (i >= local.length - 1) return false\n } else if (ch === '\"') {\n return false\n }\n i++\n }\n return true\n}\n\nfunction isUnquotedLocalPart(local: string): boolean {\n if (local.length === 0 || local.startsWith('.') || local.endsWith('.')) return false\n\n for (let i = 0; i < local.length; i++) {\n const ch = local[i]!\n if (\n (ch >= 'a' && ch <= 'z') ||\n (ch >= 'A' && ch <= 'Z') ||\n (ch >= '0' && ch <= '9') ||\n ch === '.' ||\n LOCAL_SPECIAL.includes(ch)\n ) {\n continue\n }\n return false\n }\n\n for (let i = 1; i < local.length; i++) {\n if (local[i] === '.' && local[i - 1] === '.') return false\n }\n\n return true\n}\n\nfunction isValidDomain(domain: string): boolean {\n if (domain.length === 0 || domain.startsWith('.') || domain.endsWith('.')) return false\n\n const labels = domain.split('.')\n if (labels.length < 2) return false\n\n for (const label of labels) {\n if (label.length === 0 || label.length > 63) return false\n if (label.startsWith('-') || label.endsWith('-')) return false\n\n for (let i = 0; i < label.length; i++) {\n const ch = label[i]!\n if (\n !(\n (ch >= 'a' && ch <= 'z') ||\n (ch >= 'A' && ch <= 'Z') ||\n (ch >= '0' && ch <= '9') ||\n ch === '-'\n )\n ) {\n return false\n }\n }\n }\n\n return true\n}\n\n/**\n * RFC‑compliant email address validation.\n *\n * Validation rules:\n * - Total length ≤ 254 characters\n * - Local part ≤ 64 characters; supports quoted strings (including escaped\n * characters), unquoted letters / digits / `!#$%&'*+/=?^_`{|}~-`, and dots\n * (no leading, trailing, or consecutive dots)\n * - Domain part ≤ 255 characters; valid DNS labels separated by dots, each\n * label ≤ 63 characters, no leading/trailing hyphens, at least two labels\n *\n * @param value - The email address string\n * @returns `true` if the value is a syntactically valid email address\n *\n * @example isEmail('user@example.com') // => true\n * @example isEmail('user.name+tag@example.co.id') // => true\n * @example isEmail('\"quoted@local\"@example.com') // => true\n * @example isEmail('not-an-email') // => false\n */\nexport function isEmail(value: string): boolean {\n if (value.length > 254) return false\n\n const atIndex = value.lastIndexOf('@')\n if (atIndex < 1 || atIndex === value.length - 1) return false\n\n const localPart = value.slice(0, atIndex)\n const domainPart = value.slice(atIndex + 1)\n\n if (localPart.length > 64) return false\n if (domainPart.length > 255) return false\n\n if (localPart.startsWith('\"') && localPart.endsWith('\"')) {\n if (!isQuotedLocalPart(localPart)) return false\n } else {\n if (!isUnquotedLocalPart(localPart)) return false\n }\n\n return isValidDomain(domainPart)\n}\n","const IPV4_OCTET = /^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/\n\nfunction isValidIPv4(hostname: string): boolean {\n const octets = hostname.split('.')\n if (octets.length !== 4) return false\n return octets.every(octet => IPV4_OCTET.test(octet))\n}\n\nfunction isValidDNSHostname(hostname: string): boolean {\n if (hostname.startsWith('.') || hostname.endsWith('.')) return false\n\n const labels = hostname.split('.')\n if (labels.length < 2) return false\n\n for (const label of labels) {\n if (label.length === 0 || label.length > 63) return false\n if (label.startsWith('-') || label.endsWith('-')) return false\n\n for (let i = 0; i < label.length; i++) {\n const ch = label[i]!\n if (\n !(\n (ch >= 'a' && ch <= 'z') ||\n (ch >= 'A' && ch <= 'Z') ||\n (ch >= '0' && ch <= '9') ||\n ch === '-'\n )\n ) {\n return false\n }\n }\n }\n\n return true\n}\n\nfunction isValidHostname(hostname: string): boolean {\n if (hostname.length === 0) return false\n\n // IPv6 literal\n if (hostname.startsWith('[') && hostname.endsWith(']')) {\n return hostname.length > 2\n }\n\n // IPv4\n if (/^\\d/.test(hostname) || /\\d$/.test(hostname)) {\n if (isValidIPv4(hostname)) return true\n }\n\n // localhost\n if (hostname === 'localhost') return true\n\n // DNS hostname\n return isValidDNSHostname(hostname)\n}\n\n/**\n * Validates a URL.\n *\n * A valid URL:\n * - Must use the `http` or `https` protocol\n * - Must have a valid hostname (DNS name, IPv4, IPv6 literal, or `localhost`)\n * - May include an optional port, path, query string, and fragment\n *\n * @param value - The URL string\n * @returns `true` if the value is a valid http/https URL\n *\n * @example isURL('https://example.com') // => true\n * @example isURL('http://example.com:8080/path?q=1#f') // => true\n * @example isURL('ftp://example.com') // => false\n * @example isURL('not-a-url') // => false\n */\nexport function isURL(value: string): boolean {\n try {\n const url = new URL(value)\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n return false\n }\n\n return isValidHostname(url.hostname)\n } catch {\n return false\n }\n}\n"],"mappings":";AAqBO,SAAS,MAAM,OAAwB;AAC5C,QAAM,SAAS,MAAM,QAAQ,OAAO,EAAE;AACtC,MAAI,OAAO,WAAW,GAAI,QAAO;AAEjC,QAAM,SAAS,OAAO,SAAS,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE;AACrD,QAAM,QAAQ,OAAO,SAAS,OAAO,MAAM,GAAG,EAAE,GAAG,EAAE;AACrD,QAAM,OAAO,OAAO,SAAS,OAAO,MAAM,IAAI,EAAE,GAAG,EAAE;AAErD,MAAI,SAAS,KAAK,SAAS,GAAI,QAAO;AACtC,MAAI,QAAQ,KAAK,QAAQ,GAAI,QAAO;AAEpC,MAAI,MAAM;AACV,MAAI,OAAO,GAAI,QAAO;AAEtB,QAAM,WAAW,OAAO,KAAK,MAAO,OAAO,OAAO;AAClD,QAAM,OAAO,IAAI,KAAK,UAAU,QAAQ,GAAG,GAAG;AAE9C,SACE,KAAK,YAAY,MAAM,YACvB,KAAK,SAAS,MAAM,QAAQ,KAC5B,KAAK,QAAQ,MAAM;AAEvB;;;AC1BO,SAAS,OAAO,OAAwB;AAC7C,QAAM,SAAS,MAAM,QAAQ,OAAO,EAAE;AACtC,MAAI,OAAO,WAAW,MAAM,OAAO,WAAW,GAAI,QAAO;AAEzD,QAAM,OAAiB,CAAC;AACxB,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,SAAK,KAAK,OAAO,SAAS,OAAO,CAAC,GAAI,EAAE,CAAC;AAAA,EAC3C;AAEA,QAAM,aAAa,KAAK,KAAK,SAAS,CAAC;AAEvC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,WAAO,KAAK,CAAC,IAAK,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC;AAAA,EACnC;AAEA,QAAM,YAAY,KAAM,MAAM,MAAO;AACrC,SAAO,aAAa;AACtB;;;ACnCA,IAAM,sBAAuD;AAAA,EAC3D,CAAC,IAAI,EAAE;AAAA,EACP,CAAC,IAAI,EAAE;AAAA,EACP,CAAC,IAAI,EAAE;AAAA,EACP,CAAC,IAAI,EAAE;AAAA,EACP,CAAC,IAAI,EAAE;AACT;AAEA,SAAS,wBAAwB,QAAyB;AACxD,aAAW,CAAC,KAAK,GAAG,KAAK,qBAAqB;AAC5C,QAAI,UAAU,OAAO,UAAU,IAAK,QAAO;AAAA,EAC7C;AACA,SAAO;AACT;AAwBO,SAAS,QAAQ,OAAe,UAAwB,MAAe;AAC5E,QAAM,SAAS,MAAM,QAAQ,OAAO,EAAE;AAEtC,MAAI,YAAY,OAAO;AACrB,WAAO,OAAO,UAAU,MAAM,OAAO,UAAU;AAAA,EACjD;AAEA,MAAI,OAAO,SAAS,GAAI,QAAO;AAE/B,MAAI;AACJ,MAAI,OAAO,WAAW,IAAI,GAAG;AAC3B,iBAAa,OAAO,MAAM,CAAC;AAAA,EAC7B,WAAW,OAAO,WAAW,GAAG,GAAG;AACjC,iBAAa,OAAO,MAAM,CAAC;AAAA,EAC7B,OAAO;AACL,iBAAa;AAAA,EACf;AAEA,MAAI,WAAW,SAAS,MAAM,WAAW,SAAS,GAAI,QAAO;AAC7D,MAAI,CAAC,WAAW,WAAW,GAAG,EAAG,QAAO;AAExC,QAAM,SAAS,OAAO,SAAS,WAAW,MAAM,GAAG,CAAC,GAAG,EAAE;AACzD,SAAO,wBAAwB,MAAM;AACvC;;;AC5DA,IAAM,gBAAgB;AAEtB,SAAS,kBAAkB,OAAwB;AACjD,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,MAAI,IAAI;AACR,SAAO,IAAI,MAAM,SAAS,GAAG;AAC3B,UAAM,KAAK,MAAM,CAAC;AAClB,QAAI,OAAO,MAAM;AACf;AACA,UAAI,KAAK,MAAM,SAAS,EAAG,QAAO;AAAA,IACpC,WAAW,OAAO,KAAK;AACrB,aAAO;AAAA,IACT;AACA;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAwB;AACnD,MAAI,MAAM,WAAW,KAAK,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,EAAG,QAAO;AAE/E,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,KAAK,MAAM,CAAC;AAClB,QACG,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM,OACpB,OAAO,OACP,cAAc,SAAS,EAAE,GACzB;AACA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI,MAAM,CAAC,MAAM,OAAO,MAAM,IAAI,CAAC,MAAM,IAAK,QAAO;AAAA,EACvD;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,QAAyB;AAC9C,MAAI,OAAO,WAAW,KAAK,OAAO,WAAW,GAAG,KAAK,OAAO,SAAS,GAAG,EAAG,QAAO;AAElF,QAAM,SAAS,OAAO,MAAM,GAAG;AAC/B,MAAI,OAAO,SAAS,EAAG,QAAO;AAE9B,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,WAAW,KAAK,MAAM,SAAS,GAAI,QAAO;AACpD,QAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,EAAG,QAAO;AAEzD,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,KAAK,MAAM,CAAC;AAClB,UACE,EACG,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM,OACpB,OAAO,MAET;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAqBO,SAAS,QAAQ,OAAwB;AAC9C,MAAI,MAAM,SAAS,IAAK,QAAO;AAE/B,QAAM,UAAU,MAAM,YAAY,GAAG;AACrC,MAAI,UAAU,KAAK,YAAY,MAAM,SAAS,EAAG,QAAO;AAExD,QAAM,YAAY,MAAM,MAAM,GAAG,OAAO;AACxC,QAAM,aAAa,MAAM,MAAM,UAAU,CAAC;AAE1C,MAAI,UAAU,SAAS,GAAI,QAAO;AAClC,MAAI,WAAW,SAAS,IAAK,QAAO;AAEpC,MAAI,UAAU,WAAW,GAAG,KAAK,UAAU,SAAS,GAAG,GAAG;AACxD,QAAI,CAAC,kBAAkB,SAAS,EAAG,QAAO;AAAA,EAC5C,OAAO;AACL,QAAI,CAAC,oBAAoB,SAAS,EAAG,QAAO;AAAA,EAC9C;AAEA,SAAO,cAAc,UAAU;AACjC;;;AC5GA,IAAM,aAAa;AAEnB,SAAS,YAAY,UAA2B;AAC9C,QAAM,SAAS,SAAS,MAAM,GAAG;AACjC,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,OAAO,MAAM,WAAS,WAAW,KAAK,KAAK,CAAC;AACrD;AAEA,SAAS,mBAAmB,UAA2B;AACrD,MAAI,SAAS,WAAW,GAAG,KAAK,SAAS,SAAS,GAAG,EAAG,QAAO;AAE/D,QAAM,SAAS,SAAS,MAAM,GAAG;AACjC,MAAI,OAAO,SAAS,EAAG,QAAO;AAE9B,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,WAAW,KAAK,MAAM,SAAS,GAAI,QAAO;AACpD,QAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,EAAG,QAAO;AAEzD,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,KAAK,MAAM,CAAC;AAClB,UACE,EACG,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM,OACpB,OAAO,MAET;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,gBAAgB,UAA2B;AAClD,MAAI,SAAS,WAAW,EAAG,QAAO;AAGlC,MAAI,SAAS,WAAW,GAAG,KAAK,SAAS,SAAS,GAAG,GAAG;AACtD,WAAO,SAAS,SAAS;AAAA,EAC3B;AAGA,MAAI,MAAM,KAAK,QAAQ,KAAK,MAAM,KAAK,QAAQ,GAAG;AAChD,QAAI,YAAY,QAAQ,EAAG,QAAO;AAAA,EACpC;AAGA,MAAI,aAAa,YAAa,QAAO;AAGrC,SAAO,mBAAmB,QAAQ;AACpC;AAkBO,SAAS,MAAM,OAAwB;AAC5C,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK;AAEzB,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAAU;AACzD,aAAO;AAAA,IACT;AAEA,WAAO,gBAAgB,IAAI,QAAQ;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superjs-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Zero-dependency JavaScript standard library + dependency health scanner — core, math, date, collection, string, async, io, type, crypto, path & dep-exray",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -81,6 +81,22 @@
|
|
|
81
81
|
"./dep-exray/cli": {
|
|
82
82
|
"import": "./dist/dep-exray/cli.js",
|
|
83
83
|
"types": "./dist/dep-exray/cli.d.ts"
|
|
84
|
+
},
|
|
85
|
+
"./validation": {
|
|
86
|
+
"import": "./dist/validation/index.js",
|
|
87
|
+
"types": "./dist/validation/index.d.ts"
|
|
88
|
+
},
|
|
89
|
+
"./error": {
|
|
90
|
+
"import": "./dist/error/index.js",
|
|
91
|
+
"types": "./dist/error/index.d.ts"
|
|
92
|
+
},
|
|
93
|
+
"./logger": {
|
|
94
|
+
"import": "./dist/logger/index.js",
|
|
95
|
+
"types": "./dist/logger/index.d.ts"
|
|
96
|
+
},
|
|
97
|
+
"./logger/transports": {
|
|
98
|
+
"import": "./dist/logger/transports.js",
|
|
99
|
+
"types": "./dist/logger/transports.d.ts"
|
|
84
100
|
}
|
|
85
101
|
},
|
|
86
102
|
"sideEffects": false,
|