@exodus/bytes 1.9.0 → 1.11.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.
package/README.md CHANGED
@@ -4,11 +4,14 @@
4
4
  [![](https://flat.badgen.net/github/release/ExodusOSS/bytes?icon=github)](https://github.com/ExodusOSS/bytes/releases)
5
5
  [![](https://flat.badgen.net/npm/dm/@exodus/bytes)](https://www.npmcharts.com/compare/@exodus/bytes?minimal=true)
6
6
  [![](https://flat.badgen.net/npm/license/@exodus/bytes)](https://github.com/ExodusOSS/bytes/blob/HEAD/LICENSE)
7
+ [![](https://flat.badgen.net/github/checks/ExodusOSS/bytes/main?icon=github)](https://github.com/ExodusOSS/bytes/actions/workflows/test.yml?query=branch%3Amain)
7
8
 
8
9
  `Uint8Array` conversion to and from `base64`, `base32`, `base58`, `hex`, `utf8`, `utf16`, `bech32` and `wif`
9
10
 
10
11
  And a [`TextEncoder` / `TextDecoder` polyfill](#textencoder--textdecoder-polyfill)
11
12
 
13
+ See [documentation](https://exodusoss.github.io/bytes).
14
+
12
15
  ## Strict
13
16
 
14
17
  Performs proper input validation, ensures no garbage-in-garbage-out
@@ -99,6 +102,8 @@ See [the list of encodings](https://encoding.spec.whatwg.org/#names-and-labels).
99
102
 
100
103
  ### `@exodus/bytes/utf8.js`
101
104
 
105
+ UTF-8 encoding/decoding
106
+
102
107
  ```js
103
108
  import { utf8fromString, utf8toString } from '@exodus/bytes/utf8.js'
104
109
 
@@ -106,13 +111,65 @@ import { utf8fromString, utf8toString } from '@exodus/bytes/utf8.js'
106
111
  import { utf8fromStringLoose, utf8toStringLoose } from '@exodus/bytes/utf8.js'
107
112
  ```
108
113
 
109
- ##### `utf8fromString(str, format = 'uint8')`
110
- ##### `utf8fromStringLoose(str, format = 'uint8')`
111
- ##### `utf8toString(arr)`
112
- ##### `utf8toStringLoose(arr)`
114
+ _These methods by design encode/decode BOM (codepoint `U+FEFF` Byte Order Mark) as-is._\
115
+ _If you need BOM handling or detection, use `@exodus/bytes/encoding.js`_
116
+
117
+ #### `utf8fromString(string, format = 'uint8')`
118
+
119
+ Encode a string to UTF-8 bytes (strict mode)
120
+
121
+ Throws on invalid Unicode (unpaired surrogates)
122
+
123
+ This is similar to the following snippet (but works on all engines):
124
+ ```js
125
+ // Strict encode, requiring Unicode codepoints to be valid
126
+ if (typeof string !== 'string' || !string.isWellFormed()) throw new TypeError()
127
+ return new TextEncoder().encode(string)
128
+ ```
129
+
130
+ #### `utf8fromStringLoose(string, format = 'uint8')`
131
+
132
+ Encode a string to UTF-8 bytes (loose mode)
133
+
134
+ Replaces invalid Unicode (unpaired surrogates) with replacement codepoints `U+FFFD`
135
+ per [WHATWG Encoding](https://encoding.spec.whatwg.org/) specification.
136
+
137
+ _Such replacement is a non-injective function, is irreversable and causes collisions.\
138
+ Prefer using strict throwing methods for cryptography applications._
139
+
140
+ This is similar to the following snippet (but works on all engines):
141
+ ```js
142
+ // Loose encode, replacing invalid Unicode codepoints with U+FFFD
143
+ if (typeof string !== 'string') throw new TypeError()
144
+ return new TextEncoder().encode(string)
145
+ ```
146
+
147
+ #### `utf8toString(arr)`
148
+
149
+ Decode UTF-8 bytes to a string (strict mode)
150
+
151
+ Throws on invalid UTF-8 byte sequences
152
+
153
+ This is similar to `new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(arr)`,
154
+ but works on all engines.
155
+
156
+ #### `utf8toStringLoose(arr)`
157
+
158
+ Decode UTF-8 bytes to a string (loose mode)
159
+
160
+ Replaces invalid UTF-8 byte sequences with replacement codepoints `U+FFFD`
161
+ per [WHATWG Encoding](https://encoding.spec.whatwg.org/) specification.
162
+
163
+ _Such replacement is a non-injective function, is irreversable and causes collisions.\
164
+ Prefer using strict throwing methods for cryptography applications._
165
+
166
+ This is similar to `new TextDecoder('utf-8', { ignoreBOM: true }).decode(arr)`,
167
+ but works on all engines.
113
168
 
114
169
  ### `@exodus/bytes/utf16.js`
115
170
 
171
+ UTF-16 encoding/decoding
172
+
116
173
  ```js
117
174
  import { utf16fromString, utf16toString } from '@exodus/bytes/utf16.js'
118
175
 
@@ -120,24 +177,67 @@ import { utf16fromString, utf16toString } from '@exodus/bytes/utf16.js'
120
177
  import { utf16fromStringLoose, utf16toStringLoose } from '@exodus/bytes/utf16.js'
121
178
  ```
122
179
 
123
- ##### `utf16fromString(str, format = 'uint16')`
124
- ##### `utf16fromStringLoose(str, format = 'uint16')`
125
- ##### `utf16toString(arr, 'uint16')`
126
- ##### `utf16toStringLoose(arr, 'uint16')`
180
+ _These methods by design encode/decode BOM (codepoint `U+FEFF` Byte Order Mark) as-is._\
181
+ _If you need BOM handling or detection, use `@exodus/bytes/encoding.js`_
182
+
183
+ #### `utf16fromString(string, format = 'uint16')`
184
+
185
+ Encode a string to UTF-16 bytes (strict mode)
186
+
187
+ Throws on invalid Unicode (unpaired surrogates)
188
+
189
+ #### `utf16fromStringLoose(string, format = 'uint16')`
190
+
191
+ Encode a string to UTF-16 bytes (loose mode)
192
+
193
+ Replaces invalid Unicode (unpaired surrogates) with replacement codepoints `U+FFFD`
194
+ per [WHATWG Encoding](https://encoding.spec.whatwg.org/) specification.
195
+
196
+ _Such replacement is a non-injective function, is irreversible and causes collisions.\
197
+ Prefer using strict throwing methods for cryptography applications._
198
+
199
+ #### `utf16toString(arr, format = 'uint16')`
200
+
201
+ Decode UTF-16 bytes to a string (strict mode)
202
+
203
+ Throws on invalid UTF-16 byte sequences
204
+
205
+ Throws on non-even byte length.
206
+
207
+ #### `utf16toStringLoose(arr, format = 'uint16')`
208
+
209
+ Decode UTF-16 bytes to a string (loose mode)
210
+
211
+ Replaces invalid UTF-16 byte sequences with replacement codepoints `U+FFFD`
212
+ per [WHATWG Encoding](https://encoding.spec.whatwg.org/) specification.
213
+
214
+ _Such replacement is a non-injective function, is irreversible and causes collisions.\
215
+ Prefer using strict throwing methods for cryptography applications._
216
+
217
+ Throws on non-even byte length.
127
218
 
128
219
  ### `@exodus/bytes/single-byte.js`
129
220
 
221
+ Decode / encode the legacy single-byte encodings according to the
222
+ [Encoding standard](https://encoding.spec.whatwg.org/)
223
+ ([§9](https://encoding.spec.whatwg.org/#legacy-single-byte-encodings),
224
+ [§14.5](https://encoding.spec.whatwg.org/#x-user-defined)),
225
+ and [unicode.org](https://unicode.org/Public/MAPPINGS/ISO8859) `iso-8859-*` mappings.
226
+
130
227
  ```js
131
228
  import { createSinglebyteDecoder, createSinglebyteEncoder } from '@exodus/bytes/single-byte.js'
132
229
  import { windows1252toString, windows1252fromString } from '@exodus/bytes/single-byte.js'
133
230
  import { latin1toString, latin1fromString } from '@exodus/bytes/single-byte.js'
134
231
  ```
135
232
 
136
- Decode / encode the legacy single-byte encodings according to the
137
- [Encoding standard](https://encoding.spec.whatwg.org/)
138
- ([§9](https://encoding.spec.whatwg.org/#legacy-single-byte-encodings),
139
- [§14.5](https://encoding.spec.whatwg.org/#x-user-defined)),
140
- and [unicode.org](https://unicode.org/Public/MAPPINGS/ISO8859) `iso-8859-*` mappings.
233
+ > [!WARNING]
234
+ > This is a lower-level API for single-byte encodings.
235
+ > It might not match what you expect, as it supports both WHATWG and unicode.org encodings under
236
+ > different names, with the main intended usecase for the latter being either non-web or legacy contexts.
237
+ >
238
+ > For a safe WHATWG Encoding-compatible API, see `@exodus/bytes/encoding.js` import (and variants of it).
239
+ >
240
+ > Be sure to know what you are doing and check documentation when directly using encodings from this file.
141
241
 
142
242
  Supports all single-byte encodings listed in the WHATWG Encoding standard:
143
243
  `ibm866`, `iso-8859-2`, `iso-8859-3`, `iso-8859-4`, `iso-8859-5`, `iso-8859-6`, `iso-8859-7`, `iso-8859-8`,
@@ -174,13 +274,13 @@ corresponding [unicode.org encodings](https://unicode.org/Public/MAPPINGS/VENDOR
174
274
  they encode/decode all the old valid (non-replacement) strings / byte sequences identically, but can also support
175
275
  a wider range of inputs.
176
276
 
177
- ##### `createSinglebyteDecoder(encoding, loose = false)`
277
+ #### `createSinglebyteDecoder(encoding, loose = false)`
178
278
 
179
279
  Create a decoder for a supported one-byte `encoding`, given its lowercased name `encoding`.
180
280
 
181
281
  Returns a function `decode(arr)` that decodes bytes to a string.
182
282
 
183
- ##### `createSinglebyteEncoder(encoding, { mode = 'fatal' })`
283
+ #### `createSinglebyteEncoder(encoding, { mode = 'fatal' })`
184
284
 
185
285
  Create an encoder for a supported one-byte `encoding`, given its lowercased name `encoding`.
186
286
 
@@ -189,7 +289,7 @@ Returns a function `encode(string)` that encodes a string to bytes.
189
289
  In `'fatal'` mode (default), will throw on non well-formed strings or any codepoints which could
190
290
  not be encoded in the target encoding.
191
291
 
192
- ##### `latin1toString(arr)`
292
+ #### `latin1toString(arr)`
193
293
 
194
294
  Decode `iso-8859-1` bytes to a string.
195
295
 
@@ -200,21 +300,22 @@ Same as:
200
300
  const latin1toString = createSinglebyteDecoder('iso-8859-1')
201
301
  ```
202
302
 
203
- Note: this is different from `new TextDecoder('iso-8859-1')` and `new TextDecoder('latin1')`, as
204
- those alias to `new TextDecoder('windows-1252')`.
303
+ > [!NOTE]
304
+ > This is different from `new TextDecoder('iso-8859-1')` and `new TextDecoder('latin1')`, as those
305
+ > alias to `new TextDecoder('windows-1252')`.
205
306
 
206
- ##### `latin1fromString(string)`
307
+ #### `latin1fromString(string)`
207
308
 
208
309
  Encode a string to `iso-8859-1` bytes.
209
310
 
210
- Will throw on non well-formed strings or any codepoints which could not be encoded in `iso-8859-1`.
311
+ Throws on non well-formed strings or any codepoints which could not be encoded in `iso-8859-1`.
211
312
 
212
313
  Same as:
213
314
  ```js
214
315
  const latin1fromString = createSinglebyteEncoder('iso-8859-1', { mode: 'fatal' })
215
316
  ```
216
317
 
217
- ##### `windows1252toString(arr)`
318
+ #### `windows1252toString(arr)`
218
319
 
219
320
  Decode `windows-1252` bytes to a string.
220
321
 
@@ -225,11 +326,11 @@ Same as:
225
326
  const windows1252toString = createSinglebyteDecoder('windows-1252')
226
327
  ```
227
328
 
228
- ##### `windows1252fromString(string)`
329
+ #### `windows1252fromString(string)`
229
330
 
230
331
  Encode a string to `windows-1252` bytes.
231
332
 
232
- Will throw on non well-formed strings or any codepoints which could not be encoded in `windows-1252`.
333
+ Throws on non well-formed strings or any codepoints which could not be encoded in `windows-1252`.
233
334
 
234
335
  Same as:
235
336
  ```js
@@ -238,50 +339,91 @@ const windows1252fromString = createSinglebyteEncoder('windows-1252', { mode: 'f
238
339
 
239
340
  ### `@exodus/bytes/multi-byte.js`
240
341
 
241
- ```js
242
- import { createMultibyteDecoder } from '@exodus/bytes/multi-byte.js'
243
- ```
244
-
245
- Decode the legacy multi-byte encodings according to the [Encoding standard](https://encoding.spec.whatwg.org/)
342
+ Decode / encode the legacy multi-byte encodings according to the
343
+ [Encoding standard](https://encoding.spec.whatwg.org/)
246
344
  ([§10](https://encoding.spec.whatwg.org/#legacy-multi-byte-chinese-(simplified)-encodings),
247
345
  [§11](https://encoding.spec.whatwg.org/#legacy-multi-byte-chinese-(traditional)-encodings),
248
346
  [§12](https://encoding.spec.whatwg.org/#legacy-multi-byte-japanese-encodings),
249
347
  [§13](https://encoding.spec.whatwg.org/#legacy-multi-byte-korean-encodings)).
250
348
 
251
- Supports all legacy multi-byte encodings listed in the standard:
349
+ ```js
350
+ import { createMultibyteDecoder, createMultibyteEncoder } from '@exodus/bytes/multi-byte.js'
351
+ ```
352
+
353
+ > [!WARNING]
354
+ > This is a lower-level API for legacy multi-byte encodings.
355
+ >
356
+ > For a safe WHATWG Encoding-compatible API, see `@exodus/bytes/encoding.js` import (and variants of it).
357
+ >
358
+ > Be sure to know what you are doing and check documentation when directly using encodings from this file.
359
+
360
+ Supports all legacy multi-byte encodings listed in the WHATWG Encoding standard:
252
361
  `gbk`, `gb18030`, `big5`, `euc-jp`, `iso-2022-jp`, `shift_jis`, `euc-kr`.
253
362
 
254
- ##### `createMultibyteDecoder(encoding, loose = false)`
363
+ #### `createMultibyteDecoder(encoding, loose = false)`
255
364
 
256
365
  Create a decoder for a supported legacy multi-byte `encoding`, given its lowercased name `encoding`.
257
366
 
258
367
  Returns a function `decode(arr, stream = false)` that decodes bytes to a string.
259
368
 
260
- That function will have state while `stream = true` is used.
369
+ The returned function will maintain internal state while `stream = true` is used, allowing it to
370
+ handle incomplete multi-byte sequences across multiple calls.
371
+ State is reset when `stream = false` or when the function is called without the `stream` parameter.
372
+
373
+ #### `createMultibyteEncoder(encoding, { mode = 'fatal' })`
374
+
375
+ Create an encoder for a supported legacy multi-byte `encoding`, given its lowercased name `encoding`.
376
+
377
+ Returns a function `encode(string)` that encodes a string to bytes.
378
+
379
+ In `'fatal'` mode (default), will throw on non well-formed strings or any codepoints which could
380
+ not be encoded in the target encoding.
261
381
 
262
382
  ### `@exodus/bytes/bigint.js`
263
383
 
384
+ Convert between BigInt and Uint8Array
385
+
264
386
  ```js
265
387
  import { fromBigInt, toBigInt } from '@exodus/bytes/bigint.js'
266
388
  ```
267
389
 
268
- ##### `fromBigInt(bigint, { length, format = 'uint8' })`
269
- ##### `toBigInt(arr)`
390
+ #### `fromBigInt(bigint, { length, format = 'uint8' })`
391
+
392
+ Convert a BigInt to a Uint8Array or Buffer
393
+
394
+ The output bytes are in big-endian format.
395
+
396
+ Throws if the BigInt is negative or cannot fit into the specified length.
397
+
398
+ #### `toBigInt(arr)`
399
+
400
+ Convert a Uint8Array or Buffer to a BigInt
401
+
402
+ The bytes are interpreted as a big-endian unsigned integer.
270
403
 
271
404
  ### `@exodus/bytes/hex.js`
272
405
 
273
- Implements Base16 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648) (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
406
+ Implements Base16 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
407
+ (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
274
408
 
275
409
  ```js
276
410
  import { fromHex, toHex } from '@exodus/bytes/hex.js'
277
411
  ```
278
412
 
279
- ##### `fromHex(string)`
280
- ##### `toHex(arr)`
413
+ #### `fromHex(string, format = 'uint8')`
414
+
415
+ Decode a hex string to bytes
416
+
417
+ Unlike `Buffer.from()`, throws on invalid input
418
+
419
+ #### `toHex(arr)`
420
+
421
+ Encode a `Uint8Array` to a lowercase hex string
281
422
 
282
423
  ### `@exodus/bytes/base64.js`
283
424
 
284
- Implements Base64 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648) (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
425
+ Implements base64 and base64url from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
426
+ (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
285
427
 
286
428
  ```js
287
429
  import { fromBase64, toBase64 } from '@exodus/bytes/base64.js'
@@ -289,29 +431,67 @@ import { fromBase64url, toBase64url } from '@exodus/bytes/base64.js'
289
431
  import { fromBase64any } from '@exodus/bytes/base64.js'
290
432
  ```
291
433
 
292
- ##### `fromBase64(str, { format = 'uint8', padding = 'both' })`
293
- ##### `fromBase64url(str, { format = 'uint8', padding = false })`
294
- ##### `fromBase64any(str, { format = 'uint8', padding = 'both' })`
295
- ##### `toBase64(arr, { padding = true })`
296
- ##### `toBase64url(arr, { padding = false })`
434
+ #### `fromBase64(string, { format = 'uint8', padding = 'both' })`
435
+
436
+ Decode a base64 string to bytes
437
+
438
+ Operates in strict mode for last chunk, does not allow whitespace
439
+
440
+ #### `fromBase64url(string, { format = 'uint8', padding = false })`
441
+
442
+ Decode a base64url string to bytes
443
+
444
+ Operates in strict mode for last chunk, does not allow whitespace
445
+
446
+ #### `fromBase64any(string, { format = 'uint8', padding = 'both' })`
447
+
448
+ Decode either base64 or base64url string to bytes
449
+
450
+ Automatically detects the variant based on characters present
451
+
452
+ #### `toBase64(arr, { padding = true })`
453
+
454
+ Encode a `Uint8Array` to a base64 string (RFC 4648)
455
+
456
+ #### `toBase64url(arr, { padding = false })`
457
+
458
+ Encode a `Uint8Array` to a base64url string (RFC 4648)
297
459
 
298
460
  ### `@exodus/bytes/base32.js`
299
461
 
300
- Implements Base32 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648) (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
462
+ Implements base32 and base32hex from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
463
+ (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
301
464
 
302
465
  ```js
303
466
  import { fromBase32, toBase32 } from '@exodus/bytes/base32.js'
304
467
  import { fromBase32hex, toBase32hex } from '@exodus/bytes/base32.js'
305
468
  ```
306
469
 
307
- ##### `fromBase32(str, { format = 'uint8', padding = 'both' })`
308
- ##### `fromBase32hex(str, { format = 'uint8', padding = 'both' })`
309
- ##### `toBase32(arr, { padding = false })`
310
- ##### `toBase32hex(arr, { padding = false })`
470
+ #### `fromBase32(string, { format = 'uint8', padding = 'both' })`
471
+
472
+ Decode a base32 string to bytes
473
+
474
+ Operates in strict mode for last chunk, does not allow whitespace
475
+
476
+ #### `fromBase32hex(string, { format = 'uint8', padding = 'both' })`
477
+
478
+ Decode a base32hex string to bytes
479
+
480
+ Operates in strict mode for last chunk, does not allow whitespace
481
+
482
+ #### `toBase32(arr, { padding = false })`
483
+
484
+ Encode a `Uint8Array` to a base32 string (RFC 4648)
485
+
486
+ #### `toBase32hex(arr, { padding = false })`
487
+
488
+ Encode a `Uint8Array` to a base32hex string (RFC 4648)
311
489
 
312
490
  ### `@exodus/bytes/bech32.js`
313
491
 
314
- Implements [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#specification) and [BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki#specification).
492
+ Implements bech32 and bech32m from
493
+ [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#specification)
494
+ and [BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki#specification).
315
495
 
316
496
  ```js
317
497
  import { fromBech32, toBech32 } from '@exodus/bytes/bech32.js'
@@ -319,29 +499,67 @@ import { fromBech32m, toBech32m } from '@exodus/bytes/bech32.js'
319
499
  import { getPrefix } from '@exodus/bytes/bech32.js'
320
500
  ```
321
501
 
322
- ##### `getPrefix(str, limit = 90)`
502
+ #### `getPrefix(string, limit = 90)`
503
+
504
+ Extract the prefix from a bech32 or bech32m string without full validation
505
+
506
+ This is a quick check that skips most validation.
507
+
508
+ #### `fromBech32(string, limit = 90)`
509
+
510
+ Decode a bech32 string to bytes
511
+
512
+ #### `toBech32(prefix, bytes, limit = 90)`
513
+
514
+ Encode bytes to a bech32 string
323
515
 
324
- ##### `fromBech32(str, limit = 90)`
325
- ##### `toBech32(prefix, bytes, limit = 90)`
516
+ #### `fromBech32m(string, limit = 90)`
326
517
 
327
- ##### `fromBech32m(str, limit = 90)`
328
- ##### `toBech32m(prefix, bytes, limit = 90)`
518
+ Decode a bech32m string to bytes
519
+
520
+ #### `toBech32m(prefix, bytes, limit = 90)`
521
+
522
+ Encode bytes to a bech32m string
329
523
 
330
524
  ### `@exodus/bytes/base58.js`
331
525
 
526
+ Implements [base58](https://www.ietf.org/archive/id/draft-msporny-base58-03.txt) encoding.
527
+
528
+ Supports both standard base58 and XRP variant alphabets.
529
+
332
530
  ```js
333
531
  import { fromBase58, toBase58 } from '@exodus/bytes/base58.js'
334
532
  import { fromBase58xrp, toBase58xrp } from '@exodus/bytes/base58.js'
335
533
  ```
336
534
 
337
- ##### `fromBase58(str, format = 'uint8')`
338
- ##### `toBase58(arr)`
535
+ #### `fromBase58(string, format = 'uint8')`
536
+
537
+ Decode a base58 string to bytes
538
+
539
+ Uses the standard Bitcoin base58 alphabet
339
540
 
340
- ##### `fromBase58xrp(str, format = 'uint8')`
341
- ##### `toBase58xrp(arr)`
541
+ #### `toBase58(arr)`
542
+
543
+ Encode a `Uint8Array` to a base58 string
544
+
545
+ Uses the standard Bitcoin base58 alphabet
546
+
547
+ #### `fromBase58xrp(string, format = 'uint8')`
548
+
549
+ Decode a base58 string to bytes using XRP alphabet
550
+
551
+ Uses the XRP variant base58 alphabet
552
+
553
+ #### `toBase58xrp(arr)`
554
+
555
+ Encode a `Uint8Array` to a base58 string using XRP alphabet
556
+
557
+ Uses the XRP variant base58 alphabet
342
558
 
343
559
  ### `@exodus/bytes/base58check.js`
344
560
 
561
+ Implements [base58check](https://en.bitcoin.it/wiki/Base58Check_encoding) encoding.
562
+
345
563
  ```js
346
564
  import { fromBase58check, toBase58check } from '@exodus/bytes/base58check.js'
347
565
  import { fromBase58checkSync, toBase58checkSync } from '@exodus/bytes/base58check.js'
@@ -350,14 +568,38 @@ import { makeBase58check } from '@exodus/bytes/base58check.js'
350
568
 
351
569
  On non-Node.js, requires peer dependency [@noble/hashes](https://www.npmjs.com/package/@noble/hashes) to be installed.
352
570
 
353
- ##### `async fromBase58check(str, format = 'uint8')`
354
- ##### `async toBase58check(arr)`
355
- ##### `fromBase58checkSync(str, format = 'uint8')`
356
- ##### `toBase58checkSync(arr)`
357
- ##### `makeBase58check(hashAlgo, hashAlgoSync)`
571
+ #### `async fromBase58check(string, format = 'uint8')`
572
+
573
+ Decode a base58check string to bytes asynchronously
574
+
575
+ Validates the checksum using double SHA-256
576
+
577
+ #### `async toBase58check(arr)`
578
+
579
+ Encode bytes to base58check string asynchronously
580
+
581
+ Uses double SHA-256 for checksum calculation
582
+
583
+ #### `fromBase58checkSync(string, format = 'uint8')`
584
+
585
+ Decode a base58check string to bytes synchronously
586
+
587
+ Validates the checksum using double SHA-256
588
+
589
+ #### `toBase58checkSync(arr)`
590
+
591
+ Encode bytes to base58check string synchronously
592
+
593
+ Uses double SHA-256 for checksum calculation
594
+
595
+ #### `makeBase58check(hashAlgo, hashAlgoSync)`
596
+
597
+ Create a base58check encoder/decoder with custom hash functions
358
598
 
359
599
  ### `@exodus/bytes/wif.js`
360
600
 
601
+ Wallet Import Format (WIF) encoding and decoding.
602
+
361
603
  ```js
362
604
  import { fromWifString, toWifString } from '@exodus/bytes/wif.js'
363
605
  import { fromWifStringSync, toWifStringSync } from '@exodus/bytes/wif.js'
@@ -365,40 +607,84 @@ import { fromWifStringSync, toWifStringSync } from '@exodus/bytes/wif.js'
365
607
 
366
608
  On non-Node.js, requires peer dependency [@noble/hashes](https://www.npmjs.com/package/@noble/hashes) to be installed.
367
609
 
368
- ##### `async fromWifString(string, version)`
369
- ##### `fromWifStringSync(string, version)`
370
- ##### `async toWifString({ version, privateKey, compressed })`
371
- ##### `toWifStringSync({ version, privateKey, compressed })`
610
+ #### `async fromWifString(string[, version])`
372
611
 
373
- ### `@exodus/bytes/encoding.js`
612
+ Decode a WIF string to WIF data
374
613
 
375
- ```js
376
- import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding.js'
377
- import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding.js' // Requires Streams
614
+ Returns a promise that resolves to an object with `{ version, privateKey, compressed }`.
378
615
 
379
- // Hooks for standards
380
- import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
616
+ The optional `version` parameter validates the version byte.
617
+
618
+ Throws if the WIF string is invalid or version doesn't match.
619
+
620
+ #### `fromWifStringSync(string[, version])`
621
+
622
+ Decode a WIF string to WIF data (synchronous)
623
+
624
+ Returns an object with `{ version, privateKey, compressed }`.
625
+
626
+ The optional `version` parameter validates the version byte.
627
+
628
+ Throws if the WIF string is invalid or version doesn't match.
629
+
630
+ #### `async toWifString({ version, privateKey, compressed })`
631
+
632
+ Encode WIF data to a WIF string
633
+
634
+ #### `toWifStringSync({ version, privateKey, compressed })`
635
+
636
+ Encode WIF data to a WIF string (synchronous)
637
+
638
+ ### `@exodus/bytes/array.js`
639
+
640
+ TypedArray utils and conversions.
641
+
642
+ ```js
643
+ import { typedView } from '@exodus/bytes/array.js'
381
644
  ```
382
645
 
646
+ #### `typedView(arr, format = 'uint8')`
647
+
648
+ Create a view of a TypedArray in the specified format (`'uint8'` or `'buffer'`)
649
+
650
+ > [!IMPORTANT]
651
+ > Does not copy data, returns a view on the same underlying buffer
652
+
653
+ ### `@exodus/bytes/encoding.js`
654
+
383
655
  Implements the [Encoding standard](https://encoding.spec.whatwg.org/):
384
656
  [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder),
385
657
  [TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder),
386
658
  [TextDecoderStream](https://encoding.spec.whatwg.org/#interface-textdecoderstream),
387
659
  [TextEncoderStream](https://encoding.spec.whatwg.org/#interface-textencoderstream),
388
- some [hooks](https://encoding.spec.whatwg.org/#specification-hooks) (see below).
660
+ some [hooks](https://encoding.spec.whatwg.org/#specification-hooks).
661
+
662
+ ```js
663
+ import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding.js'
664
+ import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding.js' // Requires Streams
665
+
666
+ // Hooks for standards
667
+ import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
668
+ ```
389
669
 
390
670
  #### `new TextDecoder(label = 'utf-8', { fatal = false, ignoreBOM = false })`
391
671
 
392
672
  [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) implementation/polyfill.
393
673
 
674
+ Decode bytes to strings according to [WHATWG Encoding](https://encoding.spec.whatwg.org) specification.
675
+
394
676
  #### `new TextEncoder()`
395
677
 
396
678
  [TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) implementation/polyfill.
397
679
 
680
+ Encode strings to UTF-8 bytes according to [WHATWG Encoding](https://encoding.spec.whatwg.org) specification.
681
+
398
682
  #### `new TextDecoderStream(label = 'utf-8', { fatal = false, ignoreBOM = false })`
399
683
 
400
684
  [TextDecoderStream](https://encoding.spec.whatwg.org/#interface-textdecoderstream) implementation/polyfill.
401
685
 
686
+ A [Streams](https://streams.spec.whatwg.org/) wrapper for `TextDecoder`.
687
+
402
688
  Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by the platform or
403
689
  [polyfilled](https://npmjs.com/package/web-streams-polyfill).
404
690
 
@@ -406,6 +692,8 @@ Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by t
406
692
 
407
693
  [TextEncoderStream](https://encoding.spec.whatwg.org/#interface-textencoderstream) implementation/polyfill.
408
694
 
695
+ A [Streams](https://streams.spec.whatwg.org/) wrapper for `TextEncoder`.
696
+
409
697
  Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by the platform or
410
698
  [polyfilled](https://npmjs.com/package/web-streams-polyfill).
411
699
 
@@ -413,7 +701,7 @@ Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by t
413
701
 
414
702
  Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
415
703
 
416
- Converts an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
704
+ Convert an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
417
705
  as a case-sensitive string.
418
706
 
419
707
  If an encoding with that label does not exist, returns `null`.
@@ -422,7 +710,7 @@ All encoding names are also valid labels for corresponding encodings.
422
710
 
423
711
  #### `normalizeEncoding(label)`
424
712
 
425
- Converts an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
713
+ Convert an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
426
714
  as an ASCII-lowercased string.
427
715
 
428
716
  If an encoding with that label does not exist, returns `null`.
@@ -445,10 +733,10 @@ All encoding names are also valid labels for corresponding encodings.
445
733
  Implements [BOM sniff](https://encoding.spec.whatwg.org/#bom-sniff) legacy hook.
446
734
 
447
735
  Given a `TypedArray` or an `ArrayBuffer` instance `input`, returns either of:
448
- * `'utf-8'`, if `input` starts with UTF-8 byte order mark.
449
- * `'utf-16le'`, if `input` starts with UTF-16LE byte order mark.
450
- * `'utf-16be'`, if `input` starts with UTF-16BE byte order mark.
451
- * `null` otherwise.
736
+ - `'utf-8'`, if `input` starts with UTF-8 byte order mark.
737
+ - `'utf-16le'`, if `input` starts with UTF-16LE byte order mark.
738
+ - `'utf-16be'`, if `input` starts with UTF-16BE byte order mark.
739
+ - `null` otherwise.
452
740
 
453
741
  #### `legacyHookDecode(input, fallbackEncoding = 'utf-8')`
454
742
 
@@ -461,10 +749,10 @@ decodes the `input` using that encoding, skipping BOM if it was present.
461
749
 
462
750
  Notes:
463
751
 
464
- * BOM-sniffed encoding takes precedence over `fallbackEncoding` option per spec.
465
- Use with care.
466
- * Always operates in non-fatal [mode](https://encoding.spec.whatwg.org/#textdecoder-error-mode),
467
- aka replacement. It can convert different byte sequences to equal strings.
752
+ - BOM-sniffed encoding takes precedence over `fallbackEncoding` option per spec.
753
+ Use with care.
754
+ - Always operates in non-fatal [mode](https://encoding.spec.whatwg.org/#textdecoder-error-mode),
755
+ aka replacement. It can convert different byte sequences to equal strings.
468
756
 
469
757
  This method is similar to the following code, except that it doesn't support encoding labels and
470
758
  only expects lowercased encoding name:
@@ -475,6 +763,10 @@ new TextDecoder(getBOMEncoding(input) ?? fallbackEncoding).decode(input)
475
763
 
476
764
  ### `@exodus/bytes/encoding-lite.js`
477
765
 
766
+ The exact same exports as `@exodus/bytes/encoding.js` are also exported as
767
+ `@exodus/bytes/encoding-lite.js`, with the difference that the lite version does not load
768
+ multi-byte `TextDecoder` encodings by default to reduce bundle size 10x.
769
+
478
770
  ```js
479
771
  import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
480
772
  import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lite.js' // Requires Streams
@@ -483,10 +775,6 @@ import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lit
483
775
  import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
484
776
  ```
485
777
 
486
- The exact same exports as `@exodus/bytes/encoding.js` are also exported as
487
- `@exodus/bytes/encoding-lite.js`, with the difference that the lite version does not load
488
- multi-byte `TextDecoder` encodings by default to reduce bundle size 10x.
489
-
490
778
  The only affected encodings are: `gbk`, `gb18030`, `big5`, `euc-jp`, `iso-2022-jp`, `shift_jis`
491
779
  and their [labels](https://encoding.spec.whatwg.org/#names-and-labels) when used with `TextDecoder`.
492
780
 
@@ -534,6 +822,9 @@ true
534
822
 
535
823
  ### `@exodus/bytes/encoding-browser.js`
536
824
 
825
+ Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
826
+ browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
827
+
537
828
  ```js
538
829
  import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-browser.js'
539
830
  import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-browser.js' // Requires Streams
@@ -542,9 +833,6 @@ import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-bro
542
833
  import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-browser.js'
543
834
  ```
544
835
 
545
- Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
546
- browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
547
-
548
836
  Under non-browser engines (Node.js, React Native, etc.) a full polyfill is used as those platforms
549
837
  do not provide sufficiently complete / non-buggy `TextDecoder` APIs.
550
838
 
@@ -553,6 +841,45 @@ do not provide sufficiently complete / non-buggy `TextDecoder` APIs.
553
841
  > but they are fixing them and the expected update window is short.\
554
842
  > If you want to circumvent browser bugs, use full `@exodus/bytes/encoding.js` import.
555
843
 
844
+ ### `@exodus/bytes/whatwg.js`
845
+
846
+ WHATWG helpers
847
+
848
+ ```js
849
+ import '@exodus/bytes/encoding.js' // For full legacy multi-byte encodings support
850
+ import { percentEncodeAfterEncoding } from '@exodus/bytes/whatwg.js'
851
+ ```
852
+
853
+ #### `percentEncodeAfterEncoding(encoding, input, percentEncodeSet, spaceAsPlus = false)`
854
+
855
+ Implements [percent-encode after encoding](https://url.spec.whatwg.org/#string-percent-encode-after-encoding)
856
+ per WHATWG URL specification.
857
+
858
+ > [!IMPORTANT]
859
+ > You must import `@exodus/bytes/encoding.js` for this API to accept legacy multi-byte encodings.
860
+
861
+ Encodings `utf16-le`, `utf16-be`, and `replacement` are not accepted.
862
+
863
+ [C0 control percent-encode set](https://url.spec.whatwg.org/#c0-control-percent-encode-set) is
864
+ always percent-encoded.
865
+
866
+ `percentEncodeSet` is an addition to that, and must be a string of unique increasing codepoints
867
+ in range 0x20 - 0x7e, e.g. `' "#<>'`.
868
+
869
+ This method accepts [DOMStrings](https://webidl.spec.whatwg.org/#idl-DOMString) and converts them
870
+ to [USVStrings](https://webidl.spec.whatwg.org/#idl-USVString).
871
+ This is different from e.g. `encodeURI` and `encodeURIComponent` which throw on surrogates:
872
+ ```js
873
+ > percentEncodeAfterEncoding('utf8', '\ud800', ' "#$%&+,/:;<=>?@[\\]^`{|}') // component
874
+ '%EF%BF%BD'
875
+ > encodeURIComponent('\ud800')
876
+ Uncaught URIError: URI malformed
877
+ ```
878
+
879
+ ## Changelog
880
+
881
+ See [GitHub Releases](https://github.com/ExodusOSS/bytes/releases) tab
882
+
556
883
  ## License
557
884
 
558
885
  [MIT](./LICENSE)