toolbox-x 1.0.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 (190) hide show
  1. package/LICENSE +201 -0
  2. package/dist/Color-B3mgF9Dh.d.cts +486 -0
  3. package/dist/Color-D38Xrw65.d.mts +486 -0
  4. package/dist/Stylog-Df7eq3-j.d.cts +519 -0
  5. package/dist/Stylog-jvlLcMQq.d.mts +519 -0
  6. package/dist/array-DvW0zIu6.d.mts +130 -0
  7. package/dist/array-rUnEVisO.d.cts +130 -0
  8. package/dist/basics-D_eSv0cu.cjs +132 -0
  9. package/dist/basics-Dp_aEK81.mjs +115 -0
  10. package/dist/basics-WEYWlnRO.d.cts +95 -0
  11. package/dist/basics-uBSfkBEI.d.mts +95 -0
  12. package/dist/case-BWIt8Ash.mjs +449 -0
  13. package/dist/case-C-S-b5YP.d.cts +327 -0
  14. package/dist/case-CS8Ii3A7.cjs +526 -0
  15. package/dist/case-CybASFPD.d.mts +327 -0
  16. package/dist/change-case.cjs +32 -0
  17. package/dist/change-case.d.cts +18 -0
  18. package/dist/change-case.d.mts +18 -0
  19. package/dist/change-case.mjs +19 -0
  20. package/dist/colors.cjs +574 -0
  21. package/dist/colors.d.cts +355 -0
  22. package/dist/colors.d.mts +355 -0
  23. package/dist/colors.mjs +547 -0
  24. package/dist/constants-2gAw23_7.mjs +144 -0
  25. package/dist/constants-B34K0QPi.d.cts +21 -0
  26. package/dist/constants-BIBDKY1u.cjs +924 -0
  27. package/dist/constants-BWT-810U.cjs +158 -0
  28. package/dist/constants-BwbHnXlM.mjs +662 -0
  29. package/dist/constants-BxN9l5el.cjs +74 -0
  30. package/dist/constants-CLS_bgKD.d.mts +847 -0
  31. package/dist/constants-D73iFu8g.mjs +171 -0
  32. package/dist/constants-DAfRxaa8.mjs +62 -0
  33. package/dist/constants-DQYeCjlx.cjs +207 -0
  34. package/dist/constants-Deeie-iH.d.mts +21 -0
  35. package/dist/constants-DpTG9RP6.d.mts +29 -0
  36. package/dist/constants-DqwnkJ_d.cjs +740 -0
  37. package/dist/constants-DvRUY_FY.cjs +150 -0
  38. package/dist/constants-VcRtQu0K.d.cts +29 -0
  39. package/dist/constants-X5hm1UtB.mjs +912 -0
  40. package/dist/constants-eNd-iYsV.mjs +134 -0
  41. package/dist/constants-qm8FafmD.d.cts +847 -0
  42. package/dist/constants.cjs +415 -0
  43. package/dist/constants.d.cts +184 -0
  44. package/dist/constants.d.mts +184 -0
  45. package/dist/constants.mjs +378 -0
  46. package/dist/convert-BOCgUv2D.cjs +252 -0
  47. package/dist/convert-Bn4jFomQ.mjs +169 -0
  48. package/dist/convert-BrzlG-m_.cjs +475 -0
  49. package/dist/convert-DhaUoPVU.mjs +368 -0
  50. package/dist/converter-1P90_RcP.d.mts +402 -0
  51. package/dist/converter-CmkcAppi.d.cts +402 -0
  52. package/dist/converter.cjs +780 -0
  53. package/dist/converter.d.cts +29 -0
  54. package/dist/converter.d.mts +29 -0
  55. package/dist/converter.mjs +771 -0
  56. package/dist/countries-CIpmtEzV.cjs +1469 -0
  57. package/dist/countries-Cy0xiqS3.mjs +1463 -0
  58. package/dist/css-colors-Bx947Ng3.d.cts +179 -0
  59. package/dist/css-colors-CXCDqQbG.cjs +186 -0
  60. package/dist/css-colors-CXTp1vvy.d.mts +179 -0
  61. package/dist/css-colors-DfUW3nTR.mjs +180 -0
  62. package/dist/date.cjs +332 -0
  63. package/dist/date.d.cts +213 -0
  64. package/dist/date.d.mts +213 -0
  65. package/dist/date.mjs +298 -0
  66. package/dist/dom.cjs +461 -0
  67. package/dist/dom.d.cts +228 -0
  68. package/dist/dom.d.mts +228 -0
  69. package/dist/dom.mjs +429 -0
  70. package/dist/form-BMFVGUrN.d.mts +118 -0
  71. package/dist/form-DRFbryvK.d.cts +118 -0
  72. package/dist/guards-3kaUX66g.mjs +157 -0
  73. package/dist/guards-C8gkvIHb.cjs +240 -0
  74. package/dist/guards-DdyU4h4o.mjs +110 -0
  75. package/dist/guards-Efhp1mNy.cjs +151 -0
  76. package/dist/guards.cjs +172 -0
  77. package/dist/guards.d.cts +399 -0
  78. package/dist/guards.d.mts +399 -0
  79. package/dist/guards.mjs +75 -0
  80. package/dist/hash-B6JPEyAz.d.mts +131 -0
  81. package/dist/hash-NTpeKYB_.d.cts +131 -0
  82. package/dist/hash.cjs +2126 -0
  83. package/dist/hash.d.cts +1239 -0
  84. package/dist/hash.d.mts +1239 -0
  85. package/dist/hash.mjs +2095 -0
  86. package/dist/http-status-BAZdtr7-.d.mts +65 -0
  87. package/dist/http-status-U_3MtoGb.d.cts +65 -0
  88. package/dist/http-status.cjs +173 -0
  89. package/dist/http-status.d.cts +142 -0
  90. package/dist/http-status.d.mts +142 -0
  91. package/dist/http-status.mjs +171 -0
  92. package/dist/index.cjs +2551 -0
  93. package/dist/index.d.cts +1493 -0
  94. package/dist/index.d.mts +1493 -0
  95. package/dist/index.mjs +2357 -0
  96. package/dist/object-B0TV3eHx.d.mts +8052 -0
  97. package/dist/object-Blq0Amdv.d.cts +8052 -0
  98. package/dist/objectify-CDs0Fbr1.mjs +417 -0
  99. package/dist/objectify-DIJ-OBmo.cjs +524 -0
  100. package/dist/paginator.cjs +245 -0
  101. package/dist/paginator.d.cts +144 -0
  102. package/dist/paginator.d.mts +144 -0
  103. package/dist/paginator.mjs +243 -0
  104. package/dist/parse-2ubxXZRp.cjs +211 -0
  105. package/dist/parse-N7g942uy.mjs +164 -0
  106. package/dist/pluralizer-BjMIc6uT.d.mts +42 -0
  107. package/dist/pluralizer-Cb6ZmrDl.d.cts +42 -0
  108. package/dist/pluralizer.cjs +678 -0
  109. package/dist/pluralizer.d.cts +152 -0
  110. package/dist/pluralizer.d.mts +152 -0
  111. package/dist/pluralizer.mjs +676 -0
  112. package/dist/primitives-B26uZolQ.cjs +228 -0
  113. package/dist/primitives-KsFUp3kQ.mjs +144 -0
  114. package/dist/specials-D48_IZbd.d.mts +108 -0
  115. package/dist/specials-DzLr1ZgU.cjs +477 -0
  116. package/dist/specials-LVONlKbQ.d.cts +108 -0
  117. package/dist/specials-uhDuRg8H.mjs +292 -0
  118. package/dist/string-CBAbxaG1.d.mts +258 -0
  119. package/dist/string-CsNsm_65.d.cts +258 -0
  120. package/dist/stylog.cjs +621 -0
  121. package/dist/stylog.d.cts +49 -0
  122. package/dist/stylog.d.mts +49 -0
  123. package/dist/stylog.mjs +614 -0
  124. package/dist/timezone-B2OYK6Fh.mjs +5589 -0
  125. package/dist/timezone-Beh9IGpw.cjs +5625 -0
  126. package/dist/types/array.cjs +16 -0
  127. package/dist/types/array.d.cts +18 -0
  128. package/dist/types/array.d.mts +18 -0
  129. package/dist/types/array.mjs +17 -0
  130. package/dist/types/colors.cjs +16 -0
  131. package/dist/types/colors.d.cts +18 -0
  132. package/dist/types/colors.d.mts +18 -0
  133. package/dist/types/colors.mjs +17 -0
  134. package/dist/types/converter.cjs +16 -0
  135. package/dist/types/converter.d.cts +18 -0
  136. package/dist/types/converter.d.mts +18 -0
  137. package/dist/types/converter.mjs +17 -0
  138. package/dist/types/form.cjs +16 -0
  139. package/dist/types/form.d.cts +18 -0
  140. package/dist/types/form.d.mts +18 -0
  141. package/dist/types/form.mjs +17 -0
  142. package/dist/types/hash.cjs +16 -0
  143. package/dist/types/hash.d.cts +18 -0
  144. package/dist/types/hash.d.mts +18 -0
  145. package/dist/types/hash.mjs +17 -0
  146. package/dist/types/http-status.cjs +16 -0
  147. package/dist/types/http-status.d.cts +18 -0
  148. package/dist/types/http-status.d.mts +18 -0
  149. package/dist/types/http-status.mjs +17 -0
  150. package/dist/types/index.cjs +16 -0
  151. package/dist/types/index.d.cts +18 -0
  152. package/dist/types/index.d.mts +18 -0
  153. package/dist/types/index.mjs +17 -0
  154. package/dist/types/number.cjs +16 -0
  155. package/dist/types/number.d.cts +18 -0
  156. package/dist/types/number.d.mts +18 -0
  157. package/dist/types/number.mjs +17 -0
  158. package/dist/types/object.cjs +16 -0
  159. package/dist/types/object.d.cts +18 -0
  160. package/dist/types/object.d.mts +18 -0
  161. package/dist/types/object.mjs +17 -0
  162. package/dist/types/pluralizer.cjs +16 -0
  163. package/dist/types/pluralizer.d.cts +18 -0
  164. package/dist/types/pluralizer.d.mts +18 -0
  165. package/dist/types/pluralizer.mjs +17 -0
  166. package/dist/types/string.cjs +16 -0
  167. package/dist/types/string.d.cts +18 -0
  168. package/dist/types/string.d.mts +18 -0
  169. package/dist/types/string.mjs +17 -0
  170. package/dist/types/stylog.cjs +16 -0
  171. package/dist/types/stylog.d.cts +18 -0
  172. package/dist/types/stylog.d.mts +18 -0
  173. package/dist/types/stylog.mjs +17 -0
  174. package/dist/types/utils.cjs +16 -0
  175. package/dist/types/utils.d.cts +18 -0
  176. package/dist/types/utils.d.mts +18 -0
  177. package/dist/types/utils.mjs +17 -0
  178. package/dist/types/verbalizer.cjs +16 -0
  179. package/dist/types/verbalizer.d.cts +30 -0
  180. package/dist/types/verbalizer.d.mts +30 -0
  181. package/dist/types/verbalizer.mjs +17 -0
  182. package/dist/utilities-CLUmdQeV.cjs +140 -0
  183. package/dist/utilities-m5yFKqLd.mjs +105 -0
  184. package/dist/utils-ClW9LA6f.mjs +449 -0
  185. package/dist/utils-DLFRgXUC.cjs +568 -0
  186. package/dist/verbalizer.cjs +998 -0
  187. package/dist/verbalizer.d.cts +148 -0
  188. package/dist/verbalizer.d.mts +148 -0
  189. package/dist/verbalizer.mjs +996 -0
  190. package/package.json +249 -0
