Back to packages list

Vals using multiformats

Description from the NPM package:
Interface for multihash, multicodec, multibase and CID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// SPDX-License-Identifier: 0BSD
import { varint } from "npm:multiformats";
import { base58btc } from "npm:multiformats/bases/base58";
const KeyType = {
"P-256": { name: "P-256", byteSize: 256 / 8 + 1 },
"P-384": { name: "P-384", byteSize: 384 / 8 + 1 },
"secp256k1": { name: "secp256k1", byteSize: 256 / 8 + 1 },
"X25519": { name: "X25519", byteSize: 32 },
"Ed25519": { name: "Ed25519", byteSize: 32 },
"JWK-JCS": { name: "JWK-JCS" },
} as const;
const Codec = {
0x1200: KeyType["P-256"],
0x1201: KeyType["P-384"],
0xe7: KeyType["secp256k1"],
0xec: KeyType["X25519"],
0xed: KeyType["Ed25519"],
0xeb51: KeyType["JWK-JCS"],
} as const;
export function multibaseToBytes(input: string) {
const bytes = base58btc.decode(input);
const [codec, prefixLength] = varint.decode(bytes);
const keyType = Codec[codec];
if (!keyType) throw new TypeError("error: unsupported key type");
const keyBytes = bytes.slice(prefixLength);
if ("byteSize" in keyType && (keyBytes.length !== keyType.byteSize)) {
throw new Error("error: invalid key size");
}
return { keyBytes, keyType: keyType.name };
}
export function bytesToMultibase(keyBytes: Uint8Array, keyType: string) {
const type = KeyType[keyType];
if (!type) throw new TypeError("error: unsupported key type");
if ("byteSize" in type && (keyBytes.length !== type.byteSize)) {
throw new Error("error: invalid key size");
}
const [codecCode] = Object.entries(Codec).find(([, keyType]) => keyType === type);
const codec = parseInt(codecCode, 10);
const prefixLength = varint.encodingLength(codec);
const bytes = new Uint8Array(prefixLength + keyBytes.length);
varint.encodeTo(codec, bytes);
bytes.set(keyBytes, prefixLength);
return base58btc.encode(bytes);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Initially forked from @lukas.sampleFirehose
export let firehoseReadMessagesExample = (async () => {
const cborx = await import("https://deno.land/x/cbor@v1.5.2/index.js");
const multiformats = await import("npm:multiformats");
const uint8arrays = await import("npm:uint8arrays");
const { z } = await import("https://deno.land/x/zod@v3.21.4/mod.ts");
const xrpc = await import("npm:@atproto/xrpc");
const cborCodec = await import(
"https://cdn.jsdelivr.net/npm/@ipld/dag-cbor/+esm"
);
const cborEncode = cborCodec.encode;
enum FrameType {
Message = 1,
Error = -1,
}
const messageFrameHeader = z.object({
op: z.literal(FrameType.Message),
t: z.string().optional(), // Message body type discriminator
});
type MessageFrameHeader = z.infer<typeof messageFrameHeader>;
const errorFrameHeader = z.object({
op: z.literal(FrameType.Error),
});
const errorFrameBody = z.object({
error: z.string(),
message: z.string().optional(), // Error message
});
type ErrorFrameHeader = z.infer<typeof errorFrameHeader>;
type ErrorFrameBody<T extends string = string> = {
error: T;
} & z.infer<typeof errorFrameBody>;
const frameHeader = z.union([messageFrameHeader, errorFrameHeader]);
type FrameHeader = z.infer<typeof frameHeader>;
abstract class Frame {
header: FrameHeader;
body: unknown;
get op(): FrameType {
return this.header.op;
}
toBytes(): Uint8Array {
return uint8arrays.concat([
cborEncode(this.header),
cborEncode(this.body),
]);
}
isMessage(): this is MessageFrame<unknown> {
return this.op === FrameType.Message;
}
isError(): this is ErrorFrame {
return this.op === FrameType.Error;
}
static fromBytes(bytes: Uint8Array) {
const decoded = cborDecodeMulti(bytes);
if (decoded.length > 2) {
throw new Error("Too many CBOR data items in frame");
}
const header = decoded[0];
let body: unknown = kUnset;
if (decoded.length > 1) {
body = decoded[1];
}
const parsedHeader = frameHeader.safeParse(header);
if (!parsedHeader.success) {
throw new Error(`Invalid frame header: ${parsedHeader.error.message}`);
}
if (body === kUnset) {
throw new Error("Missing frame body");
}
const frameOp = parsedHeader.data.op;
if (frameOp === FrameType.Message) {
return new MessageFrame(body, {
type: parsedHeader.data.t,
});
}
else if (frameOp === FrameType.Error) {
const parsedBody = errorFrameBody.safeParse(body);
if (!parsedBody.success) {
throw new Error(
`Invalid error frame body: ${parsedBody.error.message}`,
);
}
return new ErrorFrame(parsedBody.data);
}
else {
const exhaustiveCheck: never = frameOp;
throw new Error(`Unknown frame op: ${exhaustiveCheck}`);
}
}
}
class MessageFrame<T = Record<string, unknown>> extends Frame {
header: MessageFrameHeader;
body: T;
constructor(body: T, opts?: {
type?: string;
}) {
super();
this.header = opts?.type !== undefined
? { op: FrameType.Message, t: opts?.type }
: { op: FrameType.Message };
this.body = body;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
export async function sampleFirehose() {
const cborx = await import("https://deno.land/x/cbor@v1.5.2/index.js");
const multiformats = await import("npm:multiformats");
const uint8arrays = await import("npm:uint8arrays");
const { z } = await import("https://deno.land/x/zod@v3.21.4/mod.ts");
const xrpc = await import("npm:@atproto/xrpc");
const cborCodec = await import(
"https://cdn.jsdelivr.net/npm/@ipld/dag-cbor/+esm"
);
const cborEncode = cborCodec.encode;
enum FrameType {
Message = 1,
Error = -1,
}
const messageFrameHeader = z.object({
op: z.literal(FrameType.Message),
t: z.string().optional(), // Message body type discriminator
});
type MessageFrameHeader = z.infer<typeof messageFrameHeader>;
const errorFrameHeader = z.object({
op: z.literal(FrameType.Error),
});
const errorFrameBody = z.object({
error: z.string(),
message: z.string().optional(), // Error message
});
type ErrorFrameHeader = z.infer<typeof errorFrameHeader>;
type ErrorFrameBody<T extends string = string> = {
error: T;
} & z.infer<typeof errorFrameBody>;
const frameHeader = z.union([messageFrameHeader, errorFrameHeader]);
type FrameHeader = z.infer<typeof frameHeader>;
abstract class Frame {
header: FrameHeader;
body: unknown;
get op(): FrameType {
return this.header.op;
}
toBytes(): Uint8Array {
return uint8arrays.concat([
cborEncode(this.header),
cborEncode(this.body),
]);
}
isMessage(): this is MessageFrame<unknown> {
return this.op === FrameType.Message;
}
isError(): this is ErrorFrame {
return this.op === FrameType.Error;
}
static fromBytes(bytes: Uint8Array) {
const decoded = cborDecodeMulti(bytes);
if (decoded.length > 2) {
throw new Error("Too many CBOR data items in frame");
}
const header = decoded[0];
let body: unknown = kUnset;
if (decoded.length > 1) {
body = decoded[1];
}
const parsedHeader = frameHeader.safeParse(header);
if (!parsedHeader.success) {
throw new Error(`Invalid frame header: ${parsedHeader.error.message}`);
}
if (body === kUnset) {
throw new Error("Missing frame body");
}
const frameOp = parsedHeader.data.op;
if (frameOp === FrameType.Message) {
return new MessageFrame(body, {
type: parsedHeader.data.t,
});
}
else if (frameOp === FrameType.Error) {
const parsedBody = errorFrameBody.safeParse(body);
if (!parsedBody.success) {
throw new Error(
`Invalid error frame body: ${parsedBody.error.message}`,
);
}
return new ErrorFrame(parsedBody.data);
}
else {
const exhaustiveCheck: never = frameOp;
throw new Error(`Unknown frame op: ${exhaustiveCheck}`);
}
}
}
class MessageFrame<T = Record<string, unknown>> extends Frame {
header: MessageFrameHeader;
body: T;
constructor(body: T, opts?: {
type?: string;
}) {
super();
this.header = opts?.type !== undefined
? { op: FrameType.Message, t: opts?.type }
: { op: FrameType.Message };
this.body = body;
}
1
Next