chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// @pdsjs/spaces/uri - permissioned space AT-URI addressing.
//
// at://{spaceDid}/space/{spaceType}/{skey}[/{authorDid}/{collection}/{rkey}]
//
// Space URIs are distinguished from public ones by the literal `space` segment,
// which contains no dots, whereas a collection NSID always contains at least two.
export const SPACE_MARKER = 'space';
/**
* @param {{spaceDid: string, spaceType: string, skey: string,
* authorDid?: string|null, collection?: string|null, rkey?: string|null}} parts
* @returns {string}
*/
export function formatSpaceUri(parts) {
const { spaceDid, spaceType, skey, authorDid, collection, rkey } = parts;
const optionalCount = [authorDid, collection, rkey].filter(Boolean).length;
if (optionalCount !== 0 && optionalCount !== 3) {
throw new Error(
'authorDid, collection, and rkey must be all present or all absent',
);
}
let uri = `at://${spaceDid}/${SPACE_MARKER}/${spaceType}/${skey}`;
if (authorDid) uri += `/${authorDid}`;
if (collection) uri += `/${collection}`;
if (rkey) uri += `/${rkey}`;
return uri;
}
/**
* @param {string} uri
* @returns {boolean}
*/
export function isSpaceUri(uri) {
const segments = splitUri(uri);
return segments !== null && segments[1] === SPACE_MARKER;
}
/**
* @param {string} uri
* @returns {{spaceDid: string, spaceType: string, skey: string,
* authorDid: string|null, collection: string|null, rkey: string|null}}
*/
export function parseSpaceUri(uri) {
const segments = splitUri(uri);
if (segments === null || segments[1] !== SPACE_MARKER) {
throw new Error(`not a space URI: ${uri}`);
}
const [spaceDid, , spaceType, skey, authorDid, collection, rkey] = segments;
if (segments.length !== 4 && segments.length !== 7) {
throw new Error(`invalid space URI: ${uri}`);
}
if (!spaceDid.startsWith('did:')) {
throw new Error(`space authority must be a DID: ${uri}`);
}
if (!spaceType.includes('.')) {
throw new Error(`space type must be an NSID: ${uri}`);
}
if (!skey) throw new Error(`invalid space URI: ${uri}`);
if (segments.length === 4) {
return {
spaceDid,
spaceType,
skey,
authorDid: null,
collection: null,
rkey: null,
};
}
if (!authorDid.startsWith('did:')) {
throw new Error(`space record author must be a DID: ${uri}`);
}
if (!collection.includes('.')) {
throw new Error(`collection must be an NSID: ${uri}`);
}
if (!rkey) throw new Error(`invalid space URI: ${uri}`);
return { spaceDid, spaceType, skey, authorDid, collection, rkey };
}
/**
* @param {string} uri
* @returns {string[]|null} host followed by path segments, or null if not an at:// URI
*/
function splitUri(uri) {
if (!uri.startsWith('at://')) return null;
const segments = uri.slice('at://'.length).split('/');
if (segments.length < 2 || segments.some((s) => s === '')) return null;
return segments;
}