chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// A space's policy, read off the wire and written back to it.
//
// The policy is an open union. Its `$type` names one of the three baseline
// policies, and its other fields configure that policy.
/** @type {Record<string, string>} */
const POLICY_TYPES = {
personalspace: 'com.atproto.personalspace.policy',
memberspace: 'com.atproto.memberspace.policy',
managedspace: 'com.atproto.managedspace.policy',
};
/** The kinds a page offers, in the order it offers them. */
export const POLICY_KINDS = ['memberspace', 'personalspace', 'managedspace'];
/** @type {Record<string, string>} */
export const POLICY_LABELS = {
personalspace: 'The authority alone',
memberspace: 'Members on a list',
managedspace: 'Whoever the managing app admits',
};
export const ACCESS_KINDS = ['open', 'allowList'];
/** @type {Record<string, string>} */
export const ACCESS_LABELS = {
open: 'Any client',
allowList: 'Clients on an allow list',
};
/**
* @param {any} config - a getSpace answer
* @returns {Record<string, unknown>}
*/
function policyOf(config) {
const policy = config?.policy;
return policy && typeof policy === 'object' ? policy : {};
}
/**
* Which policy governs a space, or null for one this app does not know.
* @param {any} config
* @returns {string|null}
*/
export function policyKind(config) {
const type = policyOf(config).$type;
const found = Object.entries(POLICY_TYPES).find(([, name]) => name === type);
return found ? found[0] : null;
}
/**
* Whether a memberspace lets anyone read, or anyone's writes count.
* @param {any} config
* @param {'read'|'write'} access
* @returns {boolean}
*/
export function isPublic(config, access) {
if (policyKind(config) !== 'memberspace') return false;
const policy = policyOf(config);
return (access === 'read' ? policy.publicRead : policy.publicWrite) === true;
}
/**
* The app a managedspace defers each decision to, or null.
* @param {any} config
* @returns {string|null}
*/
export function managingAppOf(config) {
const app = policyOf(config).managingApp;
return typeof app === 'string' && app ? app : null;
}
/**
* Whether a memberspace restricts the apps that may read it.
* @param {any} config
* @returns {string}
*/
export function accessKind(config) {
return Array.isArray(policyOf(config).allowedApps) ? 'allowList' : 'open';
}
/**
* The client IDs a memberspace allows.
* @param {any} config
* @returns {string[]}
*/
export function allowedOf(config) {
const allowed = policyOf(config).allowedApps;
return Array.isArray(allowed)
? allowed.filter((entry) => typeof entry === 'string')
: [];
}
/**
* Whether the policy asks the authority to publish it, or its member list,
* as records.
* @param {any} config
* @param {'publishPolicy'|'publishMembers'} field
* @returns {boolean}
*/
export function publishes(config, field) {
return policyOf(config)[field] === true;
}
/**
* The policy union to send.
* @param {string} kind
* @param {Object} [fields]
* @param {boolean} [fields.publicRead]
* @param {boolean} [fields.publicWrite]
* @param {string[]|null} [fields.allowedApps] - null lets any app read
* @param {string|null} [fields.managingApp]
* @param {boolean} [fields.publishPolicy]
* @param {boolean} [fields.publishMembers]
* @returns {Record<string, unknown>}
*/
export function policyUnion(kind, fields = {}) {
const $type = POLICY_TYPES[kind];
if (kind === 'memberspace') {
return {
$type,
publicRead: fields.publicRead ?? false,
publicWrite: fields.publicWrite ?? false,
...(fields.allowedApps ? { allowedApps: fields.allowedApps } : {}),
publishPolicy: fields.publishPolicy ?? false,
publishMembers: fields.publishMembers ?? false,
};
}
if (kind === 'managedspace') {
return {
$type,
managingApp: fields.managingApp,
publishPolicy: fields.publishPolicy ?? false,
};
}
return { $type };
}