package/dist/hash.mjs ADDED
@@ -0,0 +1,2095 @@
1
+ /**
2
+ * Copyright 2026 - present Nazmul Hassan
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { a as isNonEmptyString, d as isString } from "./primitives-KsFUp3kQ.mjs";
18
+ import { E as isObjectWithKeys, c as isHexString, m as isUUID, n as isBinaryString, t as isBase64, w as isNotEmptyObject } from "./specials-uhDuRg8H.mjs";
19
+ import { c as _toSeconds, s as _secToDate, t as parseMSec } from "./parse-N7g942uy.mjs";
20
+ import { h as stripJsonEdgeGarbage, m as stableStringify } from "./utils-ClW9LA6f.mjs";
21
+ import { t as generateRandomID } from "./basics-Dp_aEK81.mjs";
22
+
23
+ //#region src/hash/utils.ts
24
+ /**
25
+ * * Generates a random hexadecimal string of the specified length.
26
+ *
27
+ * @param length - Number of hex characters to generate.
28
+ * @param uppercase - Whether to return uppercase `A–F` characters. Defaults to `false` (lowercase).
29
+ *
30
+ * @returns A randomly generated hexadecimal string.
31
+ *
32
+ * @remarks
33
+ * - This function generates a random hexadecimal string of the specified length.
34
+ * - It uses {@link crypto.getRandomValues} when available for secure randomness, and falls back to {@link Math.random} if not.
35
+ * - The output is a string of hex characters (`0–9`, `a–f` or `A–F`) with no prefixes or separators.
36
+ * - If `length` is `0` or negative, an empty string is returned.
37
+ *
38
+ * @example
39
+ * // 16-character lowercase hex
40
+ * const id = randomHex(16);
41
+ *
42
+ * @example
43
+ * // 8-character uppercase hex
44
+ * const token = randomHex(8, true);
45
+ */
46
+ function randomHex(length, uppercase = false) {
47
+ if (length <= 0) return "";
48
+ const expected = _bytesToRandomHex(new Uint8Array(Math.ceil(length / 2))).slice(0, length);
49
+ return uppercase ? expected.toUpperCase() : expected;
50
+ }
51
+ /**
52
+ * * Converts a UTF-8 string to a byte array (`Uint8Array`).
53
+ *
54
+ * This function encodes a JavaScript string into UTF-8 bytes, handling all Unicode code points including supplementary characters (surrogate pairs).
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Basic ASCII
59
+ * const asciiBytes = utf8ToBytes('hello');
60
+ * // Returns:
61
+ * Uint8Array(5) [104, 101, 108, 108, 111]
62
+ *
63
+ * // Unicode characters
64
+ * const unicodeBytes = utf8ToBytes('Hello পৃথিবী!');
65
+ * // Returns:
66
+ * Uint8Array(25) [
67
+ * 72, 101, 108, 108, 111, 32,
68
+ * 224, 166, 170, 224, 167, 131,
69
+ * 224, 166, 165, 224, 166, 191,
70
+ * 224, 166, 172, 224, 167, 128,
71
+ * 33
72
+ * ]
73
+ * ```
74
+ *
75
+ * @param str - The input string to encode as UTF-8 bytes.
76
+ * @returns A `Uint8Array` containing the UTF-8 encoded bytes.
77
+ *
78
+ * @remarks
79
+ * - The encoding follows the UTF-8 specification:
80
+ * - 1-byte sequence for code points U+0000 to U+007F (ASCII)
81
+ * - 2-byte sequence for code points U+0080 to U+07FF
82
+ * - 3-byte sequence for code points U+0800 to U+FFFF
83
+ * - 4-byte sequence for code points U+10000 to U+10FFFF (surrogate pairs)
84
+ *
85
+ * **Note:** Invalid surrogate pairs in the input string are silently ignored.
86
+ *
87
+ * @see {@link bytesToUtf8} for the inverse operation
88
+ */
89
+ function utf8ToBytes(str) {
90
+ const out = [];
91
+ for (let i = 0; i < str.length; i++) {
92
+ const code = str.charCodeAt(i);
93
+ if (code < 128) out.push(code);
94
+ else if (code < 2048) out.push(192 | code >> 6, 128 | code & 63);
95
+ else if (code >= 55296 && code <= 57343) {
96
+ if (code < 56320 && i + 1 < str.length) {
97
+ const hi = code;
98
+ const lo = str.charCodeAt(++i);
99
+ const codePoint = 65536 + (hi - 55296 << 10) + (lo - 56320);
100
+ out.push(240 | codePoint >> 18, 128 | codePoint >> 12 & 63, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
101
+ }
102
+ } else out.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
103
+ }
104
+ return new Uint8Array(out);
105
+ }
106
+ /**
107
+ * * Converts `UTF-8` encoded bytes back to a string.
108
+ *
109
+ * This function decodes a `Uint8Array` containing `UTF-8` bytes into a JavaScript string.
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * // Decode UTF-8 bytes
114
+ * const bytes = new Uint8Array([104, 101, 108, 108, 111]);
115
+ * const str = bytesToUtf8(bytes);
116
+ * // Returns: 'hello'
117
+ *
118
+ * // Round-trip conversion
119
+ * const original = 'Hello 🌍';
120
+ * const bytes = utf8ToBytes(original);
121
+ * const decoded = bytesToUtf8(bytes);
122
+ * console.log(original === decoded); // true
123
+ * ```
124
+ *
125
+ * @param bytes - A `Uint8Array` containing `UTF-8` encoded bytes.
126
+ * @returns The decoded string.
127
+ *
128
+ * @remarks
129
+ * - The function handles all valid `UTF-8` sequences:
130
+ * - 1-byte sequences (0xxxxxxx) → ASCII characters
131
+ * - 2-byte sequences (110xxxxx 10xxxxxx)
132
+ * - 3-byte sequences (1110xxxx 10xxxxxx 10xxxxxx)
133
+ * - 4-byte sequences (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx) → surrogate pairs
134
+ *
135
+ * @see {@link utf8ToBytes} for the inverse operation
136
+ */
137
+ function bytesToUtf8(bytes) {
138
+ let out = "";
139
+ let i = 0;
140
+ while (i < bytes.length) {
141
+ const b1 = bytes[i++];
142
+ if (b1 < 128) out += String.fromCharCode(b1);
143
+ else if (b1 >= 192 && b1 < 224) {
144
+ const b2 = bytes[i++];
145
+ out += String.fromCharCode((b1 & 31) << 6 | b2 & 63);
146
+ } else if (b1 >= 224 && b1 < 240) {
147
+ const b2 = bytes[i++];
148
+ const b3 = bytes[i++];
149
+ out += String.fromCharCode((b1 & 15) << 12 | (b2 & 63) << 6 | b3 & 63);
150
+ } else {
151
+ const b2 = bytes[i++];
152
+ const b3 = bytes[i++];
153
+ const b4 = bytes[i++];
154
+ let codePoint = (b1 & 7) << 18 | (b2 & 63) << 12 | (b3 & 63) << 6 | b4 & 63;
155
+ codePoint -= 65536;
156
+ out += String.fromCharCode(55296 + (codePoint >> 10 & 1023), 56320 + (codePoint & 1023));
157
+ }
158
+ }
159
+ return out;
160
+ }
161
+ /**
162
+ * * Decodes a `Base64` string to bytes.
163
+ * - This function converts a `Base64`-encoded string back to its original byte representation.
164
+ * - It handles standard `Base64` encoding with '=', '+', '/' characters.
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * // Decode Base64 string
169
+ * const bytes = base64ToBytes('aGVsbG8gd29ybGQ=');
170
+ * // Returns: Uint8Array(11) [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
171
+ *
172
+ * // Empty string
173
+ * const empty = base64ToBytes('');
174
+ * // Returns: Uint8Array(0) []
175
+ * ```
176
+ *
177
+ * @param str - The `Base64`-encoded string to decode.
178
+ * @returns A `Uint8Array` containing the decoded bytes.
179
+ *
180
+ * @remarks
181
+ * - The function supports:
182
+ * - Standard `Base64` alphabet (A-Z, a-z, 0-9, +, /)
183
+ * - Padding with '=' characters
184
+ * - Ignores whitespace (though not explicitly trimmed in this implementation)
185
+ *
186
+ * @see {@link bytesToBase64} for the inverse operation
187
+ */
188
+ function base64ToBytes(str) {
189
+ const _b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
190
+ const out = [];
191
+ let i = 0;
192
+ while (i < str.length) {
193
+ const h1 = _b64chars.indexOf(str[i++]);
194
+ const h2 = _b64chars.indexOf(str[i++]);
195
+ const h3 = _b64chars.indexOf(str[i++]);
196
+ const h4 = _b64chars.indexOf(str[i++]);
197
+ const o1 = h1 << 2 | h2 >> 4;
198
+ const o2 = (h2 & 15) << 4 | h3 >> 2;
199
+ const o3 = (h3 & 3) << 6 | h4;
200
+ out.push(o1);
201
+ if (h3 !== 64) out.push(o2);
202
+ if (h4 !== 64) out.push(o3);
203
+ }
204
+ return new Uint8Array(out);
205
+ }
206
+ /**
207
+ * * Encodes bytes to a `Base64` string.
208
+ * - This function converts a `Uint8Array` to a `Base64`-encoded string using the standard `Base64` alphabet with padding.
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * // Encode bytes to Base64
213
+ * const bytes = new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]);
214
+ * const b64 = bytesToBase64(bytes);
215
+ * // Returns: 'aGVsbG8gd29ybGQ='
216
+ *
217
+ * // Empty array
218
+ * const empty = bytesToBase64(new Uint8Array(0));
219
+ * // Returns: ''
220
+ * ```
221
+ *
222
+ * @param bytes - The bytes to encode as `Base64`.
223
+ * @returns The `Base64`-encoded string.
224
+ *
225
+ * @remarks
226
+ * The encoding uses:
227
+ * - Standard `Base64` alphabet (A-Z, a-z, 0-9, +, /)
228
+ * - '=' padding for incomplete groups
229
+ * - No line breaks or whitespace
230
+ *
231
+ * This is a pure JavaScript implementation that doesn't rely on `btoa()`.
232
+ *
233
+ * @see {@link base64ToBytes} for the inverse operation
234
+ */
235
+ function bytesToBase64(bytes) {
236
+ const _b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
237
+ let out = "";
238
+ let i = 0;
239
+ while (i < bytes.length) {
240
+ const o1 = bytes[i++];
241
+ const o2 = bytes[i++];
242
+ const o3 = bytes[i++];
243
+ const h1 = o1 >> 2;
244
+ const h2 = (o1 & 3) << 4 | o2 >> 4;
245
+ const h3 = (o2 & 15) << 2 | o3 >> 6;
246
+ const h4 = o3 & 63;
247
+ out += _b64chars[h1] + _b64chars[h2] + _b64chars[isNaN(o2) ? 64 : h3] + _b64chars[isNaN(o3) ? 64 : h4];
248
+ }
249
+ return out;
250
+ }
251
+ /**
252
+ * * Concatenates multiple `Uint8Array`s into a single `Uint8Array`.
253
+ * - This function efficiently combines multiple byte arrays without creating intermediate strings or arrays.
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * // Concatenate multiple arrays
258
+ * const a = new Uint8Array([1, 2, 3]);
259
+ * const b = new Uint8Array([4, 5]);
260
+ * const c = new Uint8Array([6, 7, 8, 9]);
261
+ * const result = concatBytes(a, b, c);
262
+ * // Returns: Uint8Array(9) [1, 2, 3, 4, 5, 6, 7, 8, 9]
263
+ *
264
+ * // Single array
265
+ * const single = concatBytes(new Uint8Array([1, 2, 3]));
266
+ * // Returns: Uint8Array(3) [1, 2, 3]
267
+ *
268
+ * // No arrays
269
+ * const empty = concatBytes();
270
+ * // Returns: Uint8Array(0) []
271
+ * ```
272
+ *
273
+ * @param parts - One or more `Uint8Array`s to concatenate.
274
+ * @returns A new `Uint8Array` containing all the bytes from the input arrays in the order they were provided.
275
+ *
276
+ * @remarks The function allocates a single `Uint8Array` of the total combined length and copies all bytes into it using `set()` for optimal performance.
277
+ */
278
+ function concatBytes(...parts) {
279
+ const len = parts.reduce((s, p) => s + p.length, 0);
280
+ const out = new Uint8Array(len);
281
+ let offset = 0;
282
+ for (const p of parts) {
283
+ out.set(p, offset);
284
+ offset += p.length;
285
+ }
286
+ return out;
287
+ }
288
+ /**
289
+ * * Computes the `SHA-256` hash of raw bytes.
290
+ * - This is a pure JavaScript implementation of the `SHA-256` cryptographic hash function that operates directly on byte arrays (`Uint8Array`).
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * // Hash raw bytes
295
+ * const bytes = new Uint8Array([104, 101, 108, 108, 111]); // "hello"
296
+ * const hash = sha256Bytes(bytes);
297
+ * // Returns: Uint8Array(32) with SHA-256 hash
298
+ *
299
+ * // Verify with string hash
300
+ * const strHash = sha256('hello');
301
+ * const bytesHash = bytesToHex(sha256Bytes(utf8ToBytes('hello')));
302
+ * console.log(strHash === bytesHash); // true
303
+ * ```
304
+ *
305
+ * @param message - The bytes to hash as a `Uint8Array`.
306
+ * @returns A `Uint8Array` of 32 bytes (256 bits) containing the `SHA-256` hash.
307
+ *
308
+ * @remarks
309
+ * - Implementation details:
310
+ * - Follows the `SHA-256` specification (FIPS 180-4)
311
+ * - Uses big-endian byte order throughout
312
+ * - Processes messages in 512-bit (64-byte) blocks
313
+ * - Applies proper padding with message length
314
+ * - Uses all required `SHA-256` round constants
315
+ * - Returns hash as 32-byte array
316
+ *
317
+ * @see {@link hmacSha256} for `HMAC-SHA256` computation
318
+ */
319
+ function sha256Bytes(message) {
320
+ const H = new Uint32Array([
321
+ 1779033703,
322
+ 3144134277,
323
+ 1013904242,
324
+ 2773480762,
325
+ 1359893119,
326
+ 2600822924,
327
+ 528734635,
328
+ 1541459225
329
+ ]);
330
+ const K = new Uint32Array([
331
+ 1116352408,
332
+ 1899447441,
333
+ 3049323471,
334
+ 3921009573,
335
+ 961987163,
336
+ 1508970993,
337
+ 2453635748,
338
+ 2870763221,
339
+ 3624381080,
340
+ 310598401,
341
+ 607225278,
342
+ 1426881987,
343
+ 1925078388,
344
+ 2162078206,
345
+ 2614888103,
346
+ 3248222580,
347
+ 3835390401,
348
+ 4022224774,
349
+ 264347078,
350
+ 604807628,
351
+ 770255983,
352
+ 1249150122,
353
+ 1555081692,
354
+ 1996064986,
355
+ 2554220882,
356
+ 2821834349,
357
+ 2952996808,
358
+ 3210313671,
359
+ 3336571891,
360
+ 3584528711,
361
+ 113926993,
362
+ 338241895,
363
+ 666307205,
364
+ 773529912,
365
+ 1294757372,
366
+ 1396182291,
367
+ 1695183700,
368
+ 1986661051,
369
+ 2177026350,
370
+ 2456956037,
371
+ 2730485921,
372
+ 2820302411,
373
+ 3259730800,
374
+ 3345764771,
375
+ 3516065817,
376
+ 3600352804,
377
+ 4094571909,
378
+ 275423344,
379
+ 430227734,
380
+ 506948616,
381
+ 659060556,
382
+ 883997877,
383
+ 958139571,
384
+ 1322822218,
385
+ 1537002063,
386
+ 1747873779,
387
+ 1955562222,
388
+ 2024104815,
389
+ 2227730452,
390
+ 2361852424,
391
+ 2428436474,
392
+ 2756734187,
393
+ 3204031479,
394
+ 3329325298
395
+ ]);
396
+ const ml = message.length * 8;
397
+ const padding = new Uint8Array(message.length + 8 + 64 & -64 || 64);
398
+ padding.set(message, 0);
399
+ padding[message.length] = 128;
400
+ const lenView = new DataView(padding.buffer);
401
+ lenView.setUint32(padding.length - 8, Math.floor(ml / 4294967296), false);
402
+ lenView.setUint32(padding.length - 4, ml & 4294967295, false);
403
+ const w = new Uint32Array(64);
404
+ const chunkView = new DataView(padding.buffer);
405
+ for (let offset = 0; offset < padding.length; offset += 64) {
406
+ for (let t = 0; t < 16; t++) w[t] = chunkView.getUint32(offset + t * 4, false);
407
+ for (let t = 16; t < 64; t++) {
408
+ const s0 = (w[t - 15] >>> 7 | w[t - 15] << 25) ^ (w[t - 15] >>> 18 | w[t - 15] << 14) ^ w[t - 15] >>> 3;
409
+ const s1 = (w[t - 2] >>> 17 | w[t - 2] << 15) ^ (w[t - 2] >>> 19 | w[t - 2] << 13) ^ w[t - 2] >>> 10;
410
+ w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
411
+ }
412
+ let a = H[0];
413
+ let b = H[1];
414
+ let c = H[2];
415
+ let d = H[3];
416
+ let e = H[4];
417
+ let f = H[5];
418
+ let g = H[6];
419
+ let h = H[7];
420
+ for (let t = 0; t < 64; t++) {
421
+ const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
422
+ const ch = e & f ^ ~e & g;
423
+ const temp1 = h + S1 + ch + K[t] + w[t] >>> 0;
424
+ const temp2 = ((a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10)) + (a & b ^ a & c ^ b & c) >>> 0;
425
+ h = g;
426
+ g = f;
427
+ f = e;
428
+ e = d + temp1 >>> 0;
429
+ d = c;
430
+ c = b;
431
+ b = a;
432
+ a = temp1 + temp2 >>> 0;
433
+ }
434
+ H[0] = H[0] + a >>> 0;
435
+ H[1] = H[1] + b >>> 0;
436
+ H[2] = H[2] + c >>> 0;
437
+ H[3] = H[3] + d >>> 0;
438
+ H[4] = H[4] + e >>> 0;
439
+ H[5] = H[5] + f >>> 0;
440
+ H[6] = H[6] + g >>> 0;
441
+ H[7] = H[7] + h >>> 0;
442
+ }
443
+ const out = new Uint8Array(32);
444
+ const outView = new DataView(out.buffer);
445
+ for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i], false);
446
+ return out;
447
+ }
448
+ /**
449
+ * * Computes `HMAC-SHA256` (Hash-based Message Authentication Code using `SHA-256`).
450
+ * - This function implements the `HMAC` algorithm with `SHA-256` as the underlying hash function, providing message authentication and integrity verification.
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * // Basic HMAC calculation
455
+ * const key = new TextEncoder().encode('secret-key');
456
+ * const message = new TextEncoder().encode('Hello, world!');
457
+ * const hmac = hmacSha256(key, message);
458
+ *
459
+ * // Using with string inputs
460
+ * const keyBytes = new TextEncoder().encode('my-key');
461
+ * const msgBytes = new TextEncoder().encode('data to authenticate');
462
+ * const hmacResult = hmacSha256(keyBytes, msgBytes);
463
+ * const hexResult = bytesToHex(hmacResult);
464
+ * ```
465
+ *
466
+ * @param key - The secret key as a `Uint8Array`.
467
+ * @param message - The message to authenticate as a `Uint8Array`.
468
+ * @returns A `Uint8Array` of 32 bytes containing the `HMAC-SHA256` tag.
469
+ *
470
+ * @remarks
471
+ * - Algorithm steps:
472
+ * - 1. Keys longer than 64 bytes are hashed with `SHA-256`
473
+ * - 2. Keys shorter than 64 bytes are padded with zeros
474
+ * - 3. Inner hash: `SHA-256((key ⊕ ipad) || message)` where ipad = 0x36 repeated
475
+ * - 4. Outer hash: `SHA-256((key ⊕ opad) || inner_hash)` where opad = 0x5C repeated
476
+ *
477
+ * - The implementation follows RFC 2104 and RFC 4231 specifications.
478
+ * - Block size for `SHA-256` HMAC is 64 bytes (512 bits).
479
+ *
480
+ * **Common use cases:**
481
+ * - API authentication tokens
482
+ * - Message integrity verification
483
+ * - Key derivation (as part of `HKDF`)
484
+ *
485
+ * @see {@link sha256Bytes} for the underlying hash function
486
+ */
487
+ function hmacSha256(key, message) {
488
+ const blockSize = 64;
489
+ let k = key;
490
+ if (k.length > blockSize) k = sha256Bytes(k);
491
+ if (k.length < blockSize) {
492
+ const tmp = new Uint8Array(blockSize);
493
+ tmp.set(k, 0);
494
+ k = tmp;
495
+ }
496
+ const oKeyPad = new Uint8Array(blockSize);
497
+ const iKeyPad = new Uint8Array(blockSize);
498
+ for (let i = 0; i < blockSize; i++) {
499
+ oKeyPad[i] = k[i] ^ 92;
500
+ iKeyPad[i] = k[i] ^ 54;
501
+ }
502
+ const inner = new Uint8Array(iKeyPad.length + message.length);
503
+ inner.set(iKeyPad, 0);
504
+ inner.set(message, iKeyPad.length);
505
+ const innerHash = sha256Bytes(inner);
506
+ const outer = new Uint8Array(oKeyPad.length + innerHash.length);
507
+ outer.set(oKeyPad, 0);
508
+ outer.set(innerHash, oKeyPad.length);
509
+ return sha256Bytes(outer);
510
+ }
511
+ /**
512
+ * * Converts a `Uint8Array` to a `Uint32Array` with big-endian byte order.
513
+ * - This function groups bytes into 32-bit integers, reading them in big-endian (most significant byte first) order. Missing bytes are treated as zero.
514
+ *
515
+ * @example
516
+ * ```typescript
517
+ * // Convert bytes to 32-bit integers
518
+ * const bytes = new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
519
+ * const words = uint8To32ArrayBE(bytes);
520
+ * // Returns: Uint32Array(2) [0x12345678, 0x9ABC0000] or equivalent: Uint32Array(2) [ 305419896, 2596012032 ]
521
+ *
522
+ * // Partial final word
523
+ * const partial = new Uint8Array([0xFF, 0xEE, 0xDD]);
524
+ * const words2 = uint8To32ArrayBE(partial);
525
+ * // Returns: Uint32Array(1) [0xFFEEDD00] or equivalent: Uint32Array(1) [ 4293844224 ]
526
+ * ```
527
+ *
528
+ * @param bytes - The bytes to convert to 32-bit words.
529
+ * @returns A `Uint32Array` containing the 32-bit big-endian words.
530
+ *
531
+ * @remarks
532
+ * - Input length doesn't need to be a multiple of 4
533
+ * - Missing bytes in the final word are padded with zeros
534
+ * - Byte order: `bytes[0]` is the most significant byte of `out[0]`
535
+ * - This is useful for cryptographic operations that work with 32-bit words
536
+ */
537
+ function uint8To32ArrayBE(bytes) {
538
+ const len = Math.ceil(bytes.length / 4);
539
+ const out = new Uint32Array(len);
540
+ for (let i = 0; i < len; i++) {
541
+ const base = i * 4;
542
+ out[i] = (bytes[base] || 0) << 24 | (bytes[base + 1] || 0) << 16 | (bytes[base + 2] || 0) << 8 | (bytes[base + 3] || 0) << 0;
543
+ }
544
+ return out;
545
+ }
546
+ /**
547
+ * * Converts a 32-bit integer into a 4-byte `Uint8Array` in big-endian (network) byte order.
548
+ * - This function takes a 32-bit integer and encodes it as 4 bytes with the most significant byte first (big-endian order), which is the standard for network protocols and many cryptographic operations.
549
+ *
550
+ * @example
551
+ * ```typescript
552
+ * // Convert integer to bytes
553
+ * const bytes = intTo4BytesBE(0x12345678);
554
+ * // Returns: Uint8Array(4) [0x12, 0x34, 0x56, 0x78] or equivalent: Uint8Array(4) [ 18, 52, 86, 120 ]
555
+ *
556
+ * // Maximum 32-bit value
557
+ * const maxBytes = intTo4BytesBE(0xFFFFFFFF);
558
+ * // Returns: Uint8Array(4) [0xFF, 0xFF, 0xFF, 0xFF]
559
+ *
560
+ * // Zero
561
+ * const zeroBytes = intTo4BytesBE(0);
562
+ * // Returns: Uint8Array(4) [0x00, 0x00, 0x00, 0x00]
563
+ * ```
564
+ *
565
+ * @param n - The 32-bit integer to convert. Values beyond 32 bits will be truncated.
566
+ * @returns A 4-byte `Uint8Array` representing the value in big-endian format.
567
+ *
568
+ * @remarks
569
+ * - The function uses unsigned 32-bit arithmetic (`>>>` operator)
570
+ * - Only the lower 32 bits of the input are used (truncation)
571
+ * - Output is always exactly 4 bytes
572
+ * - Big-endian order: byte[0] = most significant, byte[3] = least significant
573
+ *
574
+ * **Common use cases:**
575
+ * - Encoding message lengths in network protocols
576
+ * - Preparing data for cryptographic operations
577
+ * - Converting integers for storage or transmission
578
+ *
579
+ * @see {@link uint8To32ArrayBE} for bytes to 32-bit integers conversion
580
+ */
581
+ function intTo4BytesBE(n) {
582
+ const b = new Uint8Array(4);
583
+ b[0] = n >>> 24 & 255;
584
+ b[1] = n >>> 16 & 255;
585
+ b[2] = n >>> 8 & 255;
586
+ b[3] = n & 255;
587
+ return b;
588
+ }
589
+ /**
590
+ * * Converts a `Uint8Array` to a lowercase hexadecimal string.
591
+ * - This function encodes binary data (bytes) as a hexadecimal string, with each byte represented as two lowercase hexadecimal digits (0-9, a-f).
592
+ *
593
+ * @example
594
+ * ```typescript
595
+ * // Convert bytes to hex
596
+ * const bytes = new Uint8Array([0x12, 0xAB, 0xFF, 0x00]);
597
+ * const hex = bytesToHex(bytes);
598
+ * // Returns: '12abff00'
599
+ *
600
+ * // Empty array
601
+ * const emptyHex = bytesToHex(new Uint8Array(0));
602
+ * // Returns: ''
603
+ *
604
+ * // SHA-256 hash to hex
605
+ * const hashBytes = sha256Bytes(utf8ToBytes('hello'));
606
+ * const hashHex = bytesToHex(hashBytes);
607
+ * // Returns: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
608
+ * ```
609
+ *
610
+ * @param bytes - The bytes to convert to hexadecimal representation.
611
+ * @returns A lowercase hexadecimal string where each byte is represented by two characters (00-ff).
612
+ *
613
+ * @remarks
614
+ * - Always returns lowercase letters (a-f)
615
+ * - Zero pads single-digit hex values (e.g., 0x0F → "0f", not "f")
616
+ * - Efficient O(n) implementation using string concatenation
617
+ * - No prefix (e.g., no "0x" at the beginning)
618
+ *
619
+ * **Common use cases:**
620
+ * - Displaying cryptographic hashes and signatures
621
+ * - Debugging binary data
622
+ * - Converting binary data for JSON serialization
623
+ * - Creating hex-encoded strings for APIs and protocols
624
+ *
625
+ * @see {@link hexToBytes} for reverse process
626
+ */
627
+ function bytesToHex(bytes) {
628
+ let hex = "";
629
+ for (let i = 0; i < bytes.length; i++) {
630
+ const byte = bytes[i].toString(16).padStart(2, "0");
631
+ hex += byte;
632
+ }
633
+ return hex;
634
+ }
635
+ /**
636
+ * * Converts a hexadecimal string to a `Uint8Array`.
637
+ * - This function decodes a hexadecimal-encoded string into its raw byte representation, where every two hexadecimal characters (00–ff) are converted into one byte.
638
+ *
639
+ * @example
640
+ * // Convert hex to bytes
641
+ * const hex = '12abff00';
642
+ * const bytes = hexToBytes(hex);
643
+ * // Returns: Uint8Array(4) [18, 171, 255, 0]
644
+ *
645
+ * // Empty string
646
+ * const emptyBytes = hexToBytes('');
647
+ * // Returns: Uint8Array []
648
+ *
649
+ * // Decode SHA-256 hash from hex
650
+ * const hashHex = '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824';
651
+ * const hashBytes = hexToBytes(hashHex);
652
+ * // Returns: Uint8Array(32)
653
+ *
654
+ * @param hex - A hexadecimal string where each byte is represented by two characters (00–ff).
655
+ * @returns A `Uint8Array` containing the decoded bytes. Returns an empty array for invalid input.
656
+ *
657
+ * @remarks
658
+ * - Accepts lowercase and uppercase hexadecimal characters (0–9, a–f, A–F) with or without space between bytes
659
+ * - Ignores no prefixes (e.g., does not support "0x")
660
+ * - Requires an even number of hexadecimal characters
661
+ * - Efficient O(n) implementation with direct byte parsing
662
+ *
663
+ * **Common use cases:**
664
+ * - Decoding cryptographic hashes and signatures
665
+ * - Parsing hex-encoded binary payloads
666
+ * - Reconstructing binary data from storage or transport formats
667
+ * - Working with low-level protocols and binary APIs
668
+ *
669
+ * @see {@link bytesToHex} for the reverse process
670
+ */
671
+ function hexToBytes(hex) {
672
+ if (!isHexString(hex)) return new Uint8Array();
673
+ const bytes = _splitByCharLength(hex, 2).map((h) => parseInt(h, 16));
674
+ return new Uint8Array(bytes);
675
+ }
676
+
677
+ //#endregion
678
+ //#region src/hash/helpers.ts
679
+ /** Adds two 32-bit numbers */
680
+ function _add32(x, y) {
681
+ return x + y & 4294967295;
682
+ }
683
+ /** Converts a 32-bit number to a 4-byte hex string */
684
+ function _numToHex(n) {
685
+ return [
686
+ 0,
687
+ 8,
688
+ 16,
689
+ 24
690
+ ].map((shift) => (n >> shift & 255).toString(16).padStart(2, "0")).join("");
691
+ }
692
+ /** Converts a 64-character string block to an array of 16 numbers */
693
+ function _stringToNumbers(s) {
694
+ return Array.from({ length: 16 }, (_, i) => {
695
+ const x = i << 2;
696
+ return s.charCodeAt(x) | s.charCodeAt(x + 1) << 8 | s.charCodeAt(x + 2) << 16 | s.charCodeAt(x + 3) << 24;
697
+ });
698
+ }
699
+ /** Common MD5 transformation function */
700
+ function _transform(q, a, b, x, s, t) {
701
+ const a1 = _add32(_add32(a, q), _add32(x, t));
702
+ return _add32(a1 << s | a1 >>> 32 - s, b);
703
+ }
704
+ /** Round 1 operation */
705
+ const ff = (a, b, c, d, x, s, t) => {
706
+ return _transform(b & c | ~b & d, a, b, x, s, t);
707
+ };
708
+ /** Round 2 operation */
709
+ const gg = (a, b, c, d, x, s, t) => {
710
+ return _transform(b & d | c & ~d, a, b, x, s, t);
711
+ };
712
+ /** Round 3 operation */
713
+ const hh = (a, b, c, d, x, s, t) => {
714
+ return _transform(b ^ c ^ d, a, b, x, s, t);
715
+ };
716
+ /** Round 4 operation */
717
+ const ii = (a, b, c, d, x, s, t) => {
718
+ return _transform(c ^ (b | ~d), a, b, x, s, t);
719
+ };
720
+ /** Performs one MD5 cycle on a 4-element state with 16-word block */
721
+ function _md5cycle(x, k) {
722
+ let a = x[0];
723
+ let b = x[1];
724
+ let c = x[2];
725
+ let d = x[3];
726
+ a = ff(a, b, c, d, k[0], 7, -680876936);
727
+ d = ff(d, a, b, c, k[1], 12, -389564586);
728
+ c = ff(c, d, a, b, k[2], 17, 606105819);
729
+ b = ff(b, c, d, a, k[3], 22, -1044525330);
730
+ a = ff(a, b, c, d, k[4], 7, -176418897);
731
+ d = ff(d, a, b, c, k[5], 12, 1200080426);
732
+ c = ff(c, d, a, b, k[6], 17, -1473231341);
733
+ b = ff(b, c, d, a, k[7], 22, -45705983);
734
+ a = ff(a, b, c, d, k[8], 7, 1770035416);
735
+ d = ff(d, a, b, c, k[9], 12, -1958414417);
736
+ c = ff(c, d, a, b, k[10], 17, -42063);
737
+ b = ff(b, c, d, a, k[11], 22, -1990404162);
738
+ a = ff(a, b, c, d, k[12], 7, 1804603682);
739
+ d = ff(d, a, b, c, k[13], 12, -40341101);
740
+ c = ff(c, d, a, b, k[14], 17, -1502002290);
741
+ b = ff(b, c, d, a, k[15], 22, 1236535329);
742
+ a = gg(a, b, c, d, k[1], 5, -165796510);
743
+ d = gg(d, a, b, c, k[6], 9, -1069501632);
744
+ c = gg(c, d, a, b, k[11], 14, 643717713);
745
+ b = gg(b, c, d, a, k[0], 20, -373897302);
746
+ a = gg(a, b, c, d, k[5], 5, -701558691);
747
+ d = gg(d, a, b, c, k[10], 9, 38016083);
748
+ c = gg(c, d, a, b, k[15], 14, -660478335);
749
+ b = gg(b, c, d, a, k[4], 20, -405537848);
750
+ a = gg(a, b, c, d, k[9], 5, 568446438);
751
+ d = gg(d, a, b, c, k[14], 9, -1019803690);
752
+ c = gg(c, d, a, b, k[3], 14, -187363961);
753
+ b = gg(b, c, d, a, k[8], 20, 1163531501);
754
+ a = gg(a, b, c, d, k[13], 5, -1444681467);
755
+ d = gg(d, a, b, c, k[2], 9, -51403784);
756
+ c = gg(c, d, a, b, k[7], 14, 1735328473);
757
+ b = gg(b, c, d, a, k[12], 20, -1926607734);
758
+ a = hh(a, b, c, d, k[5], 4, -378558);
759
+ d = hh(d, a, b, c, k[8], 11, -2022574463);
760
+ c = hh(c, d, a, b, k[11], 16, 1839030562);
761
+ b = hh(b, c, d, a, k[14], 23, -35309556);
762
+ a = hh(a, b, c, d, k[1], 4, -1530992060);
763
+ d = hh(d, a, b, c, k[4], 11, 1272893353);
764
+ c = hh(c, d, a, b, k[7], 16, -155497632);
765
+ b = hh(b, c, d, a, k[10], 23, -1094730640);
766
+ a = hh(a, b, c, d, k[13], 4, 681279174);
767
+ d = hh(d, a, b, c, k[0], 11, -358537222);
768
+ c = hh(c, d, a, b, k[3], 16, -722521979);
769
+ b = hh(b, c, d, a, k[6], 23, 76029189);
770
+ a = hh(a, b, c, d, k[9], 4, -640364487);
771
+ d = hh(d, a, b, c, k[12], 11, -421815835);
772
+ c = hh(c, d, a, b, k[15], 16, 530742520);
773
+ b = hh(b, c, d, a, k[2], 23, -995338651);
774
+ a = ii(a, b, c, d, k[0], 6, -198630844);
775
+ d = ii(d, a, b, c, k[7], 10, 1126891415);
776
+ c = ii(c, d, a, b, k[14], 15, -1416354905);
777
+ b = ii(b, c, d, a, k[5], 21, -57434055);
778
+ a = ii(a, b, c, d, k[12], 6, 1700485571);
779
+ d = ii(d, a, b, c, k[3], 10, -1894986606);
780
+ c = ii(c, d, a, b, k[10], 15, -1051523);
781
+ b = ii(b, c, d, a, k[1], 21, -2054922799);
782
+ a = ii(a, b, c, d, k[8], 6, 1873313359);
783
+ d = ii(d, a, b, c, k[15], 10, -30611744);
784
+ c = ii(c, d, a, b, k[6], 15, -1560198380);
785
+ b = ii(b, c, d, a, k[13], 21, 1309151649);
786
+ a = ii(a, b, c, d, k[4], 6, -145523070);
787
+ d = ii(d, a, b, c, k[11], 10, -1120210379);
788
+ c = ii(c, d, a, b, k[2], 15, 718787259);
789
+ b = ii(b, c, d, a, k[9], 21, -343485551);
790
+ x[0] = _add32(a, x[0]);
791
+ x[1] = _add32(b, x[1]);
792
+ x[2] = _add32(c, x[2]);
793
+ x[3] = _add32(d, x[3]);
794
+ }
795
+ /**
796
+ * Computes UUID timestamp in 100-nanosecond intervals since
797
+ * 00:00:00.00 15 October 1582 (Gregorian epoch).
798
+ */
799
+ function _uuidTimestamp() {
800
+ return (BigInt(Date.now()) + 12219292800000n) * 10000n;
801
+ }
802
+ /**
803
+ * Generates a random 48-bit node ID.
804
+ * LSB of first byte must be 1 to indicate a randomly generated node.
805
+ */
806
+ function _randomNode48() {
807
+ const node = randomHex(12).split("");
808
+ return (parseInt(node.slice(0, 2).join(""), 16) | 1).toString(16).padStart(2, "0") + node.slice(2).join("");
809
+ }
810
+ /**
811
+ * Generates random hex string of given byte length using crypto API if available, otherwise falls back to Math.random.
812
+ * @param bytes Instance of {@link Uint8Array} to fill with random values.
813
+ * @returns Hex string representation of the random bytes.
814
+ */
815
+ function _bytesToRandomHex(bytes) {
816
+ if (crypto?.getRandomValues) crypto.getRandomValues(bytes);
817
+ else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
818
+ let hex = "";
819
+ for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
820
+ return hex;
821
+ }
822
+ /** * Generates a 14-bit clock sequence (2 bytes, but only 14 bits used). */
823
+ function _clockSeq14() {
824
+ return (parseInt(randomHex(4), 16) & 16383).toString(16).padStart(4, "0");
825
+ }
826
+ /** Convert a hex string to UUID format */
827
+ function _formatUUID(h, v, up) {
828
+ const part3 = String(v) + h.slice(13, 16);
829
+ const part4 = _hexVariant(h.slice(16, 18)) + h.slice(18, 20);
830
+ const formatted = [
831
+ h.slice(0, 8),
832
+ h.slice(8, 12),
833
+ part3,
834
+ part4,
835
+ h.slice(20, 32)
836
+ ].join("-");
837
+ return up ? formatted.toUpperCase() : formatted;
838
+ }
839
+ /** Ensure UUID variant is RFC4122 compliant */
840
+ function _hexVariant(hex) {
841
+ return (parseInt(hex, 16) & 63 | 128).toString(16).padStart(2, "0");
842
+ }
843
+ /** Check if the uuid `options` is compatible for `v3` and `v5` */
844
+ function _isOptionV3V5(opt) {
845
+ if (isObjectWithKeys(opt, ["name", "namespace"])) return isUUID(opt?.namespace) && isNonEmptyString(opt?.name);
846
+ return false;
847
+ }
848
+ /** Check UUID version after checking for valid UUID from `v1-v8` */
849
+ function _checkUUIDVersion(value, v) {
850
+ return isUUID(value) && value[14] === v;
851
+ }
852
+ /**
853
+ * Compares two encrypted strings or byte arrays in constant time.
854
+ * Prevents timing attacks by ensuring equal-time checks regardless of data differences.
855
+ */
856
+ function _constantTimeEquals(a, b) {
857
+ if (a.length !== b.length) return false;
858
+ const _getRes = (x, idx) => {
859
+ return isString(x) ? x.charCodeAt(idx) : x[idx];
860
+ };
861
+ let res = 0;
862
+ for (let i = 0; i < a.length; i++) res |= _getRes(a, i) ^ _getRes(b, i);
863
+ return res === 0;
864
+ }
865
+ /** Split string by substring length, intended to be used internally for hex and binary converters only */
866
+ function _splitByCharLength(str, splitByChars = 2) {
867
+ if (!isNonEmptyString(str)) return [];
868
+ const sanitized = str.replace(/\s+/g, "");
869
+ const result = [];
870
+ for (let i = 0; i < sanitized.length; i += splitByChars) result.push(sanitized.slice(i, i + splitByChars));
871
+ return result;
872
+ }
873
+ /** Pad start of a byte with 0 for `hex` or `binary` */
874
+ function _padStartWith0(byte, type) {
875
+ return byte.toString(type === "hex" ? 16 : 2).padStart(type === "hex" ? 2 : 8, "0");
876
+ }
877
+
878
+ //#endregion
879
+ //#region src/hash/Cipher.ts
880
+ /**
881
+ * @class Lightweight stream-cipher–style encryption utility using `HMAC-SHA256` for keystream generation and authentication.
882
+ * - The class derives separate encryption and MAC keys from the provided secret.
883
+ *
884
+ * @remarks
885
+ * - **The encryption scheme is:**
886
+ * - keystream = `HMAC(encKey, iv || counter)`
887
+ * - ciphertext = `plaintext XOR keystream`
888
+ * - tag = `HMAC(macKey, iv || ciphertext)`
889
+ * - This is a custom construction and should not be used for production-grade cryptographic security.
890
+ * - `Cipher` class is a pure JS implementation. It does not rely on `crypto` or Web APIs.
891
+ */
892
+ var Cipher = class {
893
+ #secretBytes;
894
+ #encKey;
895
+ #macKey;
896
+ /**
897
+ * * Creates a new `Cipher` instance using the provided secret.
898
+ *
899
+ * @param secret - The secret string used to derive encryption and MAC keys.
900
+ * Must be a non-empty string.
901
+ */
902
+ constructor(secret) {
903
+ if (!isNonEmptyString(secret)) throw new Error("Secret must be non-empty string!");
904
+ this.#secretBytes = utf8ToBytes(secret);
905
+ this.#encKey = hmacSha256(this.#secretBytes, utf8ToBytes("enc"));
906
+ this.#macKey = hmacSha256(this.#secretBytes, utf8ToBytes("mac"));
907
+ }
908
+ /**
909
+ * Generates a keystream of the same length as the provided data using a `HMAC`-based counter mode.
910
+ * The keystream is deterministic from the encryption key and IV.
911
+ *
912
+ * @param target - The byte array whose length determines the keystream size.
913
+ * @param iv - The initialization vector used as input to the `HMAC`.
914
+ * @returns A byte array representing the generated keystream.
915
+ */
916
+ #genKeystream(target, iv) {
917
+ const blocks = Math.ceil(target.length / 32);
918
+ const keystreamParts = [];
919
+ for (let counter = 0; counter < blocks; counter++) keystreamParts.push(hmacSha256(this.#encKey, concatBytes(iv, intTo4BytesBE(counter))));
920
+ return concatBytes(...keystreamParts).subarray(0, target.length);
921
+ }
922
+ /**
923
+ * * Encrypts a UTF-8 string.
924
+ * - The output format is: `base64( iv || ciphertext || tag )`
925
+ *
926
+ * @param text - The plaintext string to encrypt.
927
+ * @returns A base64-encoded encrypted token.
928
+ */
929
+ encrypt(text) {
930
+ const plain = utf8ToBytes(text);
931
+ const iv = sha256Bytes(utf8ToBytes(String(Date.now()) + "-" + String(Math.random()))).subarray(0, 16);
932
+ const keystream = this.#genKeystream(plain, iv);
933
+ const ct = new Uint8Array(plain.length);
934
+ for (let i = 0; i < plain.length; i++) ct[i] = plain[i] ^ keystream[i];
935
+ return bytesToBase64(concatBytes(iv, ct, hmacSha256(this.#macKey, concatBytes(iv, ct))));
936
+ }
937
+ /**
938
+ * * Checks if a token is structurally valid and contains a matching MAC using the same secret.
939
+ *
940
+ * @param token - The base64-encoded encrypted blob to validate.
941
+ * @returns `true` if the MAC is valid, `false` otherwise.
942
+ */
943
+ isValid(token) {
944
+ if (!isBase64(token)) return false;
945
+ const blob = base64ToBytes(token);
946
+ if (blob.length < 48) return false;
947
+ const iv = blob.subarray(0, 16);
948
+ const tag = blob.subarray(blob.length - 32);
949
+ const ct = blob.subarray(16, blob.length - 32);
950
+ return _constantTimeEquals(hmacSha256(this.#macKey, concatBytes(iv, ct)), tag);
951
+ }
952
+ /**
953
+ * * Decrypts a previously encrypted token.
954
+ * - Throws an error if the tag does not match or the token is malformed.
955
+ *
956
+ * @param token - The base64-encoded token produced by `encrypt`.
957
+ * @returns The decrypted plaintext string.
958
+ */
959
+ decrypt(token) {
960
+ if (!isBase64(token)) throw new Error("Token must be a base64 string!");
961
+ const blob = base64ToBytes(token);
962
+ if (blob.length < 48) throw new Error("Malformed or tampered token!");
963
+ const iv = blob.subarray(0, 16);
964
+ const tag = blob.subarray(blob.length - 32);
965
+ const ct = blob.subarray(16, blob.length - 32);
966
+ if (!_constantTimeEquals(hmacSha256(this.#macKey, concatBytes(iv, ct)), tag)) throw new Error("Key in the token is tampered or invalid!)");
967
+ const keystream = this.#genKeystream(ct, iv);
968
+ const pt = new Uint8Array(ct.length);
969
+ for (let i = 0; i < ct.length; i++) pt[i] = ct[i] ^ keystream[i];
970
+ return bytesToUtf8(pt);
971
+ }
972
+ };
973
+
974
+ //#endregion
975
+ //#region src/hash/core.ts
976
+ /**
977
+ * * Computes the `MD5` digest of the given string using a pure JavaScript implementation.
978
+ *
979
+ * @remarks
980
+ * - Pure JavaScript implementation — runs on any JS engine. Does not rely on `crypto` or **Web APIs** or other external libraries.
981
+ * - Highly inspired by the algorithm used in {@link https://github.com/eustatos/pure-md5.git pure-md5} package.
982
+ *
983
+ * @param str - Input text to hash.
984
+ *
985
+ * @returns The `MD5` hash as a 32-character hex string.
986
+ *
987
+ * @example
988
+ * const hash = md5("hello");
989
+ * // → "5d41402abc4b2a76b9719d911017c592" *
990
+ *
991
+ * @example
992
+ * // Used inside UUID v3
993
+ * const digest = md5(namespace + name);
994
+ */
995
+ function md5(str) {
996
+ const state = [
997
+ 1732584193,
998
+ -271733879,
999
+ -1732584194,
1000
+ 271733878
1001
+ ];
1002
+ const len = str.length;
1003
+ let i;
1004
+ for (i = 64; i <= len; i += 64) _md5cycle(state, _stringToNumbers(str.substring(i - 64, i)));
1005
+ const $str = str.substring(i - 64);
1006
+ const tail = [
1007
+ 0,
1008
+ 0,
1009
+ 0,
1010
+ 0,
1011
+ 0,
1012
+ 0,
1013
+ 0,
1014
+ 0,
1015
+ 0,
1016
+ 0,
1017
+ 0,
1018
+ 0,
1019
+ 0,
1020
+ 0,
1021
+ 0,
1022
+ 0
1023
+ ];
1024
+ for (i = 0; i < $str.length; i++) tail[i >> 2] |= $str.charCodeAt(i) << (i % 4 << 3);
1025
+ tail[i >> 2] |= 128 << (i % 4 << 3);
1026
+ if (i > 55) {
1027
+ _md5cycle(state, tail);
1028
+ for (let j = 0; j < 16; j++) tail[j] = 0;
1029
+ }
1030
+ tail[14] = len * 8;
1031
+ _md5cycle(state, tail);
1032
+ return state.map(_numToHex).join("");
1033
+ }
1034
+ /**
1035
+ * * Computes the `SHA-1` digest of the given string using a pure JavaScript implementation.
1036
+ *
1037
+ * @remarks Pure JavaScript implementation — runs on any JS engine. Does not rely on `crypto` or **Web APIs** or other external libraries.
1038
+ *
1039
+ * @param msg - Input text to hash.
1040
+ *
1041
+ * @returns The `SHA-1` hash as a 40-character hex string.
1042
+ *
1043
+ * @example
1044
+ * const hash = sha1("hello");
1045
+ * // → "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
1046
+ *
1047
+ * @example
1048
+ * // Used inside UUID v5
1049
+ * const digest = sha1(namespace + name);
1050
+ */
1051
+ function sha1(msg) {
1052
+ const K = [
1053
+ 1518500249,
1054
+ 1859775393,
1055
+ 2400959708,
1056
+ 3395469782
1057
+ ];
1058
+ const utf8 = utf8ToBytes(msg);
1059
+ const rotl = (n, bits) => n << bits | n >>> 32 - bits;
1060
+ const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
1061
+ const len = utf8.length;
1062
+ const padBytes = (len + 9) % 64 ? 64 - (len + 9) % 64 : 0;
1063
+ const total = len + 1 + padBytes + 8;
1064
+ const words = new Uint32Array(total >>> 2);
1065
+ for (let i = 0; i < utf8.length; i++) words[i >> 2] |= utf8[i] << 24 - i % 4 * 8;
1066
+ words[utf8.length >> 2] |= 128 << 24 - utf8.length % 4 * 8;
1067
+ words[words.length - 1] = utf8.length * 8;
1068
+ const h = [
1069
+ 1732584193,
1070
+ 4023233417,
1071
+ 2562383102,
1072
+ 271733878,
1073
+ 3285377520
1074
+ ];
1075
+ const W = new Uint32Array(80);
1076
+ for (let i = 0; i < words.length; i += 16) {
1077
+ for (let j = 0; j < 16; j++) W[j] = words[i + j] | 0;
1078
+ for (let j = 16; j < 80; j++) W[j] = rotl(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16], 1);
1079
+ let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4];
1080
+ for (let j = 0; j < 80; j++) {
1081
+ let f, k;
1082
+ if (j < 20) {
1083
+ f = b & c | ~b & d;
1084
+ k = K[0];
1085
+ } else if (j < 40) {
1086
+ f = b ^ c ^ d;
1087
+ k = K[1];
1088
+ } else if (j < 60) {
1089
+ f = b & c | b & d | c & d;
1090
+ k = K[2];
1091
+ } else {
1092
+ f = b ^ c ^ d;
1093
+ k = K[3];
1094
+ }
1095
+ const temp = rotl(a, 5) + f + e + k + W[j] | 0;
1096
+ e = d;
1097
+ d = c;
1098
+ c = rotl(b, 30);
1099
+ b = a;
1100
+ a = temp;
1101
+ }
1102
+ h[0] = h[0] + a | 0;
1103
+ h[1] = h[1] + b | 0;
1104
+ h[2] = h[2] + c | 0;
1105
+ h[3] = h[3] + d | 0;
1106
+ h[4] = h[4] + e | 0;
1107
+ }
1108
+ return h.map(toHex).join("");
1109
+ }
1110
+ /**
1111
+ * * Computes the `SHA-256` hash of a `UTF-8` string and returns it as a lowercase hexadecimal string.
1112
+ *
1113
+ * @param msg - The input string to hash. Can contain any `UTF-8` characters.
1114
+ * @returns A 64-character lowercase hexadecimal string representing the `SHA-256` hash.
1115
+ *
1116
+ * @remarks Pure JavaScript implementation — runs on any JS engine. Does not rely on `crypto` or **Web APIs** or other external libraries.
1117
+ *
1118
+ * @example
1119
+ * ```typescript
1120
+ * // Basic usage
1121
+ * const hash = sha256('hello');
1122
+ * // Returns: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
1123
+ *
1124
+ * // Empty string
1125
+ * const emptyHash = sha256('');
1126
+ * // Returns: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
1127
+ *
1128
+ * // Unicode string
1129
+ * const unicodeHash = sha256('Hello পৃথিবী!');
1130
+ * // Returns: '7037e204b825b83553ba336a6ec35b796d505599286ae864729ed6cb33ae9fe1'
1131
+ * ```
1132
+ *
1133
+ * @see {@link https://toolbox.nazmul-nhb.dev/docs/utilities/hash/encoding#sha256bytes sha256Bytes} for hashing raw bytes
1134
+ * @see {@link https://toolbox.nazmul-nhb.dev/docs/utilities/hash/encoding#utf8tobytes utf8ToBytes} for converting string to bytes
1135
+ * @see {@link https://toolbox.nazmul-nhb.dev/docs/utilities/hash/encoding#bytestohex bytesToHex} for converting bytes to a hexadecimal string
1136
+ */
1137
+ function sha256(msg) {
1138
+ if (!isString(msg)) throw new TypeError("Input must be of type string!");
1139
+ return bytesToHex(sha256Bytes(utf8ToBytes(msg)));
1140
+ }
1141
+
1142
+ //#endregion
1143
+ //#region src/hash/Signet.ts
1144
+ /**
1145
+ * @class A lightweight, secure implementation of JWT-like tokens using `HMAC-SHA256` signatures.
1146
+ * - This class provides methods to create, verify, and decode tokens with a simple API similar to JSON Web Tokens (`JWT`)
1147
+ * but with a smaller footprint and zero dependencies.
1148
+ *
1149
+ * @remarks
1150
+ * - **Features:**
1151
+ * - `HMAC-SHA256` signatures for security
1152
+ * - Time-based claims (expiration, not-before)
1153
+ * - Standard claims (audience, issuer, subject)
1154
+ * - Constant-time signature comparison to prevent timing attacks
1155
+ * - Automatic date conversion for timestamp claims
1156
+ * - `Base64` URL-safe encoding (standard `Base64` in this implementation)
1157
+ *
1158
+ * - **Security considerations:**
1159
+ * - Keep the secret key secure and rotate periodically
1160
+ * - Use appropriate token expiration times
1161
+ * - Validate all claims relevant to your application
1162
+ * - Store tokens securely (HTTP-only cookies recommended for web)
1163
+ *
1164
+ * @example
1165
+ * ```typescript
1166
+ * // Create a token signer
1167
+ * const signet = new Signet('my-secret-key');
1168
+ *
1169
+ * // Sign a token with custom payload and options
1170
+ * const token = signet.sign(
1171
+ * { userId: 123, role: 'admin' },
1172
+ * {
1173
+ * expiresIn: '1h',
1174
+ * audience: 'my-app',
1175
+ * issuer: 'auth-service'
1176
+ * }
1177
+ * );
1178
+ *
1179
+ * // Verify a token
1180
+ * const result = signet.verify(token, {
1181
+ * audience: 'my-app',
1182
+ * issuer: 'auth-service'
1183
+ * });
1184
+ *
1185
+ * if (result.isValid) {
1186
+ * console.log('Valid token for user:', result.payload.userId);
1187
+ * } else {
1188
+ * console.log('Invalid token:', result.error);
1189
+ * }
1190
+ *
1191
+ * // Decode without verification
1192
+ * const decoded = signet.decode(token);
1193
+ * console.log('Token payload:', decoded.payload);
1194
+ * ```
1195
+ */
1196
+ var Signet = class {
1197
+ #secretBytes;
1198
+ /**
1199
+ * * Creates a new `Signet` instance with the specified secret key.
1200
+ *
1201
+ * @param secret - The secret key used for signing and verifying tokens.
1202
+ * Must be a non-empty string.
1203
+ *
1204
+ * @throws If the secret is not a non-empty string.
1205
+ *
1206
+ * @remarks
1207
+ * - The secret is converted to `UTF-8` bytes and stored internally.
1208
+ * - Choose a strong secret (at least 32 characters) and store it securely.
1209
+ * - For production, consider using key rotation strategies.
1210
+ *
1211
+ * @example
1212
+ * ```typescript
1213
+ * // Initialize with a secret key
1214
+ * const signet = new Signet('super-secret-key-123');
1215
+ *
1216
+ * // Use environment variable for the secret
1217
+ * const signet = new Signet(process.env.JWT_SECRET!);
1218
+ * ```
1219
+ */
1220
+ constructor(secret) {
1221
+ if (!isNonEmptyString(secret)) throw new Error("Secret must be a non-empty string!");
1222
+ this.#secretBytes = utf8ToBytes(secret);
1223
+ }
1224
+ /** Decodes a token without verifying its signature. */
1225
+ #decode(token) {
1226
+ if (!isNonEmptyString(token)) throw new Error("Token must be a non-empty string!");
1227
+ const parts = token.split(".");
1228
+ if (parts.length !== 3) throw new Error("Token is tampered or malformed!");
1229
+ const [hdr, pld, signature] = parts;
1230
+ const headerBytes = base64ToBytes(hdr);
1231
+ const payloadBytes = base64ToBytes(pld);
1232
+ const headerStr = stripJsonEdgeGarbage(bytesToUtf8(headerBytes));
1233
+ const payloadStr = stripJsonEdgeGarbage(bytesToUtf8(payloadBytes));
1234
+ let header;
1235
+ try {
1236
+ header = JSON.parse(headerStr);
1237
+ } catch {
1238
+ throw new Error("Cannot parse header!");
1239
+ }
1240
+ let payload;
1241
+ try {
1242
+ const { iat, iatDate, exp, expDate, nbf, nbfDate, aud, sub, iss, ...rest } = JSON.parse(payloadStr);
1243
+ payload = {
1244
+ iat,
1245
+ iatDate: iatDate ? new Date(iatDate) : _secToDate(iat),
1246
+ ...exp && { exp },
1247
+ ...exp && { expDate: expDate ? new Date(expDate) : _secToDate(exp) },
1248
+ ...nbf && { nbf },
1249
+ ...nbf && { nbfDate: nbfDate ? new Date(nbfDate) : _secToDate(nbf) },
1250
+ ...aud && { aud },
1251
+ ...sub && { sub },
1252
+ ...iss && { iss },
1253
+ ...rest
1254
+ };
1255
+ } catch {
1256
+ throw new Error("Cannot parse payload!");
1257
+ }
1258
+ return {
1259
+ header,
1260
+ payload,
1261
+ signature,
1262
+ signingInput: `${hdr}.${pld}`
1263
+ };
1264
+ }
1265
+ /**
1266
+ * * Creates and signs a new token with the given payload and options.
1267
+ *
1268
+ * @param payload - Custom data to include in the token payload.
1269
+ * Must be a `non-empty object`.
1270
+ * @param options - Optional configuration for token claims and expiration.
1271
+ *
1272
+ * @returns A signed token string in the format `header.payload.signature`.
1273
+ *
1274
+ * @throws If payload is not a valid object.
1275
+ *
1276
+ * @remarks
1277
+ * - **The token structure follows JWT format:**
1278
+ * - Header: Contains algorithm (`HS256`) and token type (`SIGNET+JWT`)
1279
+ * - Payload: Includes standard claims (`iat`, `exp`, `nbf`, `aud`, `sub`, `iss`) plus custom data
1280
+ * - Signature: `HMAC-SHA256(signingInput, secret)` (the result of the hash)
1281
+ * - Signing Inputs: `base64(header) + "." + base64(payload)` (the string that gets hashed)
1282
+ *
1283
+ * - **Automatic claims added:**
1284
+ * - `iat` (issued at): Current time in seconds
1285
+ * - `iatDate`: Current time as Date object
1286
+ * - If `expiresIn` is provided: `exp` and `expDate`
1287
+ * - If `notBefore` is provided: `nbf` and `nbfDate`
1288
+ *
1289
+ * @example
1290
+ * ```typescript
1291
+ * // Basic token with custom data
1292
+ * const token = signet.sign({ userId: 123, name: 'John' });
1293
+ *
1294
+ * // Token with expiration and claims
1295
+ * const token = signet.sign(
1296
+ * { userId: 123 },
1297
+ * {
1298
+ * expiresIn: '2h',
1299
+ * audience: 'api.example.com',
1300
+ * issuer: 'auth-service',
1301
+ * subject: 'user-123'
1302
+ * }
1303
+ * );
1304
+ *
1305
+ * // Token valid after 5 minutes
1306
+ * const token = signet.sign(
1307
+ * { action: 'reset-password' },
1308
+ * { notBefore: '5m' }
1309
+ * );
1310
+ * ```
1311
+ */
1312
+ sign(payload, options) {
1313
+ if (!isNotEmptyObject(payload)) throw new Error("Payload must be a valid object!");
1314
+ const { expiresIn, notBefore, audience, issuer, subject } = options || {};
1315
+ const iat = _toSeconds(Date.now());
1316
+ const $payload = {
1317
+ iat,
1318
+ iatDate: _secToDate(iat),
1319
+ ...expiresIn && { exp: iat + _toSeconds(parseMSec(expiresIn)) },
1320
+ ...expiresIn && { expDate: _secToDate(iat + _toSeconds(parseMSec(expiresIn))) },
1321
+ ...notBefore && { nbf: iat + _toSeconds(parseMSec(notBefore)) },
1322
+ ...notBefore && { nbfDate: _secToDate(iat + _toSeconds(parseMSec(notBefore))) },
1323
+ ...audience && { aud: audience },
1324
+ ...subject && { sub: subject },
1325
+ ...issuer && { iss: issuer },
1326
+ ...payload
1327
+ };
1328
+ const headerJson = stableStringify({
1329
+ alg: "HS256",
1330
+ typ: "SIGNET+JWT"
1331
+ });
1332
+ const payloadJson = stableStringify($payload);
1333
+ const headerB = utf8ToBytes(headerJson);
1334
+ const payloadB = utf8ToBytes(payloadJson);
1335
+ const signingInput = `${bytesToBase64(headerB)}.${bytesToBase64(payloadB)}`;
1336
+ return `${signingInput}.${bytesToBase64(hmacSha256(this.#secretBytes, utf8ToBytes(signingInput)))}`;
1337
+ }
1338
+ /**
1339
+ * * Decodes a token without verifying its signature.
1340
+ *
1341
+ * @typeParam T - Type of custom data in the token payload.
1342
+ * @param token - The token string to decode.
1343
+ *
1344
+ * @returns The decoded token parts including header, payload, and signatures.
1345
+ *
1346
+ * @throws If the token is malformed, empty, or cannot be parsed.
1347
+ *
1348
+ * @remarks
1349
+ * - Use this method when you need to inspect token contents without verification.
1350
+ * - **Warning:** This does not validate the signature, so the data may have been tampered with.
1351
+ * - Always use {@link verify} method for security-critical operations.
1352
+ * - The payload includes both timestamp values (numbers) and {@link Date} objects for convenience.
1353
+ *
1354
+ * @example
1355
+ * ```typescript
1356
+ * // Decode token to inspect contents
1357
+ * const decoded = signet.decode(token);
1358
+ * console.log('Header:', decoded.header);
1359
+ * console.log('Payload:', decoded.payload);
1360
+ * console.log('Signature:', decoded.signature);
1361
+ *
1362
+ * // Access custom payload data with type safety
1363
+ * const decoded = signet.decode<{ userId: number }>(token);
1364
+ * const userId = decoded.payload.userId; // Type: number
1365
+ * ```
1366
+ */
1367
+ decode(token) {
1368
+ return this.#decode(token);
1369
+ }
1370
+ /**
1371
+ * * Checks if a token has expired based on its `exp` claim.
1372
+ *
1373
+ * @param token - The token to check.
1374
+ *
1375
+ * @returns `true` if the token has an `exp` claim and current time is past it,
1376
+ * `false` if token has no expiration or is still valid.
1377
+ *
1378
+ * @throws If the token is malformed or cannot be decoded.
1379
+ *
1380
+ * @remarks
1381
+ * - Tokens without `exp` claim are considered non-expiring (returns `false`)
1382
+ * - Uses current system time for comparison ({@link Date.now()})
1383
+ * - Does not verify the signature (use only with trusted tokens or after verification)
1384
+ *
1385
+ * @example
1386
+ * ```typescript
1387
+ * // Check expiration
1388
+ * if (signet.hasExpired(token)) {
1389
+ * console.log('Token has expired');
1390
+ * // Prompt user to re-authenticate
1391
+ * }
1392
+ *
1393
+ * // Use with other validation
1394
+ * const isValid = !signet.hasExpired(token) && !signet.isTooEarly(token);
1395
+ * ```
1396
+ */
1397
+ hasExpired(token) {
1398
+ const { exp } = this.#decode(token).payload;
1399
+ return exp ? _toSeconds(Date.now()) > exp : false;
1400
+ }
1401
+ /**
1402
+ * * Checks if a token's `nbf` (not-before) claim indicates it's too early to use.
1403
+ *
1404
+ * @param token - The token to check.
1405
+ *
1406
+ * @returns `true` if the token has an `nbf` claim and current time is before it,
1407
+ * `false` if token has no `nbf` claim or is already valid.
1408
+ *
1409
+ * @throws If the token is malformed or cannot be decoded.
1410
+ *
1411
+ * @remarks
1412
+ * - Useful for implementing time-based access control, like activation links that shouldn't be used until a certain time.
1413
+ * - Uses current system time for comparison ({@link Date.now()})
1414
+ * - Does not verify the signature (use only with trusted tokens or after verification)
1415
+ *
1416
+ * @example
1417
+ * ```typescript
1418
+ * // Check if token is active yet
1419
+ * if (signet.isTooEarly(token)) {
1420
+ * console.log('Token not valid yet');
1421
+ * // Wait before using
1422
+ * }
1423
+ * ```
1424
+ */
1425
+ isTooEarly(token) {
1426
+ const { nbf } = this.#decode(token).payload;
1427
+ return nbf ? _toSeconds(Date.now()) < nbf : false;
1428
+ }
1429
+ /**
1430
+ * * Validates a token's `iss` (issuer) claim against an expected value.
1431
+ *
1432
+ * @param token - The token to check.
1433
+ * @param expected - The expected issuer value. If `undefined`, always returns `false`.
1434
+ *
1435
+ * @returns `true` if the token has an `iss` claim that doesn't match the expected value,
1436
+ * `false` if issuer matches, token has no issuer claim, or expected issuer is undefined.
1437
+ *
1438
+ * @throws If the token is malformed or cannot be decoded.
1439
+ *
1440
+ * @remarks Use this to ensure tokens come from trusted sources in multi-issuer scenarios.
1441
+ *
1442
+ * @example
1443
+ * ```typescript
1444
+ * // Validate issuer
1445
+ * if (signet.isInvalidIssuer(token, 'auth-service')) {
1446
+ * console.log('Token from unexpected issuer');
1447
+ * // Reject token
1448
+ * }
1449
+ *
1450
+ * // With optional issuer check
1451
+ * const issuer = process.env.EXPECTED_ISSUER;
1452
+ * if (issuer && signet.isInvalidIssuer(token, issuer)) {
1453
+ * throw new Error('Invalid issuer');
1454
+ * }
1455
+ * ```
1456
+ */
1457
+ isInvalidIssuer(token, expected) {
1458
+ if (!expected) return false;
1459
+ const { iss } = this.#decode(token).payload;
1460
+ return iss ? iss !== expected : false;
1461
+ }
1462
+ /**
1463
+ * * Validates a token's `aud` (audience) claim against expected values.
1464
+ *
1465
+ * @param token - The token to check.
1466
+ * @param expected - The expected audience(s). Can be a string or array of strings.
1467
+ * If `undefined`, always returns `false`.
1468
+ *
1469
+ * @returns `true` if the token has an `aud` claim and none of its values match any of the expected audiences, `false` otherwise.
1470
+ *
1471
+ * @throws If the token is malformed or cannot be decoded.
1472
+ *
1473
+ * @remarks
1474
+ * - Tokens can have single audience (string) or multiple audiences (string[])
1475
+ * - Returns `false` (valid) if at least one audience matches
1476
+ * - Useful for multi-tenant or multi-service architectures
1477
+ *
1478
+ * @example
1479
+ * ```typescript
1480
+ * // Single audience check
1481
+ * if (signet.isInvalidAudience(token, 'api.example.com')) {
1482
+ * console.log('Token not intended for this audience');
1483
+ * }
1484
+ *
1485
+ * // Multiple allowed audiences
1486
+ * const validAudiences = ['web-app', 'mobile-app', 'admin-panel'];
1487
+ * if (signet.isInvalidAudience(token, validAudiences)) {
1488
+ * throw new Error('Invalid audience');
1489
+ * }
1490
+ *
1491
+ * // Token with multiple audiences
1492
+ * // Token payload: { aud: ['web-app', 'mobile-app'] }
1493
+ * // Check if at least one matches
1494
+ * const isValid = !signet.isInvalidAudience(token, ['web-app', 'admin-panel']);
1495
+ * // Returns false (valid) because 'web-app' matches
1496
+ * ```
1497
+ */
1498
+ isInvalidAudience(token, expected) {
1499
+ if (!expected) return false;
1500
+ const { aud } = this.#decode(token).payload;
1501
+ if (!aud) return false;
1502
+ const payloadAud = Array.isArray(aud) ? aud : [aud];
1503
+ const expectedAud = Array.isArray(expected) ? expected : [expected];
1504
+ return payloadAud.some((tokenAud) => expectedAud.includes(tokenAud));
1505
+ }
1506
+ /**
1507
+ * * Validates a token's `sub` (subject) claim against an expected value.
1508
+ *
1509
+ * @param token - The token to check.
1510
+ * @param expected - The expected subject value. If `undefined`, always returns `false`.
1511
+ *
1512
+ * @returns `true` if the token has a `sub` claim that doesn't match the expected value,
1513
+ * `false` if subject matches, token has no subject claim, or expected subject is undefined.
1514
+ *
1515
+ * @throws If the token is malformed or cannot be decoded.
1516
+ *
1517
+ * @remarks
1518
+ * - Use this to ensure tokens are being used by the intended user/entity.
1519
+ * - Common for authorization checks where tokens should be user-specific.
1520
+ *
1521
+ * @example
1522
+ * ```typescript
1523
+ * // Validate subject
1524
+ * const userId = 'user-123';
1525
+ * if (signet.isInvalidSubject(token, userId)) {
1526
+ * console.log('Token not for this user');
1527
+ * // Reject request
1528
+ * }
1529
+ *
1530
+ * // Optional subject validation
1531
+ * const expectedSubject = getExpectedSubjectFromRequest();
1532
+ * if (expectedSubject && signet.isInvalidSubject(token, expectedSubject)) {
1533
+ * return response.status(403).send('Invalid subject');
1534
+ * }
1535
+ * ```
1536
+ */
1537
+ isInvalidSubject(token, expected) {
1538
+ if (!expected) return false;
1539
+ const { sub } = this.#decode(token).payload;
1540
+ return sub ? sub !== expected : false;
1541
+ }
1542
+ /**
1543
+ * * Verifies a token's signature and validates all claims.
1544
+ *
1545
+ * @typeParam T - Type of custom data in the token payload.
1546
+ * @param token - The token string to verify.
1547
+ * @param options - Optional validation criteria for token claims.
1548
+ *
1549
+ * @returns A {@link VerifiedToken} object indicating success or failure.
1550
+ * - If valid: `{ isValid: true, payload: SignetPayload<T> }`
1551
+ * - If invalid: `{ isValid: false, error: string }`
1552
+ *
1553
+ * @remarks
1554
+ * - **Performs the following checks in order:**
1555
+ * - Token structure (3 parts separated by dots)
1556
+ * - Base64 decoding of header and payload
1557
+ * - JSON parsing of header and payload
1558
+ * - Signature verification (constant-time comparison)
1559
+ * - Expiration check (if `exp` claim exists)
1560
+ * - Not-before check (if `nbf` claim exists)
1561
+ * - Issuer validation (if provided in options)
1562
+ * - Audience validation (if provided in options)
1563
+ * - Subject validation (if provided in options)
1564
+ *
1565
+ * - **This is the recommended method for most token validation scenarios.**
1566
+ *
1567
+ * @example
1568
+ * ```typescript
1569
+ * // Basic verification
1570
+ * const result = signet.verify(token);
1571
+ * if (result.isValid) {
1572
+ * console.log('Valid token:', result.payload);
1573
+ * } else {
1574
+ * console.log('Invalid token:', result.error);
1575
+ * }
1576
+ *
1577
+ * // With claim validation
1578
+ * const result = signet.verify(token, {
1579
+ * audience: 'api.example.com',
1580
+ * issuer: 'auth-service',
1581
+ * subject: 'user-123'
1582
+ * });
1583
+ *
1584
+ * // Type-safe custom payload
1585
+ * interface UserToken {
1586
+ * userId: number;
1587
+ * role: string;
1588
+ * }
1589
+ * const result = signet.verify<UserToken>(token);
1590
+ * if (result.isValid) {
1591
+ * const { userId, role } = result.payload;
1592
+ * // userId and role are typed
1593
+ * }
1594
+ * ```
1595
+ */
1596
+ verify(token, options) {
1597
+ try {
1598
+ const { signature, signingInput, payload } = this.#decode(token);
1599
+ const { audience, issuer, subject } = options || {};
1600
+ if (!_constantTimeEquals(signature, bytesToBase64(hmacSha256(this.#secretBytes, utf8ToBytes(signingInput))))) throw new Error("Invalid or tampered signature!");
1601
+ if (this.hasExpired(token)) throw new Error("Token has expired!");
1602
+ if (this.isTooEarly(token)) throw new Error("Token is not active yet!");
1603
+ if (this.isInvalidIssuer(token, issuer)) throw new Error("Invalid token issuer!");
1604
+ if (this.isInvalidAudience(token, audience)) throw new Error("Invalid token audience(s!");
1605
+ if (this.isInvalidSubject(token, subject)) throw new Error("Invalid token subject!");
1606
+ return {
1607
+ isValid: true,
1608
+ payload
1609
+ };
1610
+ } catch (e) {
1611
+ return {
1612
+ isValid: false,
1613
+ error: e instanceof Error ? e.message : String(e)
1614
+ };
1615
+ }
1616
+ }
1617
+ /**
1618
+ * * Verifies a token and throws an error if invalid.
1619
+ *
1620
+ * @typeParam T - Type of custom data in the token payload.
1621
+ * @param token - The token string to verify.
1622
+ * @param options - Optional validation criteria for token claims.
1623
+ *
1624
+ * @returns A valid {@link VerifiedToken} with `isValid: true`.
1625
+ *
1626
+ * @throws If the token is invalid, with a message describing the failure.
1627
+ *
1628
+ * @remarks
1629
+ * - This method is a convenience wrapper around {@link verify} that throws instead of returning an error object.
1630
+ * - Useful for `express`-style middleware or when you want to handle authentication failures with exceptions.
1631
+ * - The thrown error message is the same as the `error` property in the invalid result from {@link verify}.
1632
+ *
1633
+ * @example
1634
+ * ```typescript
1635
+ * // Use in middleware/guard
1636
+ * function authMiddleware(req, res, next) {
1637
+ * const token = req.headers.authorization?.replace('Bearer ', '');
1638
+ *
1639
+ * try {
1640
+ * const result = signet.verifyOrThrow(token, {
1641
+ * audience: 'api.example.com'
1642
+ * });
1643
+ * req.user = result.payload;
1644
+ * next();
1645
+ * } catch (error) {
1646
+ * res.status(401).json({ error: error.message });
1647
+ * }
1648
+ * }
1649
+ *
1650
+ * // In application code
1651
+ * try {
1652
+ * const result = signet.verifyOrThrow(token);
1653
+ * // Token is guaranteed valid here
1654
+ * processUserRequest(result.payload);
1655
+ * } catch (error) {
1656
+ * handleAuthError(error);
1657
+ * }
1658
+ * ```
1659
+ */
1660
+ verifyOrThrow(token, options) {
1661
+ const res = this.verify(token, options);
1662
+ if (!res.isValid) throw new Error(res.error || "Invalid, malformed or expired token!");
1663
+ return res;
1664
+ }
1665
+ /**
1666
+ * * Extracts only the payload from a token without full verification.
1667
+ *
1668
+ * @typeParam T - Type of custom data in the token payload.
1669
+ * @param token - The token string to decode.
1670
+ *
1671
+ * @returns The token payload including standard claims and custom data.
1672
+ *
1673
+ * @throws If the token is malformed, empty, or cannot be parsed.
1674
+ *
1675
+ * @remarks
1676
+ * - This is a convenience method equivalent to `decode(token).payload`.
1677
+ *
1678
+ * - **Security Note:** This method does NOT verify the token signature.
1679
+ *
1680
+ * - **Only use it when:**
1681
+ * - You've already verified the token elsewhere
1682
+ * - The token comes from a trusted source
1683
+ * - You're debugging or logging
1684
+ * - The operation is not security-critical
1685
+ *
1686
+ * - For security-sensitive operations, always use {@link verify} or {@link verifyOrThrow} method.
1687
+ *
1688
+ * @example
1689
+ * ```typescript
1690
+ * // Quick payload extraction for non-critical operations
1691
+ * const payload = signet.decodePayload(token);
1692
+ * console.log('User ID:', payload.userId);
1693
+ * console.log('Issued at:', payload.iatDate);
1694
+ *
1695
+ * // Type-safe with custom interface
1696
+ * interface AppToken {
1697
+ * userId: number;
1698
+ * permissions: string[];
1699
+ * }
1700
+ * const payload = signet.decodePayload<AppToken>(token);
1701
+ * const canDelete = payload.permissions.includes('delete');
1702
+ * ```
1703
+ */
1704
+ decodePayload(token) {
1705
+ return this.#decode(token).payload;
1706
+ }
1707
+ };
1708
+
1709
+ //#endregion
1710
+ //#region src/hash/TextCodec.ts
1711
+ /**
1712
+ * @class `TextCodec` provides **UTF-8–safe** conversions between `text`, `hex`, `binary`, and `Base64` representations using byte-level transformations.
1713
+ *
1714
+ * @example
1715
+ * TextCodec.utf8ToHex('ভাষা'); // 'e0 a6 ad e0 a6 be e0 a6 b7 e0 a6 be'
1716
+ * TextCodec.hexToUtf8('e0 a6 ad e0 a6 be'); // 'ভা'
1717
+ */
1718
+ var TextCodec = class {
1719
+ constructor() {}
1720
+ /**
1721
+ * @static Validates whether a string represents a valid hexadecimal byte sequence.
1722
+ *
1723
+ * @param hex - Hex string, spaced or un-spaced (e.g. "ff 0a" or "ff0a")
1724
+ * @returns `true` if the input is valid hex byte string
1725
+ *
1726
+ * @example
1727
+ * TextCodec.isValidHex('ff 0a');
1728
+ */
1729
+ static isValidHex(hex) {
1730
+ return isHexString(hex);
1731
+ }
1732
+ /**
1733
+ * @static Validates whether a string represents a valid binary byte sequence.
1734
+ *
1735
+ * @param binary - Binary string, spaced or un-spaced
1736
+ * @returns `true` if the input is valid binary byte string
1737
+ *
1738
+ * @example
1739
+ * TextCodec.isValidBinary('01000001');
1740
+ */
1741
+ static isValidBinary(binary) {
1742
+ return isBinaryString(binary);
1743
+ }
1744
+ /**
1745
+ * @static Validates whether a string represents a valid Base64-encoded string.
1746
+ *
1747
+ * @param b64 - Base64 string to check
1748
+ * @returns `true` if the input is valid Base64-encoded string
1749
+ *
1750
+ * @example
1751
+ * TextCodec.isValidBase64('SGVsbG8=');
1752
+ */
1753
+ static isValidBase64(b64) {
1754
+ return isBase64(b64);
1755
+ }
1756
+ /**
1757
+ * @static Converts UTF-8 text into hexadecimal byte representation.
1758
+ *
1759
+ * @param text - UTF-8 text to convert
1760
+ * @param spaced - Whether to separate bytes with spaces, defaults to `true`
1761
+ * @returns Hexadecimal byte string
1762
+ *
1763
+ * @example
1764
+ * TextCodec.utf8ToHex('Hi');
1765
+ */
1766
+ static utf8ToHex(text, spaced = true) {
1767
+ return [...utf8ToBytes(text)].map((b) => _padStartWith0(b, "hex")).join(spaced ? " " : "");
1768
+ }
1769
+ /**
1770
+ * @static Converts UTF-8 text into binary byte representation.
1771
+ *
1772
+ * @param text - UTF-8 text to convert
1773
+ * @param spaced - Whether to separate bytes with spaces, defaults to `true`
1774
+ * @returns Binary byte string
1775
+ *
1776
+ * @example
1777
+ * TextCodec.utf8ToBinary('A');
1778
+ */
1779
+ static utf8ToBinary(text, spaced = true) {
1780
+ return [...utf8ToBytes(text)].map((b) => _padStartWith0(b, "binary")).join(spaced ? " " : "");
1781
+ }
1782
+ /**
1783
+ * @static Converts hexadecimal byte string into UTF-8 text.
1784
+ *
1785
+ * @param hex - Hexadecimal byte string
1786
+ * @returns Decoded UTF-8 text
1787
+ *
1788
+ * @example
1789
+ * TextCodec.hexToUtf8('48 69');
1790
+ */
1791
+ static hexToUtf8(hex) {
1792
+ return bytesToUtf8(hexToBytes(hex));
1793
+ }
1794
+ /**
1795
+ * @static Converts binary byte string into UTF-8 text.
1796
+ *
1797
+ * @param binary - Binary byte string
1798
+ * @returns Decoded UTF-8 text
1799
+ *
1800
+ * @example
1801
+ * TextCodec.binaryToUtf8('01001000 01101001');
1802
+ */
1803
+ static binaryToUtf8(binary) {
1804
+ if (!isBinaryString(binary)) return "";
1805
+ const bytes = _splitByCharLength(binary, 8).map((b) => parseInt(b, 2));
1806
+ return bytesToUtf8(new Uint8Array(bytes));
1807
+ }
1808
+ /**
1809
+ * @static Converts hexadecimal byte string into binary byte string.
1810
+ *
1811
+ * @param hex - Hexadecimal byte string
1812
+ * @param spaced - Whether to separate bytes with spaces, defaults to `true`
1813
+ * @returns Binary byte string
1814
+ *
1815
+ * @example
1816
+ * TextCodec.hexToBinary('ff');
1817
+ */
1818
+ static hexToBinary(hex, spaced = true) {
1819
+ if (!isHexString(hex)) return "";
1820
+ return _splitByCharLength(hex, 2).map((h) => _padStartWith0(parseInt(h, 16), "binary")).join(spaced ? " " : "");
1821
+ }
1822
+ /**
1823
+ * @static Converts binary byte string into hexadecimal byte string.
1824
+ *
1825
+ * @param binary - Binary byte string
1826
+ * @param spaced - Whether to separate bytes with spaces, defaults to `true`
1827
+ * @returns Hexadecimal byte string
1828
+ *
1829
+ * @example
1830
+ * TextCodec.binaryToHex('00000001');
1831
+ */
1832
+ static binaryToHex(binary, spaced = true) {
1833
+ if (!isBinaryString(binary)) return "";
1834
+ return _splitByCharLength(binary, 8).map((b) => _padStartWith0(parseInt(b, 2), "hex")).join(spaced ? " " : "");
1835
+ }
1836
+ /**
1837
+ * @static Converts a Base64-encoded string into UTF-8 text.
1838
+ *
1839
+ * @param b64 - Base64 encoded string
1840
+ * @returns Decoded UTF-8 text
1841
+ *
1842
+ * @example
1843
+ * TextCodec.base64ToUtf8('SGVsbG8=');
1844
+ */
1845
+ static base64ToUtf8(b64) {
1846
+ if (!isBase64(b64)) return "";
1847
+ return bytesToUtf8(base64ToBytes(b64));
1848
+ }
1849
+ /**
1850
+ * @static Converts UTF-8 text into a Base64-encoded string.
1851
+ *
1852
+ * @param text - UTF-8 text to encode
1853
+ * @returns Base64 encoded string
1854
+ *
1855
+ * @example
1856
+ * TextCodec.utf8ToBase64('Hello');
1857
+ */
1858
+ static utf8ToBase64(text) {
1859
+ if (!isNonEmptyString(text)) return "";
1860
+ return bytesToBase64(utf8ToBytes(text));
1861
+ }
1862
+ /**
1863
+ * @static Converts Base64 directly into hexadecimal byte string.
1864
+ *
1865
+ * @param b64 - Base64 encoded string
1866
+ * @param spaced - Whether to separate bytes with spaces, defaults to `true`
1867
+ * @returns Hexadecimal byte string
1868
+ *
1869
+ * @example
1870
+ * TextCodec.base64ToHex('SGVsbG8=');
1871
+ */
1872
+ static base64ToHex(b64, spaced = true) {
1873
+ return this.utf8ToHex(this.base64ToUtf8(b64), spaced);
1874
+ }
1875
+ /**
1876
+ * @static Converts Base64 directly into binary byte string.
1877
+ *
1878
+ * @param b64 - Base64 encoded string
1879
+ * @param spaced - Whether to separate bytes with spaces, defaults to `true`
1880
+ * @returns Binary byte string
1881
+ *
1882
+ * @example
1883
+ * TextCodec.base64ToBinary('SGVsbG8=');
1884
+ */
1885
+ static base64ToBinary(b64, spaced = true) {
1886
+ return this.utf8ToBinary(this.base64ToUtf8(b64), spaced);
1887
+ }
1888
+ /**
1889
+ * @static Converts hexadecimal byte string into a Base64 string.
1890
+ *
1891
+ * @param hex - Hexadecimal byte string
1892
+ * @returns Base64 encoded string
1893
+ *
1894
+ * @example
1895
+ * TextCodec.hexToBase64('48 69');
1896
+ */
1897
+ static hexToBase64(hex) {
1898
+ return this.utf8ToBase64(this.hexToUtf8(hex));
1899
+ }
1900
+ /**
1901
+ * @static Converts binary byte string into a Base64 string.
1902
+ *
1903
+ * @param binary - Binary byte string
1904
+ * @returns Base64 encoded string
1905
+ *
1906
+ * @example
1907
+ * TextCodec.binaryToBase64('01001000 01101001');
1908
+ */
1909
+ static binaryToBase64(binary) {
1910
+ return this.utf8ToBase64(this.binaryToUtf8(binary));
1911
+ }
1912
+ };
1913
+
1914
+ //#endregion
1915
+ //#region src/hash/uuid.ts
1916
+ /**
1917
+ * * Generates UUIDs across all major RFC-compliant versions (1, 3, 4, 5, 6, 7, 8), following standards from `RFC4122`. Default version is `v4`.
1918
+ *
1919
+ * - **Version behavior:**
1920
+ * - `v1` → Timestamp & node-identifier–based
1921
+ * - `v3` → MD5(namespace + name)
1922
+ * - `v4` → Pure random (correct variant + version injection)
1923
+ * - `v5` → SHA-1(namespace + name)
1924
+ * - `v6` → Re-ordered timestamp variant of `v1` (lexicographically sortable)
1925
+ * - `v7` → Unix-time–based, monotonic-friendly
1926
+ * - `v8` → Custom layout, '“Future'` variant (timestamp + randomness)
1927
+ *
1928
+ * @param options Controls version, formatting, and required fields for `v3` and `v5`.
1929
+ * @returns A 5-parts UUID string formatted with correct version/variant bits.
1930
+ *
1931
+ * @example
1932
+ * // Generate a random UUID v4
1933
+ * const id = uuid();
1934
+ *
1935
+ * @example
1936
+ * // Generate uppercase v7
1937
+ * const id = uuid({ version: 'v7', uppercase: true });
1938
+ *
1939
+ * @example
1940
+ * // Generate v5 UUID
1941
+ * const id = uuid({
1942
+ * version: 'v5',
1943
+ * namespace: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
1944
+ * name: 'example'
1945
+ * });
1946
+ *
1947
+ * @remarks
1948
+ * - This utility provides a complete, engine-agnostic UUID generator with full RFC compliance, predictable formatting, and reliable uniqueness characteristics, suitable for browsers, Node.js, and restricted JavaScript runtimes.
1949
+ * - **Notes**
1950
+ * - `v1` and `v6` use a generated pseudo-node identifier.
1951
+ * - `v4` and `v8` uses {@link Math.random} when {@link crypto.getRandomValues} is unavailable, ensuring broad compatibility.
1952
+ * - `v3` and `v5` use internal `MD5`/`SHA-1` implementations and remain fully deterministic.
1953
+ * - `v7` **do not rely on crypto APIs**, preserving engine-agnostic behavior.
1954
+ *
1955
+ * - **Limitations**
1956
+ * - `v1`/`v6`: Node identifier is pseudo-random, not derived from real MAC addresses (for privacy).
1957
+ * - `v3`/`v5`: Hash algorithms (`MD5`/`SHA-1`) follow RFC specs but are not cryptographically secure.
1958
+ * - `v7`: Millisecond precision; extremely high throughput may still cause rare collisions.
1959
+ * - `v8`: Uses a simple timestamp + randomness layout; custom layouts are not supported here.
1960
+ *
1961
+ * - Use {@link https://toolbox.nazmul-nhb.dev/docs/utilities/string/generateRandomID generateRandomID} for customized id generation or {@link https://toolbox.nazmul-nhb.dev/docs/utilities/hash/randomHex randomHex} for hex-only random string with custom length.
1962
+ */
1963
+ function uuid(options) {
1964
+ const { version = "v4", uppercase = false } = options || {};
1965
+ switch (version) {
1966
+ case "v1": {
1967
+ const timestamp = _uuidTimestamp().toString(16).padStart(15, "0");
1968
+ const vTimeHigh = (parseInt(timestamp.slice(0, -12).padStart(3, "0"), 16) | 4096).toString(16).padStart(4, "0");
1969
+ const clockSeq = _clockSeq14();
1970
+ const clockSeqHi = (parseInt(clockSeq.slice(0, 2), 16) & 63 | 128).toString(16).padStart(2, "0");
1971
+ return _formatUUID(timestamp.slice(-8) + timestamp.slice(-12, -8) + vTimeHigh + clockSeqHi + clockSeq.slice(2) + _randomNode48(), 1, uppercase);
1972
+ }
1973
+ case "v3":
1974
+ if (_isOptionV3V5(options)) return _formatUUID(md5(options.namespace + options.name), 3, uppercase);
1975
+ throw new Error("v3 requires valid namespace (uuid) and name!");
1976
+ case "v4": return _formatUUID(_bytesToRandomHex(new Uint8Array(16)), 4, uppercase);
1977
+ case "v5":
1978
+ if (_isOptionV3V5(options)) return _formatUUID(sha1(options.namespace + options.name).slice(0, 32), 5, uppercase);
1979
+ throw new Error("v5 requires valid namespace (uuid) and name!");
1980
+ case "v6": {
1981
+ const timestamp = _uuidTimestamp().toString(16).padStart(15, "0");
1982
+ const vTimeLow = (parseInt(timestamp.slice(12).padStart(3, "0"), 16) | 24576).toString(16).padStart(4, "0");
1983
+ const clockSeq = _clockSeq14();
1984
+ const clockSeqHi = (parseInt(clockSeq.slice(0, 2), 16) & 63 | 128).toString(16).padStart(2, "0");
1985
+ return _formatUUID(timestamp.slice(0, 8) + timestamp.slice(8, 12) + vTimeLow + clockSeqHi + clockSeq.slice(2) + _randomNode48(), 6, uppercase);
1986
+ }
1987
+ case "v7": return _formatUUID(Date.now().toString(16).padStart(12, "0") + randomHex(20), 7, uppercase);
1988
+ case "v8": {
1989
+ const ts = BigInt(Date.now());
1990
+ const bytes = new Uint8Array(16);
1991
+ let temp = ts;
1992
+ for (let i = 5; i >= 0; i--) {
1993
+ bytes[i] = Number(temp & 255n);
1994
+ temp >>= 8n;
1995
+ }
1996
+ return _formatUUID(_bytesToRandomHex(bytes), 8, uppercase);
1997
+ }
1998
+ default: throw new RangeError("Unsupported UUID version!");
1999
+ }
2000
+ }
2001
+ /**
2002
+ * * Decodes a UUID into its internal components, including version, variant, timestamps for time-based UUIDs and other metadata.
2003
+ * - Supports `RFC4122` UUID versions: 1-8.
2004
+ *
2005
+ * @param uuid The UUID string to decode.
2006
+ * @returns A structured `DecodedUUID` object, or `null` for invalid UUIDs.
2007
+ *
2008
+ * @example
2009
+ * const info = decodeUUID("f47ac10b-58cc-4372-a567-0e02b2c3d479");
2010
+ *
2011
+ * @example
2012
+ * const info = decodeUUID(uuid({ version: "v1" }));
2013
+ *
2014
+ * @remarks
2015
+ * - Provides a cross-runtime UUID decoder covering essential metadata and timestamp interpretation for time-ordered UUID versions.
2016
+ * - **Notes**
2017
+ * - `v1/v6` timestamps are converted from the UUID epoch (1582-10-15) to standard Unix milliseconds.
2018
+ * - `v6` timestamps are lexicographically sortable and decoded accordingly.
2019
+ * - `v7` timestamps map directly to Unix time (48-bit millisecond precision).
2020
+ * - `v8` decoding is minimal because layouts are intentionally user-defined.
2021
+ *
2022
+ * - **Limitations**
2023
+ * - `v2` decoding is not implemented specifically.
2024
+ * - `v8` decoding only returns timestamp if it matches a known layout.
2025
+ * - `v3/v5` hash UUIDs contain no timestamp information.
2026
+ */
2027
+ function decodeUUID(uuid) {
2028
+ if (!isUUID(uuid)) return null;
2029
+ const plain = uuid.replace(/-/g, "");
2030
+ const parts = uuid.toLowerCase().split("-");
2031
+ const version = parseInt(parts[2][0], 16);
2032
+ const variantNibble = parseInt(parts[3][0], 16);
2033
+ let variant = "RFC4122";
2034
+ if ((variantNibble & 8) === 0) variant = "NCS";
2035
+ else if ((variantNibble & 12) === 8) variant = "RFC4122";
2036
+ else if ((variantNibble & 14) === 12) variant = "Microsoft";
2037
+ else variant = "Future";
2038
+ const decoded = {
2039
+ version,
2040
+ variant,
2041
+ raw: uuid,
2042
+ plain,
2043
+ singleInt: BigInt("0x" + plain)
2044
+ };
2045
+ const UUID_EPOCH_DIFF = 122192928000000000n;
2046
+ if (version === 1 || version === 6) {
2047
+ let tsHex = parts[2].slice(1) + parts[1] + parts[0];
2048
+ if (version === 6) tsHex = parts[0] + parts[1] + parts[2].slice(1);
2049
+ decoded.timestamp = Number((BigInt("0x" + tsHex) - UUID_EPOCH_DIFF) / 10000n);
2050
+ decoded.node = parts[4];
2051
+ return decoded;
2052
+ }
2053
+ if (version === 7 || version === 8) {
2054
+ const tsHex = parts.join("").slice(0, 12);
2055
+ decoded.variant = version === 7 ? "RFC4122" : "Future";
2056
+ decoded.timestamp = parseInt(tsHex, 16);
2057
+ return decoded;
2058
+ }
2059
+ return decoded;
2060
+ }
2061
+ /** Check if a value is UUID version 1 */
2062
+ function isUUIDv1(value) {
2063
+ return _checkUUIDVersion(value, "1");
2064
+ }
2065
+ /** Check if a value is UUID version 2 */
2066
+ function isUUIDv2(value) {
2067
+ return _checkUUIDVersion(value, "2");
2068
+ }
2069
+ /** Check if a value is UUID version 3 */
2070
+ function isUUIDv3(value) {
2071
+ return _checkUUIDVersion(value, "3");
2072
+ }
2073
+ /** Check if a value is UUID version 4 */
2074
+ function isUUIDv4(value) {
2075
+ return _checkUUIDVersion(value, "4");
2076
+ }
2077
+ /** Check if a value is UUID version 5 */
2078
+ function isUUIDv5(value) {
2079
+ return _checkUUIDVersion(value, "5");
2080
+ }
2081
+ /** Check if a value is UUID version 6 */
2082
+ function isUUIDv6(value) {
2083
+ return _checkUUIDVersion(value, "6");
2084
+ }
2085
+ /** Check if a value is UUID version 7 */
2086
+ function isUUIDv7(value) {
2087
+ return _checkUUIDVersion(value, "7");
2088
+ }
2089
+ /** Check if a value is UUID version 8 */
2090
+ function isUUIDv8(value) {
2091
+ return _checkUUIDVersion(value, "8");
2092
+ }
2093
+
2094
+ //#endregion
2095
+ export { Cipher, Signet, TextCodec, base64ToBytes, bytesToBase64, bytesToHex, bytesToUtf8, concatBytes, decodeUUID, generateRandomID, generateRandomID as randomID, hexToBytes, hmacSha256, intTo4BytesBE, isUUID, isUUIDv1, isUUIDv2, isUUIDv3, isUUIDv4, isUUIDv5, isUUIDv6, isUUIDv7, isUUIDv8, md5, randomHex, sha1, sha256, sha256Bytes, uint8To32ArrayBE, utf8ToBytes, uuid };