~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
137 lines · 3.8 KB Raw History
// 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 };
}