@exodus/bytes 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -161,6 +161,8 @@ Same as `windows1252toString = createSinglebyteDecoder('windows-1252')`.
161
161
 
162
162
  ### `@exodus/bytes/base58check.js`
163
163
 
164
+ On non-Node.js, requires peer dependency [@exodus/crypto](https://www.npmjs.com/package/@exodus/crypto) to be installed.
165
+
164
166
  ##### `async toBase58check(arr)`
165
167
  ##### `toBase58checkSync(arr)`
166
168
  ##### `async fromBase58check(str, format = 'uint8')`
@@ -185,7 +187,7 @@ some [hooks](https://encoding.spec.whatwg.org/#specification-hooks) (see below).
185
187
  import { TextDecoder, TextDecoder } from '@exodus/bytes/encoding.js'
186
188
 
187
189
  // Hooks for standards
188
- import { getBOMEncoding, legacyHookDecode, normalizeEncoding } from '@exodus/bytes/encoding.js'
190
+ import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
189
191
  ```
190
192
 
191
193
  #### `new TextDecoder(label = 'utf-8', { fatal = false, ignoreBOM = false })`
@@ -196,10 +198,19 @@ import { getBOMEncoding, legacyHookDecode, normalizeEncoding } from '@exodus/byt
196
198
 
197
199
  [TextEncoder](https://encoding.spec.whatwg.org/#interface-textdecoder) implementation/polyfill.
198
200
 
199
- #### `normalizeEncoding(label)`
201
+ #### `labelToName(label)`
200
202
 
201
203
  Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
202
204
 
205
+ Converts an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
206
+ as a case-sensitive string.
207
+
208
+ If an encoding with that label does not exist, returns `null`.
209
+
210
+ All encoding names are also valid labels for corresponding encodings.
211
+
212
+ #### `normalizeEncoding(label)`
213
+
203
214
  Converts an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
204
215
  as an ASCII-lowercased string.
205
216
 
@@ -211,6 +222,11 @@ except that it:
211
222
  [labels](https://encoding.spec.whatwg.org/#ref-for-replacement%E2%91%A1)
212
223
  2. Does not throw for invalid labels and instead returns `null`
213
224
 
225
+ It is identical to:
226
+ ```js
227
+ labelToName(label)?.toLowerCase() ?? null
228
+ ```
229
+
214
230
  All encoding names are also valid labels for corresponding encodings.
215
231
 
216
232
  #### `getBOMEncoding(input)`
@@ -252,7 +268,7 @@ new TextDecoder(getBOMEncoding(input) ?? fallbackEncoding).decode(input)
252
268
  import { TextDecoder, TextDecoder } from '@exodus/bytes/encoding-lite.js'
253
269
 
254
270
  // Hooks for standards
255
- import { getBOMEncoding, legacyHookDecode, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
271
+ import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
256
272
  ```
257
273
 
258
274
  The exact same exports as `@exodus/bytes/encoding.js` are also exported as
@@ -264,7 +280,7 @@ and their [labels](https://encoding.spec.whatwg.org/#names-and-labels) when used
264
280
 
265
281
  Legacy single-byte encodingds are loaded by default in both cases.
266
282
 
267
- `TextEncoder` and hooks for standards (including `normalizeEncoding`) do not have any behavior
283
+ `TextEncoder` and hooks for standards (including `labelToName` / `normalizeEncoding`) do not have any behavior
268
284
  differences in the lite version and support full range if inputs.
269
285
 
270
286
  To avoid inconsistencies, the exported classes and methods are exactly the same objects.
@@ -275,6 +291,7 @@ To avoid inconsistencies, the exported classes and methods are exactly the same
275
291
  TextDecoder: [class TextDecoder],
276
292
  TextEncoder: [class TextEncoder],
277
293
  getBOMEncoding: [Function: getBOMEncoding],
294
+ labelToName: [Function: labelToName],
278
295
  legacyHookDecode: [Function: legacyHookDecode],
279
296
  normalizeEncoding: [Function: normalizeEncoding]
280
297
  }
@@ -287,6 +304,7 @@ Error: Legacy multi-byte encodings are disabled in /encoding-lite.js, use /encod
287
304
  TextDecoder: [class TextDecoder],
288
305
  TextEncoder: [class TextEncoder],
289
306
  getBOMEncoding: [Function: getBOMEncoding],
307
+ labelToName: [Function: labelToName],
290
308
  legacyHookDecode: [Function: legacyHookDecode],
291
309
  normalizeEncoding: [Function: normalizeEncoding]
292
310
  }
package/base58check.js CHANGED
@@ -1,63 +1,12 @@
1
- import { typedView } from './array.js'
2
- import { assertUint8 } from './assert.js'
3
- import { toBase58, fromBase58 } from './base58.js'
4
1
  import { hashSync } from '@exodus/crypto/hash' // eslint-disable-line @exodus/import/no-deprecated
2
+ import { makeBase58check } from './fallback/base58check.js'
5
3
 
6
4
  // Note: while API is async, we use hashSync for now until we improve webcrypto perf for hash256
7
5
  // Inputs to base58 are typically very small, and that makes a difference
8
6
 
9
- const E_CHECKSUM = 'Invalid checksum'
10
-
11
- // checksum length is 4, i.e. only the first 4 bytes of the hash are used
12
-
13
- function encodeWithChecksum(arr, checksum) {
14
- // arr type in already validated in input
15
- const res = new Uint8Array(arr.length + 4)
16
- res.set(arr, 0)
17
- res.set(checksum.subarray(0, 4), arr.length)
18
- return toBase58(res)
19
- }
20
-
21
- function decodeWithChecksum(str) {
22
- const arr = fromBase58(str) // checks input
23
- const payloadSize = arr.length - 4
24
- if (payloadSize < 0) throw new Error(E_CHECKSUM)
25
- return [arr.subarray(0, payloadSize), arr.subarray(payloadSize)]
26
- }
27
-
28
- function assertChecksum(c, r) {
29
- if ((c[0] ^ r[0]) | (c[1] ^ r[1]) | (c[2] ^ r[2]) | (c[3] ^ r[3])) throw new Error(E_CHECKSUM)
30
- }
31
-
32
- export const makeBase58check = (hashAlgo, hashAlgoSync) => {
33
- const apis = {
34
- async encode(arr) {
35
- assertUint8(arr)
36
- return encodeWithChecksum(arr, await hashAlgo(arr))
37
- },
38
- async decode(str, format = 'uint8') {
39
- const [payload, checksum] = decodeWithChecksum(str)
40
- assertChecksum(checksum, await hashAlgo(payload))
41
- return typedView(payload, format)
42
- },
43
- }
44
- if (!hashAlgoSync) return apis
45
- return {
46
- ...apis,
47
- encodeSync(arr) {
48
- assertUint8(arr)
49
- return encodeWithChecksum(arr, hashAlgoSync(arr))
50
- },
51
- decodeSync(str, format = 'uint8') {
52
- const [payload, checksum] = decodeWithChecksum(str)
53
- assertChecksum(checksum, hashAlgoSync(payload))
54
- return typedView(payload, format)
55
- },
56
- }
57
- }
58
-
59
7
  // eslint-disable-next-line @exodus/import/no-deprecated
60
- const hash256sync = (x) => hashSync('sha256', hashSync('sha256', x, 'uint8'), 'uint8')
8
+ const sha256 = (x) => hashSync('sha256', x, 'uint8')
9
+ const hash256sync = (x) => sha256(sha256(x))
61
10
  const hash256 = hash256sync // See note at the top
62
11
  const {
63
12
  encode: toBase58check,
@@ -66,4 +15,5 @@ const {
66
15
  decodeSync: fromBase58checkSync,
67
16
  } = makeBase58check(hash256, hash256sync)
68
17
 
18
+ export { makeBase58check } from './fallback/base58check.js'
69
19
  export { toBase58check, fromBase58check, toBase58checkSync, fromBase58checkSync }
@@ -0,0 +1,14 @@
1
+ import { hash } from 'node:crypto'
2
+ import { makeBase58check } from './fallback/base58check.js'
3
+
4
+ const sha256 = (x) => hash('sha256', x, 'buffer')
5
+ const hash256 = (x) => sha256(sha256(x))
6
+ const {
7
+ encode: toBase58check,
8
+ decode: fromBase58check,
9
+ encodeSync: toBase58checkSync,
10
+ decodeSync: fromBase58checkSync,
11
+ } = makeBase58check(hash256, hash256)
12
+
13
+ export { makeBase58check } from './fallback/base58check.js'
14
+ export { toBase58check, fromBase58check, toBase58checkSync, fromBase58checkSync }
package/encoding-lite.js CHANGED
@@ -3,5 +3,6 @@ export {
3
3
  TextEncoder,
4
4
  normalizeEncoding,
5
5
  getBOMEncoding,
6
+ labelToName,
6
7
  legacyHookDecode,
7
8
  } from './fallback/encoding.js'
package/encoding.js CHANGED
@@ -8,5 +8,6 @@ export {
8
8
  TextEncoder,
9
9
  normalizeEncoding,
10
10
  getBOMEncoding,
11
+ labelToName,
11
12
  legacyHookDecode,
12
13
  } from './fallback/encoding.js'
@@ -0,0 +1,53 @@
1
+ import { typedView } from '@exodus/bytes/array.js'
2
+ import { toBase58, fromBase58 } from '@exodus/bytes/base58.js'
3
+ import { assertUint8 } from '../assert.js'
4
+
5
+ const E_CHECKSUM = 'Invalid checksum'
6
+
7
+ // checksum length is 4, i.e. only the first 4 bytes of the hash are used
8
+
9
+ function encodeWithChecksum(arr, checksum) {
10
+ // arr type in already validated in input
11
+ const res = new Uint8Array(arr.length + 4)
12
+ res.set(arr, 0)
13
+ res.set(checksum.subarray(0, 4), arr.length)
14
+ return toBase58(res)
15
+ }
16
+
17
+ function decodeWithChecksum(str) {
18
+ const arr = fromBase58(str) // checks input
19
+ const payloadSize = arr.length - 4
20
+ if (payloadSize < 0) throw new Error(E_CHECKSUM)
21
+ return [arr.subarray(0, payloadSize), arr.subarray(payloadSize)]
22
+ }
23
+
24
+ function assertChecksum(c, r) {
25
+ if ((c[0] ^ r[0]) | (c[1] ^ r[1]) | (c[2] ^ r[2]) | (c[3] ^ r[3])) throw new Error(E_CHECKSUM)
26
+ }
27
+
28
+ export const makeBase58check = (hashAlgo, hashAlgoSync) => {
29
+ const apis = {
30
+ async encode(arr) {
31
+ assertUint8(arr)
32
+ return encodeWithChecksum(arr, await hashAlgo(arr))
33
+ },
34
+ async decode(str, format = 'uint8') {
35
+ const [payload, checksum] = decodeWithChecksum(str)
36
+ assertChecksum(checksum, await hashAlgo(payload))
37
+ return typedView(payload, format)
38
+ },
39
+ }
40
+ if (!hashAlgoSync) return apis
41
+ return {
42
+ ...apis,
43
+ encodeSync(arr) {
44
+ assertUint8(arr)
45
+ return encodeWithChecksum(arr, hashAlgoSync(arr))
46
+ },
47
+ decodeSync(str, format = 'uint8') {
48
+ const [payload, checksum] = decodeWithChecksum(str)
49
+ assertChecksum(checksum, hashAlgoSync(payload))
50
+ return typedView(payload, format)
51
+ },
52
+ }
53
+ }
@@ -288,3 +288,16 @@ export function legacyHookDecode(input, fallbackEncoding = 'utf-8') {
288
288
 
289
289
  return createSinglebyteDecoder(enc, true)(u8)
290
290
  }
291
+
292
+ const uppercasePrefixes = new Set(['utf', 'iso', 'koi', 'euc', 'ibm', 'gbk'])
293
+
294
+ // Unlike normalizeEncoding, case-sensitive
295
+ // https://encoding.spec.whatwg.org/#names-and-labels
296
+ export function labelToName(label) {
297
+ const enc = normalizeEncoding(label)
298
+ if (!enc) return enc
299
+ if (uppercasePrefixes.has(enc.slice(0, 3))) return enc.toUpperCase()
300
+ if (enc === 'big5') return 'Big5'
301
+ if (enc === 'shift_jis') return 'Shift_JIS'
302
+ return enc
303
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bytes",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Various operations on Uint8Array data",
5
5
  "scripts": {
6
6
  "lint": "eslint .",
@@ -45,6 +45,7 @@
45
45
  "files": [
46
46
  "/fallback/_utils.js",
47
47
  "/fallback/base32.js",
48
+ "/fallback/base58check.js",
48
49
  "/fallback/base64.js",
49
50
  "/fallback/encoding.js",
50
51
  "/fallback/encoding.labels.js",
@@ -65,6 +66,7 @@
65
66
  "/base32.js",
66
67
  "/base58.js",
67
68
  "/base58check.js",
69
+ "/base58check.node.js",
68
70
  "/base64.js",
69
71
  "/base64.d.ts",
70
72
  "/bech32.js",
@@ -92,7 +94,10 @@
92
94
  },
93
95
  "./base32.js": "./base32.js",
94
96
  "./base58.js": "./base58.js",
95
- "./base58check.js": "./base58check.js",
97
+ "./base58check.js": {
98
+ "node": "./base58check.node.js",
99
+ "default": "./base58check.js"
100
+ },
96
101
  "./base64.js": {
97
102
  "types": "./base64.d.ts",
98
103
  "default": "./base64.js"
package/utf16.js CHANGED
@@ -9,7 +9,7 @@ const decoderFatalBE = canDecoders ? new TextDecoder('utf-16be', { ignoreBOM, fa
9
9
  const decoderLooseBE = canDecoders ? new TextDecoder('utf-16be', { ignoreBOM }) : null
10
10
  const decoderFatal16 = isLE ? decoderFatalLE : decoderFatalBE
11
11
  const decoderLoose16 = isLE ? decoderLooseLE : decoderFatalBE
12
- const { isWellFormed } = String.prototype
12
+ const { isWellFormed, toWellFormed } = String.prototype
13
13
 
14
14
  const { E_STRICT, E_STRICT_UNICODE } = js
15
15
 
@@ -61,8 +61,9 @@ function decode(input, loose = false, format = 'uint16') {
61
61
  throw new TypeError('Unknown format')
62
62
  }
63
63
 
64
- const str = js.decode(u16, loose, !loose && isWellFormed)
64
+ const str = js.decode(u16, loose, (!loose && isWellFormed) || (loose && toWellFormed))
65
65
  if (!loose && isWellFormed && !isWellFormed.call(str)) throw new TypeError(E_STRICT)
66
+ if (loose && toWellFormed) return toWellFormed.call(str)
66
67
 
67
68
  return str
68
69
  }
package/utf16.node.js CHANGED
@@ -1,9 +1,9 @@
1
- import { nativeDecoder, isDeno, isLE } from './fallback/_utils.js'
1
+ import { isDeno, isLE } from './fallback/_utils.js'
2
2
  import { E_STRICT, E_STRICT_UNICODE } from './fallback/utf16.js'
3
3
 
4
4
  if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
5
5
 
6
- const { isWellFormed } = String.prototype
6
+ const { isWellFormed, toWellFormed } = String.prototype
7
7
  const to8 = (a) => new Uint8Array(a.buffer, a.byteOffset, a.byteLength)
8
8
 
9
9
  // Unlike utf8, operates on Uint16Arrays by default
@@ -14,9 +14,10 @@ function encode(str, loose = false, format = 'uint16') {
14
14
  throw new TypeError('Unknown format')
15
15
  }
16
16
 
17
- if (!isWellFormed.call(str)) {
18
- if (!loose) throw new TypeError(E_STRICT_UNICODE)
19
- str = nativeDecoder.decode(Buffer.from(str)) // well, let's fix up (Buffer doesn't do this with utf16 encoding)
17
+ if (loose) {
18
+ str = toWellFormed.call(str) // Buffer doesn't do this with utf16 encoding
19
+ } else if (!isWellFormed.call(str)) {
20
+ throw new TypeError(E_STRICT_UNICODE)
20
21
  }
21
22
 
22
23
  const ble = Buffer.from(str, 'utf-16le')
@@ -51,9 +52,9 @@ function decodeNode(input, loose = false, format = 'uint16') {
51
52
  }
52
53
 
53
54
  const str = ble.ucs2Slice(0, ble.byteLength)
55
+ if (loose) return toWellFormed.call(str)
54
56
  if (isWellFormed.call(str)) return str
55
- if (!loose) throw new TypeError(E_STRICT)
56
- return nativeDecoder.decode(Buffer.from(str)) // fixup (see above)
57
+ throw new TypeError(E_STRICT)
57
58
  }
58
59
 
59
60
  function decodeDecoder(input, loose = false, format = 'uint16') {