Skip to content

Commit c7b82b8

Browse files
Merge pull request #22 from sovereignbase/feat/issue-21-bigint
Add BigInt methods and CloudflareWorkers test runner.
2 parents 9b6d7f0 + bc57eab commit c7b82b8

30 files changed

Lines changed: 3748 additions & 1794 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: Set up Deno
3737
uses: denoland/setup-deno@v2
3838
with:
39-
deno-version: v2.2.5
39+
deno-version: v2.7.7
4040

4141
- name: Install Playwright browsers
4242
run: npx playwright install --with-deps

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ test-results/
1919

2020
.DS_Store
2121
Thumbs.db
22+
23+
.wrangler/

README.md

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55

66
# bytecodec
77

8-
Typed JavaScript and TypeScript byte utilities for base64, base64url, hex, Z85, UTF-8 strings, JSON, gzip, concatenation, comparison, and byte-source normalization. The package ships tree-shakeable ESM plus CommonJS entry points and keeps the same API across Node, Bun, Deno, browsers, and edge runtimes.
8+
Typed JavaScript and TypeScript byte utilities for base64, base64url, hex, Z85, UTF-8 strings, unsigned BigInt conversion, JSON, gzip, concatenation, comparison, and byte-source normalization. The package ships tree-shakeable ESM plus CommonJS entry points and keeps the same API across Node, Bun, Deno, browsers, and edge runtimes.
99

1010
## Compatibility
1111

12-
- Runtimes: Node, Bun, Deno, browsers, and edge runtimes.
12+
- Runtimes: Node, Bun, Deno, browsers, Cloudflare Workers, and edge runtimes.
1313
- Module formats: ESM by default, with CommonJS exports for `require()` consumers in Node and Bun.
1414
- Node and Bun runtime behavior: uses `Buffer` for base64 helpers and `node:zlib` for gzip.
1515
- Browser and edge gzip support requires `CompressionStream` and `DecompressionStream`.
1616
- TypeScript: bundled types.
1717

1818
## Goals
1919

20-
- Developer-friendly API for base64, base64url, hex, Z85, UTF-8, JSON, gzip, concat, equality, and byte normalization.
20+
- Developer-friendly API for base64, base64url, hex, Z85, UTF-8, unsigned BigInt conversion, JSON, gzip, concat, equality, and byte normalization.
2121
- No runtime dependencies or bundler shims.
2222
- Tree-shakeable ESM by default with CommonJS compatibility and no side effects.
2323
- Returns copies for safety when normalizing inputs.
@@ -113,6 +113,18 @@ const textBytes = fromString('caffe and rockets') // Uint8Array
113113
const text = toString(textBytes) // "caffe and rockets"
114114
```
115115

116+
### BigInt
117+
118+
```js
119+
import { fromBigInt, toBigInt } from '@sovereignbase/bytecodec'
120+
121+
const bytes = fromBigInt(0x1234n) // Uint8Array([0x12, 0x34])
122+
const value = toBigInt(bytes) // 0x1234n
123+
```
124+
125+
BigInt helpers use unsigned big-endian encoding. `fromBigInt(0n)` returns an empty `Uint8Array`, because no byte width is implied.
126+
Leading zero bytes are not preserved, so the helpers model integers rather than fixed-width binary fields.
127+
116128
### JSON
117129

118130
```js
@@ -147,8 +159,8 @@ const bufferSource = toBufferSource(normalized) // Uint8Array as BufferSource
147159

148160
Accepted byte inputs (`ByteSource`) are:
149161

150-
- `Uint8Array`
151162
- `ArrayBuffer`
163+
- `SharedArrayBuffer`
152164
- `ArrayBufferView`
153165
- `number[]`
154166

