@itshackers/utilix 0.0.0-stage → 0.3.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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +304 -3
  3. package/dist/common/index.cjs +102 -0
  4. package/dist/common/index.cjs.map +1 -0
  5. package/dist/common/index.d.cts +58 -0
  6. package/dist/common/index.d.ts +58 -0
  7. package/dist/common/index.js +93 -0
  8. package/dist/common/index.js.map +1 -0
  9. package/dist/date/index.cjs +154 -0
  10. package/dist/date/index.cjs.map +1 -0
  11. package/dist/date/index.d.cts +65 -0
  12. package/dist/date/index.d.ts +65 -0
  13. package/dist/date/index.js +146 -0
  14. package/dist/date/index.js.map +1 -0
  15. package/dist/format/index.cjs +136 -0
  16. package/dist/format/index.cjs.map +1 -0
  17. package/dist/format/index.d.cts +56 -0
  18. package/dist/format/index.d.ts +56 -0
  19. package/dist/format/index.js +130 -0
  20. package/dist/format/index.js.map +1 -0
  21. package/dist/id/index.cjs +66 -0
  22. package/dist/id/index.cjs.map +1 -0
  23. package/dist/id/index.d.cts +19 -0
  24. package/dist/id/index.d.ts +19 -0
  25. package/dist/id/index.js +64 -0
  26. package/dist/id/index.js.map +1 -0
  27. package/dist/index.cjs +906 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +12 -0
  30. package/dist/index.d.ts +12 -0
  31. package/dist/index.js +853 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/number/index.cjs +107 -0
  34. package/dist/number/index.cjs.map +1 -0
  35. package/dist/number/index.d.cts +90 -0
  36. package/dist/number/index.d.ts +90 -0
  37. package/dist/number/index.js +99 -0
  38. package/dist/number/index.js.map +1 -0
  39. package/dist/password/index.cjs +71 -0
  40. package/dist/password/index.cjs.map +1 -0
  41. package/dist/password/index.d.cts +15 -0
  42. package/dist/password/index.d.ts +15 -0
  43. package/dist/password/index.js +69 -0
  44. package/dist/password/index.js.map +1 -0
  45. package/dist/phone/index.cjs +19 -0
  46. package/dist/phone/index.cjs.map +1 -0
  47. package/dist/phone/index.d.cts +12 -0
  48. package/dist/phone/index.d.ts +12 -0
  49. package/dist/phone/index.js +17 -0
  50. package/dist/phone/index.js.map +1 -0
  51. package/dist/regex/index.cjs +73 -0
  52. package/dist/regex/index.cjs.map +1 -0
  53. package/dist/regex/index.d.cts +65 -0
  54. package/dist/regex/index.d.ts +65 -0
  55. package/dist/regex/index.js +67 -0
  56. package/dist/regex/index.js.map +1 -0
  57. package/dist/response/index.cjs +43 -0
  58. package/dist/response/index.cjs.map +1 -0
  59. package/dist/response/index.d.cts +41 -0
  60. package/dist/response/index.d.ts +41 -0
  61. package/dist/response/index.js +40 -0
  62. package/dist/response/index.js.map +1 -0
  63. package/dist/sanitize/index.cjs +83 -0
  64. package/dist/sanitize/index.cjs.map +1 -0
  65. package/dist/sanitize/index.d.cts +20 -0
  66. package/dist/sanitize/index.d.ts +20 -0
  67. package/dist/sanitize/index.js +80 -0
  68. package/dist/sanitize/index.js.map +1 -0
  69. package/dist/slug/index.cjs +33 -0
  70. package/dist/slug/index.cjs.map +1 -0
  71. package/dist/slug/index.d.cts +38 -0
  72. package/dist/slug/index.d.ts +38 -0
  73. package/dist/slug/index.js +31 -0
  74. package/dist/slug/index.js.map +1 -0
  75. package/dist/string/index.cjs +130 -0
  76. package/dist/string/index.cjs.map +1 -0
  77. package/dist/string/index.d.cts +112 -0
  78. package/dist/string/index.d.ts +112 -0
  79. package/dist/string/index.js +115 -0
  80. package/dist/string/index.js.map +1 -0
  81. package/package.json +187 -3
