base60-codec 0.1.8 → 0.1.9
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/dist/index.cjs +191 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +165 -0
- package/package.json +1 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
base60: () => base60,
|
|
24
|
+
createBase60Codec: () => createBase60Codec
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var FIXED_LEN_UUID = 22;
|
|
28
|
+
var FIXED_LEN_ULID = 22;
|
|
29
|
+
var FIXED_LEN_INT64 = 11;
|
|
30
|
+
var alphabet = "0123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
31
|
+
var crockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
32
|
+
function createBase60Codec() {
|
|
33
|
+
const base = BigInt(alphabet.length);
|
|
34
|
+
const charToValue = /* @__PURE__ */ new Map();
|
|
35
|
+
for (let i = 0; i < alphabet.length; i++) {
|
|
36
|
+
charToValue.set(alphabet[i], BigInt(i));
|
|
37
|
+
}
|
|
38
|
+
function leftPad(text, length) {
|
|
39
|
+
if (text.length > length) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Encoded value length (${text.length}) exceeds fixed length ${length}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return text.padStart(length, alphabet[0]);
|
|
45
|
+
}
|
|
46
|
+
function bytesToBigInt(bytes) {
|
|
47
|
+
let result = 0n;
|
|
48
|
+
for (const b of bytes) {
|
|
49
|
+
result = (result << 8n) + BigInt(b);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
function bigIntToBytes(value, fixedLength) {
|
|
54
|
+
if (value < 0n) {
|
|
55
|
+
throw new Error("negative bigint is not supported");
|
|
56
|
+
}
|
|
57
|
+
let hex = value.toString(16);
|
|
58
|
+
if (hex.length % 2 === 1) hex = "0" + hex;
|
|
59
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
60
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
61
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
62
|
+
}
|
|
63
|
+
if (fixedLength !== void 0) {
|
|
64
|
+
if (bytes.length > fixedLength) {
|
|
65
|
+
throw new Error("decoded bytes exceed fixed length");
|
|
66
|
+
}
|
|
67
|
+
const padded = new Uint8Array(fixedLength);
|
|
68
|
+
padded.set(bytes, fixedLength - bytes.length);
|
|
69
|
+
return padded;
|
|
70
|
+
}
|
|
71
|
+
return bytes;
|
|
72
|
+
}
|
|
73
|
+
function encodeBigInt(value, padLength) {
|
|
74
|
+
if (value === 0n) {
|
|
75
|
+
return padLength != null ? leftPad(alphabet[0], padLength) : alphabet[0];
|
|
76
|
+
}
|
|
77
|
+
let v = value;
|
|
78
|
+
let out = "";
|
|
79
|
+
while (v > 0n) {
|
|
80
|
+
const mod = v % base;
|
|
81
|
+
v = v / base;
|
|
82
|
+
out = alphabet[Number(mod)] + out;
|
|
83
|
+
}
|
|
84
|
+
return padLength != null ? leftPad(out, padLength) : out;
|
|
85
|
+
}
|
|
86
|
+
function decodeToBigInt(text) {
|
|
87
|
+
let v = 0n;
|
|
88
|
+
for (const ch of text) {
|
|
89
|
+
const d = charToValue.get(ch);
|
|
90
|
+
if (d === void 0) {
|
|
91
|
+
throw new Error(`invalid character "${ch}"`);
|
|
92
|
+
}
|
|
93
|
+
v = v * base + d;
|
|
94
|
+
}
|
|
95
|
+
return v;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
alphabet,
|
|
99
|
+
encodeBytes(bytes) {
|
|
100
|
+
return encodeBigInt(bytesToBigInt(bytes));
|
|
101
|
+
},
|
|
102
|
+
decodeToBytes(text) {
|
|
103
|
+
return bigIntToBytes(decodeToBigInt(text));
|
|
104
|
+
},
|
|
105
|
+
encodeBigInt,
|
|
106
|
+
decodeToBigInt,
|
|
107
|
+
encodeInt64(value) {
|
|
108
|
+
return leftPad(
|
|
109
|
+
encodeBigInt(BigInt(value)),
|
|
110
|
+
FIXED_LEN_INT64
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
decodeInt64(text) {
|
|
114
|
+
if (text.length !== FIXED_LEN_INT64) {
|
|
115
|
+
throw new Error(`Expected ${FIXED_LEN_INT64} chars for Base60 Int64`);
|
|
116
|
+
}
|
|
117
|
+
return decodeToBigInt(text);
|
|
118
|
+
},
|
|
119
|
+
encodeUUID(uuid) {
|
|
120
|
+
const hex = uuid.replace(/-/g, "");
|
|
121
|
+
if (hex.length !== 32) throw new Error("invalid UUID");
|
|
122
|
+
const bytes = new Uint8Array(16);
|
|
123
|
+
for (let i = 0; i < 16; i++) {
|
|
124
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
125
|
+
}
|
|
126
|
+
return leftPad(
|
|
127
|
+
encodeBigInt(bytesToBigInt(bytes)),
|
|
128
|
+
FIXED_LEN_UUID
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
decodeUUID(text) {
|
|
132
|
+
const bytes = bigIntToBytes(decodeToBigInt(text), 16);
|
|
133
|
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
134
|
+
return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20);
|
|
135
|
+
},
|
|
136
|
+
encodeULID(ulid) {
|
|
137
|
+
if (!/^[0-9A-HJKMNP-TV-Z]{26}$/i.test(ulid)) {
|
|
138
|
+
throw new Error("Invalid ULID format");
|
|
139
|
+
}
|
|
140
|
+
const map32 = /* @__PURE__ */ new Map();
|
|
141
|
+
for (let i = 0; i < crockford.length; i++) {
|
|
142
|
+
map32.set(crockford[i], i);
|
|
143
|
+
}
|
|
144
|
+
let value = 0n;
|
|
145
|
+
for (const ch of ulid.toUpperCase()) {
|
|
146
|
+
const v = map32.get(ch);
|
|
147
|
+
if (v === void 0) throw new Error(`Invalid ULID char: ${ch}`);
|
|
148
|
+
value = (value << 5n) + BigInt(v);
|
|
149
|
+
}
|
|
150
|
+
const raw = encodeBigInt(value);
|
|
151
|
+
return leftPad(raw, FIXED_LEN_ULID);
|
|
152
|
+
},
|
|
153
|
+
decodeULID(text) {
|
|
154
|
+
if (text.length !== 22) {
|
|
155
|
+
throw new Error("Expected 22-char Base60 ULID");
|
|
156
|
+
}
|
|
157
|
+
const value = decodeToBigInt(text);
|
|
158
|
+
const bytes = bigIntToBytes(value, 16);
|
|
159
|
+
let bits = "";
|
|
160
|
+
for (const b of bytes) {
|
|
161
|
+
bits += b.toString(2).padStart(8, "0");
|
|
162
|
+
}
|
|
163
|
+
const padded = bits.padStart(130, "0");
|
|
164
|
+
let ulid = "";
|
|
165
|
+
for (let i = 0; i < 26; i++) {
|
|
166
|
+
const chunk = padded.slice(i * 5, i * 5 + 5);
|
|
167
|
+
ulid += crockford[parseInt(chunk, 2)];
|
|
168
|
+
}
|
|
169
|
+
return ulid;
|
|
170
|
+
},
|
|
171
|
+
compareAsBigInt(a, b) {
|
|
172
|
+
const ai = decodeToBigInt(a);
|
|
173
|
+
const bi = decodeToBigInt(b);
|
|
174
|
+
if (ai < bi) return -1;
|
|
175
|
+
if (ai > bi) return 1;
|
|
176
|
+
return 0;
|
|
177
|
+
},
|
|
178
|
+
isValidBase60: (text) => {
|
|
179
|
+
for (const ch of text) {
|
|
180
|
+
if (!alphabet.includes(ch)) return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
var base60 = createBase60Codec();
|
|
187
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
188
|
+
0 && (module.exports = {
|
|
189
|
+
base60,
|
|
190
|
+
createBase60Codec
|
|
191
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type Base60String = string & {
|
|
2
|
+
__brand_base60: true;
|
|
3
|
+
};
|
|
4
|
+
interface Base60Codec {
|
|
5
|
+
alphabet: Base60String;
|
|
6
|
+
encodeBytes(bytes: Uint8Array): string;
|
|
7
|
+
decodeToBytes(text: Base60String): Uint8Array;
|
|
8
|
+
encodeBigInt(value: bigint, padLength?: number): Base60String;
|
|
9
|
+
decodeToBigInt(text: Base60String): bigint;
|
|
10
|
+
encodeInt64(value: number | bigint): Base60String;
|
|
11
|
+
decodeInt64(text: Base60String): bigint;
|
|
12
|
+
encodeUUID(uuid: string): Base60String;
|
|
13
|
+
decodeUUID(text: Base60String): string;
|
|
14
|
+
encodeULID(ulid: string): Base60String;
|
|
15
|
+
decodeULID(text: Base60String): string;
|
|
16
|
+
compareAsBigInt(a: Base60String, b: Base60String): number;
|
|
17
|
+
isValidBase60(text: string): text is Base60String;
|
|
18
|
+
}
|
|
19
|
+
declare function createBase60Codec(): Base60Codec;
|
|
20
|
+
declare const base60: Base60Codec;
|
|
21
|
+
|
|
22
|
+
export { type Base60Codec, type Base60String, base60, createBase60Codec };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type Base60String = string & {
|
|
2
|
+
__brand_base60: true;
|
|
3
|
+
};
|
|
4
|
+
interface Base60Codec {
|
|
5
|
+
alphabet: Base60String;
|
|
6
|
+
encodeBytes(bytes: Uint8Array): string;
|
|
7
|
+
decodeToBytes(text: Base60String): Uint8Array;
|
|
8
|
+
encodeBigInt(value: bigint, padLength?: number): Base60String;
|
|
9
|
+
decodeToBigInt(text: Base60String): bigint;
|
|
10
|
+
encodeInt64(value: number | bigint): Base60String;
|
|
11
|
+
decodeInt64(text: Base60String): bigint;
|
|
12
|
+
encodeUUID(uuid: string): Base60String;
|
|
13
|
+
decodeUUID(text: Base60String): string;
|
|
14
|
+
encodeULID(ulid: string): Base60String;
|
|
15
|
+
decodeULID(text: Base60String): string;
|
|
16
|
+
compareAsBigInt(a: Base60String, b: Base60String): number;
|
|
17
|
+
isValidBase60(text: string): text is Base60String;
|
|
18
|
+
}
|
|
19
|
+
declare function createBase60Codec(): Base60Codec;
|
|
20
|
+
declare const base60: Base60Codec;
|
|
21
|
+
|
|
22
|
+
export { type Base60Codec, type Base60String, base60, createBase60Codec };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var FIXED_LEN_UUID = 22;
|
|
3
|
+
var FIXED_LEN_ULID = 22;
|
|
4
|
+
var FIXED_LEN_INT64 = 11;
|
|
5
|
+
var alphabet = "0123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
6
|
+
var crockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
7
|
+
function createBase60Codec() {
|
|
8
|
+
const base = BigInt(alphabet.length);
|
|
9
|
+
const charToValue = /* @__PURE__ */ new Map();
|
|
10
|
+
for (let i = 0; i < alphabet.length; i++) {
|
|
11
|
+
charToValue.set(alphabet[i], BigInt(i));
|
|
12
|
+
}
|
|
13
|
+
function leftPad(text, length) {
|
|
14
|
+
if (text.length > length) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Encoded value length (${text.length}) exceeds fixed length ${length}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return text.padStart(length, alphabet[0]);
|
|
20
|
+
}
|
|
21
|
+
function bytesToBigInt(bytes) {
|
|
22
|
+
let result = 0n;
|
|
23
|
+
for (const b of bytes) {
|
|
24
|
+
result = (result << 8n) + BigInt(b);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function bigIntToBytes(value, fixedLength) {
|
|
29
|
+
if (value < 0n) {
|
|
30
|
+
throw new Error("negative bigint is not supported");
|
|
31
|
+
}
|
|
32
|
+
let hex = value.toString(16);
|
|
33
|
+
if (hex.length % 2 === 1) hex = "0" + hex;
|
|
34
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
35
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
36
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
37
|
+
}
|
|
38
|
+
if (fixedLength !== void 0) {
|
|
39
|
+
if (bytes.length > fixedLength) {
|
|
40
|
+
throw new Error("decoded bytes exceed fixed length");
|
|
41
|
+
}
|
|
42
|
+
const padded = new Uint8Array(fixedLength);
|
|
43
|
+
padded.set(bytes, fixedLength - bytes.length);
|
|
44
|
+
return padded;
|
|
45
|
+
}
|
|
46
|
+
return bytes;
|
|
47
|
+
}
|
|
48
|
+
function encodeBigInt(value, padLength) {
|
|
49
|
+
if (value === 0n) {
|
|
50
|
+
return padLength != null ? leftPad(alphabet[0], padLength) : alphabet[0];
|
|
51
|
+
}
|
|
52
|
+
let v = value;
|
|
53
|
+
let out = "";
|
|
54
|
+
while (v > 0n) {
|
|
55
|
+
const mod = v % base;
|
|
56
|
+
v = v / base;
|
|
57
|
+
out = alphabet[Number(mod)] + out;
|
|
58
|
+
}
|
|
59
|
+
return padLength != null ? leftPad(out, padLength) : out;
|
|
60
|
+
}
|
|
61
|
+
function decodeToBigInt(text) {
|
|
62
|
+
let v = 0n;
|
|
63
|
+
for (const ch of text) {
|
|
64
|
+
const d = charToValue.get(ch);
|
|
65
|
+
if (d === void 0) {
|
|
66
|
+
throw new Error(`invalid character "${ch}"`);
|
|
67
|
+
}
|
|
68
|
+
v = v * base + d;
|
|
69
|
+
}
|
|
70
|
+
return v;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
alphabet,
|
|
74
|
+
encodeBytes(bytes) {
|
|
75
|
+
return encodeBigInt(bytesToBigInt(bytes));
|
|
76
|
+
},
|
|
77
|
+
decodeToBytes(text) {
|
|
78
|
+
return bigIntToBytes(decodeToBigInt(text));
|
|
79
|
+
},
|
|
80
|
+
encodeBigInt,
|
|
81
|
+
decodeToBigInt,
|
|
82
|
+
encodeInt64(value) {
|
|
83
|
+
return leftPad(
|
|
84
|
+
encodeBigInt(BigInt(value)),
|
|
85
|
+
FIXED_LEN_INT64
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
decodeInt64(text) {
|
|
89
|
+
if (text.length !== FIXED_LEN_INT64) {
|
|
90
|
+
throw new Error(`Expected ${FIXED_LEN_INT64} chars for Base60 Int64`);
|
|
91
|
+
}
|
|
92
|
+
return decodeToBigInt(text);
|
|
93
|
+
},
|
|
94
|
+
encodeUUID(uuid) {
|
|
95
|
+
const hex = uuid.replace(/-/g, "");
|
|
96
|
+
if (hex.length !== 32) throw new Error("invalid UUID");
|
|
97
|
+
const bytes = new Uint8Array(16);
|
|
98
|
+
for (let i = 0; i < 16; i++) {
|
|
99
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
100
|
+
}
|
|
101
|
+
return leftPad(
|
|
102
|
+
encodeBigInt(bytesToBigInt(bytes)),
|
|
103
|
+
FIXED_LEN_UUID
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
decodeUUID(text) {
|
|
107
|
+
const bytes = bigIntToBytes(decodeToBigInt(text), 16);
|
|
108
|
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
109
|
+
return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20);
|
|
110
|
+
},
|
|
111
|
+
encodeULID(ulid) {
|
|
112
|
+
if (!/^[0-9A-HJKMNP-TV-Z]{26}$/i.test(ulid)) {
|
|
113
|
+
throw new Error("Invalid ULID format");
|
|
114
|
+
}
|
|
115
|
+
const map32 = /* @__PURE__ */ new Map();
|
|
116
|
+
for (let i = 0; i < crockford.length; i++) {
|
|
117
|
+
map32.set(crockford[i], i);
|
|
118
|
+
}
|
|
119
|
+
let value = 0n;
|
|
120
|
+
for (const ch of ulid.toUpperCase()) {
|
|
121
|
+
const v = map32.get(ch);
|
|
122
|
+
if (v === void 0) throw new Error(`Invalid ULID char: ${ch}`);
|
|
123
|
+
value = (value << 5n) + BigInt(v);
|
|
124
|
+
}
|
|
125
|
+
const raw = encodeBigInt(value);
|
|
126
|
+
return leftPad(raw, FIXED_LEN_ULID);
|
|
127
|
+
},
|
|
128
|
+
decodeULID(text) {
|
|
129
|
+
if (text.length !== 22) {
|
|
130
|
+
throw new Error("Expected 22-char Base60 ULID");
|
|
131
|
+
}
|
|
132
|
+
const value = decodeToBigInt(text);
|
|
133
|
+
const bytes = bigIntToBytes(value, 16);
|
|
134
|
+
let bits = "";
|
|
135
|
+
for (const b of bytes) {
|
|
136
|
+
bits += b.toString(2).padStart(8, "0");
|
|
137
|
+
}
|
|
138
|
+
const padded = bits.padStart(130, "0");
|
|
139
|
+
let ulid = "";
|
|
140
|
+
for (let i = 0; i < 26; i++) {
|
|
141
|
+
const chunk = padded.slice(i * 5, i * 5 + 5);
|
|
142
|
+
ulid += crockford[parseInt(chunk, 2)];
|
|
143
|
+
}
|
|
144
|
+
return ulid;
|
|
145
|
+
},
|
|
146
|
+
compareAsBigInt(a, b) {
|
|
147
|
+
const ai = decodeToBigInt(a);
|
|
148
|
+
const bi = decodeToBigInt(b);
|
|
149
|
+
if (ai < bi) return -1;
|
|
150
|
+
if (ai > bi) return 1;
|
|
151
|
+
return 0;
|
|
152
|
+
},
|
|
153
|
+
isValidBase60: (text) => {
|
|
154
|
+
for (const ch of text) {
|
|
155
|
+
if (!alphabet.includes(ch)) return false;
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
var base60 = createBase60Codec();
|
|
162
|
+
export {
|
|
163
|
+
base60,
|
|
164
|
+
createBase60Codec
|
|
165
|
+
};
|
package/package.json
CHANGED