chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* pkt-line framing for the git smart protocol: each line is a 4-hex-digit
* length (including the prefix itself) followed by payload; '0000' is the
* flush packet that separates sections.
*/
const encoder = new TextEncoder();
const decoder = new TextDecoder();
export const FLUSH = new Uint8Array([0x30, 0x30, 0x30, 0x30]);
/**
* @param {string|Uint8Array} payload
* @returns {Uint8Array}
*/
export function pktLine(payload) {
const bytes = typeof payload === 'string' ? encoder.encode(payload) : payload;
const length = bytes.length + 4;
if (length > 0xffff) throw new Error('pkt-line payload too large');
const out = new Uint8Array(length);
out.set(encoder.encode(length.toString(16).padStart(4, '0')), 0);
out.set(bytes, 4);
return out;
}
/**
* @param {Array<Uint8Array>} parts
* @returns {Uint8Array}
*/
export function concatBytes(parts) {
const total = parts.reduce((sum, p) => sum + p.length, 0);
const out = new Uint8Array(total);
let offset = 0;
for (const part of parts) {
out.set(part, offset);
offset += part.length;
}
return out;
}
/**
* @typedef {{type: 'flush'}|{type: 'line', data: Uint8Array}} Pkt
*/
/**
* Parse a buffer of pkt-lines. Stops cleanly at the end of the buffer;
* throws on a truncated or malformed frame.
* @param {Uint8Array} bytes
* @returns {Pkt[]}
*/
export function parsePkts(bytes) {
/** @type {Pkt[]} */
const pkts = [];
let offset = 0;
while (offset < bytes.length) {
if (offset + 4 > bytes.length) throw new Error('truncated pkt-line');
const lenHex = decoder.decode(bytes.subarray(offset, offset + 4));
const length = Number.parseInt(lenHex, 16);
if (Number.isNaN(length)) throw new Error(`bad pkt-line length ${lenHex}`);
if (length === 0) {
pkts.push({ type: 'flush' });
offset += 4;
continue;
}
if (length < 4 || offset + length > bytes.length) {
throw new Error('malformed pkt-line');
}
pkts.push({
type: 'line',
data: bytes.subarray(offset + 4, offset + length),
});
offset += length;
}
return pkts;
}
/**
* Decode a pkt payload as text with the customary trailing newline removed.
* @param {Uint8Array} data
* @returns {string}
*/
export function pktText(data) {
const text = decoder.decode(data);
return text.endsWith('\n') ? text.slice(0, -1) : text;
}