@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
@@ -0,0 +1,56 @@
1
+ interface FormatDateTimeOptions extends Intl.DateTimeFormatOptions {
2
+ locale?: string;
3
+ }
4
+ interface FormatDurationOptions {
5
+ /**
6
+ * Interpret `value` as milliseconds instead of seconds.
7
+ * @default "seconds"
8
+ */
9
+ unit?: "seconds" | "milliseconds";
10
+ }
11
+ /**
12
+ * Formats a date with `Intl.DateTimeFormat`.
13
+ * Defaults to `en-IN` in the `Asia/Kolkata` time zone, including time.
14
+ * Returns `null` for missing or invalid values.
15
+ *
16
+ * @example
17
+ * formatDateTime("2024-01-15T00:00:00.000Z")
18
+ * formatDateTime(date, { hour: undefined, minute: undefined, second: undefined })
19
+ */
20
+ declare function formatDateTime(value: Date | string | number | null | undefined, options?: FormatDateTimeOptions): string | null;
21
+ /**
22
+ * Formats a byte count as a human-readable file size.
23
+ *
24
+ * @example
25
+ * formatFileSize(0) // "0 Bytes"
26
+ * formatFileSize(1536) // "1.5 KB"
27
+ */
28
+ declare function formatFileSize(bytes: number, decimals?: number): string;
29
+ /**
30
+ * Formats a duration in hours, minutes, and seconds.
31
+ *
32
+ * @example
33
+ * formatDuration(3661) // "1 hr 1 min 1 sec"
34
+ * formatDuration(0) // "0 sec"
35
+ */
36
+ declare function formatDuration(seconds: number): string;
37
+ /**
38
+ * Formats a duration as a compact `y d h m s` string.
39
+ * Month and year units use fixed 30-day and 365-day approximations.
40
+ *
41
+ * @example
42
+ * formatDurationCompact(3661) // "1h 1m 1s"
43
+ * formatDurationCompact(1500, { unit: "milliseconds" }) // "1s"
44
+ */
45
+ declare function formatDurationCompact(value: number, options?: FormatDurationOptions): string;
46
+ /**
47
+ * Formats a price in Indian lakh units.
48
+ * Returns an empty string for missing or invalid values.
49
+ *
50
+ * @example
51
+ * formatPriceToLakh(2_000_000) // "20.00 Lakh"
52
+ */
53
+ declare function formatPriceToLakh(price: unknown, decimals?: number): string;
54
+
55
+ export { formatDateTime, formatDuration, formatDurationCompact, formatFileSize, formatPriceToLakh };
56
+ export type { FormatDateTimeOptions, FormatDurationOptions };
@@ -0,0 +1,56 @@
1
+ interface FormatDateTimeOptions extends Intl.DateTimeFormatOptions {
2
+ locale?: string;
3
+ }
4
+ interface FormatDurationOptions {
5
+ /**
6
+ * Interpret `value` as milliseconds instead of seconds.
7
+ * @default "seconds"
8
+ */
9
+ unit?: "seconds" | "milliseconds";
10
+ }
11
+ /**
12
+ * Formats a date with `Intl.DateTimeFormat`.
13
+ * Defaults to `en-IN` in the `Asia/Kolkata` time zone, including time.
14
+ * Returns `null` for missing or invalid values.
15
+ *
16
+ * @example
17
+ * formatDateTime("2024-01-15T00:00:00.000Z")
18
+ * formatDateTime(date, { hour: undefined, minute: undefined, second: undefined })
19
+ */
20
+ declare function formatDateTime(value: Date | string | number | null | undefined, options?: FormatDateTimeOptions): string | null;
21
+ /**
22
+ * Formats a byte count as a human-readable file size.
23
+ *
24
+ * @example
25
+ * formatFileSize(0) // "0 Bytes"
26
+ * formatFileSize(1536) // "1.5 KB"
27
+ */
28
+ declare function formatFileSize(bytes: number, decimals?: number): string;
29
+ /**
30
+ * Formats a duration in hours, minutes, and seconds.
31
+ *
32
+ * @example
33
+ * formatDuration(3661) // "1 hr 1 min 1 sec"
34
+ * formatDuration(0) // "0 sec"
35
+ */
36
+ declare function formatDuration(seconds: number): string;
37
+ /**
38
+ * Formats a duration as a compact `y d h m s` string.
39
+ * Month and year units use fixed 30-day and 365-day approximations.
40
+ *
41
+ * @example
42
+ * formatDurationCompact(3661) // "1h 1m 1s"
43
+ * formatDurationCompact(1500, { unit: "milliseconds" }) // "1s"
44
+ */
45
+ declare function formatDurationCompact(value: number, options?: FormatDurationOptions): string;
46
+ /**
47
+ * Formats a price in Indian lakh units.
48
+ * Returns an empty string for missing or invalid values.
49
+ *
50
+ * @example
51
+ * formatPriceToLakh(2_000_000) // "20.00 Lakh"
52
+ */
53
+ declare function formatPriceToLakh(price: unknown, decimals?: number): string;
54
+
55
+ export { formatDateTime, formatDuration, formatDurationCompact, formatFileSize, formatPriceToLakh };
56
+ export type { FormatDateTimeOptions, FormatDurationOptions };
@@ -0,0 +1,130 @@
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
+
8
+ // src/format/index.ts
9
+ var DEFAULT_LOCALE = "en-IN";
10
+ var DEFAULT_TIME_ZONE = "Asia/Kolkata";
11
+ var FILE_SIZE_UNITS = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
12
+ function formatDateTime(value, options = {}) {
13
+ if (value == null || value === "") {
14
+ return null;
15
+ }
16
+ const date = value instanceof Date ? value : new Date(value);
17
+ if (Number.isNaN(date.getTime())) {
18
+ return null;
19
+ }
20
+ const { locale = DEFAULT_LOCALE, ...intlOptions } = options;
21
+ if (typeof locale !== "string" || locale.trim().length === 0) {
22
+ throw new TypeError("locale must be a non-empty BCP 47 locale tag");
23
+ }
24
+ return new Intl.DateTimeFormat(locale, {
25
+ timeZone: DEFAULT_TIME_ZONE,
26
+ day: "2-digit",
27
+ month: "short",
28
+ year: "numeric",
29
+ hour: "2-digit",
30
+ minute: "2-digit",
31
+ second: "2-digit",
32
+ hour12: true,
33
+ ...intlOptions
34
+ }).format(date);
35
+ }
36
+ function formatFileSize(bytes, decimals = 2) {
37
+ assertFiniteNumber(bytes, "bytes");
38
+ if (bytes < 0) {
39
+ throw new RangeError("bytes must be greater than or equal to 0");
40
+ }
41
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {
42
+ throw new RangeError("decimals must be an integer between 0 and 8");
43
+ }
44
+ if (bytes === 0) {
45
+ return "0 Bytes";
46
+ }
47
+ const unitIndex = Math.min(
48
+ Math.floor(Math.log(bytes) / Math.log(1024)),
49
+ FILE_SIZE_UNITS.length - 1
50
+ );
51
+ const value = bytes / 1024 ** unitIndex;
52
+ const unit = FILE_SIZE_UNITS[unitIndex] ?? "Bytes";
53
+ return `${Number(value.toFixed(decimals))} ${unit}`;
54
+ }
55
+ function formatDuration(seconds) {
56
+ assertFiniteNumber(seconds, "seconds");
57
+ if (seconds <= 0) {
58
+ return "0 sec";
59
+ }
60
+ const total = Math.floor(seconds);
61
+ const hours = Math.floor(total / 3600);
62
+ const minutes = Math.floor(total % 3600 / 60);
63
+ const remainingSeconds = total % 60;
64
+ const parts = [];
65
+ if (hours > 0) {
66
+ parts.push(`${hours} hr`);
67
+ }
68
+ if (minutes > 0) {
69
+ parts.push(`${minutes} min`);
70
+ }
71
+ if (remainingSeconds > 0 || parts.length === 0) {
72
+ parts.push(`${remainingSeconds} sec`);
73
+ }
74
+ return parts.join(" ");
75
+ }
76
+ function formatDurationCompact(value, options = {}) {
77
+ assertFiniteNumber(value, "value");
78
+ const unit = options.unit ?? "seconds";
79
+ if (unit !== "seconds" && unit !== "milliseconds") {
80
+ throw new TypeError('unit must be "seconds" or "milliseconds"');
81
+ }
82
+ let remaining = Math.floor(unit === "milliseconds" ? value / 1e3 : value);
83
+ if (remaining <= 0) {
84
+ return "0s";
85
+ }
86
+ const years = Math.floor(remaining / 31536e3);
87
+ remaining %= 31536e3;
88
+ const months = Math.floor(remaining / 2592e3);
89
+ remaining %= 2592e3;
90
+ const days = Math.floor(remaining / 86400);
91
+ remaining %= 86400;
92
+ const hours = Math.floor(remaining / 3600);
93
+ remaining %= 3600;
94
+ const minutes = Math.floor(remaining / 60);
95
+ const seconds = remaining % 60;
96
+ const parts = [];
97
+ if (years > 0) {
98
+ parts.push(`${years}y`);
99
+ }
100
+ if (months > 0) {
101
+ parts.push(`${months}mo`);
102
+ }
103
+ if (days > 0) {
104
+ parts.push(`${days}d`);
105
+ }
106
+ if (hours > 0) {
107
+ parts.push(`${hours}h`);
108
+ }
109
+ if (minutes > 0) {
110
+ parts.push(`${minutes}m`);
111
+ }
112
+ if (seconds > 0 || parts.length === 0) {
113
+ parts.push(`${seconds}s`);
114
+ }
115
+ return parts.join(" ");
116
+ }
117
+ function formatPriceToLakh(price, decimals = 2) {
118
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {
119
+ throw new RangeError("decimals must be an integer between 0 and 8");
120
+ }
121
+ const numeric = typeof price === "number" ? price : typeof price === "string" ? Number(price.trim()) : Number.NaN;
122
+ if (!Number.isFinite(numeric)) {
123
+ return "";
124
+ }
125
+ return `${(numeric / 1e5).toFixed(decimals)} Lakh`;
126
+ }
127
+
128
+ export { formatDateTime, formatDuration, formatDurationCompact, formatFileSize, formatPriceToLakh };
129
+ //# sourceMappingURL=index.js.map
130
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/validation.ts","../../src/format/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export function assertFiniteNumber(\r\n value: unknown,\r\n name: string,\r\n): asserts value is number {\r\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\r\n throw new TypeError(`${name} must be a finite number`);\r\n }\r\n}\r\n\r\nexport function escapeRegExp(value: string): string {\r\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n","import { assertFiniteNumber } from \"../internal/validation.js\";\r\n\r\nconst DEFAULT_LOCALE = \"en-IN\";\r\nconst DEFAULT_TIME_ZONE = \"Asia/Kolkata\";\r\nconst FILE_SIZE_UNITS = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\r\n\r\nexport interface FormatDateTimeOptions extends Intl.DateTimeFormatOptions {\r\n locale?: string;\r\n}\r\n\r\nexport interface FormatDurationOptions {\r\n /**\r\n * Interpret `value` as milliseconds instead of seconds.\r\n * @default \"seconds\"\r\n */\r\n unit?: \"seconds\" | \"milliseconds\";\r\n}\r\n\r\n/**\r\n * Formats a date with `Intl.DateTimeFormat`.\r\n * Defaults to `en-IN` in the `Asia/Kolkata` time zone, including time.\r\n * Returns `null` for missing or invalid values.\r\n *\r\n * @example\r\n * formatDateTime(\"2024-01-15T00:00:00.000Z\")\r\n * formatDateTime(date, { hour: undefined, minute: undefined, second: undefined })\r\n */\r\nexport function formatDateTime(\r\n value: Date | string | number | null | undefined,\r\n options: FormatDateTimeOptions = {},\r\n): string | null {\r\n if (value == null || value === \"\") {\r\n return null;\r\n }\r\n\r\n const date = value instanceof Date ? value : new Date(value);\r\n if (Number.isNaN(date.getTime())) {\r\n return null;\r\n }\r\n\r\n const { locale = DEFAULT_LOCALE, ...intlOptions } = options;\r\n if (typeof locale !== \"string\" || locale.trim().length === 0) {\r\n throw new TypeError(\"locale must be a non-empty BCP 47 locale tag\");\r\n }\r\n\r\n return new Intl.DateTimeFormat(locale, {\r\n timeZone: DEFAULT_TIME_ZONE,\r\n day: \"2-digit\",\r\n month: \"short\",\r\n year: \"numeric\",\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n second: \"2-digit\",\r\n hour12: true,\r\n ...intlOptions,\r\n }).format(date);\r\n}\r\n\r\n/**\r\n * Formats a byte count as a human-readable file size.\r\n *\r\n * @example\r\n * formatFileSize(0) // \"0 Bytes\"\r\n * formatFileSize(1536) // \"1.5 KB\"\r\n */\r\nexport function formatFileSize(bytes: number, decimals = 2): string {\r\n assertFiniteNumber(bytes, \"bytes\");\r\n if (bytes < 0) {\r\n throw new RangeError(\"bytes must be greater than or equal to 0\");\r\n }\r\n\r\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {\r\n throw new RangeError(\"decimals must be an integer between 0 and 8\");\r\n }\r\n\r\n if (bytes === 0) {\r\n return \"0 Bytes\";\r\n }\r\n\r\n const unitIndex = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(1024)),\r\n FILE_SIZE_UNITS.length - 1,\r\n );\r\n const value = bytes / 1024 ** unitIndex;\r\n const unit = FILE_SIZE_UNITS[unitIndex] ?? \"Bytes\";\r\n\r\n return `${Number(value.toFixed(decimals))} ${unit}`;\r\n}\r\n\r\n/**\r\n * Formats a duration in hours, minutes, and seconds.\r\n *\r\n * @example\r\n * formatDuration(3661) // \"1 hr 1 min 1 sec\"\r\n * formatDuration(0) // \"0 sec\"\r\n */\r\nexport function formatDuration(seconds: number): string {\r\n assertFiniteNumber(seconds, \"seconds\");\r\n\r\n if (seconds <= 0) {\r\n return \"0 sec\";\r\n }\r\n\r\n const total = Math.floor(seconds);\r\n const hours = Math.floor(total / 3600);\r\n const minutes = Math.floor((total % 3600) / 60);\r\n const remainingSeconds = total % 60;\r\n\r\n const parts: string[] = [];\r\n if (hours > 0) {\r\n parts.push(`${hours} hr`);\r\n }\r\n if (minutes > 0) {\r\n parts.push(`${minutes} min`);\r\n }\r\n if (remainingSeconds > 0 || parts.length === 0) {\r\n parts.push(`${remainingSeconds} sec`);\r\n }\r\n\r\n return parts.join(\" \");\r\n}\r\n\r\n/**\r\n * Formats a duration as a compact `y d h m s` string.\r\n * Month and year units use fixed 30-day and 365-day approximations.\r\n *\r\n * @example\r\n * formatDurationCompact(3661) // \"1h 1m 1s\"\r\n * formatDurationCompact(1500, { unit: \"milliseconds\" }) // \"1s\"\r\n */\r\nexport function formatDurationCompact(\r\n value: number,\r\n options: FormatDurationOptions = {},\r\n): string {\r\n assertFiniteNumber(value, \"value\");\r\n\r\n const unit = options.unit ?? \"seconds\";\r\n if (unit !== \"seconds\" && unit !== \"milliseconds\") {\r\n throw new TypeError('unit must be \"seconds\" or \"milliseconds\"');\r\n }\r\n\r\n let remaining = Math.floor(unit === \"milliseconds\" ? value / 1000 : value);\r\n if (remaining <= 0) {\r\n return \"0s\";\r\n }\r\n\r\n const years = Math.floor(remaining / 31_536_000);\r\n remaining %= 31_536_000;\r\n const months = Math.floor(remaining / 2_592_000);\r\n remaining %= 2_592_000;\r\n const days = Math.floor(remaining / 86_400);\r\n remaining %= 86_400;\r\n const hours = Math.floor(remaining / 3600);\r\n remaining %= 3600;\r\n const minutes = Math.floor(remaining / 60);\r\n const seconds = remaining % 60;\r\n\r\n const parts: string[] = [];\r\n if (years > 0) {\r\n parts.push(`${years}y`);\r\n }\r\n if (months > 0) {\r\n parts.push(`${months}mo`);\r\n }\r\n if (days > 0) {\r\n parts.push(`${days}d`);\r\n }\r\n if (hours > 0) {\r\n parts.push(`${hours}h`);\r\n }\r\n if (minutes > 0) {\r\n parts.push(`${minutes}m`);\r\n }\r\n if (seconds > 0 || parts.length === 0) {\r\n parts.push(`${seconds}s`);\r\n }\r\n\r\n return parts.join(\" \");\r\n}\r\n\r\n/**\r\n * Formats a price in Indian lakh units.\r\n * Returns an empty string for missing or invalid values.\r\n *\r\n * @example\r\n * formatPriceToLakh(2_000_000) // \"20.00 Lakh\"\r\n */\r\nexport function formatPriceToLakh(price: unknown, decimals = 2): string {\r\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {\r\n throw new RangeError(\"decimals must be an integer between 0 and 8\");\r\n }\r\n\r\n const numeric =\r\n typeof price === \"number\"\r\n ? price\r\n : typeof price === \"string\"\r\n ? Number(price.trim())\r\n : Number.NaN;\r\n\r\n if (!Number.isFinite(numeric)) {\r\n return \"\";\r\n }\r\n\r\n return `${(numeric / 100_000).toFixed(decimals)} Lakh`;\r\n}\r\n"]}
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ // src/internal/random.ts
4
+ function getCrypto() {
5
+ const cryptoApi = globalThis.crypto;
6
+ if (!cryptoApi?.getRandomValues) {
7
+ throw new Error("Secure random number generation is not available in this runtime");
8
+ }
9
+ return cryptoApi;
10
+ }
11
+ function randomIndex(length) {
12
+ if (length <= 0) {
13
+ throw new RangeError("length must be greater than 0");
14
+ }
15
+ const cryptoApi = getCrypto();
16
+ const limit = 4294967296;
17
+ const unbiasedMax = Math.floor(limit / length) * length;
18
+ const bytes = new Uint32Array(1);
19
+ let value = 0;
20
+ do {
21
+ cryptoApi.getRandomValues(bytes);
22
+ value = bytes[0] ?? 0;
23
+ } while (value >= unbiasedMax);
24
+ return value % length;
25
+ }
26
+ function pickCharacter(charset) {
27
+ return charset[randomIndex(charset.length)] ?? charset[0] ?? "A";
28
+ }
29
+ function randomString(length, charset) {
30
+ return Array.from({ length }, () => pickCharacter(charset)).join("");
31
+ }
32
+
33
+ // src/id/index.ts
34
+ var DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
35
+ function pad(value) {
36
+ return String(value).padStart(2, "0");
37
+ }
38
+ function generatePrefixedCode(options) {
39
+ const prefix = options.prefix.trim();
40
+ if (prefix.length === 0) {
41
+ throw new TypeError("prefix must be a non-empty string");
42
+ }
43
+ const randomLength = options.randomLength ?? 5;
44
+ if (!Number.isInteger(randomLength) || randomLength < 0) {
45
+ throw new RangeError("randomLength must be an integer greater than or equal to 0");
46
+ }
47
+ const charset = options.charset ?? DEFAULT_CHARSET;
48
+ if (charset.length === 0) {
49
+ throw new TypeError("charset must be a non-empty string");
50
+ }
51
+ const includeTimestamp = options.includeTimestamp ?? true;
52
+ const now = options.now ?? /* @__PURE__ */ new Date();
53
+ const randomPart = randomLength > 0 ? randomString(randomLength, charset) : "";
54
+ if (!includeTimestamp) {
55
+ return `${prefix}${randomPart}`;
56
+ }
57
+ const day = pad(now.getDate());
58
+ const month = pad(now.getMonth() + 1);
59
+ const hour = pad(now.getHours());
60
+ const minute = pad(now.getMinutes());
61
+ return `${prefix}${day}${month}${randomPart}${hour}${minute}`;
62
+ }
63
+
64
+ exports.generatePrefixedCode = generatePrefixedCode;
65
+ //# sourceMappingURL=index.cjs.map
66
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/random.ts","../../src/id/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["function getCrypto(): Crypto {\r\n const cryptoApi = globalThis.crypto;\r\n if (!cryptoApi?.getRandomValues) {\r\n throw new Error(\"Secure random number generation is not available in this runtime\");\r\n }\r\n\r\n return cryptoApi;\r\n}\r\n\r\nexport function randomIndex(length: number): number {\r\n if (length <= 0) {\r\n throw new RangeError(\"length must be greater than 0\");\r\n }\r\n\r\n const cryptoApi = getCrypto();\r\n const limit = 0x1_0000_0000;\r\n const unbiasedMax = Math.floor(limit / length) * length;\r\n const bytes = new Uint32Array(1);\r\n\r\n let value = 0;\r\n do {\r\n cryptoApi.getRandomValues(bytes);\r\n value = bytes[0] ?? 0;\r\n } while (value >= unbiasedMax);\r\n\r\n return value % length;\r\n}\r\n\r\nexport function pickCharacter(charset: string): string {\r\n return charset[randomIndex(charset.length)] ?? charset[0] ?? \"A\";\r\n}\r\n\r\nexport function randomString(length: number, charset: string): string {\r\n return Array.from({ length }, () => pickCharacter(charset)).join(\"\");\r\n}\r\n\r\nexport function shuffle<T>(items: T[]): T[] {\r\n const result = [...items];\r\n for (let index = result.length - 1; index > 0; index -= 1) {\r\n const swapIndex = randomIndex(index + 1);\r\n const current = result[index];\r\n const other = result[swapIndex];\r\n if (current === undefined || other === undefined) {\r\n continue;\r\n }\r\n result[index] = other;\r\n result[swapIndex] = current;\r\n }\r\n return result;\r\n}\r\n","import { randomString } from \"../internal/random.js\";\r\n\r\nconst DEFAULT_CHARSET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n\r\nexport interface GeneratePrefixedCodeOptions {\r\n prefix: string;\r\n randomLength?: number;\r\n includeTimestamp?: boolean;\r\n now?: Date;\r\n charset?: string;\r\n}\r\n\r\nfunction pad(value: number): string {\r\n return String(value).padStart(2, \"0\");\r\n}\r\n\r\n/**\r\n * Builds a configurable unique-looking code such as `PLAN1503AB12X0931`.\r\n * Uses `crypto.getRandomValues`. This does not guarantee global uniqueness.\r\n *\r\n * @example\r\n * generatePrefixedCode({ prefix: \"PLAN\" })\r\n * generatePrefixedCode({ prefix: \"SAVE\", randomLength: 6, includeTimestamp: false })\r\n */\r\nexport function generatePrefixedCode(options: GeneratePrefixedCodeOptions): string {\r\n const prefix = options.prefix.trim();\r\n if (prefix.length === 0) {\r\n throw new TypeError(\"prefix must be a non-empty string\");\r\n }\r\n\r\n const randomLength = options.randomLength ?? 5;\r\n if (!Number.isInteger(randomLength) || randomLength < 0) {\r\n throw new RangeError(\"randomLength must be an integer greater than or equal to 0\");\r\n }\r\n\r\n const charset = options.charset ?? DEFAULT_CHARSET;\r\n if (charset.length === 0) {\r\n throw new TypeError(\"charset must be a non-empty string\");\r\n }\r\n\r\n const includeTimestamp = options.includeTimestamp ?? true;\r\n const now = options.now ?? new Date();\r\n const randomPart = randomLength > 0 ? randomString(randomLength, charset) : \"\";\r\n\r\n if (!includeTimestamp) {\r\n return `${prefix}${randomPart}`;\r\n }\r\n\r\n const day = pad(now.getDate());\r\n const month = pad(now.getMonth() + 1);\r\n const hour = pad(now.getHours());\r\n const minute = pad(now.getMinutes());\r\n\r\n return `${prefix}${day}${month}${randomPart}${hour}${minute}`;\r\n}\r\n"]}
@@ -0,0 +1,19 @@
1
+ interface GeneratePrefixedCodeOptions {
2
+ prefix: string;
3
+ randomLength?: number;
4
+ includeTimestamp?: boolean;
5
+ now?: Date;
6
+ charset?: string;
7
+ }
8
+ /**
9
+ * Builds a configurable unique-looking code such as `PLAN1503AB12X0931`.
10
+ * Uses `crypto.getRandomValues`. This does not guarantee global uniqueness.
11
+ *
12
+ * @example
13
+ * generatePrefixedCode({ prefix: "PLAN" })
14
+ * generatePrefixedCode({ prefix: "SAVE", randomLength: 6, includeTimestamp: false })
15
+ */
16
+ declare function generatePrefixedCode(options: GeneratePrefixedCodeOptions): string;
17
+
18
+ export { generatePrefixedCode };
19
+ export type { GeneratePrefixedCodeOptions };
@@ -0,0 +1,19 @@
1
+ interface GeneratePrefixedCodeOptions {
2
+ prefix: string;
3
+ randomLength?: number;
4
+ includeTimestamp?: boolean;
5
+ now?: Date;
6
+ charset?: string;
7
+ }
8
+ /**
9
+ * Builds a configurable unique-looking code such as `PLAN1503AB12X0931`.
10
+ * Uses `crypto.getRandomValues`. This does not guarantee global uniqueness.
11
+ *
12
+ * @example
13
+ * generatePrefixedCode({ prefix: "PLAN" })
14
+ * generatePrefixedCode({ prefix: "SAVE", randomLength: 6, includeTimestamp: false })
15
+ */
16
+ declare function generatePrefixedCode(options: GeneratePrefixedCodeOptions): string;
17
+
18
+ export { generatePrefixedCode };
19
+ export type { GeneratePrefixedCodeOptions };
@@ -0,0 +1,64 @@
1
+ // src/internal/random.ts
2
+ function getCrypto() {
3
+ const cryptoApi = globalThis.crypto;
4
+ if (!cryptoApi?.getRandomValues) {
5
+ throw new Error("Secure random number generation is not available in this runtime");
6
+ }
7
+ return cryptoApi;
8
+ }
9
+ function randomIndex(length) {
10
+ if (length <= 0) {
11
+ throw new RangeError("length must be greater than 0");
12
+ }
13
+ const cryptoApi = getCrypto();
14
+ const limit = 4294967296;
15
+ const unbiasedMax = Math.floor(limit / length) * length;
16
+ const bytes = new Uint32Array(1);
17
+ let value = 0;
18
+ do {
19
+ cryptoApi.getRandomValues(bytes);
20
+ value = bytes[0] ?? 0;
21
+ } while (value >= unbiasedMax);
22
+ return value % length;
23
+ }
24
+ function pickCharacter(charset) {
25
+ return charset[randomIndex(charset.length)] ?? charset[0] ?? "A";
26
+ }
27
+ function randomString(length, charset) {
28
+ return Array.from({ length }, () => pickCharacter(charset)).join("");
29
+ }
30
+
31
+ // src/id/index.ts
32
+ var DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
33
+ function pad(value) {
34
+ return String(value).padStart(2, "0");
35
+ }
36
+ function generatePrefixedCode(options) {
37
+ const prefix = options.prefix.trim();
38
+ if (prefix.length === 0) {
39
+ throw new TypeError("prefix must be a non-empty string");
40
+ }
41
+ const randomLength = options.randomLength ?? 5;
42
+ if (!Number.isInteger(randomLength) || randomLength < 0) {
43
+ throw new RangeError("randomLength must be an integer greater than or equal to 0");
44
+ }
45
+ const charset = options.charset ?? DEFAULT_CHARSET;
46
+ if (charset.length === 0) {
47
+ throw new TypeError("charset must be a non-empty string");
48
+ }
49
+ const includeTimestamp = options.includeTimestamp ?? true;
50
+ const now = options.now ?? /* @__PURE__ */ new Date();
51
+ const randomPart = randomLength > 0 ? randomString(randomLength, charset) : "";
52
+ if (!includeTimestamp) {
53
+ return `${prefix}${randomPart}`;
54
+ }
55
+ const day = pad(now.getDate());
56
+ const month = pad(now.getMonth() + 1);
57
+ const hour = pad(now.getHours());
58
+ const minute = pad(now.getMinutes());
59
+ return `${prefix}${day}${month}${randomPart}${hour}${minute}`;
60
+ }
61
+
62
+ export { generatePrefixedCode };
63
+ //# sourceMappingURL=index.js.map
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/random.ts","../../src/id/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["function getCrypto(): Crypto {\r\n const cryptoApi = globalThis.crypto;\r\n if (!cryptoApi?.getRandomValues) {\r\n throw new Error(\"Secure random number generation is not available in this runtime\");\r\n }\r\n\r\n return cryptoApi;\r\n}\r\n\r\nexport function randomIndex(length: number): number {\r\n if (length <= 0) {\r\n throw new RangeError(\"length must be greater than 0\");\r\n }\r\n\r\n const cryptoApi = getCrypto();\r\n const limit = 0x1_0000_0000;\r\n const unbiasedMax = Math.floor(limit / length) * length;\r\n const bytes = new Uint32Array(1);\r\n\r\n let value = 0;\r\n do {\r\n cryptoApi.getRandomValues(bytes);\r\n value = bytes[0] ?? 0;\r\n } while (value >= unbiasedMax);\r\n\r\n return value % length;\r\n}\r\n\r\nexport function pickCharacter(charset: string): string {\r\n return charset[randomIndex(charset.length)] ?? charset[0] ?? \"A\";\r\n}\r\n\r\nexport function randomString(length: number, charset: string): string {\r\n return Array.from({ length }, () => pickCharacter(charset)).join(\"\");\r\n}\r\n\r\nexport function shuffle<T>(items: T[]): T[] {\r\n const result = [...items];\r\n for (let index = result.length - 1; index > 0; index -= 1) {\r\n const swapIndex = randomIndex(index + 1);\r\n const current = result[index];\r\n const other = result[swapIndex];\r\n if (current === undefined || other === undefined) {\r\n continue;\r\n }\r\n result[index] = other;\r\n result[swapIndex] = current;\r\n }\r\n return result;\r\n}\r\n","import { randomString } from \"../internal/random.js\";\r\n\r\nconst DEFAULT_CHARSET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n\r\nexport interface GeneratePrefixedCodeOptions {\r\n prefix: string;\r\n randomLength?: number;\r\n includeTimestamp?: boolean;\r\n now?: Date;\r\n charset?: string;\r\n}\r\n\r\nfunction pad(value: number): string {\r\n return String(value).padStart(2, \"0\");\r\n}\r\n\r\n/**\r\n * Builds a configurable unique-looking code such as `PLAN1503AB12X0931`.\r\n * Uses `crypto.getRandomValues`. This does not guarantee global uniqueness.\r\n *\r\n * @example\r\n * generatePrefixedCode({ prefix: \"PLAN\" })\r\n * generatePrefixedCode({ prefix: \"SAVE\", randomLength: 6, includeTimestamp: false })\r\n */\r\nexport function generatePrefixedCode(options: GeneratePrefixedCodeOptions): string {\r\n const prefix = options.prefix.trim();\r\n if (prefix.length === 0) {\r\n throw new TypeError(\"prefix must be a non-empty string\");\r\n }\r\n\r\n const randomLength = options.randomLength ?? 5;\r\n if (!Number.isInteger(randomLength) || randomLength < 0) {\r\n throw new RangeError(\"randomLength must be an integer greater than or equal to 0\");\r\n }\r\n\r\n const charset = options.charset ?? DEFAULT_CHARSET;\r\n if (charset.length === 0) {\r\n throw new TypeError(\"charset must be a non-empty string\");\r\n }\r\n\r\n const includeTimestamp = options.includeTimestamp ?? true;\r\n const now = options.now ?? new Date();\r\n const randomPart = randomLength > 0 ? randomString(randomLength, charset) : \"\";\r\n\r\n if (!includeTimestamp) {\r\n return `${prefix}${randomPart}`;\r\n }\r\n\r\n const day = pad(now.getDate());\r\n const month = pad(now.getMonth() + 1);\r\n const hour = pad(now.getHours());\r\n const minute = pad(now.getMinutes());\r\n\r\n return `${prefix}${day}${month}${randomPart}${hour}${minute}`;\r\n}\r\n"]}