@exodus/bytes 1.12.0 → 1.14.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
@@ -30,7 +30,7 @@ Tested in CI with [@exodus/test](https://github.com/ExodusMovement/test#exoduste
30
30
  [![Hermes](https://img.shields.io/badge/Hermes-282C34?style=for-the-badge&logo=React)](https://hermesengine.dev)
31
31
  [![V8](https://img.shields.io/badge/V8-4285F4?style=for-the-badge&logo=V8&logoColor=white)](https://v8.dev/docs/d8)
32
32
  [![JavaScriptCore](https://img.shields.io/badge/JavaScriptCore-006CFF?style=for-the-badge)](https://docs.webkit.org/Deep%20Dive/JSC/JavaScriptCore.html)
33
- [![SpiderMonkey](https://img.shields.io/badge/SpiderMonkey-FFD681?style=for-the-badge)](https://spidermonkey.dev/)
33
+ [![SpiderMonkey](https://img.shields.io/badge/SpiderMonkey-FFD681?style=for-the-badge)](https://spidermonkey.dev/)\
34
34
  [![QuickJS](https://img.shields.io/badge/QuickJS-E58200?style=for-the-badge)](https://github.com/quickjs-ng/quickjs)
35
35
  [![XS](https://img.shields.io/badge/XS-0B307A?style=for-the-badge)](https://github.com/Moddable-OpenSource/moddable)
36
36
  [![GraalJS](https://img.shields.io/badge/GraalJS-C74634?style=for-the-badge)](https://github.com/oracle/graaljs)
@@ -100,24 +100,47 @@ _These are only provided as a compatibility layer, prefer hardened APIs instead
100
100
 
101
101
  ### Lite version
102
102
 
103
- If you don't need support for legacy multi-byte encodings, you can use the lite import:
104
- ```js
105
- import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
106
- import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lite.js' // Requires Streams
107
- ```
103
+ Alternate exports exist that can help reduce bundle size, see comparison:
104
+
105
+ | import | size |
106
+ | - | - |
107
+ | [@exodus/bytes/encoding-browser.js](#exodusbytesencoding-browserjs-) | <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-browser.js?style=flat-square)</sub> |
108
+ | [@exodus/bytes/encoding-lite.js](#exodusbytesencoding-litejs-) | <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-lite.js?style=flat-square)</sub> |
109
+ | [@exodus/bytes/encoding.js](#exodusbytesencodingjs-) | <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding.js?style=flat-square)</sub> |
110
+ | `text-encoding` | <sub>![](https://img.shields.io/bundlejs/size/text-encoding?style=flat-square)</sub> |
111
+ | `iconv-lite` | <sub>![](https://img.shields.io/bundlejs/size/iconv-lite/lib/index.js?style=flat-square)</sub> |
112
+ | `whatwg-encoding` | <sub>![](https://img.shields.io/bundlejs/size/whatwg-encoding?style=flat-square)</sub> |
113
+
114
+ Libraries are advised to use single-purpose hardened `@exodus/bytes/utf8.js` / `@exodus/bytes/utf16.js` APIs for Unicode.
115
+
116
+ Applications (including React Native apps) are advised to load either `@exodus/bytes/encoding-lite.js` or `@exodus/bytes/encoding.js`
117
+ (depending on whether legacy multi-byte support is needed) and use that as a global polyfill.
108
118
 
109
- This reduces the bundle size 9x:\
110
- from 90 KiB gzipped for `@exodus/bytes/encoding.js` to 10 KiB gzipped for `@exodus/bytes/encoding-lite.js`.\
111
- (For comparison, `text-encoding` module is 190 KiB gzipped, and `iconv-lite` is 194 KiB gzipped):
119
+ #### `@exodus/bytes/encoding-lite.js`
112
120
 
113
- It still supports `utf-8`, `utf-16le`, `utf-16be` and all single-byte encodings specified by the spec,
114
- the only difference is support for legacy multi-byte encodings.
121
+ If you don't need support for legacy multi-byte encodings.
122
+
123
+ Reduces the bundle size ~12x, while still keeping `utf-8`, `utf-16le`, `utf-16be` and all single-byte encodings specified by the spec.
124
+ The only difference is support for legacy multi-byte encodings.
115
125
 
116
126
  See [the list of encodings](https://encoding.spec.whatwg.org/#names-and-labels).
117
127
 
128
+ This can be useful for example in React Native global TextDecoder polyfill,
129
+ if you are sure that you don't need legacy multi-byte encodings support.
130
+
131
+ #### `@exodus/bytes/encoding-browser.js`
132
+
133
+ Resolves to a tiny import in browser bundles, preferring native `TextDecoder` / `TextEncoder`.
134
+
135
+ For non-browsers (Node.js, React Native), loads a full implementation.
136
+
137
+ > [!NOTE]
138
+ > This is not the default behavior for `@exodus/bytes/encoding.js` because all major browser implementations have bugs,
139
+ > which `@exodus/bytes/encoding.js` fixes. Only use if you are ok with that.
140
+
118
141
  ## API
119
142
 
120
- ### @exodus/bytes/utf8.js
143
+ ### @exodus/bytes/utf8.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/utf8.js?style=flat-square)<sub>
121
144
 
122
145
  UTF-8 encoding/decoding
123
146
 
@@ -183,7 +206,7 @@ Prefer using strict throwing methods for cryptography applications._
183
206
  This is similar to `new TextDecoder('utf-8', { ignoreBOM: true }).decode(arr)`,
184
207
  but works on all engines.
185
208
 
186
- ### @exodus/bytes/utf16.js
209
+ ### @exodus/bytes/utf16.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/utf16.js?style=flat-square)<sub>
187
210
 
188
211
  UTF-16 encoding/decoding
189
212
 
@@ -233,7 +256,7 @@ Prefer using strict throwing methods for cryptography applications._
233
256
 
234
257
  Throws on non-even byte length.
235
258
 
236
- ### @exodus/bytes/single-byte.js
259
+ ### @exodus/bytes/single-byte.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/single-byte.js?style=flat-square)</sub>
237
260
 
238
261
  Decode / encode the legacy single-byte encodings according to the
239
262
  [Encoding standard](https://encoding.spec.whatwg.org/)
@@ -354,7 +377,7 @@ Same as:
354
377
  const windows1252fromString = createSinglebyteEncoder('windows-1252', { mode: 'fatal' })
355
378
  ```
356
379
 
357
- ### @exodus/bytes/multi-byte.js
380
+ ### @exodus/bytes/multi-byte.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/multi-byte.js?style=flat-square)</sub>
358
381
 
359
382
  Decode / encode the legacy multi-byte encodings according to the
360
383
  [Encoding standard](https://encoding.spec.whatwg.org/)
@@ -396,7 +419,7 @@ Returns a function `encode(string)` that encodes a string to bytes.
396
419
  In `'fatal'` mode (default), will throw on non well-formed strings or any codepoints which could
397
420
  not be encoded in the target encoding.
398
421
 
399
- ### @exodus/bytes/bigint.js
422
+ ### @exodus/bytes/bigint.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/bigint.js?style=flat-square)</sub>
400
423
 
401
424
  Convert between BigInt and Uint8Array
402
425
 
@@ -418,7 +441,7 @@ Convert a Uint8Array or Buffer to a BigInt
418
441
 
419
442
  The bytes are interpreted as a big-endian unsigned integer.
420
443
 
421
- ### @exodus/bytes/hex.js
444
+ ### @exodus/bytes/hex.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/hex.js?style=flat-square)</sub>
422
445
 
423
446
  Implements Base16 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
424
447
  (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
@@ -437,7 +460,7 @@ Unlike `Buffer.from()`, throws on invalid input
437
460
 
438
461
  Encode a `Uint8Array` to a lowercase hex string
439
462
 
440
- ### @exodus/bytes/base64.js
463
+ ### @exodus/bytes/base64.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/base64.js?style=flat-square)</sub>
441
464
 
442
465
  Implements base64 and base64url from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
443
466
  (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
@@ -474,7 +497,7 @@ Encode a `Uint8Array` to a base64 string (RFC 4648)
474
497
 
475
498
  Encode a `Uint8Array` to a base64url string (RFC 4648)
476
499
 
477
- ### @exodus/bytes/base32.js
500
+ ### @exodus/bytes/base32.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/base32.js?style=flat-square)</sub>
478
501
 
479
502
  Implements base32 and base32hex from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
480
503
  (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
@@ -504,7 +527,7 @@ Encode a `Uint8Array` to a base32 string (RFC 4648)
504
527
 
505
528
  Encode a `Uint8Array` to a base32hex string (RFC 4648)
506
529
 
507
- ### @exodus/bytes/bech32.js
530
+ ### @exodus/bytes/bech32.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/bech32.js?style=flat-square)</sub>
508
531
 
509
532
  Implements bech32 and bech32m from
510
533
  [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#specification)
@@ -538,7 +561,7 @@ Decode a bech32m string to bytes
538
561
 
539
562
  Encode bytes to a bech32m string
540
563
 
541
- ### @exodus/bytes/base58.js
564
+ ### @exodus/bytes/base58.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/base58.js?style=flat-square)</sub>
542
565
 
543
566
  Implements [base58](https://www.ietf.org/archive/id/draft-msporny-base58-03.txt) encoding.
544
567
 
@@ -573,7 +596,7 @@ Encode a `Uint8Array` to a base58 string using XRP alphabet
573
596
 
574
597
  Uses the XRP variant base58 alphabet
575
598
 
576
- ### @exodus/bytes/base58check.js
599
+ ### @exodus/bytes/base58check.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/base58check.js?style=flat-square)</sub>
577
600
 
578
601
  Implements [base58check](https://en.bitcoin.it/wiki/Base58Check_encoding) encoding.
579
602
 
@@ -613,7 +636,7 @@ Uses double SHA-256 for checksum calculation
613
636
 
614
637
  Create a base58check encoder/decoder with custom hash functions
615
638
 
616
- ### @exodus/bytes/wif.js
639
+ ### @exodus/bytes/wif.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/wif.js?style=flat-square)</sub>
617
640
 
618
641
  Wallet Import Format (WIF) encoding and decoding.
619
642
 
@@ -652,7 +675,7 @@ Encode WIF data to a WIF string
652
675
 
653
676
  Encode WIF data to a WIF string (synchronous)
654
677
 
655
- ### @exodus/bytes/array.js
678
+ ### @exodus/bytes/array.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/array.js?style=flat-square)</sub>
656
679
 
657
680
  TypedArray utils and conversions.
658
681
 
@@ -667,7 +690,7 @@ Create a view of a TypedArray in the specified format (`'uint8'` or `'buffer'`)
667
690
  > [!IMPORTANT]
668
691
  > Does not copy data, returns a view on the same underlying buffer
669
692
 
670
- ### @exodus/bytes/encoding.js
693
+ ### @exodus/bytes/encoding.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding.js?style=flat-square)</sub>
671
694
 
672
695
  Implements the [Encoding standard](https://encoding.spec.whatwg.org/):
673
696
  [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder),
@@ -778,11 +801,11 @@ only expects lowercased encoding name:
778
801
  new TextDecoder(getBOMEncoding(input) ?? fallbackEncoding).decode(input)
779
802
  ```
780
803
 
781
- ### @exodus/bytes/encoding-lite.js
804
+ ### @exodus/bytes/encoding-lite.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-lite.js?style=flat-square)</sub>
782
805
 
783
806
  The exact same exports as `@exodus/bytes/encoding.js` are also exported as
784
807
  `@exodus/bytes/encoding-lite.js`, with the difference that the lite version does not load
785
- multi-byte `TextDecoder` encodings by default to reduce bundle size 10x.
808
+ multi-byte `TextDecoder` encodings by default to reduce bundle size ~12x.
786
809
 
787
810
  ```js
788
811
  import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
@@ -837,7 +860,7 @@ true
837
860
  '%'
838
861
  ```
839
862
 
840
- ### @exodus/bytes/encoding-browser.js
863
+ ### @exodus/bytes/encoding-browser.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-browser.js?style=flat-square)<sub>
841
864
 
842
865
  Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
843
866
  browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
@@ -858,7 +881,7 @@ do not provide sufficiently complete / non-buggy `TextDecoder` APIs.
858
881
  > but they are fixing them and the expected update window is short.\
859
882
  > If you want to circumvent browser bugs, use full `@exodus/bytes/encoding.js` import.
860
883
 
861
- ### @exodus/bytes/whatwg.js
884
+ ### @exodus/bytes/whatwg.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/whatwg.js?style=flat-square)</sub>
862
885
 
863
886
  WHATWG helpers
864
887
 
package/array.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { assertTypedArray } from './assert.js'
2
2
 
3
- const { Buffer } = globalThis // Buffer is optional
3
+ const Buffer = globalThis.Buffer // Buffer is optional
4
4
 
5
5
  export function typedView(arr, format) {
6
6
  assertTypedArray(arr)
package/base32.js CHANGED
@@ -7,8 +7,6 @@ import * as js from './fallback/base32.js'
7
7
 
8
8
  // 8 chars per 5 bytes
9
9
 
10
- const { E_PADDING } = js
11
-
12
10
  export const toBase32 = (arr, { padding = false } = {}) => js.toBase32(arr, false, padding)
13
11
  export const toBase32hex = (arr, { padding = false } = {}) => js.toBase32(arr, true, padding)
14
12
 
@@ -30,7 +28,7 @@ function fromBase32common(str, isBase32Hex, padding, format, rest) {
30
28
  if (rest !== null) assertEmptyRest(rest)
31
29
 
32
30
  if (padding === true) {
33
- if (str.length % 8 !== 0) throw new SyntaxError(E_PADDING)
31
+ if (str.length % 8 !== 0) throw new SyntaxError(js.E_PADDING)
34
32
  } else if (padding === false) {
35
33
  if (str.endsWith('=')) throw new SyntaxError('Did not expect padding in base32 input')
36
34
  } else if (padding !== 'both') {
package/base58.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { typedView } from './array.js'
2
- import { assertUint8 } from './assert.js'
3
- import { nativeDecoder, nativeEncoder, isHermes, E_STRING } from './fallback/_utils.js'
2
+ import { assertU8, E_STRING } from './fallback/_utils.js'
3
+ import { nativeDecoder, nativeEncoder, isHermes } from './fallback/platform.js'
4
4
  import { encodeAscii, decodeAscii } from './fallback/latin1.js'
5
5
 
6
6
  const alphabet58 = [...'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz']
@@ -23,7 +23,7 @@ const E_CHAR = 'Invalid character in base58 input'
23
23
  const shouldUseBigIntFrom = isHermes // faster only on Hermes, numbers path beats it on normal engines
24
24
 
25
25
  function toBase58core(arr, alphabet, codes) {
26
- assertUint8(arr)
26
+ assertU8(arr)
27
27
  const length = arr.length
28
28
  if (length === 0) return ''
29
29
 
package/base58check.d.ts CHANGED
@@ -81,8 +81,8 @@ export interface Base58CheckSync extends Base58CheckAsync {
81
81
  * @param hashAlgoSync - Optional sync hash function
82
82
  * @returns Base58Check encoder/decoder instance
83
83
  */
84
- export function makeBase58check(hashAlgo: HashFunction, hashAlgoSync?: HashFunctionSync): Base58CheckSync;
85
- export function makeBase58check(hashAlgo: HashFunction): Base58CheckAsync;
84
+ export function makeBase58check(hashAlgo: HashFunction | HashFunctionSync, hashAlgoSync: HashFunctionSync): Base58CheckSync;
85
+ export function makeBase58check(hashAlgo: HashFunction | HashFunctionSync, hashAlgoSync?: undefined): Base58CheckAsync;
86
86
 
87
87
  /**
88
88
  * Encode bytes to base58check string asynchronously
package/base58check.js CHANGED
@@ -8,12 +8,11 @@ import { makeBase58check } from './fallback/base58check.js'
8
8
 
9
9
  const hash256sync = (x) => sha256(sha256(x))
10
10
  const hash256 = hash256sync // See note at the top
11
- const {
12
- encode: toBase58check,
13
- decode: fromBase58check,
14
- encodeSync: toBase58checkSync,
15
- decodeSync: fromBase58checkSync,
16
- } = makeBase58check(hash256, hash256sync)
11
+
12
+ const b58c = /* @__PURE__ */ makeBase58check(hash256, hash256sync)
13
+ export const toBase58check = /* @__PURE__ */ (() => b58c.encode)()
14
+ export const fromBase58check = /* @__PURE__ */ (() => b58c.decode)()
15
+ export const toBase58checkSync = /* @__PURE__ */ (() => b58c.encodeSync)()
16
+ export const fromBase58checkSync = /* @__PURE__ */ (() => b58c.decodeSync)()
17
17
 
18
18
  export { makeBase58check } from './fallback/base58check.js'
19
- export { toBase58check, fromBase58check, toBase58checkSync, fromBase58checkSync }
package/base64.js CHANGED
@@ -1,6 +1,7 @@
1
- import { assertUint8, assertEmptyRest } from './assert.js'
1
+ import { assertEmptyRest } from './assert.js'
2
2
  import { typedView } from './array.js'
3
- import { isHermes, skipWeb, E_STRING } from './fallback/_utils.js'
3
+ import { assertU8, E_STRING } from './fallback/_utils.js'
4
+ import { isHermes } from './fallback/platform.js'
4
5
  import { decodeLatin1, encodeLatin1 } from './fallback/latin1.js'
5
6
  import * as js from './fallback/base64.js'
6
7
 
@@ -34,10 +35,10 @@ function maybePad(res, padding) {
34
35
  }
35
36
 
36
37
  const toUrl = (x) => x.replaceAll('+', '-').replaceAll('/', '_')
37
- const haveWeb = (x) => !skipWeb && web64 && x.toBase64 === web64
38
+ const haveWeb = (x) => web64 && x.toBase64 === web64
38
39
 
39
40
  export function toBase64(x, { padding = true } = {}) {
40
- assertUint8(x)
41
+ assertU8(x)
41
42
  if (haveWeb(x)) return padding ? x.toBase64() : x.toBase64({ omitPadding: !padding }) // Modern, optionless is slightly faster
42
43
  if (haveNativeBuffer) return maybeUnpad(toBuffer(x).base64Slice(0, x.byteLength), padding) // Older Node.js
43
44
  if (shouldUseBtoa) return maybeUnpad(btoa(decodeLatin1(x)), padding)
@@ -46,7 +47,7 @@ export function toBase64(x, { padding = true } = {}) {
46
47
 
47
48
  // NOTE: base64url omits padding by default
48
49
  export function toBase64url(x, { padding = false } = {}) {
49
- assertUint8(x)
50
+ assertU8(x)
50
51
  if (haveWeb(x)) return x.toBase64({ alphabet: 'base64url', omitPadding: !padding }) // Modern
51
52
  if (haveNativeBuffer) return maybePad(toBuffer(x).base64urlSlice(0, x.byteLength), padding) // Older Node.js
52
53
  if (shouldUseBtoa) return maybeUnpad(toUrl(btoa(decodeLatin1(x))), padding)
@@ -111,7 +112,7 @@ function noWhitespaceSeen(str, arr) {
111
112
  }
112
113
 
113
114
  let fromBase64impl
114
- if (!skipWeb && Uint8Array.fromBase64) {
115
+ if (Uint8Array.fromBase64) {
115
116
  // NOTICE: this is actually slower than our JS impl in older JavaScriptCore and (slightly) in SpiderMonkey, but faster on V8 and new JavaScriptCore
116
117
  fromBase64impl = (str, isBase64url, padding) => {
117
118
  const alphabet = isBase64url ? 'base64url' : 'base64'
package/bech32.js CHANGED
@@ -1,5 +1,5 @@
1
- import { assertUint8 } from './assert.js'
2
- import { nativeEncoder, E_STRING } from './fallback/_utils.js'
1
+ import { assertU8, E_STRING } from './fallback/_utils.js'
2
+ import { nativeEncoder } from './fallback/platform.js'
3
3
  import { decodeAscii, encodeAscii, encodeLatin1 } from './fallback/latin1.js'
4
4
 
5
5
  const alphabet = [...'qpzry9x8gf2tvdw0s3jn54khce6mua7l']
@@ -109,7 +109,7 @@ function pPrefix(prefix) {
109
109
  function toBech32enc(prefix, bytes, limit, encoding) {
110
110
  if (typeof prefix !== 'string' || !prefix) throw new TypeError(E_PREFIX)
111
111
  if (typeof limit !== 'number') throw new TypeError(E_SIZE)
112
- assertUint8(bytes)
112
+ assertU8(bytes)
113
113
  const bytesLength = bytes.length
114
114
  const wordsLength = Math.ceil((bytesLength * 8) / 5)
115
115
  if (!(prefix.length + 7 + wordsLength <= limit)) throw new TypeError(E_SIZE)
@@ -1,10 +1,4 @@
1
- import {
2
- fromSource,
3
- getBOMEncoding,
4
- normalizeEncoding,
5
- E_ENCODING,
6
- } from './fallback/encoding.api.js'
7
- import labels from './fallback/encoding.labels.js'
1
+ import { getBOMEncoding } from './fallback/encoding.api.js'
8
2
 
9
3
  // Lite-weight version which re-exports existing implementations on browsers,
10
4
  // while still being aliased to the full impl in RN and Node.js
@@ -13,17 +7,49 @@ import labels from './fallback/encoding.labels.js'
13
7
 
14
8
  const { TextDecoder, TextEncoder, TextDecoderStream, TextEncoderStream } = globalThis
15
9
 
16
- export { normalizeEncoding, getBOMEncoding, labelToName } from './fallback/encoding.api.js'
10
+ export { getBOMEncoding } from './fallback/encoding.api.js'
17
11
  export { TextDecoder, TextEncoder, TextDecoderStream, TextEncoderStream }
18
12
 
19
- // https://encoding.spec.whatwg.org/#decode
13
+ export function normalizeEncoding(label) {
14
+ if (label === 'utf-8' || label === 'utf8' || label === 'UTF-8' || label === 'UTF8') return 'utf-8'
15
+ if (label === 'windows-1252' || label === 'ascii' || label === 'latin1') return 'windows-1252'
16
+ if (/[^\w\t\n\f\r .:-]/i.test(label)) return null
17
+ const l = `${label}`.trim().toLowerCase()
18
+ try {
19
+ return new TextDecoder(l).encoding
20
+ } catch {}
21
+
22
+ if (l === 'x-user-defined') return l
23
+ if (
24
+ l === 'replacement' ||
25
+ l === 'csiso2022kr' ||
26
+ l === 'hz-gb-2312' ||
27
+ l === 'iso-2022-cn' ||
28
+ l === 'iso-2022-cn-ext' ||
29
+ l === 'iso-2022-kr'
30
+ ) {
31
+ return 'replacement'
32
+ }
33
+
34
+ return null
35
+ }
36
+
20
37
  export function legacyHookDecode(input, fallbackEncoding = 'utf-8') {
21
- let u8 = fromSource(input)
22
- const bomEncoding = getBOMEncoding(u8)
23
- if (bomEncoding) u8 = u8.subarray(bomEncoding === 'utf-8' ? 3 : 2)
24
- const enc = bomEncoding ?? normalizeEncoding(fallbackEncoding) // "the byte order mark is more authoritative than anything else"
25
- if (enc === 'utf-8') return new TextDecoder('utf-8', { ignoreBOM: true }).decode(u8) // fast path
26
- if (enc === 'replacement') return u8.byteLength > 0 ? '\uFFFD' : ''
27
- if (!Object.hasOwn(labels, enc)) throw new RangeError(E_ENCODING)
28
- return new TextDecoder(enc, { ignoreBOM: true }).decode(u8)
38
+ const enc = getBOMEncoding(input) ?? normalizeEncoding(fallbackEncoding)
39
+ if (enc === 'replacement') return input.byteLength > 0 ? '\uFFFD' : ''
40
+ return new TextDecoder(enc).decode(input)
41
+ }
42
+
43
+ export function labelToName(label) {
44
+ const enc = normalizeEncoding(label)
45
+ if (enc === 'utf-8') return 'UTF-8'
46
+ if (!enc) return enc
47
+ const p = enc.slice(0, 3)
48
+ if (p === 'utf' || p === 'iso' || p === 'koi' || p === 'euc' || p === 'ibm' || p === 'gbk') {
49
+ return enc.toUpperCase()
50
+ }
51
+
52
+ if (enc === 'big5') return 'Big5'
53
+ if (enc === 'shift_jis') return 'Shift_JIS'
54
+ return enc
29
55
  }
@@ -1,131 +1,15 @@
1
- const { Buffer, TextEncoder, TextDecoder } = globalThis
2
- const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
3
- export const nativeBuffer = haveNativeBuffer ? Buffer : null
4
- export const isHermes = !!globalThis.HermesInternal
5
- export const isDeno = !!globalThis.Deno
6
- export const isLE = /* @__PURE__ */ (() => new Uint8Array(Uint16Array.of(258).buffer)[0] === 2)()
1
+ export * from './platform.js'
7
2
 
8
- // We consider Node.js TextDecoder/TextEncoder native
9
- let isNative = (x) => x && (haveNativeBuffer || `${x}`.includes('[native code]'))
10
- if (!haveNativeBuffer && isNative(() => {})) isNative = () => false // e.g. XS, we don't want false positives
11
-
12
- export const nativeEncoder = isNative(TextEncoder) ? new TextEncoder() : null
13
- export const nativeDecoder = isNative(TextDecoder)
14
- ? new TextDecoder('utf-8', { ignoreBOM: true })
15
- : null
16
-
17
- // Actually windows-1252, compatible with ascii and latin1 decoding
18
- // Beware that on non-latin1, i.e. on windows-1252, this is broken in ~all Node.js versions released
19
- // in 2025 due to a regression, so we call it Latin1 as it's usable only for that
20
- const getNativeLatin1 = () => {
21
- // Not all barebone engines with TextDecoder support something except utf-8, detect
22
- if (nativeDecoder) {
23
- try {
24
- return new TextDecoder('latin1', { ignoreBOM: true })
25
- } catch {}
26
- }
27
-
28
- return null
29
- }
30
-
31
- export const nativeDecoderLatin1 = /* @__PURE__ */ getNativeLatin1()
32
-
33
- // Block Firefox < 146 specifically from using native hex/base64, as it's very slow there
34
- // Refs: https://bugzilla.mozilla.org/show_bug.cgi?id=1994067 (and linked issues), fixed in 146
35
- // Before that, all versions of Firefox >= 133 are slow
36
- // TODO: this could be removed when < 146 usage diminishes (note ESR)
37
- // We do not worry about false-negatives here but worry about false-positives!
38
- function shouldSkipBuiltins() {
39
- const g = globalThis
40
- // First, attempt to exclude as many things as we can using trivial checks, just in case, and to not hit ua
41
- if (haveNativeBuffer || isHermes || !g.window || g.chrome || !g.navigator) return false
42
- try {
43
- // This was fixed specifically in Firefox 146. Other engines except Hermes (already returned) get this right
44
- new WeakSet().add(Symbol()) // eslint-disable-line symbol-description
45
- return false
46
- } catch {
47
- // In catch and not after in case if something too smart optimizes out code in try. False-negative is acceptable in that case
48
- if (!('onmozfullscreenerror' in g)) return false // Firefox has it (might remove in the future, but we don't care)
49
- return /firefox/i.test(g.navigator.userAgent || '') // as simple as we can
50
- }
51
-
52
- /* c8 ignore next */
53
- return false // eslint-disable-line no-unreachable
54
- }
55
-
56
- export const skipWeb = /* @__PURE__ */ shouldSkipBuiltins()
57
-
58
- function decodePartAddition(a, start, end, m) {
59
- let o = ''
60
- let i = start
61
- for (const last3 = end - 3; i < last3; i += 4) {
62
- const x0 = a[i]
63
- const x1 = a[i + 1]
64
- const x2 = a[i + 2]
65
- const x3 = a[i + 3]
66
- o += m[x0]
67
- o += m[x1]
68
- o += m[x2]
69
- o += m[x3]
70
- }
71
-
72
- while (i < end) o += m[a[i++]]
73
- return o
74
- }
75
-
76
- // Decoding with templates is faster on Hermes
77
- function decodePartTemplates(a, start, end, m) {
78
- let o = ''
79
- let i = start
80
- for (const last15 = end - 15; i < last15; i += 16) {
81
- const x0 = a[i]
82
- const x1 = a[i + 1]
83
- const x2 = a[i + 2]
84
- const x3 = a[i + 3]
85
- const x4 = a[i + 4]
86
- const x5 = a[i + 5]
87
- const x6 = a[i + 6]
88
- const x7 = a[i + 7]
89
- const x8 = a[i + 8]
90
- const x9 = a[i + 9]
91
- const x10 = a[i + 10]
92
- const x11 = a[i + 11]
93
- const x12 = a[i + 12]
94
- const x13 = a[i + 13]
95
- const x14 = a[i + 14]
96
- const x15 = a[i + 15]
97
- o += `${m[x0]}${m[x1]}${m[x2]}${m[x3]}${m[x4]}${m[x5]}${m[x6]}${m[x7]}${m[x8]}${m[x9]}${m[x10]}${m[x11]}${m[x12]}${m[x13]}${m[x14]}${m[x15]}`
98
- }
99
-
100
- while (i < end) o += m[a[i++]]
101
- return o
102
- }
103
-
104
- const decodePart = isHermes ? decodePartTemplates : decodePartAddition
105
- export function decode2string(arr, start, end, m) {
106
- if (end - start > 30_000) {
107
- // Limit concatenation to avoid excessive GC
108
- // Thresholds checked on Hermes for toHex
109
- const concat = []
110
- for (let i = start; i < end; ) {
111
- const step = i + 500
112
- const iNext = step > end ? end : step
113
- concat.push(decodePart(arr, i, iNext, m))
114
- i = iNext
115
- }
116
-
117
- const res = concat.join('')
118
- concat.length = 0
119
- return res
120
- }
121
-
122
- return decodePart(arr, start, end, m)
123
- }
3
+ const Buffer = /* @__PURE__ */ (() => globalThis.Buffer)()
124
4
 
125
5
  export function assert(condition, msg) {
126
6
  if (!condition) throw new Error(msg)
127
7
  }
128
8
 
9
+ export function assertU8(arg) {
10
+ if (!(arg instanceof Uint8Array)) throw new TypeError('Expected an Uint8Array')
11
+ }
12
+
129
13
  // On arrays in heap (<= 64) it's cheaper to copy into a pooled buffer than lazy-create the ArrayBuffer storage
130
14
  export const toBuf = (x) =>
131
15
  x.byteLength <= 64 && x.BYTES_PER_ELEMENT === 1
@@ -133,3 +17,4 @@ export const toBuf = (x) =>
133
17
  : Buffer.from(x.buffer, x.byteOffset, x.byteLength)
134
18
 
135
19
  export const E_STRING = 'Input is not a string'
20
+ export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
@@ -1,5 +1,5 @@
1
- import { assertUint8 } from '../assert.js'
2
- import { nativeEncoder, nativeDecoder, isHermes } from './_utils.js'
1
+ import { assertU8 } from './_utils.js'
2
+ import { nativeEncoder, nativeDecoder, isHermes } from './platform.js'
3
3
  import { encodeAscii, decodeAscii } from './latin1.js'
4
4
 
5
5
  // See https://datatracker.ietf.org/doc/html/rfc4648
@@ -18,7 +18,7 @@ const useTemplates = isHermes // Faster on Hermes and JSC, but we use it only on
18
18
 
19
19
  // We construct output by concatenating chars, this seems to be fine enough on modern JS engines
20
20
  export function toBase32(arr, isBase32Hex, padding) {
21
- assertUint8(arr)
21
+ assertU8(arr)
22
22
  const fullChunks = Math.floor(arr.length / 5)
23
23
  const fullChunksBytes = fullChunks * 5
24
24
  let o = ''
@@ -1,6 +1,6 @@
1
1
  import { typedView } from '@exodus/bytes/array.js'
2
2
  import { toBase58, fromBase58 } from '@exodus/bytes/base58.js'
3
- import { assertUint8 } from '../assert.js'
3
+ import { assertU8 } from './_utils.js'
4
4
 
5
5
  const E_CHECKSUM = 'Invalid checksum'
6
6
 
@@ -28,7 +28,7 @@ function assertChecksum(c, r) {
28
28
  export const makeBase58check = (hashAlgo, hashAlgoSync) => {
29
29
  const apis = {
30
30
  async encode(arr) {
31
- assertUint8(arr)
31
+ assertU8(arr)
32
32
  return encodeWithChecksum(arr, await hashAlgo(arr))
33
33
  },
34
34
  async decode(str, format = 'uint8') {
@@ -41,7 +41,7 @@ export const makeBase58check = (hashAlgo, hashAlgoSync) => {
41
41
  return {
42
42
  ...apis,
43
43
  encodeSync(arr) {
44
- assertUint8(arr)
44
+ assertU8(arr)
45
45
  return encodeWithChecksum(arr, hashAlgoSync(arr))
46
46
  },
47
47
  decodeSync(str, format = 'uint8') {
@@ -1,5 +1,4 @@
1
- import { assertUint8 } from '../assert.js'
2
- import { nativeEncoder, nativeDecoder } from './_utils.js'
1
+ import { nativeEncoder, nativeDecoder } from './platform.js'
3
2
  import { encodeAscii, decodeAscii } from './latin1.js'
4
3
 
5
4
  // See https://datatracker.ietf.org/doc/html/rfc4648
@@ -15,8 +14,8 @@ export const E_LENGTH = 'Invalid base64 length'
15
14
  export const E_LAST = 'Invalid last chunk'
16
15
 
17
16
  // We construct output by concatenating chars, this seems to be fine enough on modern JS engines
17
+ // Expects a checked Uint8Array
18
18
  export function toBase64(arr, isURL, padding) {
19
- assertUint8(arr)
20
19
  const fullChunks = (arr.length / 3) | 0
21
20
  const fullChunksBytes = fullChunks * 3
22
21
  let o = ''