chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* The archive's on-disk block format, written by hand so a test can serve
* the bytes `getBlock` returns.
*/
import { zstdCompressSync } from 'node:zlib';
import { cborEncodeDagCbor } from '@pdsjs/core/repo';
/**
* The columnar block body exactly as the archive writes it: an event
* count, per-event fixed columns, then the concatenated variable columns.
* @param {Array<{seq: number, kind: number, did: string, collection: string, rkey: string, rev: string, payload: Uint8Array}>} rows
* @returns {Uint8Array}
*/
export function encodeBlockColumns(rows) {
/** @param {number} value @param {1|2|4|8} bytes */
const le = (value, bytes) => {
const out = new Uint8Array(bytes);
const view = new DataView(out.buffer);
if (bytes === 1) view.setUint8(0, value);
if (bytes === 2) view.setUint16(0, value, true);
if (bytes === 4) view.setUint32(0, value, true);
if (bytes === 8) view.setBigUint64(0, BigInt(value), true);
return out;
};
/** @param {string} value @returns {Uint8Array} */
const text = (value) => new TextEncoder().encode(value);
/** @param {Uint8Array[]} parts @returns {Uint8Array} */
const join = (parts) => {
const total = parts.reduce((sum, part) => sum + part.length, 0);
const out = new Uint8Array(total);
let offset = 0;
for (const part of parts) {
out.set(part, offset);
offset += part.length;
}
return out;
};
return join([
le(rows.length, 4),
...rows.map((row) => le(row.seq, 8)),
...rows.map(() => le(0, 8)),
...rows.map(() => le(0, 8)),
...rows.map((row) => le(row.kind, 1)),
...rows.map((row) => le(row.collection.length, 1)),
...rows.map((row) => le(row.did.length, 2)),
...rows.map((row) => le(row.rkey.length, 1)),
...rows.map((row) => le(row.rev.length, 1)),
...rows.map((row) => le(row.payload.length, 4)),
...rows.map((row) => text(row.collection)),
...rows.map((row) => text(row.did)),
...rows.map((row) => text(row.rkey)),
...rows.map((row) => text(row.rev)),
...rows.map((row) => row.payload),
]);
}
/**
* One row, encoded and zstd-compressed into the frame `getBlock` serves.
* @param {Array<{seq: number, kind: number, did: string, collection: string, rkey: string, rev: string, payload?: Record<string, unknown>|Uint8Array}>} rows
* @returns {Uint8Array}
*/
export function blockFrame(rows) {
const encoded = rows.map((row) => ({
...row,
payload:
row.payload instanceof Uint8Array
? row.payload
: cborEncodeDagCbor(row.payload ?? {}),
}));
return zstdCompressSync(Buffer.from(encodeBlockColumns(encoded)));
}