chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* OCI digest and blob CID conversion. Both name the same SHA-256 hash: a
* digest as `sha256:<hex>`, a blob CID as CIDv1 + raw codec + sha256
* multihash. Converting between them is re-encoding, not a lookup.
*/
import { blobCidFromDigest, cidToBytes, cidToString } from '@pdsjs/core/repo';
const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
/**
* @param {string} digest
* @returns {boolean}
*/
export function isValidDigest(digest) {
return DIGEST_PATTERN.test(digest);
}
/**
* @param {string} hex - 64 lowercase hex characters
* @returns {Uint8Array}
*/
function hexToBytes(hex) {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
}
return bytes;
}
/**
* @param {Uint8Array} bytes
* @returns {string}
*/
export function bytesToHex(bytes) {
let hex = '';
for (const byte of bytes) {
hex += byte.toString(16).padStart(2, '0');
}
return hex;
}
/**
* @param {string} digest - `sha256:<hex>`
* @returns {string} base32lower blob CID string
*/
export function digestToCid(digest) {
if (!isValidDigest(digest)) {
throw new Error(`unsupported digest: ${digest}`);
}
return cidToString(blobCidFromDigest(hexToBytes(digest.slice(7))));
}
/**
* @param {string} cid - base32lower blob CID string
* @returns {string} `sha256:<hex>`
*/
export function cidToDigest(cid) {
const bytes = cidToBytes(cid);
// CIDv1 + codec + multihash prefix take the first four bytes; the sha256
// digest is the 32 that follow.
if (bytes.length !== 36 || bytes[2] !== 0x12 || bytes[3] !== 0x20) {
throw new Error(`not a sha256 CID: ${cid}`);
}
return `sha256:${bytesToHex(bytes.subarray(4))}`;
}
/**
* @param {Uint8Array} hashBytes - 32-byte SHA-256 digest
* @returns {string} `sha256:<hex>`
*/
export function digestFromHashBytes(hashBytes) {
return `sha256:${bytesToHex(hashBytes)}`;
}