package/dist/index.js ADDED
@@ -0,0 +1,853 @@
1
+ // src/internal/validation.ts
2
+ function assertFiniteNumber(value, name) {
3
+ if (typeof value !== "number" || !Number.isFinite(value)) {
4
+ throw new TypeError(`${name} must be a finite number`);
5
+ }
6
+ }
7
+ function escapeRegExp(value) {
8
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9
+ }
10
+
11
+ // src/string/index.ts
12
+ var HTML_ESCAPE_MAP = {
13
+ "&": "&",
14
+ "<": "&lt;",
15
+ ">": "&gt;",
16
+ '"': "&quot;",
17
+ "'": "&#39;"
18
+ };
19
+ function splitWords(value) {
20
+ const trimmed = value.trim();
21
+ if (trimmed.length === 0) {
22
+ return [];
23
+ }
24
+ return trimmed.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(/[\s._-]+/u).filter((word) => word.length > 0);
25
+ }
26
+ function toLowerWords(value) {
27
+ return splitWords(value).map((word) => word.toLowerCase());
28
+ }
29
+ function capitalizeWord(word) {
30
+ const [first = "", ...rest] = [...word];
31
+ return `${first.toUpperCase()}${rest.join("").toLowerCase()}`;
32
+ }
33
+ function toCamelCase(value) {
34
+ const words = toLowerWords(value);
35
+ if (words.length === 0) {
36
+ return "";
37
+ }
38
+ const [first = "", ...rest] = words;
39
+ return `${first}${rest.map(capitalizeWord).join("")}`;
40
+ }
41
+ function toPascalCase(value) {
42
+ return toLowerWords(value).map(capitalizeWord).join("");
43
+ }
44
+ function toSnakeCase(value) {
45
+ return toLowerWords(value).join("_");
46
+ }
47
+ function toKebabCase(value) {
48
+ return toLowerWords(value).join("-");
49
+ }
50
+ function capitalize(value) {
51
+ const trimmed = value.trim();
52
+ if (trimmed.length === 0) {
53
+ return "";
54
+ }
55
+ const [first = "", ...rest] = [...trimmed];
56
+ return `${first.toUpperCase()}${rest.join("")}`;
57
+ }
58
+ function normalizeWhitespace(value) {
59
+ return value.trim().replace(/\s+/gu, " ");
60
+ }
61
+ function escapeHtml(value) {
62
+ return value.replace(/[&<>"']/g, (character) => HTML_ESCAPE_MAP[character] ?? character);
63
+ }
64
+ function escapeRegex(value) {
65
+ return escapeRegExp(value);
66
+ }
67
+ function toTitleCase(value) {
68
+ const trimmed = value.trim();
69
+ if (trimmed.length === 0) {
70
+ return "";
71
+ }
72
+ return trimmed.toLowerCase().replace(/\b\w/g, (character) => character.toUpperCase());
73
+ }
74
+ function convertCase(value, casing = "title") {
75
+ switch (casing.toLowerCase()) {
76
+ case "title":
77
+ case "titlecase":
78
+ return toTitleCase(value);
79
+ case "lower":
80
+ case "lowercase":
81
+ return value.trim().toLowerCase();
82
+ case "upper":
83
+ case "uppercase":
84
+ return value.trim().toUpperCase();
85
+ case "camel":
86
+ case "camelcase":
87
+ return toCamelCase(value);
88
+ case "pascal":
89
+ case "pascalcase":
90
+ return toPascalCase(value);
91
+ case "snake":
92
+ case "snakecase":
93
+ return toSnakeCase(value);
94
+ case "kebab":
95
+ case "kebabcase":
96
+ return toKebabCase(value);
97
+ default:
98
+ throw new TypeError(`Unsupported case style: ${casing}`);
99
+ }
100
+ }
101
+ function replaceSeparatorsWithSpace(value) {
102
+ return toTitleCase(value.replace(/[-_]+/g, " ").replace(/\s+/g, " "));
103
+ }
104
+ function htmlToPlainText(value) {
105
+ if (value.trim().length === 0) {
106
+ return "";
107
+ }
108
+ return value.replace(/<(br|\/p|\/div|\/pre|\/li)\b[^>]*>/gi, " ").replace(/<[^>]*>/g, " ").replace(/[\n\t]+/g, " ").replace(/\s+/g, " ").trim();
109
+ }
110
+ function nonEmptyString(value) {
111
+ if (typeof value !== "string") {
112
+ return null;
113
+ }
114
+ const trimmed = value.trim();
115
+ return trimmed.length === 0 ? null : trimmed;
116
+ }
117
+
118
+ // src/number/index.ts
119
+ var DEFAULT_LOCALE = "en-IN";
120
+ var DEFAULT_CURRENCY = "INR";
121
+ var MAX_DECIMAL_PLACES = 15;
122
+ var DECIMAL_NUMBER_PATTERN = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
123
+ function toNumber(value) {
124
+ if (typeof value === "number") {
125
+ return Number.isFinite(value) ? value : null;
126
+ }
127
+ if (typeof value !== "string") {
128
+ return null;
129
+ }
130
+ const trimmed = value.trim();
131
+ if (trimmed.length === 0 || /[,\s]/.test(trimmed)) {
132
+ return null;
133
+ }
134
+ if (!DECIMAL_NUMBER_PATTERN.test(trimmed)) {
135
+ return null;
136
+ }
137
+ const parsed = Number(trimmed);
138
+ return Number.isFinite(parsed) ? parsed : null;
139
+ }
140
+ function formatNumber(value, options) {
141
+ assertFiniteNumber(value, "value");
142
+ const { locale = DEFAULT_LOCALE, ...intlOptions } = options ?? {};
143
+ if (typeof locale !== "string" || locale.trim().length === 0) {
144
+ throw new TypeError("locale must be a non-empty BCP 47 locale tag");
145
+ }
146
+ return new Intl.NumberFormat(locale, intlOptions).format(value);
147
+ }
148
+ function formatCurrency(value, currency = DEFAULT_CURRENCY, locale = DEFAULT_LOCALE) {
149
+ assertFiniteNumber(value, "value");
150
+ if (typeof currency !== "string" || currency.trim().length === 0) {
151
+ throw new TypeError("currency must be a non-empty ISO 4217 currency code");
152
+ }
153
+ if (typeof locale !== "string" || locale.trim().length === 0) {
154
+ throw new TypeError("locale must be a non-empty BCP 47 locale tag");
155
+ }
156
+ return new Intl.NumberFormat(locale, {
157
+ style: "currency",
158
+ currency
159
+ }).format(value);
160
+ }
161
+ function clamp(value, min, max) {
162
+ assertFiniteNumber(value, "value");
163
+ assertFiniteNumber(min, "min");
164
+ assertFiniteNumber(max, "max");
165
+ if (min > max) {
166
+ throw new RangeError("min must be less than or equal to max");
167
+ }
168
+ return Math.min(max, Math.max(min, value));
169
+ }
170
+ function roundTo(value, decimalPlaces = 0) {
171
+ assertFiniteNumber(value, "value");
172
+ if (!Number.isInteger(decimalPlaces) || decimalPlaces < 0 || decimalPlaces > MAX_DECIMAL_PLACES) {
173
+ throw new RangeError(
174
+ `decimalPlaces must be an integer between 0 and ${MAX_DECIMAL_PLACES}`
175
+ );
176
+ }
177
+ if (Object.is(value, -0)) {
178
+ return 0;
179
+ }
180
+ const [significand = "0", exponent = "0"] = `${value}e`.split("e");
181
+ const shifted = Number(`${significand}e${Number(exponent) + decimalPlaces}`);
182
+ const rounded = Math.round(shifted);
183
+ const [roundedSignificand = "0", roundedExponent = "0"] = `${rounded}e`.split("e");
184
+ const result = Number(
185
+ `${roundedSignificand}e${Number(roundedExponent) - decimalPlaces}`
186
+ );
187
+ return Object.is(result, -0) ? 0 : result;
188
+ }
189
+ function parsePositiveInt(value, fallback) {
190
+ assertFiniteNumber(fallback, "fallback");
191
+ const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
192
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
193
+ }
194
+ function toNumberIfValid(value) {
195
+ if (typeof value === "number") {
196
+ return Number.isFinite(value) ? value : value;
197
+ }
198
+ if (typeof value === "string") {
199
+ const trimmed = value.trim();
200
+ if (/^\d+$/.test(trimmed)) {
201
+ return Number(trimmed);
202
+ }
203
+ }
204
+ return value;
205
+ }
206
+
207
+ // src/slug/index.ts
208
+ function slugify(value, options = {}) {
209
+ const separator = options.separator ?? "-";
210
+ const preserveUnicode = options.preserveUnicode ?? true;
211
+ const { maxLength } = options;
212
+ if (typeof separator !== "string" || separator.length === 0 || /\s/u.test(separator)) {
213
+ throw new TypeError("separator must be a non-empty string without whitespace");
214
+ }
215
+ if (options.preserveUnicode !== void 0 && typeof options.preserveUnicode !== "boolean") {
216
+ throw new TypeError("preserveUnicode must be a boolean");
217
+ }
218
+ if (maxLength !== void 0 && (!Number.isInteger(maxLength) || maxLength <= 0)) {
219
+ throw new RangeError("maxLength must be a positive integer");
220
+ }
221
+ const escapedSeparator = escapeRegExp(separator);
222
+ const disallowedPattern = preserveUnicode ? /[^\p{L}\p{N}]+/gu : /[^a-z0-9]+/g;
223
+ let slug = value.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase().replace(disallowedPattern, separator).replace(new RegExp(`${escapedSeparator}+`, "g"), separator).replace(new RegExp(`^${escapedSeparator}|${escapedSeparator}$`, "g"), "");
224
+ if (maxLength !== void 0 && slug.length > maxLength) {
225
+ slug = slug.slice(0, maxLength).replace(new RegExp(`${escapedSeparator}+$`, "g"), "");
226
+ }
227
+ return slug;
228
+ }
229
+
230
+ // src/common/index.ts
231
+ function isPlainObject(value) {
232
+ if (value === null || typeof value !== "object") {
233
+ return false;
234
+ }
235
+ const prototype = Object.getPrototypeOf(value);
236
+ return prototype === null || prototype === Object.prototype;
237
+ }
238
+ function isNilOrEmpty(value) {
239
+ if (value == null) {
240
+ return true;
241
+ }
242
+ if (typeof value === "string") {
243
+ return value.trim().length === 0;
244
+ }
245
+ return false;
246
+ }
247
+ function coalesce(value, fallback) {
248
+ return value ?? fallback;
249
+ }
250
+ function toBoolean(value, fallback = false) {
251
+ if (value === true || value === 1 || value === "1") {
252
+ return true;
253
+ }
254
+ if (value === false || value === 0 || value === "0") {
255
+ return false;
256
+ }
257
+ if (typeof value === "string") {
258
+ const normalized = value.trim().toLowerCase();
259
+ if (normalized.length === 0) {
260
+ return fallback;
261
+ }
262
+ if (normalized === "true") {
263
+ return true;
264
+ }
265
+ if (normalized === "false") {
266
+ return false;
267
+ }
268
+ }
269
+ if (value == null) {
270
+ return fallback;
271
+ }
272
+ return fallback;
273
+ }
274
+ function deepTrim(value) {
275
+ if (typeof value === "string") {
276
+ return value.trim();
277
+ }
278
+ if (Array.isArray(value)) {
279
+ return value.map((item) => deepTrim(item));
280
+ }
281
+ if (isPlainObject(value)) {
282
+ return Object.fromEntries(
283
+ Object.entries(value).map(([key, nested]) => [key, deepTrim(nested)])
284
+ );
285
+ }
286
+ return value;
287
+ }
288
+ function omitEmpty(value) {
289
+ if (!isPlainObject(value)) {
290
+ return value;
291
+ }
292
+ const cleaned = { ...value };
293
+ for (const key of Object.keys(cleaned)) {
294
+ if (cleaned[key] === "" || cleaned[key] === null) {
295
+ delete cleaned[key];
296
+ }
297
+ }
298
+ return cleaned;
299
+ }
300
+ function pickAllowed(value, allowed, fallback) {
301
+ return allowed.includes(value) ? value : fallback;
302
+ }
303
+ function sleep(milliseconds) {
304
+ assertFiniteNumber(milliseconds, "milliseconds");
305
+ if (milliseconds < 0) {
306
+ throw new RangeError("milliseconds must be greater than or equal to 0");
307
+ }
308
+ return new Promise((resolve) => {
309
+ setTimeout(resolve, milliseconds);
310
+ });
311
+ }
312
+
313
+ // src/format/index.ts
314
+ var DEFAULT_LOCALE2 = "en-IN";
315
+ var DEFAULT_TIME_ZONE = "Asia/Kolkata";
316
+ var FILE_SIZE_UNITS = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
317
+ function formatDateTime(value, options = {}) {
318
+ if (value == null || value === "") {
319
+ return null;
320
+ }
321
+ const date = value instanceof Date ? value : new Date(value);
322
+ if (Number.isNaN(date.getTime())) {
323
+ return null;
324
+ }
325
+ const { locale = DEFAULT_LOCALE2, ...intlOptions } = options;
326
+ if (typeof locale !== "string" || locale.trim().length === 0) {
327
+ throw new TypeError("locale must be a non-empty BCP 47 locale tag");
328
+ }
329
+ return new Intl.DateTimeFormat(locale, {
330
+ timeZone: DEFAULT_TIME_ZONE,
331
+ day: "2-digit",
332
+ month: "short",
333
+ year: "numeric",
334
+ hour: "2-digit",
335
+ minute: "2-digit",
336
+ second: "2-digit",
337
+ hour12: true,
338
+ ...intlOptions
339
+ }).format(date);
340
+ }
341
+ function formatFileSize(bytes, decimals = 2) {
342
+ assertFiniteNumber(bytes, "bytes");
343
+ if (bytes < 0) {
344
+ throw new RangeError("bytes must be greater than or equal to 0");
345
+ }
346
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {
347
+ throw new RangeError("decimals must be an integer between 0 and 8");
348
+ }
349
+ if (bytes === 0) {
350
+ return "0 Bytes";
351
+ }
352
+ const unitIndex = Math.min(
353
+ Math.floor(Math.log(bytes) / Math.log(1024)),
354
+ FILE_SIZE_UNITS.length - 1
355
+ );
356
+ const value = bytes / 1024 ** unitIndex;
357
+ const unit = FILE_SIZE_UNITS[unitIndex] ?? "Bytes";
358
+ return `${Number(value.toFixed(decimals))} ${unit}`;
359
+ }
360
+ function formatDuration(seconds) {
361
+ assertFiniteNumber(seconds, "seconds");
362
+ if (seconds <= 0) {
363
+ return "0 sec";
364
+ }
365
+ const total = Math.floor(seconds);
366
+ const hours = Math.floor(total / 3600);
367
+ const minutes = Math.floor(total % 3600 / 60);
368
+ const remainingSeconds = total % 60;
369
+ const parts = [];
370
+ if (hours > 0) {
371
+ parts.push(`${hours} hr`);
372
+ }
373
+ if (minutes > 0) {
374
+ parts.push(`${minutes} min`);
375
+ }
376
+ if (remainingSeconds > 0 || parts.length === 0) {
377
+ parts.push(`${remainingSeconds} sec`);
378
+ }
379
+ return parts.join(" ");
380
+ }
381
+ function formatDurationCompact(value, options = {}) {
382
+ assertFiniteNumber(value, "value");
383
+ const unit = options.unit ?? "seconds";
384
+ if (unit !== "seconds" && unit !== "milliseconds") {
385
+ throw new TypeError('unit must be "seconds" or "milliseconds"');
386
+ }
387
+ let remaining = Math.floor(unit === "milliseconds" ? value / 1e3 : value);
388
+ if (remaining <= 0) {
389
+ return "0s";
390
+ }
391
+ const years = Math.floor(remaining / 31536e3);
392
+ remaining %= 31536e3;
393
+ const months = Math.floor(remaining / 2592e3);
394
+ remaining %= 2592e3;
395
+ const days = Math.floor(remaining / 86400);
396
+ remaining %= 86400;
397
+ const hours = Math.floor(remaining / 3600);
398
+ remaining %= 3600;
399
+ const minutes = Math.floor(remaining / 60);
400
+ const seconds = remaining % 60;
401
+ const parts = [];
402
+ if (years > 0) {
403
+ parts.push(`${years}y`);
404
+ }
405
+ if (months > 0) {
406
+ parts.push(`${months}mo`);
407
+ }
408
+ if (days > 0) {
409
+ parts.push(`${days}d`);
410
+ }
411
+ if (hours > 0) {
412
+ parts.push(`${hours}h`);
413
+ }
414
+ if (minutes > 0) {
415
+ parts.push(`${minutes}m`);
416
+ }
417
+ if (seconds > 0 || parts.length === 0) {
418
+ parts.push(`${seconds}s`);
419
+ }
420
+ return parts.join(" ");
421
+ }
422
+ function formatPriceToLakh(price, decimals = 2) {
423
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {
424
+ throw new RangeError("decimals must be an integer between 0 and 8");
425
+ }
426
+ const numeric = typeof price === "number" ? price : typeof price === "string" ? Number(price.trim()) : Number.NaN;
427
+ if (!Number.isFinite(numeric)) {
428
+ return "";
429
+ }
430
+ return `${(numeric / 1e5).toFixed(decimals)} Lakh`;
431
+ }
432
+
433
+ // src/phone/index.ts
434
+ var INDIAN_MOBILE_PATTERN = /^[6-9]\d{9}$/;
435
+ function normalizeMobile(value) {
436
+ if (value == null) {
437
+ return null;
438
+ }
439
+ const digits = String(value).replace(/\D/g, "");
440
+ if (digits.length === 0) {
441
+ return null;
442
+ }
443
+ const withoutCountryCode = digits.length > 10 && (digits.startsWith("91") || digits.startsWith("0")) ? digits.slice(-10) : digits.length >= 10 ? digits.slice(-10) : digits;
444
+ return INDIAN_MOBILE_PATTERN.test(withoutCountryCode) ? withoutCountryCode : null;
445
+ }
446
+
447
+ // src/internal/random.ts
448
+ function getCrypto() {
449
+ const cryptoApi = globalThis.crypto;
450
+ if (!cryptoApi?.getRandomValues) {
451
+ throw new Error("Secure random number generation is not available in this runtime");
452
+ }
453
+ return cryptoApi;
454
+ }
455
+ function randomIndex(length) {
456
+ if (length <= 0) {
457
+ throw new RangeError("length must be greater than 0");
458
+ }
459
+ const cryptoApi = getCrypto();
460
+ const limit = 4294967296;
461
+ const unbiasedMax = Math.floor(limit / length) * length;
462
+ const bytes = new Uint32Array(1);
463
+ let value = 0;
464
+ do {
465
+ cryptoApi.getRandomValues(bytes);
466
+ value = bytes[0] ?? 0;
467
+ } while (value >= unbiasedMax);
468
+ return value % length;
469
+ }
470
+ function pickCharacter(charset) {
471
+ return charset[randomIndex(charset.length)] ?? charset[0] ?? "A";
472
+ }
473
+ function randomString(length, charset) {
474
+ return Array.from({ length }, () => pickCharacter(charset)).join("");
475
+ }
476
+ function shuffle(items) {
477
+ const result = [...items];
478
+ for (let index = result.length - 1; index > 0; index -= 1) {
479
+ const swapIndex = randomIndex(index + 1);
480
+ const current = result[index];
481
+ const other = result[swapIndex];
482
+ if (current === void 0 || other === void 0) {
483
+ continue;
484
+ }
485
+ result[index] = other;
486
+ result[swapIndex] = current;
487
+ }
488
+ return result;
489
+ }
490
+
491
+ // src/password/index.ts
492
+ var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
493
+ var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
494
+ var NUMBERS = "0123456789";
495
+ var SPECIAL = "!@#$%^&*()_+-=[]{}|;:,.<>?";
496
+ var ALL_CHARACTERS = `${UPPERCASE}${LOWERCASE}${NUMBERS}${SPECIAL}`;
497
+ var MIN_PASSWORD_LENGTH = 8;
498
+ var DEFAULT_PASSWORD_LENGTH = 12;
499
+ function generatePassword(length = DEFAULT_PASSWORD_LENGTH) {
500
+ if (!Number.isInteger(length) || length < MIN_PASSWORD_LENGTH) {
501
+ throw new RangeError(`length must be an integer greater than or equal to ${MIN_PASSWORD_LENGTH}`);
502
+ }
503
+ const required = [
504
+ pickCharacter(UPPERCASE),
505
+ pickCharacter(LOWERCASE),
506
+ pickCharacter(NUMBERS),
507
+ pickCharacter(SPECIAL)
508
+ ];
509
+ const remaining = Array.from(
510
+ { length: length - required.length },
511
+ () => pickCharacter(ALL_CHARACTERS)
512
+ );
513
+ return shuffle([...required, ...remaining]).join("");
514
+ }
515
+
516
+ // src/id/index.ts
517
+ var DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
518
+ function pad(value) {
519
+ return String(value).padStart(2, "0");
520
+ }
521
+ function generatePrefixedCode(options) {
522
+ const prefix = options.prefix.trim();
523
+ if (prefix.length === 0) {
524
+ throw new TypeError("prefix must be a non-empty string");
525
+ }
526
+ const randomLength = options.randomLength ?? 5;
527
+ if (!Number.isInteger(randomLength) || randomLength < 0) {
528
+ throw new RangeError("randomLength must be an integer greater than or equal to 0");
529
+ }
530
+ const charset = options.charset ?? DEFAULT_CHARSET;
531
+ if (charset.length === 0) {
532
+ throw new TypeError("charset must be a non-empty string");
533
+ }
534
+ const includeTimestamp = options.includeTimestamp ?? true;
535
+ const now = options.now ?? /* @__PURE__ */ new Date();
536
+ const randomPart = randomLength > 0 ? randomString(randomLength, charset) : "";
537
+ if (!includeTimestamp) {
538
+ return `${prefix}${randomPart}`;
539
+ }
540
+ const day = pad(now.getDate());
541
+ const month = pad(now.getMonth() + 1);
542
+ const hour = pad(now.getHours());
543
+ const minute = pad(now.getMinutes());
544
+ return `${prefix}${day}${month}${randomPart}${hour}${minute}`;
545
+ }
546
+
547
+ // src/date/index.ts
548
+ var IST_OFFSET_MS = 330 * 60 * 1e3;
549
+ var IST_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?/;
550
+ function pad2(value, length = 2) {
551
+ return String(value).padStart(length, "0");
552
+ }
553
+ function getIstDateParts(date = /* @__PURE__ */ new Date()) {
554
+ const parts = new Intl.DateTimeFormat("en-CA", {
555
+ timeZone: "Asia/Kolkata",
556
+ year: "numeric",
557
+ month: "2-digit",
558
+ day: "2-digit",
559
+ hour: "2-digit",
560
+ hourCycle: "h23"
561
+ }).formatToParts(date);
562
+ const read = (type) => {
563
+ const value = parts.find((part) => part.type === type)?.value;
564
+ return Number(value);
565
+ };
566
+ return {
567
+ year: read("year"),
568
+ month: read("month"),
569
+ day: read("day"),
570
+ hour: read("hour")
571
+ };
572
+ }
573
+ function addCalendarDays(year, month, day, days) {
574
+ const utc = new Date(Date.UTC(year, month - 1, day + days));
575
+ return {
576
+ year: utc.getUTCFullYear(),
577
+ month: utc.getUTCMonth() + 1,
578
+ day: utc.getUTCDate()
579
+ };
580
+ }
581
+ function istDateTime(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) {
582
+ return /* @__PURE__ */ new Date(
583
+ `${year}-${pad2(month)}-${pad2(day)}T${pad2(hour)}:${pad2(minute)}:${pad2(second)}.${pad2(millisecond, 3)}+05:30`
584
+ );
585
+ }
586
+ function parseIstDateTimeToUtc(value) {
587
+ if (value == null || value === "") {
588
+ return null;
589
+ }
590
+ if (value instanceof Date) {
591
+ return Number.isNaN(value.getTime()) ? null : value;
592
+ }
593
+ if (typeof value === "number") {
594
+ const parsed = new Date(value);
595
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
596
+ }
597
+ const raw = value.trim();
598
+ if (raw.length === 0) {
599
+ return null;
600
+ }
601
+ if (/[zZ]$/.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
602
+ const parsed = new Date(raw);
603
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
604
+ }
605
+ const match = raw.match(IST_DATETIME_RE);
606
+ if (!match) {
607
+ const parsed = new Date(raw);
608
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
609
+ }
610
+ const [, year = "0", month = "1", day = "1", hour = "0", minute = "0", second = "0"] = match;
611
+ const istAsUtcMs = Date.UTC(
612
+ Number(year),
613
+ Number(month) - 1,
614
+ Number(day),
615
+ Number(hour),
616
+ Number(minute),
617
+ Number(second)
618
+ );
619
+ return new Date(istAsUtcMs - IST_OFFSET_MS);
620
+ }
621
+ function formatUtcToIstDateTime(value) {
622
+ if (value == null || value === "") {
623
+ return "";
624
+ }
625
+ const date = value instanceof Date ? value : new Date(value);
626
+ if (Number.isNaN(date.getTime())) {
627
+ return "";
628
+ }
629
+ const ist = new Date(date.getTime() + IST_OFFSET_MS);
630
+ return `${ist.getUTCFullYear()}-${pad2(ist.getUTCMonth() + 1)}-${pad2(ist.getUTCDate())} ${pad2(ist.getUTCHours())}:${pad2(ist.getUTCMinutes())}:${pad2(ist.getUTCSeconds())}`;
631
+ }
632
+ function getExpiryInfo(expiresAt, options = {}) {
633
+ if (!(expiresAt instanceof Date) || Number.isNaN(expiresAt.getTime())) {
634
+ return null;
635
+ }
636
+ const warningDays = options.warningDays ?? 7;
637
+ const compact = options.compact ?? false;
638
+ const now = options.now ?? /* @__PURE__ */ new Date();
639
+ let remainingSeconds = Math.floor((expiresAt.getTime() - now.getTime()) / 1e3);
640
+ if (remainingSeconds < 0) {
641
+ remainingSeconds = 0;
642
+ }
643
+ const units = [
644
+ { label: "day", short: "d", value: 86400 },
645
+ { label: "hour", short: "h", value: 3600 },
646
+ { label: "minute", short: "m", value: 60 },
647
+ { label: "second", short: "s", value: 1 }
648
+ ];
649
+ const parts = [];
650
+ let remaining = remainingSeconds;
651
+ for (const unit of units) {
652
+ const count = Math.floor(remaining / unit.value);
653
+ if (count > 0) {
654
+ parts.push(compact ? `${count}${unit.short}` : `${count} ${unit.label}${count > 1 ? "s" : ""}`);
655
+ remaining -= count * unit.value;
656
+ }
657
+ }
658
+ const daysLeft = Math.ceil(remainingSeconds / 86400);
659
+ return {
660
+ expiresAt,
661
+ remainingSeconds,
662
+ remainingHuman: parts.join(" ") || "Expired",
663
+ daysLeft,
664
+ isExpired: remainingSeconds === 0,
665
+ needsRefresh: daysLeft <= warningDays
666
+ };
667
+ }
668
+ function getCalendarWindow(options = {}) {
669
+ const now = options.now ?? /* @__PURE__ */ new Date();
670
+ const cutoffHour = options.cutoffHour ?? 14;
671
+ const windowDays = options.windowDays ?? 36;
672
+ const startOffsetBeforeCutoff = options.startOffsetBeforeCutoff ?? 1;
673
+ const startOffsetAfterCutoff = options.startOffsetAfterCutoff ?? 2;
674
+ if (!Number.isInteger(cutoffHour) || cutoffHour < 0 || cutoffHour > 23) {
675
+ throw new RangeError("cutoffHour must be an integer between 0 and 23");
676
+ }
677
+ if (!Number.isInteger(windowDays) || windowDays < 0) {
678
+ throw new RangeError("windowDays must be an integer greater than or equal to 0");
679
+ }
680
+ const { year, month, day, hour } = getIstDateParts(now);
681
+ const startOffsetDays = hour < cutoffHour ? startOffsetBeforeCutoff : startOffsetAfterCutoff;
682
+ const minYmd = addCalendarDays(year, month, day, startOffsetDays);
683
+ const maxYmd = addCalendarDays(minYmd.year, minYmd.month, minYmd.day, windowDays);
684
+ return {
685
+ min: istDateTime(minYmd.year, minYmd.month, minYmd.day, 0, 0, 0, 0),
686
+ max: istDateTime(maxYmd.year, maxYmd.month, maxYmd.day, 23, 59, 59, 999)
687
+ };
688
+ }
689
+
690
+ // src/sanitize/index.ts
691
+ var DEFAULT_SENSITIVE_KEYS = [
692
+ "authorization",
693
+ "password",
694
+ "client_secret",
695
+ "otp",
696
+ "otp_hash",
697
+ "api_key",
698
+ "x-api-key",
699
+ "token",
700
+ "access_token",
701
+ "refresh_token",
702
+ "jwt",
703
+ "secret"
704
+ ];
705
+ function resolveOptions(options = {}) {
706
+ return {
707
+ sensitiveKeys: new Set(
708
+ (options.sensitiveKeys ?? DEFAULT_SENSITIVE_KEYS).map((key) => key.toLowerCase())
709
+ ),
710
+ maxDepth: options.maxDepth ?? 6,
711
+ maxString: options.maxString ?? 2e3,
712
+ maxArray: options.maxArray ?? 50,
713
+ redactedValue: options.redactedValue ?? "[REDACTED]",
714
+ truncatedValue: options.truncatedValue ?? "[truncated]"
715
+ };
716
+ }
717
+ function sanitizeForLog(value, options = {}, depth = 0) {
718
+ const config = resolveOptions(options);
719
+ if (depth > config.maxDepth) {
720
+ return config.truncatedValue;
721
+ }
722
+ if (value == null || typeof value === "number" || typeof value === "boolean") {
723
+ return value;
724
+ }
725
+ if (typeof value === "string") {
726
+ return value.length > config.maxString ? `${value.slice(0, config.maxString)}\u2026` : value;
727
+ }
728
+ if (Array.isArray(value)) {
729
+ return value.slice(0, config.maxArray).map((item) => sanitizeForLog(item, options, depth + 1));
730
+ }
731
+ if (isPlainObject(value)) {
732
+ const output = {};
733
+ for (const [key, nested] of Object.entries(value)) {
734
+ output[key] = config.sensitiveKeys.has(key.toLowerCase()) ? config.redactedValue : sanitizeForLog(nested, options, depth + 1);
735
+ }
736
+ return output;
737
+ }
738
+ if (value instanceof Date) {
739
+ return value.toISOString();
740
+ }
741
+ return String(value);
742
+ }
743
+ function sanitizeHeaders(headers, options = {}) {
744
+ const config = resolveOptions(options);
745
+ const output = {};
746
+ for (const [key, value] of Object.entries(headers)) {
747
+ if (config.sensitiveKeys.has(key.toLowerCase())) {
748
+ output[key] = config.redactedValue;
749
+ } else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
750
+ output[key] = value;
751
+ } else if (Array.isArray(value)) {
752
+ output[key] = value.map(String);
753
+ }
754
+ }
755
+ return output;
756
+ }
757
+
758
+ // src/response/index.ts
759
+ function createSuccessResponse(options = {}) {
760
+ const response = { success: true };
761
+ if (options.message) {
762
+ response.message = options.message;
763
+ }
764
+ if (options.data !== void 0) {
765
+ response.data = options.data;
766
+ }
767
+ if (options.meta !== void 0) {
768
+ response.meta = options.meta;
769
+ }
770
+ if (options.requestId) {
771
+ response.request_id = options.requestId;
772
+ }
773
+ return response;
774
+ }
775
+ function createErrorResponse(options = {}) {
776
+ const response = { success: false };
777
+ if (options.message) {
778
+ response.message = options.message;
779
+ }
780
+ if (options.code !== void 0) {
781
+ response.code = options.code;
782
+ }
783
+ if (options.errors !== void 0) {
784
+ response.errors = options.errors;
785
+ }
786
+ if (options.details !== void 0) {
787
+ response.details = options.details;
788
+ }
789
+ if (options.requestId) {
790
+ response.request_id = options.requestId;
791
+ }
792
+ return response;
793
+ }
794
+
795
+ // src/regex/index.ts
796
+ var ValidationPatterns = {
797
+ YEAR: /^\d{4}$/,
798
+ OTP: /^\d{6}$/,
799
+ PINCODE: /^\d{6}$/,
800
+ POSTAL_CODE: /^\d{6}$/,
801
+ NUMERIC: /^[0-9]+$/,
802
+ MOBILE: /^[6-9]\d{9}$/,
803
+ NAME: /^[a-zA-Z\s\-']+$/,
804
+ ALPHABETIC: /^[a-zA-Z]+$/,
805
+ LOWERCASE_ONLY: /^[a-z_]+$/,
806
+ UPPERCASE_ONLY: /^[A-Z_]+$/,
807
+ USERNAME: /^[a-zA-Z0-9_-]+$/,
808
+ LOWER_NUMERIC: /^[a-z0-9_]+$/,
809
+ UPPER_NUMERIC: /^[A-Z0-9_]+$/,
810
+ LOWER_WITH_HYPHENS: /^[a-z-]+$/,
811
+ UPPER_WITH_HYPHENS: /^[A-Z-]+$/,
812
+ ALPHANUMERIC: /^[a-zA-Z0-9-\s]+$/,
813
+ PERMISSION_NAME: /^[a-z0-9._-]+$/,
814
+ AADHAR_CARD: /^[2-9][0-9]{11}$/,
815
+ LOWER_NUMERIC_WITH_HYPHENS: /^[a-z0-9-]+$/,
816
+ UPPER_NUMERIC_WITH_HYPHENS: /^[A-Z0-9-]+$/,
817
+ ALPHANUMERIC_WITH_SPACES: /^[a-zA-Z0-9\s]+$/,
818
+ ALPHANUMERIC_WITHOUT_SPACES: /^[a-zA-Z0-9]+$/,
819
+ PAN_CARD: /^[A-Za-z]{5}[0-9]{4}[A-Za-z]$/,
820
+ ALPHANUMERIC_WITH_UNDERSCORES: /^[a-zA-Z0-9-_]+$/,
821
+ MODEL_NAME: /^[a-zA-Z0-9_\s\-/\\*+()[\]:'",.%&]+$/,
822
+ NOTE: /^[a-zA-Z0-9\s/\\,.\-_+=<>()[\]{}@#$%^&*~`"',;:’]+$/,
823
+ EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
824
+ VEHICLE_NUMBER: /^[A-Za-z]{2}\d{1,2}[A-Za-z]{1,3}\d{1,4}$/,
825
+ PASSWORD: /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[@$!%*?&])[a-zA-Z0-9@$!%*?&]{8,16}$/,
826
+ URL: /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/,
827
+ DATE: /^\d{4}-\d{2}-\d{2}$/,
828
+ CURRENCY: /^\d+(?:\.\d{1,2})?$/,
829
+ TIME: /^(?:[01]?\d|2[0-3]):[0-5]\d$/,
830
+ MESSAGE: /^[a-zA-Z0-9\s.,!?_\-@#()&%$+=:;"'<>[\]{}|\\/]+$/,
831
+ DOMAIN_HOSTNAME: /^(?=.{1,253}$)(?:localhost|(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$/i
832
+ };
833
+ function testPattern(value, pattern) {
834
+ if (typeof pattern === "string") {
835
+ const namedPattern = ValidationPatterns[pattern];
836
+ if (!namedPattern) {
837
+ throw new TypeError(`Unknown validation pattern: ${pattern}`);
838
+ }
839
+ return namedPattern.test(value);
840
+ }
841
+ return pattern.test(value);
842
+ }
843
+ function getPattern(name) {
844
+ const pattern = ValidationPatterns[name];
845
+ if (!pattern) {
846
+ throw new TypeError(`Unknown validation pattern: ${name}`);
847
+ }
848
+ return pattern;
849
+ }
850
+
851
+ export { ValidationPatterns, addCalendarDays, capitalize, clamp, coalesce, convertCase, createErrorResponse, createSuccessResponse, deepTrim, escapeHtml, escapeRegex as escapeRegExp, escapeRegex, formatCurrency, formatDateTime, formatDuration, formatDurationCompact, formatFileSize, formatNumber, formatPriceToLakh, formatUtcToIstDateTime, generatePassword, generatePrefixedCode, getCalendarWindow, getExpiryInfo, getIstDateParts, getPattern, htmlToPlainText, isNilOrEmpty, isPlainObject, istDateTime, nonEmptyString, normalizeMobile, normalizeWhitespace, omitEmpty, parseIstDateTimeToUtc, parsePositiveInt, pickAllowed, replaceSeparatorsWithSpace, roundTo, sanitizeForLog, sanitizeHeaders, sleep, slugify, testPattern, toBoolean, toCamelCase, toKebabCase, toNumber, toNumberIfValid, toPascalCase, toSnakeCase, toTitleCase };
852
+ //# sourceMappingURL=index.js.map
853
+ //# sourceMappingURL=index.js.map