bufferbase 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.
- package/README.md +32 -0
- package/package.json +30 -0
- package/src/bufferbase.test.ts +58 -0
- package/src/bufferbase.ts +133 -0
- package/src/index.ts +7 -0
- package/src/sample.ts +43 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# bufferbase
|
|
2
|
+
|
|
3
|
+
Buffer-to-BaseN Encoding and Decoding for Node.js.
|
|
4
|
+
|
|
5
|
+
bufferbase is a Node.js library that provides straightforward encoding and decoding of buffer objects into various BaseN formats. It's designed to be simple, efficient, and flexible for different application needs.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Supports multiple base encoding formats.
|
|
10
|
+
- Optimized for Node.js buffer handling.
|
|
11
|
+
- Intuitive API for easy integration.
|
|
12
|
+
- Suitable for various data types.
|
|
13
|
+
- Consistent and reliable performance.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Install bufferbase using npm:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install bufferbase
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
See [src/sample.ts](/src/sample.ts)
|
|
26
|
+
|
|
27
|
+
## Contributing
|
|
28
|
+
|
|
29
|
+
Contributions to bufferbase are welcome! Please read our contributing guidelines for more information.
|
|
30
|
+
License
|
|
31
|
+
|
|
32
|
+
bufferbase is ISC licensed.
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bufferbase",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Buffer-to-BaseN Encoder, Decoder, Converter, and Validator",
|
|
5
|
+
"main": "./cjs/index.js",
|
|
6
|
+
"module": "./esm/index.js",
|
|
7
|
+
"types": "./esm/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": "./dist/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"test": "node --loader tsx --test src/*.test.ts"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^20.11.5",
|
|
17
|
+
"tsx": "^4.7.0",
|
|
18
|
+
"typescript": "^5.3.3"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/misebox/bufferbase.git"
|
|
23
|
+
},
|
|
24
|
+
"author": "misebox",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/misebox/bufferbase/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/misebox/bufferbase#readme"
|
|
30
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Chars, BufferEncoder } from "./bufferbase.js";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import test from "node:test"; // ①
|
|
4
|
+
import assert from "node:assert";
|
|
5
|
+
|
|
6
|
+
test("anybase", async (t) => {
|
|
7
|
+
await t.test(
|
|
8
|
+
"encodes and decodes a string correctly with base58chars",
|
|
9
|
+
() => {
|
|
10
|
+
const encoder = new BufferEncoder(Chars.Base58);
|
|
11
|
+
const buffer = Buffer.from("Hello, World!", "utf8");
|
|
12
|
+
|
|
13
|
+
const encoded = encoder.encode(buffer);
|
|
14
|
+
const decoded = encoder.decode(encoded);
|
|
15
|
+
|
|
16
|
+
assert.strictEqual(decoded.toString("utf8"), "Hello, World!");
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
await t.test("handles empty buffer correctly", () => {
|
|
21
|
+
const encoder = new BufferEncoder(Chars.Base58);
|
|
22
|
+
const buffer = Buffer.alloc(0);
|
|
23
|
+
|
|
24
|
+
const encoded = encoder.encode(buffer);
|
|
25
|
+
const decoded = encoder.decode(encoded);
|
|
26
|
+
|
|
27
|
+
assert.strictEqual(decoded.toString("utf8"), "");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await t.test("handles long buffer correctly", () => {
|
|
31
|
+
const encoder = new BufferEncoder(Chars.Base58);
|
|
32
|
+
const buffer = Buffer.from("Hello, World!".repeat(100), "utf8");
|
|
33
|
+
|
|
34
|
+
const encoded = encoder.encode(buffer);
|
|
35
|
+
const decoded = encoder.decode(encoded);
|
|
36
|
+
|
|
37
|
+
assert.strictEqual(decoded.toString("utf8"), "Hello, World!".repeat(100));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await t.test("encode and decode various chars correctly", () => {
|
|
41
|
+
for (const charTable of Object.values(Chars)) {
|
|
42
|
+
const encoder = new BufferEncoder(charTable);
|
|
43
|
+
const buffer = Buffer.from("Hello, World!", "utf8");
|
|
44
|
+
|
|
45
|
+
const encoded = encoder.encode(buffer);
|
|
46
|
+
const decoded = encoder.decode(encoded);
|
|
47
|
+
|
|
48
|
+
assert.strictEqual(decoded.toString("utf8"), "Hello, World!");
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
await t.test("throws error on invalid characters in decode", () => {
|
|
53
|
+
assert.throws(() => {
|
|
54
|
+
const encoder = new BufferEncoder(Chars.Base58);
|
|
55
|
+
encoder.decode("InvalidString!");
|
|
56
|
+
}, new Error("Invalid character found"));
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A collection of common bases.
|
|
3
|
+
*/
|
|
4
|
+
export const Chars = {
|
|
5
|
+
Decimal: "0123456789",
|
|
6
|
+
Base16: "0123456789ABCDEF",
|
|
7
|
+
Base32: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
|
|
8
|
+
Base32Crockford: "0123456789ABCDEFGHJKMNPQRSTVWXYZ",
|
|
9
|
+
Base36: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
10
|
+
Base58: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
|
11
|
+
Base64_STD:
|
|
12
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
|
|
13
|
+
Base64_URL_SAFE:
|
|
14
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
|
15
|
+
Base64_XML_NMTOKEN:
|
|
16
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._",
|
|
17
|
+
Base64_XML_NAME:
|
|
18
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_:",
|
|
19
|
+
Ascii85:
|
|
20
|
+
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstu",
|
|
21
|
+
Base85:
|
|
22
|
+
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&()*+-;<=>?@^_`{|}~",
|
|
23
|
+
Z85: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export class InvalidCharacterError extends Error {
|
|
27
|
+
message = "Invalid character found";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Encodes and decodes buffers to and from a base.
|
|
31
|
+
*/
|
|
32
|
+
export class BufferEncoder {
|
|
33
|
+
constructor(private baseChars: string) {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Encodes a buffer to a string.
|
|
37
|
+
*/
|
|
38
|
+
encode(buffer: Buffer): string {
|
|
39
|
+
let result = [];
|
|
40
|
+
for (const byte of buffer) {
|
|
41
|
+
let carry = byte;
|
|
42
|
+
for (let j = 0; j < result.length; j++) {
|
|
43
|
+
carry += result[j] * 256;
|
|
44
|
+
result[j] = carry % this.baseChars.length;
|
|
45
|
+
carry = Math.floor(carry / this.baseChars.length);
|
|
46
|
+
}
|
|
47
|
+
while (carry > 0) {
|
|
48
|
+
result.push(carry % this.baseChars.length);
|
|
49
|
+
carry = Math.floor(carry / this.baseChars.length);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const byte of buffer) {
|
|
53
|
+
if (byte === 0) {
|
|
54
|
+
result.push(0);
|
|
55
|
+
} else {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result
|
|
60
|
+
.reverse()
|
|
61
|
+
.map((index) => this.baseChars[index])
|
|
62
|
+
.join("");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Decodes a string to a buffer.
|
|
67
|
+
*/
|
|
68
|
+
decode(encoded: string): Buffer {
|
|
69
|
+
let result = Buffer.alloc(0);
|
|
70
|
+
for (const char of encoded) {
|
|
71
|
+
const value = this.baseChars.indexOf(char);
|
|
72
|
+
if (value === -1) throw new InvalidCharacterError();
|
|
73
|
+
let carry = value;
|
|
74
|
+
let tempResult = Buffer.alloc(result.length);
|
|
75
|
+
let i;
|
|
76
|
+
for (i = 0; i < result.length; i++) {
|
|
77
|
+
carry += result[i] * this.baseChars.length;
|
|
78
|
+
tempResult[i] = carry % 256;
|
|
79
|
+
carry = Math.floor(carry / 256);
|
|
80
|
+
}
|
|
81
|
+
while (carry > 0) {
|
|
82
|
+
tempResult = Buffer.concat([tempResult, Buffer.from([carry % 256])]);
|
|
83
|
+
carry = Math.floor(carry / 256);
|
|
84
|
+
}
|
|
85
|
+
result = tempResult;
|
|
86
|
+
}
|
|
87
|
+
return result.reverse();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Creates a converter function that can convert between two bases.
|
|
93
|
+
*/
|
|
94
|
+
export class Converter {
|
|
95
|
+
decoder: BufferEncoder;
|
|
96
|
+
encoder: BufferEncoder;
|
|
97
|
+
|
|
98
|
+
constructor(inputBase: string, outputBase: string) {
|
|
99
|
+
this.decoder = new BufferEncoder(inputBase);
|
|
100
|
+
this.encoder = new BufferEncoder(outputBase);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Converts a string from the input base to the output base.
|
|
105
|
+
*/
|
|
106
|
+
convert(input: string): string {
|
|
107
|
+
return this.encoder.encode(this.decoder.decode(input));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class Validator {
|
|
112
|
+
decorder: BufferEncoder;
|
|
113
|
+
|
|
114
|
+
constructor(inputBase: string) {
|
|
115
|
+
this.decorder = new BufferEncoder(inputBase);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
validate(input: string): boolean {
|
|
119
|
+
try {
|
|
120
|
+
this.decorder.decode(input);
|
|
121
|
+
return true;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if (e instanceof InvalidCharacterError) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const validate = (input: string, base: string): boolean => {
|
|
132
|
+
return new Validator(base).validate(input);
|
|
133
|
+
};
|
package/src/index.ts
ADDED
package/src/sample.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Chars, BufferEncoder, Converter, Validator } from "./bufferbase";
|
|
2
|
+
|
|
3
|
+
// Example buffer
|
|
4
|
+
const bytes = Buffer.from("Hello, World!", "utf8");
|
|
5
|
+
|
|
6
|
+
// Encoding buffer to Base32
|
|
7
|
+
const encoder = new BufferEncoder(Chars.Base32Crockford);
|
|
8
|
+
const base32encoded = encoder.encode(bytes);
|
|
9
|
+
|
|
10
|
+
// Converting Base32 to Base58
|
|
11
|
+
const converter_32to58 = new Converter(Chars.Base32Crockford, Chars.Base58);
|
|
12
|
+
const base58encoded = converter_32to58.convert(base32encoded);
|
|
13
|
+
|
|
14
|
+
// Converting Base32 to Base58
|
|
15
|
+
const converter_58to64 = new Converter(Chars.Base58, Chars.Base64_URL_SAFE);
|
|
16
|
+
const base64encoded = converter_58to64.convert(base58encoded);
|
|
17
|
+
|
|
18
|
+
// Validating Base64
|
|
19
|
+
const validatorB64 = new Validator(Chars.Base64_URL_SAFE);
|
|
20
|
+
const isBase64 = validatorB64.validate(base64encoded);
|
|
21
|
+
|
|
22
|
+
// Decoding Base64
|
|
23
|
+
const decoder = new BufferEncoder(Chars.Base64_URL_SAFE);
|
|
24
|
+
const decoded = decoder.decode(base64encoded);
|
|
25
|
+
|
|
26
|
+
console.table({
|
|
27
|
+
bytes: bytes.toString("utf8"),
|
|
28
|
+
base32encoded,
|
|
29
|
+
base58encoded,
|
|
30
|
+
base64encoded,
|
|
31
|
+
isBase64,
|
|
32
|
+
decoded: decoded.toString("utf8"),
|
|
33
|
+
});
|
|
34
|
+
// ┌───────────────┬─────────────────────────┐
|
|
35
|
+
// │ (index) │ Values │
|
|
36
|
+
// ├───────────────┼─────────────────────────┤
|
|
37
|
+
// │ bytes │ 'Hello, World!' │
|
|
38
|
+
// │ base32encoded │ '4GSBCDHQJR82QDXS6RS11' │
|
|
39
|
+
// │ base58encoded │ '72k1xXWG59fYdzSNoA' │
|
|
40
|
+
// │ base64encoded │ 'BIZWxsbywgV29ybGQh' │
|
|
41
|
+
// │ decoded │ 'Hello, World!' │
|
|
42
|
+
// │ isBase64 │ true │
|
|
43
|
+
// └───────────────┴─────────────────────────┘
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["esnext"],
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"allowJs": false,
|
|
11
|
+
"types": [
|
|
12
|
+
"node",
|
|
13
|
+
],
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"pretty": true,
|
|
16
|
+
"newLine": "lf",
|
|
17
|
+
"outDir": "./dist",
|
|
18
|
+
},
|
|
19
|
+
}
|