@@ -184,7 +196,7 @@ Uses `TextEncoder`, `TextDecoder`, `btoa`, and `atob`. Gzip uses `CompressionStr
184196

185197
### Validation & errors
186198

187-
Validation failures throw `BytecodecError` instances with a `code` string, for example `BASE64URL_INVALID_LENGTH`, `HEX_INVALID_CHARACTER`, `Z85_INVALID_BLOCK`, `BASE64_DECODER_UNAVAILABLE`, `UTF8_DECODER_UNAVAILABLE`, and `GZIP_COMPRESSION_UNAVAILABLE`. Messages are prefixed with `{@sovereignbase/bytecodec}`.
199+
Validation failures throw `BytecodecError` instances with a `code` string, for example `BASE64URL_INVALID_LENGTH`, `BIGINT_UNSIGNED_EXPECTED`, `HEX_INVALID_CHARACTER`, `Z85_INVALID_BLOCK`, `BASE64_DECODER_UNAVAILABLE`, `UTF8_DECODER_UNAVAILABLE`, and `GZIP_COMPRESSION_UNAVAILABLE`. Messages are prefixed with `{@sovereignbase/bytecodec}`.
188200

189201
### Safety / copying semantics
190202

@@ -194,41 +206,44 @@ Validation failures throw `BytecodecError` instances with a `code` string, for e
194206

195207
`npm test` covers:
196208

197-
- 68 unit tests
198-
- 6 integration tests
199-
- Node E2E: ESM and CommonJS
200-
- Bun E2E: ESM and CommonJS
201-
- Deno E2E: ESM
202-
- Edge Runtime E2E: ESM
203-
- Browser E2E: Chromium, Firefox, WebKit, mobile-chrome, and mobile-safari
209+
- 75 unit tests
210+
- 7 integration tests
211+
- Node E2E: 23/23 passed in ESM and 23/23 passed in CommonJS
212+
- Bun E2E: 23/23 passed in ESM and 23/23 passed in CommonJS
213+
- Deno E2E: 23/23 passed in ESM
214+
- Cloudflare Workers E2E: 23/23 passed in ESM
215+
- Edge Runtime E2E: 23/23 passed in ESM
216+
- Browser E2E: 5/5 passed in Chromium, Firefox, WebKit, mobile-chrome, and mobile-safari
204217
- Coverage gate: 100% statements, branches, functions, and lines
205218

206219
## Benchmarks
207220

208-
Latest local `npm run bench` run on 2026-03-23 with Node `v22.14.0 (win32 x64)`:
221+
Latest local `npm run bench` run on 2026-03-27 with Node `v22.14.0 (win32 x64)`:
209222

210223
| Benchmark | Result |
211224
| ---------------- | ------------------------- |
212-
| base64 encode | 1,391,126 ops/s (35.9 ms) |
213-
| base64 decode | 2,089,279 ops/s (23.9 ms) |
214-
| base64url encode | 697,088 ops/s (71.7 ms) |
215-
| base64url decode | 1,095,554 ops/s (45.6 ms) |
216-
| hex encode | 1,053,832 ops/s (47.4 ms) |
217-
| hex decode | 1,027,413 ops/s (48.7 ms) |
218-
| z85 encode | 244,928 ops/s (204.1 ms) |
219-
| z85 decode | 1,596,730 ops/s (31.3 ms) |
220-
| utf8 encode | 1,537,199 ops/s (32.5 ms) |
221-
| utf8 decode | 3,481,143 ops/s (14.4 ms) |
222-
| json encode | 681,747 ops/s (29.3 ms) |
223-
| json decode | 989,746 ops/s (20.2 ms) |
224-
| concat 3 buffers | 846,612 ops/s (59.1 ms) |
225-
| toUint8Array | 9,396,818 ops/s (21.3 ms) |
226-
| toArrayBuffer | 884,096 ops/s (226.2 ms) |
227-
| toBufferSource | 9,279,881 ops/s (21.6 ms) |
228-
| equals same | 3,932,572 ops/s (50.9 ms) |
229-
| equals diff | 4,060,534 ops/s (49.3 ms) |
230-
| gzip compress | 4,126 ops/s (96.9 ms) |
231-
| gzip decompress | 5,550 ops/s (72.1 ms) |
225+
| base64 encode | 979,522 ops/s (51.0 ms) |
226+
| base64 decode | 1,825,737 ops/s (27.4 ms) |
227+
| base64url encode | 407,973 ops/s (122.6 ms) |
228+
| base64url decode | 560,991 ops/s (89.1 ms) |
229+
| hex encode | 781,944 ops/s (63.9 ms) |
230+
| hex decode | 806,002 ops/s (62.0 ms) |
231+
| z85 encode | 170,125 ops/s (293.9 ms) |
232+
| z85 decode | 1,141,472 ops/s (43.8 ms) |
233+
| utf8 encode | 1,241,977 ops/s (40.3 ms) |
234+
| utf8 decode | 2,610,407 ops/s (19.2 ms) |
235+
| bigint encode | 490,692 ops/s (101.9 ms) |
236+
| bigint decode | 428,938 ops/s (116.6 ms) |
237+
| json encode | 588,066 ops/s (34.0 ms) |
238+
| json decode | 603,058 ops/s (33.2 ms) |
239+
| concat 3 buffers | 560,639 ops/s (89.2 ms) |
240+
| toUint8Array | 6,292,910 ops/s (31.8 ms) |
241+
| toArrayBuffer | 677,822 ops/s (295.1 ms) |
242+
| toBufferSource | 7,465,472 ops/s (26.8 ms) |
243+
| equals same | 2,217,064 ops/s (90.2 ms) |
244+
| equals diff | 2,302,002 ops/s (86.9 ms) |
245+
| gzip compress | 3,473 ops/s (115.2 ms) |
246+
| gzip decompress | 4,753 ops/s (84.2 ms) |
232247

233248
Command: `npm run bench`
234249

benchmark/bench.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
equals,
66
fromBase64String,
77
fromBase64UrlString,
8+
fromBigInt,
89
fromCompressed,
910
fromHex,
1011
fromJSON,
@@ -13,6 +14,7 @@ import {
1314
toArrayBuffer,
1415
toBase64String,
1516
toBase64UrlString,
17+
toBigInt,
1618
toBufferSource,
1719
toCompressed,
1820
toHex,
@@ -51,6 +53,8 @@ const sampleBytesDiff = Uint8Array.from(sampleBytes, (value, idx) =>
5153
const sampleView = new DataView(sampleBytes.buffer, 0, sampleBytes.byteLength)
5254
const sampleText = 'caffeinated rockets at dawn'
5355
const sampleTextBytes = fromString(sampleText)
56+
const sampleBigInt = 0x1234567890abcdef1234567890abcdefn
57+
const sampleBigIntBytes = fromBigInt(sampleBigInt)
5458
const sampleJson = { ok: true, count: 42, note: '@sovereignbase/bytecodec' }
5559
const sampleJsonBytes = fromJSON(sampleJson)
5660
const base64 = toBase64String(sampleBytes)
@@ -69,6 +73,8 @@ bench('z85 encode', 50000, () => toZ85String(sampleBytes))
6973
bench('z85 decode', 50000, () => fromZ85String(z85))
7074
bench('utf8 encode', 50000, () => fromString(sampleText))
7175
bench('utf8 decode', 50000, () => toString(sampleTextBytes))
76+
bench('bigint encode', 50000, () => fromBigInt(sampleBigInt))
77+
bench('bigint decode', 50000, () => toBigInt(sampleBigIntBytes))
7278
bench('json encode', 20000, () => fromJSON(sampleJson))
7379
bench('json decode', 20000, () => toJSON(sampleJsonBytes))
7480
bench('concat 3 buffers', 50000, () =>

jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://jsr.io/schema/config-file.v1.json",
33
"name": "@sovereignbase/bytecodec",
4-
"version": "1.4.0",
4+
"version": "1.5.0",
55
"exports": "./src/index.ts",
66
"publish": {
77
"include": [

0 commit comments

Comments
 (0)