+11
packages/utilities/multibase/README.md
+11
packages/utilities/multibase/README.md
+22
packages/utilities/multibase/lib/bases/base16.ts
+22
packages/utilities/multibase/lib/bases/base16.ts
···
1
+
import { createRfc4648Decode, createRfc4648Encode } from '../utils.js';
2
+
3
+
const HAS_UINT8_BASE16_SUPPORT = 'fromHex' in Uint8Array;
4
+
5
+
const BASE16_CHARSET = '0123456789abcdef';
6
+
const UPPER_RE = /[A-F]/;
7
+
8
+
export const fromBase16 = !HAS_UINT8_BASE16_SUPPORT
9
+
? /*#__PURE__*/ createRfc4648Decode(BASE16_CHARSET, 4, false)
10
+
: (str: string): Uint8Array => {
11
+
if (UPPER_RE.test(str)) {
12
+
throw new SyntaxError(`unexpected uppercase characters in base16 string`);
13
+
}
14
+
15
+
return Uint8Array.fromHex(str);
16
+
};
17
+
18
+
export const toBase16 = !HAS_UINT8_BASE16_SUPPORT
19
+
? /*#__PURE__*/ createRfc4648Encode(BASE16_CHARSET, 4, false)
20
+
: (bytes: Uint8Array): string => {
21
+
return bytes.toHex();
22
+
};
+7
packages/utilities/multibase/lib/bases/base32.ts
+7
packages/utilities/multibase/lib/bases/base32.ts
···
1
+
import { createRfc4648Decode, createRfc4648Encode } from '../utils.js';
2
+
3
+
const BASE32_CHARSET = 'abcdefghijklmnopqrstuvwxyz234567';
4
+
5
+
export const fromBase32 = /*#__PURE__*/ createRfc4648Decode(BASE32_CHARSET, 5, false);
6
+
7
+
export const toBase32 = /*#__PURE__*/ createRfc4648Encode(BASE32_CHARSET, 5, false);
+7
packages/utilities/multibase/lib/bases/base58.ts
+7
packages/utilities/multibase/lib/bases/base58.ts
···
1
+
import { createRfc4648Decode, createRfc4648Encode } from '../utils.js';
2
+
3
+
const BASE58BTC_CHARSET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
4
+
5
+
export const fromBase58Btc = /*#__PURE__*/ createRfc4648Decode(BASE58BTC_CHARSET, 5, false);
6
+
7
+
export const toBase58Btc = /*#__PURE__*/ createRfc4648Encode(BASE58BTC_CHARSET, 5, false);
+62
packages/utilities/multibase/lib/bases/base64.ts
+62
packages/utilities/multibase/lib/bases/base64.ts
···
1
+
import { createRfc4648Decode, createRfc4648Encode } from '../utils.js';
2
+
3
+
const HAS_UINT8_BASE64_SUPPORT = 'fromBase64' in Uint8Array;
4
+
5
+
const BASE64_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
6
+
const BASE64URL_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
7
+
8
+
export const fromBase64 = !HAS_UINT8_BASE64_SUPPORT
9
+
? /*#__PURE__*/ createRfc4648Decode(BASE64_CHARSET, 6, false)
10
+
: (str: string): Uint8Array => {
11
+
if (str[str.length - 1] === '=') {
12
+
throw new SyntaxError(`unexpected padding in base64 string`);
13
+
}
14
+
15
+
return Uint8Array.fromBase64(str, { alphabet: 'base64', lastChunkHandling: 'loose' });
16
+
};
17
+
18
+
export const toBase64 = !HAS_UINT8_BASE64_SUPPORT
19
+
? /*#__PURE__*/ createRfc4648Encode(BASE64_CHARSET, 6, false)
20
+
: (bytes: Uint8Array): string => {
21
+
return bytes.toBase64({ alphabet: 'base64', omitPadding: true });
22
+
};
23
+
24
+
export const fromBase64Pad = !HAS_UINT8_BASE64_SUPPORT
25
+
? /*#__PURE__*/ createRfc4648Decode(BASE64_CHARSET, 6, true)
26
+
: (str: string): Uint8Array => {
27
+
return Uint8Array.fromBase64(str, { alphabet: 'base64', lastChunkHandling: 'strict' });
28
+
};
29
+
30
+
export const toBase64Pad = !HAS_UINT8_BASE64_SUPPORT
31
+
? /*#__PURE__*/ createRfc4648Encode(BASE64_CHARSET, 6, true)
32
+
: (bytes: Uint8Array): string => {
33
+
return bytes.toBase64({ alphabet: 'base64', omitPadding: false });
34
+
};
35
+
36
+
export const fromBase64Url = !HAS_UINT8_BASE64_SUPPORT
37
+
? /*#__PURE__*/ createRfc4648Decode(BASE64URL_CHARSET, 6, false)
38
+
: (str: string): Uint8Array => {
39
+
if (str[str.length - 1] === '=') {
40
+
throw new SyntaxError(`unexpected padding in base64 string`);
41
+
}
42
+
43
+
return Uint8Array.fromBase64(str, { alphabet: 'base64url', lastChunkHandling: 'loose' });
44
+
};
45
+
46
+
export const toBase64Url = !HAS_UINT8_BASE64_SUPPORT
47
+
? /*#__PURE__*/ createRfc4648Encode(BASE64URL_CHARSET, 6, false)
48
+
: (bytes: Uint8Array): string => {
49
+
return bytes.toBase64({ alphabet: 'base64url', omitPadding: true });
50
+
};
51
+
52
+
export const fromBase64UrlPad = !HAS_UINT8_BASE64_SUPPORT
53
+
? /*#__PURE__*/ createRfc4648Decode(BASE64URL_CHARSET, 6, true)
54
+
: (str: string): Uint8Array => {
55
+
return Uint8Array.fromBase64(str, { alphabet: 'base64url', lastChunkHandling: 'strict' });
56
+
};
57
+
58
+
export const toBase64UrlPad = !HAS_UINT8_BASE64_SUPPORT
59
+
? /*#__PURE__*/ createRfc4648Encode(BASE64URL_CHARSET, 6, true)
60
+
: (bytes: Uint8Array): string => {
61
+
return bytes.toBase64({ alphabet: 'base64url', omitPadding: false });
62
+
};
+17
packages/utilities/multibase/lib/env.ts
+17
packages/utilities/multibase/lib/env.ts
···
1
+
declare global {
2
+
interface Uint8Array {
3
+
toHex(): string;
4
+
toBase64(options?: { alphabet?: 'base64' | 'base64url'; omitPadding?: boolean }): string;
5
+
}
6
+
7
+
interface Uint8ArrayConstructor {
8
+
fromHex(base16: string): Uint8Array;
9
+
fromBase64(
10
+
base64: string,
11
+
options?: {
12
+
alphabet?: 'base64' | 'base64url';
13
+
lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial';
14
+
},
15
+
): Uint8Array;
16
+
}
17
+
}
+4
packages/utilities/multibase/lib/index.ts
+4
packages/utilities/multibase/lib/index.ts
+82
packages/utilities/multibase/lib/utils.ts
+82
packages/utilities/multibase/lib/utils.ts
···
1
+
export const createRfc4648Encode = (alphabet: string, bitsPerChar: number, pad: boolean) => {
2
+
return (bytes: Uint8Array): string => {
3
+
const mask = (1 << bitsPerChar) - 1;
4
+
let str = '';
5
+
6
+
let bits = 0; // Number of bits currently in the buffer
7
+
let buffer = 0; // Bits waiting to be written out, MSB first
8
+
for (let i = 0; i < bytes.length; ++i) {
9
+
// Slurp data into the buffer:
10
+
buffer = (buffer << 8) | bytes[i];
11
+
bits += 8;
12
+
13
+
// Write out as much as we can:
14
+
while (bits > bitsPerChar) {
15
+
bits -= bitsPerChar;
16
+
str += alphabet[mask & (buffer >> bits)];
17
+
}
18
+
}
19
+
20
+
// Partial character:
21
+
if (bits !== 0) {
22
+
str += alphabet[mask & (buffer << (bitsPerChar - bits))];
23
+
}
24
+
25
+
// Add padding characters until we hit a byte boundary:
26
+
if (pad) {
27
+
while (((str.length * bitsPerChar) & 7) !== 0) {
28
+
str += '=';
29
+
}
30
+
}
31
+
32
+
return str;
33
+
};
34
+
};
35
+
36
+
export const createRfc4648Decode = (alphabet: string, bitsPerChar: number, pad: boolean) => {
37
+
// Build the character lookup table:
38
+
const codes: Record<string, number> = {};
39
+
for (let i = 0; i < alphabet.length; ++i) {
40
+
codes[alphabet[i]] = i;
41
+
}
42
+
43
+
return (str: string) => {
44
+
// Count the padding bytes:
45
+
let end = str.length;
46
+
while (pad && str[end - 1] === '=') {
47
+
--end;
48
+
}
49
+
50
+
// Allocate the output:
51
+
const bytes = new Uint8Array(((end * bitsPerChar) / 8) | 0);
52
+
53
+
// Parse the data:
54
+
let bits = 0; // Number of bits currently in the buffer
55
+
let buffer = 0; // Bits waiting to be written out, MSB first
56
+
let written = 0; // Next byte to write
57
+
for (let i = 0; i < end; ++i) {
58
+
// Read one character from the string:
59
+
const value = codes[str[i]];
60
+
if (value === undefined) {
61
+
throw new SyntaxError(`Non-${name} character`);
62
+
}
63
+
64
+
// Append the bits to the buffer:
65
+
buffer = (buffer << bitsPerChar) | value;
66
+
bits += bitsPerChar;
67
+
68
+
// Write out some bits if the buffer has a byte's worth:
69
+
if (bits >= 8) {
70
+
bits -= 8;
71
+
bytes[written++] = 0xff & (buffer >> bits);
72
+
}
73
+
}
74
+
75
+
// Verify that we have received just enough bits:
76
+
if (bits >= bitsPerChar || (0xff & (buffer << (8 - bits))) !== 0) {
77
+
throw new SyntaxError('Unexpected end of data');
78
+
}
79
+
80
+
return bytes;
81
+
};
82
+
};
+29
packages/utilities/multibase/package.json
+29
packages/utilities/multibase/package.json
···
1
+
{
2
+
"type": "module",
3
+
"name": "@atcute/multibase",
4
+
"version": "1.0.0",
5
+
"description": "multibase utilities",
6
+
"license": "MIT",
7
+
"repository": {
8
+
"url": "https://github.com/mary-ext/atcute",
9
+
"directory": "packages/utilities/multibase"
10
+
},
11
+
"files": [
12
+
"dist/",
13
+
"lib/",
14
+
"!lib/**/*.bench.ts",
15
+
"!lib/**/*.test.ts"
16
+
],
17
+
"exports": {
18
+
".": "./dist/index.js"
19
+
},
20
+
"sideEffects": false,
21
+
"scripts": {
22
+
"build": "tsc --project tsconfig.build.json",
23
+
"test": "bun test --coverage",
24
+
"prepublish": "rm -rf dist; pnpm run build"
25
+
},
26
+
"devDependencies": {
27
+
"@types/bun": "^1.1.12"
28
+
}
29
+
}
+4
packages/utilities/multibase/tsconfig.build.json
+4
packages/utilities/multibase/tsconfig.build.json
+23
packages/utilities/multibase/tsconfig.json
+23
packages/utilities/multibase/tsconfig.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"types": ["bun"],
4
+
"outDir": "dist/",
5
+
"esModuleInterop": true,
6
+
"skipLibCheck": true,
7
+
"target": "ESNext",
8
+
"allowJs": true,
9
+
"resolveJsonModule": true,
10
+
"moduleDetection": "force",
11
+
"isolatedModules": true,
12
+
"verbatimModuleSyntax": true,
13
+
"strict": true,
14
+
"noImplicitOverride": true,
15
+
"noUnusedLocals": true,
16
+
"noUnusedParameters": true,
17
+
"noFallthroughCasesInSwitch": true,
18
+
"module": "NodeNext",
19
+
"sourceMap": true,
20
+
"declaration": true,
21
+
},
22
+
"include": ["lib"],
23
+
}